鍍金池/ 教程/ Android/ 自定義控件三部曲之動(dòng)畫(huà)篇(十三)——實(shí)現(xiàn)ListView Item進(jìn)入動(dòng)畫(huà)
聯(lián)合動(dòng)畫(huà)的 XML 實(shí)現(xiàn)與使用示例
Interpolator 插值器
高級(jí)進(jìn)階(二)
ObjectAnimator 基本使用
ValueAnimator 基本使用
alpha、scale、translate、rotate、set 的 xml 屬性及用法
PropertyValuesHolder 與 Keyframe
layoutAnimation 與 gridLayoutAnimation
自定義控件三部曲之動(dòng)畫(huà)篇(十三)——實(shí)現(xiàn)ListView Item進(jìn)入動(dòng)畫(huà)
自定義控件三部曲之動(dòng)畫(huà)篇(十二)——animateLayoutChanges與LayoutTransition
高級(jí)進(jìn)階(一)
代碼生成 alpha、scale、translate、rotate、set 及插值器動(dòng)畫(huà)
聯(lián)合動(dòng)畫(huà)的代碼實(shí)現(xiàn)

自定義控件三部曲之動(dòng)畫(huà)篇(十三)——實(shí)現(xiàn)ListView Item進(jìn)入動(dòng)畫(huà)

前面兩篇我們講解了使用layoutAnimation和LayoutTransition實(shí)現(xiàn)ViewGroup中Item加載動(dòng)畫(huà)的方法,但他們都各自存在問(wèn)題:

layoutAnimation雖然是API 1中就已經(jīng)引入,但只能在動(dòng)畫(huà)初次創(chuàng)建時(shí)才能使用指定動(dòng)畫(huà)??丶?chuàng)建以后,再往ViewGroup里加Item就不會(huì)再有動(dòng)畫(huà)。這顯然是不合適的!

LayoutTransition能夠?qū)崿F(xiàn)無(wú)論何時(shí)往ViewGroup中添加控件都可以給其中控件使用動(dòng)畫(huà)。但最大的問(wèn)題是,它的API等級(jí)是11。而且也沒(méi)有兼容包可供我們使用這個(gè)函數(shù)。

這樣問(wèn)題就來(lái)了,如果我們想在兼容API 8以上的機(jī)型,完成ListView中各個(gè)Item進(jìn)入時(shí)都添加動(dòng)畫(huà),這要怎么來(lái)做呢?

今天我們要完成的效果圖如下:

http://wiki.jikexueyuan.com/project/android-animation/images/20160327211048457.gif" alt="" />

從效果圖中可以看到,當(dāng)每個(gè)Item進(jìn)入的時(shí)候,都添加了動(dòng)畫(huà)。前面我們說(shuō)了layoutAnimation和LayoutTransition所存在的問(wèn)題,那拋開(kāi)這兩個(gè)函數(shù),我們要如何實(shí)現(xiàn)Item進(jìn)入動(dòng)畫(huà)呢?

別忘了,ListView在得到每個(gè)Item時(shí)會(huì)調(diào)用BaseAdapter的getView方法!getView中每一個(gè)convertView就是當(dāng)前要顯示的Item所對(duì)應(yīng)的View,所以我們直接對(duì)convertView添加動(dòng)畫(huà)不就好了。

上面的原理理解起來(lái)并不難,下面我們就看看如何實(shí)現(xiàn)的吧。

搭框架

這部分,我們主要是先搭出來(lái)要實(shí)現(xiàn)的框架,把ListView填充起來(lái),效果如下:

http://wiki.jikexueyuan.com/project/android-animation/images/20160327211257367.gif" alt="" />

這里實(shí)現(xiàn)的效果就是把listview填充起來(lái),總共用了九張圖片,listview列表循環(huán)顯示這九張圖片,但我在每個(gè)圖片上顯示了當(dāng)前item所在的位置。

好了,下面就來(lái)看代碼吧

item布局(item_layout.xml)

我們先來(lái)看看listview的Item是怎么布局的:

<?xml version="1.0" encoding="utf-8"?>  
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
              android:orientation="vertical"  
              android:layout_width="fill_parent"  
              android:layout_height="wrap_content">  
    <ImageView  
            android:id="@+id/img"  
            android:layout_width="fill_parent"  
            android:layout_height="250dp"  
            android:scaleType="centerCrop"  
            android:layout_margin="5dp"  
            android:layout_gravity="center"/>  

    <TextView  
            android:id="@+id/text"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:textSize="20dp"  
            android:layout_gravity="center"/>  

