Android Animation實(shí)戰(zhàn)之一個(gè)APP的ListView的動(dòng)畫效果
熟悉了基礎(chǔ)動(dòng)畫的實(shí)現(xiàn)后,便可以試著去實(shí)現(xiàn)常見(jiàn)APP中出現(xiàn)過(guò)的那些精美的動(dòng)畫。今天我主要給大家引入一個(gè)APP的ListView的動(dòng)畫效果: 當(dāng)展示ListView時(shí),Listview的每一個(gè)列表項(xiàng)都按照規(guī)定的動(dòng)畫顯示出來(lái)。
說(shuō)起來(lái)比較抽象,先給大家看一個(gè)動(dòng)畫效果,這是APP窩牛裝修的ListView顯示動(dòng)畫:
有木有覺(jué)得很酷炫?有木有?。??
一、Layout Animation
所謂的布局動(dòng)畫,其實(shí)就是為ViewGroup添加顯示動(dòng)畫效果,主要用過(guò)LayoutAnimationController來(lái)控制實(shí)現(xiàn)。LayoutAnimationController用于為一個(gè)Layout里面的控件,或者是一個(gè)ViewGroup里面的控件設(shè)置動(dòng)畫效果,可以在XML文件中設(shè)置,亦可以在Java代碼中設(shè)置。
1.1 在XML文件中設(shè)置布局動(dòng)畫
首先,我們?cè)趓es/anim文件夾下建立一個(gè)list_anim_layout.xml文件,該文件就是布局動(dòng)畫控制器。
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" android:delay="30%" android:animationOrder="random" android:animation="@anim/slide_right" />
android:delay 子類動(dòng)畫時(shí)間間隔 (延遲) 70% 也可以是一個(gè)浮點(diǎn)數(shù) 如“1.2”等。
android:animationOrder="random" 子類的顯示方式 random表示隨機(jī)。
android:animationOrder 的取值有
- normal 0 默認(rèn)
- reverse 1 倒序
- random 2 隨機(jī)
android:animation="@anim/slide_right" 表示列表項(xiàng)顯示時(shí)的具體動(dòng)畫是什么!
下面,我們定義每一個(gè)列表項(xiàng)顯示時(shí)的動(dòng)畫效果吧,及slide_right.xml:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator"> <translate android:duration="3000" android:fromXDelta="100%p" android:toXDelta="0%p" /> </set>
顯示的效果為L(zhǎng)istView第一次出現(xiàn)的時(shí)候?yàn)?item隨機(jī)出現(xiàn) 每個(gè)Item都是從右邊的區(qū)域向左滑動(dòng)到顯示的地方。
接下來(lái),你只需要把這個(gè)布局動(dòng)畫,指定到ViewGroup上就好了:
<ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent" android:layoutAnimation="@anim/list_anim_layout" > </ListView>
就這么簡(jiǎn)單就完成了,快來(lái)看下效果吧:
1.2 在Java代碼中實(shí)現(xiàn)布局動(dòng)畫
在Java代碼中實(shí)現(xiàn)布局動(dòng)畫并無(wú)難度,只要熟悉幾個(gè)API的使用即可。 關(guān)于動(dòng)畫的定義和上文一致,只是,你不需要再在把控制動(dòng)畫應(yīng)用到listview中了,即android:layoutAnimation="@anim/list_anim_layout"這行代碼可以刪除。
接下來(lái),需要在Java代碼中進(jìn)行配置:
private void startLayoutAnim() { //通過(guò)加載XML動(dòng)畫設(shè)置文件來(lái)創(chuàng)建一個(gè)Animation對(duì)象; Animation animation = AnimationUtils.loadAnimation(this, R.anim.slide_right); //得到一個(gè)LayoutAnimationController對(duì)象; LayoutAnimationController lac = new LayoutAnimationController(animation); //設(shè)置控件顯示的順序; lac.setOrder(LayoutAnimationController.ORDER_REVERSE); //設(shè)置控件顯示間隔時(shí)間; lac.setDelay(1); //為L(zhǎng)istView設(shè)置LayoutAnimationController屬性; listView.setLayoutAnimation(lac); }
觀察下,效果和之前使用xml文件肯定是一致的了。
介紹到這里,可能有人會(huì)有疑問(wèn)了,博文一開始介紹的“窩牛裝修”的那種效果,是每一個(gè)列表項(xiàng)顯示的時(shí)候才會(huì)顯示動(dòng)畫。我們這個(gè)確實(shí)所有的列表項(xiàng)的動(dòng)畫一起都顯示了,只是顯示順序不同而已。 通過(guò)我們這種方法,怎么可能會(huì)達(dá)到那種效果呢?
確實(shí),通過(guò)布局動(dòng)畫,沒(méi)辦法控制每一個(gè)Item在加載時(shí)才顯示動(dòng)畫。那該如何是好呢?
兄弟們,換個(gè)思路吧,如果布局動(dòng)畫完成不了,何必不直接用簡(jiǎn)單的補(bǔ)間動(dòng)畫,再結(jié)合每個(gè)列表項(xiàng)的顯示控制,來(lái)實(shí)現(xiàn)窩牛裝修列表顯示的效果呢?
那有人會(huì)問(wèn)了,怎么知道每一個(gè)列表項(xiàng)何時(shí)才加載么?
你難道忘了BaseAdapter的getView()方法了么?
沒(méi)錯(cuò),每當(dāng)一個(gè)列表項(xiàng)顯示時(shí),都會(huì)主動(dòng)調(diào)用BaseAdaper的getView()方法。
二、仿窩牛裝修List列表的動(dòng)畫效果
首先,我們定義一個(gè)動(dòng)畫資源,該動(dòng)畫即是列表項(xiàng)顯示時(shí)的動(dòng)畫:woniu_list_item.xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" > <!-- woniu list item animation --> <translate android:duration="500" android:fromXDelta="0" android:fromYDelta="100" android:toXDelta="0" android:toYDelta="0" /> </set>
該平移動(dòng)畫表示,從下往上,垂直平移100px,時(shí)間為500毫秒。
接下來(lái),我們需要在BaseAdapter的getView()方法里,去使用該動(dòng)畫:
import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import java.util.List; public class WoniuListAdapter extends BaseAdapter { private Context mContext; private LayoutInflater mInflater; private List<WoniuSimple> mDatas; private Animation animation; public WoniuListAdapter(Context context, List<WoniuSimple> datas) { mContext = context; mInflater = LayoutInflater.from(mContext); mDatas = datas; animation = AnimationUtils.loadAnimation(mContext, R.anim.woniu_list_item); } @Override public int getCount() { return (mDatas != null ? mDatas.size() : 0); } @Override public Object getItem(int position) { return (mDatas != null ? mDatas.get(position) : null); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder = null; int type = getItemViewType(position); if (convertView == null) { // 下拉項(xiàng)布局 convertView = mInflater.inflate(R.layout.list_item_woniu, null); holder = new ViewHolder(); holder.tem_img = (ImageView) convertView.findViewById(R.id.tem_img); holder.text_name = (TextView) convertView.findViewById(R.id.text_name); holder.text_name = (TextView) convertView.findViewById(R.id.text_name); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } convertView.startAnimation(animation); final WoniuSimple materialSimple = mDatas.get(position); if (materialSimple != null) { // holder.tem_img.setImageResource(R.mipmap.assist_default_img); // holder.text_name.setText(materialSimple.name); // holder.text_mobile.setText(materialSimple.mobile); } return convertView; } class ViewHolder { ImageView tem_img; TextView text_name; TextView text_mobile; } }
我們來(lái)簡(jiǎn)要分析應(yīng)用動(dòng)畫的地方: 1、我們Adapter的構(gòu)造方法里加載了之前定義的動(dòng)畫,活的Animation對(duì)象。 2、 我們?cè)趃etView方法里,為convertView設(shè)置并啟動(dòng)Animation,即convertView.startAnimation(animation)。
夠簡(jiǎn)單吧,只是這么兩行代碼,就可以實(shí)現(xiàn)在加載每一個(gè)View Item時(shí)啟動(dòng)動(dòng)畫效果。
可是,我們發(fā)現(xiàn),這并不是非常完美的實(shí)現(xiàn),為啥這么說(shuō)呢?
因?yàn)槟愦丝掏匣瑒?dòng)列表,會(huì)發(fā)現(xiàn),已經(jīng)加載過(guò)的Item的動(dòng)畫還會(huì)再次啟動(dòng)執(zhí)行一次。這個(gè)體驗(yàn)太糟糕了。為啥會(huì)出現(xiàn)這種情況???
因?yàn)間etVIew方法的調(diào)用時(shí)機(jī)會(huì)對(duì)動(dòng)畫產(chǎn)生影響。Adapter中的getView方法,會(huì)在每一個(gè)item處于可見(jiàn)狀態(tài)時(shí)調(diào)用,所以無(wú)論你上滑還是下滑,都會(huì)重復(fù)調(diào)用getView方法(這也是ListView為啥在使用時(shí)要進(jìn)行優(yōu)化的地方)。
所以,為了解決剛剛發(fā)生的問(wèn)題,我們可以設(shè)置標(biāo)識(shí),進(jìn)行判斷,已經(jīng)加載過(guò)的view的動(dòng)畫不再進(jìn)行啟動(dòng)加載。
完整的代碼如下:
package com.lnyp.layoutanimation; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import java.util.HashMap; import java.util.List; import java.util.Map; public class WoniuListAdapter extends BaseAdapter { private Context mContext; private LayoutInflater mInflater; private List<WoniuSimple> mDatas; private Animation animation; private Map<Integer, Boolean> isFrist; public WoniuListAdapter(Context context, List<WoniuSimple> datas) { mContext = context; mInflater = LayoutInflater.from(mContext); mDatas = datas; animation = AnimationUtils.loadAnimation(mContext, R.anim.woniu_list_item); isFrist = new HashMap<Integer, Boolean>(); } @Override public int getCount() { return (mDatas != null ? mDatas.size() : 0); } @Override public Object getItem(int position) { return (mDatas != null ? mDatas.get(position) : null); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder = null; int type = getItemViewType(position); if (convertView == null) { // 下拉項(xiàng)布局 convertView = mInflater.inflate(R.layout.list_item_woniu, null); holder = new ViewHolder(); holder.tem_img = (ImageView) convertView.findViewById(R.id.tem_img); holder.text_name = (TextView) convertView.findViewById(R.id.text_name); holder.text_name = (TextView) convertView.findViewById(R.id.text_name); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } // 如果是第一次加載該view,則使用動(dòng)畫 if (isFrist.get(position) == null || isFrist.get(position)) { convertView.startAnimation(animation); isFrist.put(position, false); } final WoniuSimple materialSimple = mDatas.get(position); if (materialSimple != null) { // holder.tem_img.setImageResource(R.mipmap.assist_default_img); // holder.text_name.setText(materialSimple.name); // holder.text_mobile.setText(materialSimple.mobile); } return convertView; } class ViewHolder { ImageView tem_img; TextView text_name; TextView text_mobile; } }
看到了么,加了一個(gè)isFirst進(jìn)行判斷,這樣,就可以有效控制動(dòng)畫的顯示了。效果如下:
本文我主要介紹了兩個(gè)部分,一個(gè)是Layout Animation布局動(dòng)畫,使用布局動(dòng)畫可以控制VIew Groups中的每一個(gè)數(shù)據(jù)的顯示動(dòng)畫; 還一個(gè)就是實(shí)戰(zhàn),仿“窩牛裝修”ListView滑動(dòng)時(shí)每一個(gè)Item滑動(dòng)進(jìn)入可見(jiàn)狀態(tài)的動(dòng)畫效果。通過(guò)這兩個(gè)動(dòng)畫示例,我相信可以幫助大家更好的處理動(dòng)畫,克服“動(dòng)畫恐懼癥”。
希望本文所述對(duì)大家學(xué)習(xí)有所幫助,大家也會(huì)喜歡,小編一定會(huì)再接再厲,為大家分享更多精彩的文章。
- Android自定義Animation實(shí)現(xiàn)View搖擺效果
- Android使用glide加載gif動(dòng)畫設(shè)置播放次數(shù)
- Android Glide圖片加載(加載監(jiān)聽(tīng)、加載動(dòng)畫)
- Android實(shí)現(xiàn)跳動(dòng)的小球加載動(dòng)畫效果
- Android自定義加載loading view動(dòng)畫組件
- Android自定義加載控件實(shí)現(xiàn)數(shù)據(jù)加載動(dòng)畫
- Android加載Gif動(dòng)畫實(shí)現(xiàn)代碼
- Android自定義view實(shí)現(xiàn)阻尼效果的加載動(dòng)畫
- Android自定義View實(shí)現(xiàn)loading動(dòng)畫加載效果
- Android使用View Animation實(shí)現(xiàn)動(dòng)畫加載界面
相關(guān)文章
Kotlin下Rxjava的基礎(chǔ)用法及流式調(diào)用示例詳解
這篇文章主要為大家介紹了Kotlin下Rxjava的基礎(chǔ)用法及流式調(diào)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11Android中pendingIntent與Intent的深入分析
這篇文章主要介紹了Android中pendingIntent的深入分析的相關(guān)資料,需要的朋友可以參考下2017-04-04Android使用Service實(shí)現(xiàn)簡(jiǎn)單音樂(lè)播放實(shí)例
這篇文章主要為大家詳細(xì)介紹了Android使用Service實(shí)現(xiàn)簡(jiǎn)單音樂(lè)播放實(shí)例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05Android保存多張圖片到本地的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Android保存多張圖片到本地的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06Android App中自定義View視圖的實(shí)例教程
這篇文章主要介紹了Android App中自定義View視圖的實(shí)例教程,詳細(xì)講解了如何在創(chuàng)建View中定義各種鎖需要的樣式屬性,需要的朋友可以參考下2016-04-04Android recycleView的應(yīng)用和點(diǎn)擊事件實(shí)例詳解
這篇文章主要介紹了Android recycleView的應(yīng)用和點(diǎn)擊事件實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2016-12-12Android SQLite事務(wù)處理結(jié)合Listview列表顯示功能示例
這篇文章主要介紹了Android SQLite事務(wù)處理結(jié)合Listview列表顯示功能,較為詳細(xì)的分析了Android使用sqlite數(shù)據(jù)庫(kù)進(jìn)行事務(wù)操作并結(jié)合Listview進(jìn)行列表顯示的相關(guān)操作技巧,需要的朋友可以參考下2017-07-07Android使alertDialog.builder不會(huì)點(diǎn)擊外面和按返回鍵消失的方法
本篇文章主要介紹了Android使alertDialog.builder不會(huì)點(diǎn)擊外面和按返回鍵消失的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-01-01