Home | History | Annotate | Download | only in listviewanimations
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.listviewanimations;
     18 
     19 import java.util.ArrayList;
     20 import java.util.HashMap;
     21 import java.util.List;
     22 
     23 import android.animation.Animator;
     24 import android.animation.AnimatorListenerAdapter;
     25 import android.animation.ObjectAnimator;
     26 import android.app.Activity;
     27 import android.content.Context;
     28 import android.os.Bundle;
     29 import android.view.View;
     30 import android.widget.AdapterView;
     31 import android.widget.ArrayAdapter;
     32 import android.widget.CheckBox;
     33 import android.widget.ListView;
     34 
     35 /**
     36  * This example shows how animating ListView items can lead to problems as views are recycled,
     37  * and how to perform these types of animations correctly with new API added in Jellybean.
     38  *
     39  * Watch the associated video for this demo on the DevBytes channel of developer.android.com
     40  * or on YouTube at https://www.youtube.com/watch?v=8MIfSxgsHIs.
     41  */
     42 public class ListViewAnimations extends Activity {
     43 
     44     @Override
     45     protected void onCreate(Bundle savedInstanceState) {
     46         super.onCreate(savedInstanceState);
     47         setContentView(R.layout.activity_list_view_animations);
     48 
     49         final CheckBox vpaCB = (CheckBox) findViewById(R.id.vpaCB);
     50         final CheckBox setTransientStateCB = (CheckBox) findViewById(R.id.setTransientStateCB);
     51         final ListView listview = (ListView) findViewById(R.id.listview);
     52         final ArrayList<String> cheeseList = new ArrayList<String>();
     53         for (int i = 0; i < Cheeses.sCheeseStrings.length; ++i) {
     54             cheeseList.add(Cheeses.sCheeseStrings[i]);
     55         }
     56         final StableArrayAdapter adapter = new StableArrayAdapter(this,
     57                 android.R.layout.simple_list_item_1, cheeseList);
     58         listview.setAdapter(adapter);
     59 
     60         listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
     61 
     62             @Override
     63             public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
     64                 final String item = (String) parent.getItemAtPosition(position);
     65                 if (vpaCB.isChecked()) {
     66                     view.animate().setDuration(1000).alpha(0).
     67                     withEndAction(new Runnable() {
     68                         @Override
     69                         public void run() {
     70                             cheeseList.remove(item);
     71                             adapter.notifyDataSetChanged();
     72                             view.setAlpha(1);
     73                         }
     74                     });
     75                 } else {
     76                     // Here's where the problem starts - this animation will animate a View object.
     77                     // But that View may get recycled if it is animated out of the container,
     78                     // and the animation will continue to fade a view that now contains unrelated
     79                     // content.
     80                     ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, 0);
     81                     anim.setDuration(1000);
     82                     if (setTransientStateCB.isChecked()) {
     83                         // Here's the correct way to do this: if you tell a view that it has
     84                         // transientState, then ListView ill avoid recycling it until the
     85                         // transientState flag is reset.
     86                         // A different approach is to use ViewPropertyAnimator, which sets the
     87                         // transientState flag internally.
     88                         view.setHasTransientState(true);
     89                     }
     90                     anim.addListener(new AnimatorListenerAdapter() {
     91                         @Override
     92                         public void onAnimationEnd(Animator animation) {
     93                             cheeseList.remove(item);
     94                             adapter.notifyDataSetChanged();
     95                             view.setAlpha(1);
     96                             if (setTransientStateCB.isChecked()) {
     97                                 view.setHasTransientState(false);
     98                             }
     99                         }
    100                     });
    101                     anim.start();
    102 
    103                 }
    104             }
    105 
    106         });
    107     }
    108 
    109     private class StableArrayAdapter extends ArrayAdapter<String> {
    110 
    111         HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();
    112 
    113         public StableArrayAdapter(Context context, int textViewResourceId,
    114                 List<String> objects) {
    115             super(context, textViewResourceId, objects);
    116             for (int i = 0; i < objects.size(); ++i) {
    117                 mIdMap.put(objects.get(i), i);
    118             }
    119         }
    120 
    121         @Override
    122         public long getItemId(int position) {
    123             String item = getItem(position);
    124             return mIdMap.get(item);
    125         }
    126 
    127         @Override
    128         public boolean hasStableIds() {
    129             return true;
    130         }
    131 
    132     }
    133 
    134 }
    135