</FrameLayout>  

代碼很好理解,從效果圖中也可以看出,底部一個(gè)imageview,中間一個(gè)文字來(lái)表示當(dāng)前item所在的位置。

ListAdapter

這里就是ListView的Adapter的代碼位置了,完整的代碼如下,然后再細(xì)講:

public class ListAdapter extends BaseAdapter {  
    private List<Drawable> mDrawableList = new ArrayList<>();  
    private int mLength = 0;  
    private LayoutInflater mInflater;  
    private Context mContext;  
    private ListView mListView;  

    public ListAdapter(Context context, ListView listView, List<Drawable> drawables, int length) {  
        mDrawableList.addAll(drawables);  
        mLength = length;  
        mInflater = LayoutInflater.from(context);  
        mContext = context;  
        mListView = listView;  
    }  

    @Override  
    public int getCount() {  
        return mLength;  
    }  

    @Override  
    public Object getItem(int position) {  
        return mDrawableList.get(position % mDrawableList.size());  
    }  

    @Override  
    public long getItemId(int position) {  
        return position;  
    }  

    @Override  
    public View getView(int position, View convertView, ViewGroup parent) {  
        ViewHolder holder = null;  

        if (convertView == null) {  

            holder = new ViewHolder();  
            convertView = mInflater.inflate(R.layout.item_layout, null);  
            holder.mImageView = (ImageView) convertView.findViewById(R.id.img);  
            holder.mTextView = (TextView) convertView.findViewById(R.id.text);  

        } else {  
            holder = (ViewHolder) convertView.getTag();  
        }  

        convertView.setTag(holder);  

        holder.mImageView.setImageDrawable(mDrawableList.get(position % mDrawableList.size()));  
        holder.mTextView.setText(position+"");  

        return convertView;  
    }  

    public class ViewHolder {  
        public ImageView mImageView;  
        public TextView mTextView;  
    }  
}  

首先是構(gòu)造函數(shù):

public ListAdapter(Context context, ListView listView, List<Drawable> drawables, int length) {  
    mDrawableList.addAll(drawables);  
    mLength = length;  
    mInflater = LayoutInflater.from(context);  
    mContext = context;  
    mListView = listView;  
}  

首先是傳進(jìn)來(lái)的幾個(gè)參數(shù),List drawables是listview要循環(huán)顯示的圖片的Drawable對(duì)象列表,length表示當(dāng)前l(fā)istview要顯示多少行??赡苡行┩瑢W(xué)會(huì)注意到,我們還把ListView對(duì)象給傳進(jìn)來(lái)了,然后在上面的代碼中并沒(méi)有用到,其中把listview封裝進(jìn)Adapter是一個(gè)好習(xí)慣,因?yàn)槲覀兛赡軙?huì)在Adapter中監(jiān)聽(tīng)listview的狀態(tài)從而改變item的顯示情況。我們這里目前還沒(méi)有用到Listview

然后是getItem函數(shù):

@Override  
public Object getItem(int position) {  
    return mDrawableList.get(position % mDrawableList.size());  
}  

我們知道本身的BaseAdapter并沒(méi)有使用getItem(int position)函數(shù),重寫(xiě)這個(gè)函數(shù)是為了讓我們?cè)贐aseAdapter實(shí)例中,可以通過(guò)getItem來(lái)獲取我們想要的實(shí)例(類(lèi)似下面這樣):

ListView listView = (ListView)findViewById(R.id.list);  
final ListAdapter adapter = new ListAdapter(this,listView,drawables,300);  
listView.setAdapter(adapter);  
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {  
    @Override  
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {  
        //關(guān)鍵在這里哦  
        Drawable drawable = (Drawable) adapter.getItem(position);  
    }  
});  

這里我們將當(dāng)前位置所對(duì)應(yīng)的圖片Drawable傳過(guò)去:

public Object getItem(int position) {  
    return mDrawableList.get(position % mDrawableList.size());  
}  

