自定义ListView实现底部动画更新item

本文介绍如何在Android ListView中实现新加载项的动画效果,通过记录更新位置并仅对新增条目应用动画,优化用户体验。文章详细解释了ListView的工作原理及其实现步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

http://www.cnblogs.com/lucifinilsu/p/3441787.html

http://www.cnblogs.com/lucifinilsu/p/3436769.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值