| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.example.android.listviewdeletion; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| import android.app.Activity; |
| import android.content.Context; |
| import android.os.Bundle; |
| import android.util.SparseBooleanArray; |
| import android.view.View; |
| import android.widget.AdapterView; |
| import android.widget.ArrayAdapter; |
| import android.widget.Button; |
| import android.widget.CheckBox; |
| import android.widget.ListView; |
| |
| /** |
| * This example shows how animating ListView views can lead to artifacts if those views are |
| * recycled before you animate them. |
| * |
| * Watch the associated video for this demo on the DevBytes channel of developer.android.com |
| * or on YouTube at https://www.youtube.com/watch?v=NewCSg2JKLk. |
| */ |
| public class ListViewDeletion extends Activity { |
| |
| final ArrayList<View> mCheckedViews = new ArrayList<View>(); |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.activity_list_view_deletion); |
| |
| final Button deleteButton = (Button) findViewById(R.id.deleteButton); |
| final CheckBox usePositionsCB = (CheckBox) findViewById(R.id.usePositionsCB); |
| final ListView listview = (ListView) findViewById(R.id.listview); |
| final ArrayList<String> cheeseList = new ArrayList<String>(); |
| for (int i = 0; i < Cheeses.sCheeseStrings.length; ++i) { |
| cheeseList.add(Cheeses.sCheeseStrings[i]); |
| } |
| final StableArrayAdapter adapter = new StableArrayAdapter(this, |
| android.R.layout.simple_list_item_multiple_choice, cheeseList); |
| listview.setAdapter(adapter); |
| listview.setItemsCanFocus(false); |
| listview.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); |
| |
| // Clicking the delete button fades out the currently selected views |
| deleteButton.setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| SparseBooleanArray checkedItems = listview.getCheckedItemPositions(); |
| int numCheckedItems = checkedItems.size(); |
| for (int i = numCheckedItems - 1; i >= 0; --i) { |
| if (!checkedItems.valueAt(i)) { |
| continue; |
| } |
| int position = checkedItems.keyAt(i); |
| final String item = adapter.getItem(position); |
| if (!usePositionsCB.isChecked()) { |
| // Remove the actual data after the time period that we're going to run |
| // the fading animation |
| v.postDelayed(new Runnable() { |
| @Override |
| public void run() { |
| adapter.remove(item); |
| } |
| }, 300); |
| } else { |
| // This is the correct way to do this: first wee whether the item is |
| // actually visible, and don't bother animating it if it's not. |
| // Next, get the view associated with the item at the time of deletion |
| // (not some old view chosen when the item was clicked). |
| mCheckedViews.clear(); |
| int positionOnScreen = position - listview.getFirstVisiblePosition(); |
| if (positionOnScreen >= 0 && |
| positionOnScreen < listview.getChildCount()) { |
| final View view = listview.getChildAt(positionOnScreen); |
| // All set to fade this view out. Using ViewPropertyAnimator accounts |
| // for possible recycling of views during the animation itself |
| // (see the ListViewAnimations example for more on this). |
| view.animate().alpha(0).withEndAction(new Runnable() { |
| @Override |
| public void run() { |
| view.setAlpha(1); |
| adapter.remove(item); |
| } |
| }); |
| } else { |
| // Not animating the view, but don't delete it yet to avoid making the |
| // list shift due to offscreen deletions |
| v.postDelayed(new Runnable() { |
| @Override |
| public void run() { |
| adapter.remove(item); |
| } |
| }, 300); |
| } |
| } |
| } |
| // THIS IS THE WRONG WAY TO DO THIS |
| // We're basing our decision of the views to be animated based on outdated |
| // information at selection time. Then we're going ahead and running an animation |
| // on those views even when the selected items might not even be in view (in which |
| // case we'll probably be mistakenly fading out something else that is on the |
| // screen and is re-using that recycled view). |
| for (int i = 0; i < mCheckedViews.size(); ++i) { |
| final View checkedView = mCheckedViews.get(i); |
| checkedView.animate().alpha(0).withEndAction(new Runnable() { |
| @Override |
| public void run() { |
| checkedView.setAlpha(1); |
| } |
| }); |
| } |
| mCheckedViews.clear(); |
| adapter.notifyDataSetChanged(); |
| } |
| }); |
| |
| listview.setOnItemClickListener(new AdapterView.OnItemClickListener() { |
| |
| @Override |
| public void onItemClick(AdapterView<?> parent, final View view, int position, long id) { |
| boolean checked = listview.isItemChecked(position); |
| if (checked) { |
| mCheckedViews.add(view); |
| } else { |
| mCheckedViews.remove(view); |
| } |
| } |
| }); |
| } |
| |
| private class StableArrayAdapter extends ArrayAdapter<String> { |
| |
| HashMap<String, Integer> mIdMap = new HashMap<String, Integer>(); |
| |
| public StableArrayAdapter(Context context, int textViewResourceId, |
| List<String> objects) { |
| super(context, textViewResourceId, objects); |
| for (int i = 0; i < objects.size(); ++i) { |
| mIdMap.put(objects.get(i), i); |
| } |
| } |
| |
| @Override |
| public long getItemId(int position) { |
| String item = getItem(position); |
| return mIdMap.get(item); |
| } |
| |
| @Override |
| public boolean hasStableIds() { |
| return true; |
| } |
| |
| } |
| |
| } |