大家可能會(huì)疑問(wèn):為什么要用position % mDrawableList.size()來(lái)得到當(dāng)前圖片在圖片列表中的索引?因?yàn)槲覀兪茄h(huán)顯示的圖片的,比如我們總共有九張圖片,那當(dāng)前是第12個(gè)item時(shí),顯示的應(yīng)當(dāng)是第3張圖了,position的值為12(因?yàn)锳dapter的position是從0開(kāi)始的),所以12%9 = 3;這就對(duì)上了。理解不了的同學(xué),多拿幾個(gè)數(shù)來(lái)算算,比如當(dāng)顯示到第15張時(shí),position是多少,對(duì)應(yīng)的圖片應(yīng)該是哪一張呢?

最后getView()函數(shù)

public View getView(int position, View convertView, ViewGroup parent) {  
   ViewHolder holder = null;  

   if (convertView == null) {  

       holder = new ViewHolder();  
       convertView = mInflater.inflate(R.layout.item_layout, null);  
       holder.mImageView = (ImageView) convertView.findViewById(R.id.img);  
       holder.mTextView = (TextView) convertView.findViewById(R.id.text);  
   } else {  
       holder = (ViewHolder) convertView.getTag();  
   }  

   convertView.setTag(holder);  

   holder.mImageView.setImageDrawable(mDrawableList.get(position % mDrawableList.size()));  
   holder.mTextView.setText(position+"");  

   return convertView;  
}  

這段代碼就不用我講了吧,就是基本的getView用法,如果大家不理解為什么要利用 convertView.setTag(holder);的方式來(lái)重復(fù)使用convertView,可以參考這篇文章《BaseAdapter——convertView回收機(jī)制與動(dòng)態(tài)控件響應(yīng)》 在理解了convertview回收機(jī)制以后,這里最難的地方應(yīng)該就是賦值的位置了:

holder.mImageView.setImageDrawable(mDrawableList.get(position % mDrawableList.size()));  
holder.mTextView.setText(position+"");  

根據(jù)位置找到圖片的drawable我們上面已經(jīng)講過(guò)了,這也就沒(méi)什么難度了,就不再細(xì)講了。

主布局(main.xml)

主布局非常簡(jiǎn)單,就只有一個(gè)ListView控件:

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
              android:orientation="vertical"  
              android:layout_width="fill_parent"  
              android:layout_height="fill_parent"  
              android:background="#ffffff">  

    <ListView  
            android:id="@+id/list"  
            android:layout_width="fill_parent"  
            android:layout_height="fill_parent"/>  
</LinearLayout>  

MyActivity.java

最后是在MyActivity中構(gòu)造ListAdapter并設(shè)置進(jìn)listview的過(guò)程了:

public class MyActivity extends Activity {  

    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  

        List<Drawable> drawables = new ArrayList<>();  
        drawables.add(getResources().getDrawable(R.drawable.pic1));  
        drawables.add(getResources().getDrawable(R.drawable.pic2));  
        drawables.add(getResources().getDrawable(R.drawable.pic3));  
        drawables.add(getResources().getDrawable(R.drawable.pic4));  
        drawables.add(getResources().getDrawable(R.drawable.pic5));  
        drawables.add(getResources().getDrawable(R.drawable.pic6));  
        drawables.add(getResources().getDrawable(R.drawable.pic7));  
        drawables.add(getResources().getDrawable(R.drawable.pic8));  
        drawables.add(getResources().getDrawable(R.drawable.pic9));  

        ListView listView = (ListView)findViewById(R.id.list);  
        ListAdapter adapter = new ListAdapter(this,listView,drawables,300);  
        listView.setAdapter(adapter);  
    }  
}  

這段代碼很簡(jiǎn)單了,就是先構(gòu)造圖片所對(duì)應(yīng)的Drawable列表,然后構(gòu)造ListAdapter實(shí)例,最后設(shè)置進(jìn)Listview將其顯示出來(lái),沒(méi)什么難度,也沒(méi)什么好講了。

到這里,我們listview就構(gòu)造完成了。下面就看如何向其中的item添加動(dòng)畫(huà)的環(huán)節(jié)了。

Item添加動(dòng)畫(huà)——初步實(shí)現(xiàn)

動(dòng)畫(huà)文件(bottom_in_anim.xml)

先定義從底部進(jìn)入的動(dòng)畫(huà):

<?xml version="1.0" encoding="utf-8"?>  
<set xmlns:android="http://schemas.android.com/apk/res/android"  
     android:duration="1000">  
    <translate android:fromYDelta="100%" android:toYDelta="0"/>  
    <alpha android:fromAlpha="0" android:toAlpha="1"/>  
