1. 源代码
实现思想:理解ListView加载items的原理:ListView永远仅仅只是使用第一次刚好可以显示一屏幕数量的items,向下拉而可见的items都是在复用前面第一次显示一屏幕的items而已;而且在适配器(继承BaseAdapter)的getView()中获得item时,每次下拉都会对所有可见的items调用getView()方法,但是这里仅仅只是要求最后更新的item做下拉动画,所有必须通过记录当前更新的item的position(索引),然后在getView()中根据该记录的position来调用动画;在getView()时候,item还没有初始化时,应设置动画。
package com.example.scrollviewdemo;
import java.util.LinkedList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends Activity {
private ListView mListView;
private List<String> mDataList = new LinkedList<String>();
private MyAdapater mAdapter = null;
/**
* 重点:记录ListView中当前正在更新的位置,初始值为是为-1表示开始没有要动画加载的item
*/
private int mCurrentPosition = -1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
for (int i = 0; i < 5; ++i) {
mDataList.add("Habby and Marry");
}
mListView = (ListView) findViewById(R.id.list);
mAdapter = new MyAdapater();
/**
* 此处设置时,则会第一次更新ListView的items(调用Adapter的getView()等函数),
* 这时,如果是超过了一屏幕,则只显示一屏的items。如果向下拉,系统就会重用
* 上面出去屏幕的item。
*/
mListView.setAdapter(mAdapter);
mListView.setOnScrollListener(new OnScrollListener() {
private boolean isBottom = false;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE && isBottom) { // 底部
mDataList.add("我们结婚吧!");
mCurrentPosition = mDataList.size() - 1; // 记录当前更新数据的position
mAdapter.notifyDataSetChanged();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem + visibleItemCount == totalItemCount) { // 滚到了底部
isBottom = true;
} else {
isBottom = false;
}
}
});
}
private class MyAdapater extends BaseAdapter {
@Override
public int getCount() {
return mDataList.size();
}
@Override
public Object getItem(int position) {
return mDataList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder mViewHolder = null;
/**
* getView()和ListView工作的原理是:当回收站里的item们来自于getView()的(convertView == null
* 表示不能从回收站里获得,必须自己创建)的时候,这些item都是新建立的,然后等到可以滑动,并且有
* 滑动隐藏掉的item时候,就把隐藏掉的item从ListView中去除,然后原封不动(不改变任何状态)的保存
* 在回收站里;当(getView != null)的时候,也就是说可以从回收站里获得item了。这就是item复用。
*/
if (convertView == null) { // 回收站里没有现成的item可用
convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.chat_item_2, null);
mViewHolder = new ViewHolder();
mViewHolder.mTextView = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(mViewHolder);
mViewHolder.animSet = (AnimationSet) AnimationUtils.loadAnimation(MainActivity.this, R.anim.translate1);
convertView.startAnimation(mViewHolder.animSet);
} else {
mViewHolder = (ViewHolder) convertView.getTag();
}
mViewHolder.mTextView.setText(mDataList.get(position));
/**
* 这里是因为,手指滑动ListView或notifyDataSetChanged()方法都会重画所以可见的item,这里仅仅是需要
* 对当前更新的(增加的)item做动画,其他的item不做动画。
*/
if (mCurrentPosition == position) {
convertView.startAnimation(mViewHolder.animSet);
}
return convertView;
}
}
private class ViewHolder {
private TextView mTextView;
private AnimationSet animSet; // item动画
}
}
2. 动画的xml(R.anim.translate1)是:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator"
android:duration="400" >
<translate
android:fromYDelta="100%"
android:toYDelta="0"
android:duration="400"/>
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="300" />
</set>
3. main.xml是:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@android:color/darker_gray" >
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:divider="@android:color/darker_gray"
android:dividerHeight="16dip"
android:fadingEdge="vertical" />
</LinearLayout>
4. chat_item_2.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:background="@android:color/transparent" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="150dip"
android:background="@android:color/white"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:paddingLeft="12dip"
android:paddingRight="12dip" >
<ImageView
android:id="@+id/iv_chat_item_2"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentRight="true"
android:contentDescription="@android:string/defaultMsisdnAlphaTag"
android:src="@drawable/boy" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
android:layout_toLeftOf="@+id/iv_chat_item_2"
android:background="@drawable/msg_sendto_done_bg_nor"
android:text=""
android:textColor="@color/grey21" />
</RelativeLayout>
</LinearLayout>
另外:对ListView加载item的理解很重要,只有理解了ListView的原理,才能写出更加好的ListView,这里有几个链接说得很好:
http://mzh3344258.blog.51cto.com/1823534/889879