相关文章:
《Android自定义控件三部曲文章索引》: http://blog.csdn.net/harvic880925/article/details/50995268
前面两篇我们讲解了使用layoutAnimation和LayoutTransition实现ViewGroup中Item加载动画的方法,但他们都各自存在问题:
layoutAnimation虽然是API 1中就已经引入,但只能在动画初次创建时才能使用指定动画。控件创建以后,再往ViewGroup里加Item就不会再有动画。这显然是不合适的!
LayoutTransition能够实现无论何时往ViewGroup中添加控件都可以给其中控件使用动画。但最大的问题是,它的API等级是11。而且也没有兼容包可供我们使用这个函数。
这样问题就来了,如果我们想在兼容API 8以上的机型,完成ListView中各个Item进入时都添加动画,这要怎么来做呢?
今天我们要完成的效果图如下:
从效果图中可以看到,当每个Item进入的时候,都添加了动画。前面我们说了layoutAnimation和LayoutTransition所存在的问题,那抛开这两个函数,我们要如何实现Item进入动画呢?
别忘了,ListView在得到每个Item时会调用BaseAdapter的getView方法!getView中每一个convertView就是当前要显示的Item所对应的View,所以我们直接对convertView添加动画不就好了。
上面的原理理解起来并不难,下面我们就看看如何实现的吧。
一、搭框架
这部分,我们主要是先搭出来要实现的框架,把ListView填充起来,效果如下:
这里实现的效果就是把listview填充起来,总共用了九张图片,listview列表循环显示这九张图片,但我在每个图片上显示了当前item所在的位置。
好了,下面就来看代码吧
1、item布局(item_layout.xml)
我们先来看看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>
2、ListAdapter
这里就是ListView的Adapter的代码位置了,完整的代码如下,然后再细讲:- 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;
- }
- }
- public ListAdapter(Context context, ListView listView, List<Drawable> drawables, int length) {
- mDrawableList.addAll(drawables);
- mLength = length;
- mInflater = LayoutInflater.from(context);
- mContext = context;
- mListView = listView;
- }
然后是getItem函数:
- @Override
- public Object getItem(int position) {
- return mDrawableList.get(position % mDrawableList.size());
- }
- 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) {
- //关键在这里哦
- Drawable drawable = (Drawable) adapter.getItem(position);
- }
- });
- public Object getItem(int position) {
- return mDrawableList.get(position % mDrawableList.size());
- }
最后getView()函数
- 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;
- }
在理解了convertview回收机制以后,这里最难的地方应该就是赋值的位置了:
- holder.mImageView.setImageDrawable(mDrawableList.get(position % mDrawableList.size()));
- holder.mTextView.setText(position+"");
3、主布局(main.xml)
主布局非常简单,就只有一个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>
4、MyActivity.java
最后是在MyActivity中构造ListAdapter并设置进listview的过程了:- 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);
- }
- }
到这里,我们listview就构造完成了。下面就看如何向其中的item添加动画的环节了。
二、Item添加动画——初步实现
1、动画文件(bottom_in_anim.xml)
先定义从底部进入的动画:- <?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>
2、在Adapter中添加动画代码
首先,我们在ListAdapter中初始化的时候,加载动画:- 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);
- }
- …………
- }
- 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;
- }
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;
- }
- }
从效果图中可以看出,我们初步实现了在item生成的时候添加进入动画
三、优化
上面虽然解决了进入时添加动画的问题,但仔细的同学可以看出,在这个效果图中还存在几个问题,可能上面的效果图还看不清楚具体存在的问题1、如果上拉的时候,一下上拉了几个item,那些要显示的item会一起从底部出现
2、在下拉的时候,上部出现的item也会应用上动画
首先,解决第二个问题比较简单,只需要判断当前手指是上滑还上下滑就可以了,只有当手指向上滑的时候,才对底部出现的item添加上入场动画,其它时间不添加动画即可
第一个问题其实也比较容易解决,我们可以通过listview.getChildCount()得到当前ListView中有多少个item。我们只给最后一个添加动画即可。其它的就直接显示就好了。
1、上下滑动问题
首先,我们首先解决上下滑动问题,这个问题其实比较好解决,只需要监听listview的OnScrollListener,根据它判断出当前ListView是上滑还是下滑就可以了。先看我们在onScrollListner中都有哪些参数:
- 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) {
- }
- };
- onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
- 第一个参数AbsListView view:是当前listview的对象
- 第二个参数int firstVisibleItem:表示当前第一个可见的item在listview所有item中的索引,这里需要非常注意,firstVisibleItem与getChildAt(int position)中的参数position的意义不同,firstVisibleItem是指在整个ListView中的位置。而getChildAt(int position)中参数position传的是当前屏幕显示区域中item的索引,屏幕中第一个item的view可以通过getChildAt(0)得到。
- 第三个参数int visibleItemCount:表示当前屏幕中可见的有几条item
- 第四个参数int totalItemCount:表示当前listview总共有多少条item,得到的值与adapter.getCount()的值相同。
向下移动包括两种情况:
第一:屏幕中第一个item或前几个item一起移出屏幕, 在这种情况下,我们只需要判断firstVisibleItem是否比上次的值大即可。即第一个显示的item是不是已经向下移了
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
- /**
- * firstVisibleItem > mFirstPosition表示向下滑动一个或多个Item
- */
- isScrollDown = firstVisibleItem > mFirstPosition;
- mFirstPosition = firstVisibleItem;
- }
如下图:
在这个效果图中,只是将第一个item向上移动了一点点,第一个item左上角的坐标从(0,-100)变成了(0,-200);所以从这个图中,我们可以得到如何计算在这种情况下是否上移。
- @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表示在当前这个item中滑动
- */
- isScrollDown = mFirstTop > top;
- mFirstTop = top;
- }
所以我们将这两种情况进行合并,得到完整的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表示向下滑动一整个Item
- * mFirstTop > top表示在当前这个item中滑动
- */
- isScrollDown = firstVisibleItem > mFirstPosition || mFirstTop > top;
- mFirstTop = top;
- mFirstPosition = firstVisibleItem;
- }
- };
- @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;
- }
2、多个item同时动画问题
由于我们只能在Item生成时给这个Item添加动画,所以要解决多个item同时移动的问题,我们只能给最后一个Item添加动画,其它item不给他们添加;但我们怎么知道当前这个item是不是要显示的最后一个item呢?无法得各,所以一个中转方案是,在每一个item在添加动画前,都把当前显示区域内所有item动画给取消,然后给当前convertView添加上动画;当listview滚动到最后一个Item的时候,自然,同样也是先把所有动画取消,然后给他自己添加上动画,所以这样看起来就好像是只给他自己添加了动画,之前滚动的item是没有动画的。代码如下:
- 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();
- }
- //清除当前显示区域中所有item的动画
- for (int i=0;i<mListView.getChildCount();i++){
- View view = mListView.getChildAt(i);
- view.clearAnimation();
- }
- //然后给当前item添加上动画
- if (isScrollDown) {
- convertView.startAnimation(animation);
- }
- convertView.setTag(holder);
- holder.mImageView.setImageDrawable(mDrawableList.get(position % mDrawableList.size()));
- holder.mTextView.setText(position + "");
- return convertView;
- }
- for (int i=0;i<mListView.getChildCount();i++){
- View view = mListView.getChildAt(i);
- view.clearAnimation();
- }
此时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表示向下滑动一整个Item
- * mFirstTop > top表示在当前这个item中滑动
- */
- 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();
- }
- //清除当前显示区域中所有item的动画
- for (int i=0;i<mListView.getChildCount();i++){
- View view = mListView.getChildAt(i);
- view.clearAnimation();
- }
- //然后给当前item添加上动画
- 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;
- }
- }
源码在文章底部给出
如果本文有帮到你,记得加关注哦
源码下载地址:http://download.csdn.net/detail/harvic880925/9474080
请大家尊重原创者版权,转载请标明出处: http://blog.csdn.net/harvic880925/article/details/50988685 谢谢