</set>  

這段動(dòng)畫(huà)不難理解,效果是從底部進(jìn)入,alpha值從0變到1;

在Adapter中添加動(dòng)畫(huà)代碼

首先,我們?cè)贚istAdapter中初始化的時(shí)候,加載動(dòng)畫(huà):

public class ListAdapter extends BaseAdapter {  
    …………  
    private Animation animation;  

    public ListAdapter(Context context, ListView listView, List<Drawable> drawables, int length) {  
        …………  
        animation = AnimationUtils.loadAnimation(mContext,R.anim.bottom_in_anim);  
    }  
    …………  
} 

然后在getView的時(shí)候?yàn)槊總€(gè)convertView添加上動(dòng)畫(huà)

public View getView(int position, View convertView, ViewGroup parent) {  
   ViewHolder holder = null;  

   if (convertView == null) {  

       holder = new ViewHolder();  
       convertView = mInflater.inflate(R.layout.item_layout, null);  
       holder.mImageView = (ImageView) convertView.findViewById(R.id.img);  
       holder.mTextView = (TextView) convertView.findViewById(R.id.text);  
   } else {  
       holder = (ViewHolder) convertView.getTag();  
   }  
   convertView.startAnimation(animation);  
   convertView.setTag(holder);  

   holder.mImageView.setImageDrawable(mDrawableList.get(position % mDrawableList.size()));  
   holder.mTextView.setText(position+"");  

   return convertView;  
}  

讓一個(gè)view開(kāi)始動(dòng)畫(huà)非常簡(jiǎn)單,只需要調(diào)用convertView.startAnimation(animation);即可;這樣就可以實(shí)現(xiàn)在構(gòu)造item的時(shí)候就開(kāi)始動(dòng)畫(huà)

ListAdapter完整代碼如下

public class ListAdapter extends BaseAdapter {  
    private List<Drawable> mDrawableList = new ArrayList<>();  
    private int mLength = 0;  
    private LayoutInflater mInflater;  
    private Context mContext;  
    private ListView mListView;  
    private Animation animation;  

    public ListAdapter(Context context, ListView listView, List<Drawable> drawables, int length) {  
        mDrawableList.addAll(drawables);  
        mLength = length;  
        mInflater = LayoutInflater.from(context);  
        mContext = context;  
        mListView = listView;  
        animation = AnimationUtils.loadAnimation(mContext,R.anim.bottom_in_anim);  
    }  

    @Override  
    public int getCount() {  
        return mLength;  
    }  

    @Override  
    public Object getItem(int position) {  
        return mDrawableList.get(position % mDrawableList.size());  
    }  

    @Override  
    public long getItemId(int position) {  
        return position;  
    }  

    @Override  
    public View getView(int position, View convertView, ViewGroup parent) {  
        ViewHolder holder = null;  

        if (convertView == null) {  

            holder = new ViewHolder();  
            convertView = mInflater.inflate(R.layout.item_layout, null);  
            holder.mImageView = (ImageView) convertView.findViewById(R.id.img);  
            holder.mTextView = (TextView) convertView.findViewById(R.id.text);  
        } else {  
            holder = (ViewHolder) convertView.getTag();  
        }  
        convertView.startAnimation(animation);  
        convertView.setTag(holder);  

        holder.mImageView.setImageDrawable(mDrawableList.get(position % mDrawableList.size()));  
        holder.mTextView.setText(position+"");  

        return convertView;  
    }  

    public class ViewHolder {  
        public ImageView mImageView;  
        public TextView mTextView;  
    }  
}  

效果圖為:

http://wiki.jikexueyuan.com/project/android-animation/images/20160327212235020.gif" alt="" />

從效果圖中可以看出,我們初步實(shí)現(xiàn)了在item生成的時(shí)候添加進(jìn)入動(dòng)畫(huà)

優(yōu)化

上面雖然解決了進(jìn)入時(shí)添加動(dòng)畫(huà)的問(wèn)題,但仔細(xì)的同學(xué)可以看出,在這個(gè)效果圖中還存在幾個(gè)問(wèn)題,可能上面的效果圖還看不清楚具體存在的問(wèn)題

  • 如果上拉的時(shí)候,一下上拉了幾個(gè)item,那些要顯示的item會(huì)一起從底部出現(xiàn)
  • 在下拉的時(shí)候,上部出現(xiàn)的item也會(huì)應(yīng)用上動(dòng)畫(huà)

首先,解決第二個(gè)問(wèn)題比較簡(jiǎn)單,只需要判斷當(dāng)前手指是上滑還上下滑就可以了,只有當(dāng)手指向上滑的時(shí)候,才對(duì)底部出現(xiàn)的item添加上入場(chǎng)動(dòng)畫(huà),其它時(shí)間不添加動(dòng)畫(huà)即可 第一個(gè)問(wèn)題其實(shí)也比較容易解決,我們可以通過(guò)listview.getChildCount()得到當(dāng)前ListView中有多少個(gè)item。我們只給最后一個(gè)添加動(dòng)畫(huà)即可。其它的就直接顯示就好了。

上下滑動(dòng)問(wèn)題

首先,我們首先解決上下滑動(dòng)問(wèn)題,這個(gè)問(wèn)題其實(shí)比較好解決,只需要監(jiān)聽(tīng)listview的OnScrollListener,根據(jù)它判斷出當(dāng)前ListView是上滑還是下滑就可以了。

先看我們?cè)趏nScrollListner中都有哪些參數(shù):

AbsListView.OnScrollListener mOnScrollListener = new AbsListView.OnScrollListener() {  
    @Override  
    public void onScrollStateChanged(AbsListView view, int scrollState) {  

    }  

    @Override  
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {  
    }  
};  

我們主要關(guān)注onScroll的監(jiān)聽(tīng),

onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)  

有四個(gè)參數(shù):

  • 第一個(gè)參數(shù)AbsListView view:是當(dāng)前l(fā)istview的對(duì)象
  • 第二個(gè)參數(shù)int firstVisibleItem:表示當(dāng)前第一個(gè)可見(jiàn)的item在listview所有item中的索引,這里需要非常注意,firstVisibleItem與getChildAt(int position)中的參數(shù)position的意義不同,firstVisibleItem是指在整個(gè)ListView中的位置。而getChildAt(int position)中參數(shù)position傳的是當(dāng)前屏幕顯示區(qū)域中item的索引,屏幕中第一個(gè)item的view可以通過(guò)getChildAt(0)得到。
  • 第三個(gè)參數(shù)int visibleItemCount:表示當(dāng)前屏幕中可見(jiàn)的有幾條item
  • 第四個(gè)參數(shù)int totalItemCount:表示當(dāng)前l(fā)istview總共有多少條item,得到的值與adapter.getCount()的值相同。

在理解了上面四個(gè)參數(shù)以后,我們?cè)賮?lái)看看下移的情況;

向下移動(dòng)包括兩種情況:

第一:屏幕中第一個(gè)item或前幾個(gè)item一起移出屏幕,在這種情況下,我們只需要判斷firstVisibleItem是否比上次的值大即可。即第一個(gè)顯示的item是不是已經(jīng)向下移了

public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {  
    /** 
     * firstVisibleItem > mFirstPosition表示向下滑動(dòng)一個(gè)或多個(gè)Item 
     */  
    isScrollDown = firstVisibleItem > mFirstPosition;  
    mFirstPosition = firstVisibleItem;  
}  

第二:可能用戶并沒(méi)有一次性移一整條item,而是僅讓當(dāng)前item向上移了一點(diǎn)點(diǎn)。這里,由于當(dāng)前可見(jiàn)的第一個(gè)item的位置仍然是firstVisibleItem,只是它的top值變了。

如下圖:

http://wiki.jikexueyuan.com/project/android-animation/images/20160327212539122.png" alt="" />

在這個(gè)效果圖中,只是將第一個(gè)item向上移動(dòng)了一點(diǎn)點(diǎn),第一個(gè)item左上角的坐標(biāo)從(0,-100)變成了(0,-200);所以從這個(gè)圖中,我們可以得到如何計(jì)算在這種情況下是否上移。

@Override  
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {  
   View firstChild = view.getChildAt(0);  
   if (firstChild == null) return;  
   int top = firstChild.getTop();  
   /** 
    * mFirstTop > top表示在當(dāng)前這個(gè)item中滑動(dòng) 
    */  
   isScrollDown = mFirstTop > top;  
   mFirstTop = top;  
}

只需要判斷當(dāng)前第一個(gè)item的左上角坐標(biāo)是不是變小了即可。這里需要注意,得到屏幕中顯示的第一個(gè)Item是通過(guò)ListView.getChildAt(int position)函數(shù)得到的。我們上面已經(jīng)講到,ListView.getChildAt(int position)中參數(shù)position表示的是當(dāng)前item所在屏幕顯示區(qū)域中的索引,屏幕中第一個(gè)item的索引是0 所以我們將這兩種情況進(jìn)行合并,得到完整的onScrollListener:

AbsListView.OnScrollListener mOnScrollListener = new AbsListView.OnScrollListener() {  
    @Override  
    public void onScrollStateChanged(AbsListView view, int scrollState) {  

    }  

    @Override  
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {  
        View firstChild = view.getChildAt(0);  
        if (firstChild == null) return;  
        int top = firstChild.getTop();  
        /** 
         * firstVisibleItem > mFirstPosition表示向下滑動(dòng)一整個(gè)Item 
         * mFirstTop > top表示在當(dāng)前這個(gè)item中滑動(dòng) 
         */  
        isScrollDown = firstVisibleItem > mFirstPosition || mFirstTop > top;  
        mFirstTop = top;  
        mFirstPosition = firstVisibleItem;  
    }  
};  

然后在getView的時(shí)候,判斷如果是向下滑動(dòng),就添加動(dòng)畫(huà)

@Override  
public View getView(int position, View convertView, ViewGroup parent) {  
   ViewHolder holder = null;  

   if (convertView == null) {  

       holder = new ViewHolder();  
       convertView = mInflater.inflate(R.layout.item_layout, null);  
       holder.mImageView = (ImageView) convertView.findViewById(R.id.img);  
       holder.mTextView = (TextView) convertView.findViewById(R.id.text);  
   } else {  
       holder = (ViewHolder) convertView.getTag();  
   }  

   if (isScrollDown) {  
       convertView.startAnimation(animation);  
   }  
   convertView.setTag(holder);  

   holder.mImageView.setImageDrawable(mDrawableList.get(position % mDrawableList.size()));  
   holder.mTextView.setText(position + "");  

   return convertView;  
}  

多個(gè)item同時(shí)動(dòng)畫(huà)問(wèn)題

由于我們只能在Item生成時(shí)給這個(gè)Item添加動(dòng)畫(huà),所以要解決多個(gè)item同時(shí)移動(dòng)的問(wèn)題,我們只能給最后一個(gè)Item添加動(dòng)畫(huà),其它item不給他們添加;但我們?cè)趺粗喇?dāng)前這個(gè)item是不是要顯示的最后一個(gè)item呢?無(wú)法得各,所以一個(gè)中轉(zhuǎn)方案是,在每一個(gè)item在添加動(dòng)畫(huà)前,都把當(dāng)前顯示區(qū)域內(nèi)所有item動(dòng)畫(huà)給取消,然后給當(dāng)前convertView添加上動(dòng)畫(huà);當(dāng)listview滾動(dòng)到最后一個(gè)Item的時(shí)候,自然,同樣也是先把所有動(dòng)畫(huà)取消,然后給他自己添加上動(dòng)畫(huà),所以這樣看起來(lái)就好像是只給他自己添加了動(dòng)畫(huà),之前滾動(dòng)的item是沒(méi)有動(dòng)畫(huà)的。 代碼如下:

public View getView(int position, View convertView, ViewGroup parent) {  
    ViewHolder holder = null;  

    if (convertView == null) {  

        holder = new ViewHolder();  
        convertView = mInflater.inflate(R.layout.item_layout, null);  
        holder.mImageView = (ImageView) convertView.findViewById(R.id.img);  
        holder.mTextView = (TextView) convertView.findViewById(R.id.text);  
    } else {  
        holder = (ViewHolder) convertView.getTag();  
    }  

    //清除當(dāng)前顯示區(qū)域中所有item的動(dòng)畫(huà)  
    for (int i=0;i<mListView.getChildCount();i++){  
        View view = mListView.getChildAt(i);  
        view.clearAnimation();  
    }  
    //然后給當(dāng)前item添加上動(dòng)畫(huà)  
    if (isScrollDown) {  
        convertView.startAnimation(animation);  
    }  
    convertView.setTag(holder);  

    holder.mImageView.setImageDrawable(mDrawableList.get(position % mDrawableList.size()));  
    holder.mTextView.setText(position + "");  

    return convertView;  
} 

在這段代碼中,相比上面的代碼,我們只添加了一段語(yǔ)句:

for (int i=0;i<mListView.getChildCount();i++){  
    View view = mListView.getChildAt(i);  
    view.clearAnimation();  
}  

上面我們講了mListView.getChildAt(int position)的用法,說(shuō)到它的參數(shù)position表示的是在當(dāng)前屏幕顯示區(qū)域中當(dāng)前item的索引。所以在當(dāng)前屏幕中第一個(gè)item的索引是0;而mListView.getChildCount()則表示當(dāng)前屏幕顯示區(qū)域中,總共有多少個(gè)item。所以我們利用上面的代碼,對(duì)屏幕顯示區(qū)域中每個(gè)item進(jìn)行索引,然后取消他們的動(dòng)畫(huà)即可。

此時(shí)ListAdapter完整的代碼為:

public class ListAdapter extends BaseAdapter {  
    private List<Drawable> mDrawableList = new ArrayList<>();  
    private int mLength = 0;  
    private LayoutInflater mInflater;  
    private Context mContext;  
    private ListView mListView;  
    private Animation animation;  
    private int mFirstTop, mFirstPosition;  
    private boolean isScrollDown;  

    public ListAdapter(Context context, ListView listView, List<Drawable> drawables, int length) {  
        mDrawableList.addAll(drawables);  
        mLength = length;  
        mInflater = LayoutInflater.from(context);  
        mContext = context;  
        mListView = listView;  
        animation = AnimationUtils.loadAnimation(mContext, R.anim.bottom_in_anim);  
        mListView.setOnScrollListener(mOnScrollListener);  
    }  

    AbsListView.OnScrollListener mOnScrollListener = new AbsListView.OnScrollListener() {  
        @Override  
        public void onScrollStateChanged(AbsListView view, int scrollState) {  

        }  

        @Override  
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {  
            View firstChild = view.getChildAt(0);  
            if (firstChild == null) return;  
            int top = firstChild.getTop();  
            /** 
             * firstVisibleItem > mFirstPosition表示向下滑動(dòng)一整個(gè)Item 
             * mFirstTop > top表示在當(dāng)前這個(gè)item中滑動(dòng) 
             */  
            isScrollDown = firstVisibleItem > mFirstPosition || mFirstTop > top;  
            mFirstTop = top;  
            mFirstPosition = firstVisibleItem;  
        }  
    };  

    @Override  
    public int getCount() {  
        return mLength;  
    }  

    @Override  
    public Object getItem(int position) {  
        return mDrawableList.get(position % mDrawableList.size());  
    }  

    @Override  
    public long getItemId(int position) {  
        return position;  
    }  

    @Override  
    public View getView(int position, View convertView, ViewGroup parent) {  
        ViewHolder holder = null;  

        if (convertView == null) {  

            holder = new ViewHolder();  
            convertView = mInflater.inflate(R.layout.item_layout, null);  
            holder.mImageView = (ImageView) convertView.findViewById(R.id.img);  
            holder.mTextView = (TextView) convertView.findViewById(R.id.text);  
        } else {  
            holder = (ViewHolder) convertView.getTag();  
        }  

        //清除當(dāng)前顯示區(qū)域中所有item的動(dòng)畫(huà)  
        for (int i=0;i<mListView.getChildCount();i++){  
            View view = mListView.getChildAt(i);  
            view.clearAnimation();  
        }  
        //然后給當(dāng)前item添加上動(dòng)畫(huà)  
        if (isScrollDown) {  
            convertView.startAnimation(animation);  
        }  
        convertView.setTag(holder);  

        holder.mImageView.setImageDrawable(mDrawableList.get(position % mDrawableList.size()));  
        holder.mTextView.setText(position + "");  

        return convertView;  
    }  

    public class ViewHolder {  
        public ImageView mImageView;  
        public TextView mTextView;  
    }  
}  

到這里,有關(guān)listview中添加動(dòng)畫(huà)的部分就結(jié)束了,下面來(lái)看一下最終的效果圖吧:

http://wiki.jikexueyuan.com/project/android-animation/images/20160327213050624.gif" alt="" />

源碼在文章底部給出

源碼下載地址:http://download.csdn.net/detail/harvic880925/9474080