ListView添加Item动画以及四种冲突情况的解决办法

本文详细介绍了在ListView中添加Item动画的实现方法,并针对动画间的冲突、动画与滑动冲突、动画与ListView缓存机制冲突以及点击耗时任务处理时与滑动的冲突等问题提出了解决方案。通过设置布尔变量控制动画执行,禁用滑动以及在关闭动画前调整ListView项数等手段,确保了ListView动画的流畅性和正确性。

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

1. 动画功能的基本实现

首先我们梳理一下需求

  1. 界面上有三个元素,一个开启动画按钮start,一个关闭动画按钮end,一个ListView展示数据
  2. ListView默认是GONE的
  3. 点击开启动画按钮前,需要先将ListView置为VISIBLE,然后再开启动画。开启之后默认展示第一项。
  4. 点击关闭动画按钮后,在动画执行结束后,将ListView置为GONE。
  5. 点击ListView的Item后,也执行第4步骤的关闭动画操作。

我们知道,ViewGroup的动画需要LayoutAnimationController来支持,那么ListView继承于ViewGroup,当然也是通过LayoutAnimationController来实现Item的动画。这里需要注意的是,怎么拿到ListView最后一项Item的动画结束后的监听回调呢?代码如下:

/**
 * Created by chenchen on 2016/11/25.
 */
public class ListViewAnimationListener implements Animation.AnimationListener {

    private int count = 0;
    private ListView listView;
    private OnAnimationFinishListener listener;

    public ListViewAnimationListener(ListView listView, OnAnimationFinishListener listener) {
        this.listView = listView;
        this.listener = listener;
    }

    @Override
    public void onAnimationStart(Animation animation) {

    }

    @Override
    public void onAnimationEnd(Animation animation) {
        // 没有子控件,则直接完成
        if(listView.getChildCount() == 0){
            if(listener != null){
                listener.onFinish();
            }
        }else{
            count++;
            if(count == listView.getChildCount() && listener != null){
                listener.onFinish();
                reset();
            }
        }
    }

    @Override
    public void onAnimationRepeat(Animation animation) {

    }

    public void reset(){
        count = 0;
    }

    public interface OnAnimationFinishListener{
        void onFinish();
    }
}

代码很少,可以看出具体的逻辑是:每次动画结束时使变量count自增,当count等于listView的getChildCount()时,我们认为所有的Item的动画结束了。至于为什么是getChildCount()而不是adapter的getCount()呢?我们知道ListView的缓存原理,内存中始终只有屏幕可见个Item,因此动画是实际作用于真实存在内存中的Item。

好了,接下来贴出全部代码,其他部分就很简单了。

首先是两个动画文件:

<!-- translate_in -->

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate xmlns:android="http://schemas.android.com/apk/res/android"
        android:fromXDelta="-100%p" android:toXDelta="0%p"
        android:duration="100"/>

</set>

<!-- translate_out -->

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate xmlns:android="http://schemas.android.com/apk/res/android"
        android:fromXDelta="0%p" android:toXDelta="-100%p"
        android:duration="100" />

</set>

然后是ListView的item文件,很简单

<?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="wrap_content"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/txt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textColor="@android:color/black"
        android:textSize="15sp" />

</LinearLayout>

然后是activity_main.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:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="horizontal"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        >

        <Button
            android:id="@+id/start"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="开启列表动画"
            android:textSize="18sp"
            android:textColor="#333333"
            />

        <Button
            android:id="@+id/end"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent"
            android:layout_marginLeft="20dp"
            android:gravity="center"
            android:text="关闭列表动画"
            android:textSize="18sp"
            android:textColor="#333333"
            />

    </LinearLayout>

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:cacheColorHint="@android:color/transparent"
        android:listSelector="@android:color/transparent"
        android:divider="#f1f1f1"
        android:dividerHeight="1px"
        android:visibility="gone"
        />
</LinearLayout>

最后是MainActivity文件

public class MainActivity extends Activity {

    private ListView listView;
    private MyAdapter adapter;
    //动画控制器,针对ViewGroup的动画
    private LayoutAnimationController inAnimationController;
    private LayoutAnimationController outAnimationController;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initListView();
        initBaseUI();
    }

    private void initBaseUI() {
        findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                oepnAnim();
            }
        });
        findViewById(R.id.end).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                closeAnim();
            }
        });
    }

    private void initListView() {
        listView = (ListView) findViewById(R.id.listView);
        adapter = new MyAdapter();
        listView.setAdapter(adapter);
        //设置点击监听器
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                closeAnim();
            }
        });
        //入场动画
        Animation inAnimation = AnimationUtils.loadAnimation(this, R.anim.translate_in);
        inAnimation.setFillAfter(true);
        inAnimationController = new LayoutAnimationController(inAnimation);
        inAnimationController.setOrder(LayoutAnimationController.ORDER_NORMAL);
        inAnimationController.setDelay(0.2f);
        //设置动画结束监听器
        ListViewAnimationListener inAnimationListener = new ListViewAnimationListener(listView, new ListViewAnimationListener.OnAnimationFinishListener() {
            @Override
            public void onFinish() {

            }
        });
        inAnimation.setAnimationListener(inAnimationListener);
        //出场动画
        Animation outAnimation = AnimationUtils.loadAnimation(this, R.anim.translate_out);
        outAnimation.setFillAfter(true);
        outAnimationController = new LayoutAnimationController(outAnimation);
        outAnimationController.setOrder(LayoutAnimationController.ORDER_NORMAL);
        outAnimationController.setDelay(0.2f);
        //设置动画结束监听器
        ListViewAnimationListener outAnimationListener = new ListViewAnimationListener(listView, new ListViewAnimationListener.OnAnimationFinishListener() {
            @Override
            public void onFinish() {
                listView.setVisibility(View.GONE);
            }
        });
        outAnimation.setAnimationListener(outAnimationListener);
    }

    public class MyAdapter extends BaseAdapter{

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

        @Override
        public Object getItem(int position) {
            return null;
        }

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if(convertView == null){
                convertView = View.inflate(MainActivity.this, R.layout.item, null);
                holder = new ViewHolder(convertView);
                convertView.setTag(holder);
            }else{
                holder = (ViewHolder) convertView.getTag();
            }
            holder.fillData(position);
            return convertView;
        }

        public class ViewHolder{
            private TextView txt;

            public ViewHolder(View rootView) {
                txt = (TextView) rootView.findViewById(R.id.txt);
            }

            public void fillData(int position){
                txt.setText("测试" + position);
            }
        }
    }

    private void oepnAnim(){
        if(listView.getVisibility() == View.GONE){
            //每次打开都在第一行
            listView.setSelection(0);
            listView.setVisibility(View.VISIBLE);
            listView.setLayoutAnimation(inAnimationController);
            listView.startLayoutAnimation();
        }
    }

    private void closeAnim(){
        if(listView.getVisibility() == View.VISIBLE){
            listView.setLayoutAnimation(outAnimationController);
            listView.startLayoutAnimation();
        }
    }
}

2. 解决动画之间的冲突

做了以上部分,你就以为能够交差了?No,还差的很远。不信?连续点两次开启动画试试?是不是觉得这样的代码提交上去会被喷?

我列举了以下需要优化的地方:

  1. 连续快速点击两次开启动画按钮
  2. 连续快速点击两次关闭动画按钮
  3. 连续快速点击两次ListView的item
  4. 快速先点击开启动画按钮,再点击关闭动画按钮
  5. 快速先点击关闭动画按钮,再点击开启动画按钮
  6. 快速先点击开启动画按钮,再点击ListView的item
  7. 快速先点击ListView的item,再点击开启动画按钮
  8. 快速先点击关闭动画按钮,再点击ListView的item
  9. 快速先点击ListView的item,再点击开启动画按钮

看到上述9种情况,是不是瞬间觉得蛋疼无比?其实我们可以总结出一条规律:

再开启动画的过程中,不允许再开启第二个动画,否则视觉上会很奇怪

那么如何解决这个问题呢?很简单,只需要给出两个布尔变量canOpenAnim和canCloseAnim,表示是否允许开启open动画和close动画,在开启动画时,首先判断canOpenAnim或canCloseAnim的值,然后将canOpenAnim和canCloseAnim都置为false,在动画的完成监听器中再将canOpenAnim和canCloseAnim都置为true。这样就可以避免两个动画同时开启的尴尬。下面给出具体的代码:

public class MainActivity extends Activity {

    private ListView listView;
    private MyAdapter adapter;
    //动画控制器,针对ViewGroup的动画
    private LayoutAnimationController inAnimationController;
    private LayoutAnimationController outAnimationController;
    //控制解决连续多次动画的冲突
    private boolean canOpenAnim = true;
    private boolean canCloseAnim = true;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initListView();
        initBaseUI();
    }

    private void initBaseUI() {
        findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                openAnim();
            }
        });
        findViewById(R.id.end).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                closeAnim();
            }
        });
    }

    private void initListView() {
        listView = (ListView) findViewById(R.id.listView);
        adapter = new MyAdapter();
        listView.setAdapter(adapter);
        //设置点击监听器
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                closeAnim();
            }
        });
        //入场动画
        Animation inAnimation = AnimationUtils.loadAnimation(this, R.anim.translate_in);
        inAnimation.setFillAfter(true);
        inAnimationController = new LayoutAnimationController(inAnimation);
        inAnimationController.setOrder(LayoutAnimationController.ORDER_NORMAL);
        inAnimationController.setDelay(0.2f);
        //设置动画结束监听器
        ListViewAnimationListener inAnimationListener = new ListViewAnimationListener(listView, new ListViewAnimationListener.OnAnimationFinishListener() {
            @Override
            public void onFinish() {
                canOpenAnim = true;
                canCloseAnim = true;
            }
        });
        inAnimation.setAnimationListener(inAnimationListener);
        //出场动画
        Animation outAnimation = AnimationUtils.loadAnimation(this, R.anim.translate_out);
        outAnimation.setFillAfter(true);
        outAnimationController = new LayoutAnimationController(outAnimation);
        outAnimationController.setOrder(LayoutAnimationController.ORDER_NORMAL);
        outAnimationController.setDelay(0.2f);
        //设置动画结束监听器
        ListViewAnimationListener outAnimationListener = new ListViewAnimationListener(listView, new ListViewAnimationListener.OnAnimationFinishListener() {
            @Override
            public void onFinish() {
                canOpenAnim = true;
                canCloseAnim = true;
                listView.setVisibility(View.GONE);
            }
        });
        outAnimation.setAnimationListener(outAnimationListener);
    }

    public class MyAdapter extends BaseAdapter{

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

        @Override
        public Object getItem(int position) {
            return null;
        }

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if(convertView == null){
                convertView = View.inflate(MainActivity.this, R.layout.item, null);
                holder = new ViewHolder(convertView);
                convertView.setTag(holder);
            }else{
                holder = (ViewHolder) convertView.getTag();
            }
            holder.fillData(position);
            return convertView;
        }

        public class ViewHolder{
            private TextView txt;

            public ViewHolder(View rootView) {
                txt = (TextView) rootView.findViewById(R.id.txt);
            }

            public void fillData(int position){
                txt.setText("测试" + position);
            }
        }
    }

    private void openAnim(){
        if(canOpenAnim && listView.getVisibility() == View.GONE){
            //要将两个标记都置为false,在动画的结束监听器中置为true
            canOpenAnim = false;
            canCloseAnim = false;
            //每次打开都在第一行
            listView.setSelection(0);
            listView.setVisibility(View.VISIBLE);
            listView.setLayoutAnimation(inAnimationController);
            listView.startLayoutAnimation();
        }
    }

    private void closeAnim(){
        if(canCloseAnim && listView.getVisibility() == View.VISIBLE){
            //要将两个标记都置为false,在动画的结束监听器中置为true
            canOpenAnim = false;
            canCloseAnim = false;
            listView.setLayoutAnimation(outAnimationController);
            listView.startLayoutAnimation();
        }
    }
}

3. 动画与滑动时的冲突解决

当你弄完上面的代码之后,难道你以为就这么结束了?我还是要笑你太天真了。现在这样做:在点击开启动画的一瞬间,马上滑动ListView,此时是不是看见了很多Item是空白的?这是为什么呢?

我们知道ListView是有View的缓存的,在滚动的时候会自动将缓存的View进行再利用。此时开启动画是针对ListView内存中真实存在的View,也就是说在动画的时候再滑动,动画会与ListView的缓存策略相互冲突。主要有以下几种情况:

  1. 点击开启动画按钮时,马上滑动ListView
  2. 点击关闭动画按钮时,马上滑动ListView
  3. 点击ListView的item时,再马上滑动ListView
  4. 滑动ListView时,点击开启动画按钮
  5. 滑动ListView时,点击关闭动画按钮
  6. 滑动ListView时,再点击ListView的item

看见这么大一堆情况,是不是瞬间又懵逼了?不用怕,其实可以总结如下:

动画时要禁止ListView的滑动,滑动时要禁止ListView的动画

  • 禁止ListView的滑动可以通过设置ListView的setEnable(false),在开启动画时setEnabled(false),在动画完毕时设置setEnabled(true)
  • 滑动时禁止动画需要在OnScrollListener监听器回调中处理,当scrollState为SCROLL_STATE_IDLE时canOpenAnim和canCloseAnim设置为true,其他状态设置为false。
public class MainActivity extends Activity {

    private ListView listView;
    private MyAdapter adapter;
    //动画控制器,针对ViewGroup的动画
    private LayoutAnimationController inAnimationController;
    private LayoutAnimationController outAnimationController;
    //控制解决连续多次动画的冲突
    private boolean canOpenAnim = true;
    private boolean canCloseAnim = true;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initListView();
        initBaseUI();
    }

    private void initBaseUI() {
        findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                openAnim();
            }
        });
        findViewById(R.id.end).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                closeAnim();
            }
        });
    }

    private void initListView() {
        listView = (ListView) findViewById(R.id.listView);
        adapter = new MyAdapter();
        listView.setAdapter(adapter);
        //设置点击监听器
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                closeAnim();
        }
        });
        listView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE){
                    //停止状态,允许开启关闭动画
                    canOpenAnim = true;
                    canCloseAnim = true;
                }else{
                    canOpenAnim = false;
                    canCloseAnim = false;
                }
            }

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

            }
        });
        //入场动画
        Animation inAnimation = AnimationUtils.loadAnimation(this, R.anim.translate_in);
        inAnimation.setFillAfter(true);
        inAnimationController = new LayoutAnimationController(inAnimation);
        inAnimationController.setOrder(LayoutAnimationController.ORDER_NORMAL);
        inAnimationController.setDelay(0.2f);
        //设置动画结束监听器
        ListViewAnimationListener inAnimationListener = new ListViewAnimationListener(listView, new ListViewAnimationListener.OnAnimationFinishListener() {
            @Override
            public void onFinish() {
                canOpenAnim = true;
                canCloseAnim = true;
                //恢复ListView的enabled属性
                listView.setEnabled(true);
            }
        });
        inAnimation.setAnimationListener(inAnimationListener);
        //出场动画
        Animation outAnimation = AnimationUtils.loadAnimation(this, R.anim.translate_out);
        outAnimation.setFillAfter(true);
        outAnimationController = new LayoutAnimationController(outAnimation);
        outAnimationController.setOrder(LayoutAnimationController.ORDER_NORMAL);
        outAnimationController.setDelay(0.2f);
        //设置动画结束监听器
        ListViewAnimationListener outAnimationListener = new ListViewAnimationListener(listView, new ListViewAnimationListener.OnAnimationFinishListener() {
            @Override
            public void onFinish() {
                canOpenAnim = true;
                canCloseAnim = true;
                //恢复ListView的enabled属性
                listView.setEnabled(true);
                listView.setVisibility(View.GONE);
            }
        });
        outAnimation.setAnimationListener(outAnimationListener);
    }

    public class MyAdapter extends BaseAdapter{

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

        @Override
        public Object getItem(int position) {
            return null;
        }

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if(convertView == null){
                convertView = View.inflate(MainActivity.this, R.layout.item, null);
                holder = new ViewHolder(convertView);
                convertView.setTag(holder);
            }else{
                holder = (ViewHolder) convertView.getTag();
            }
            holder.fillData(position);
            return convertView;
        }

        public class ViewHolder{
            private TextView txt;

            public ViewHolder(View rootView) {
                txt = (TextView) rootView.findViewById(R.id.txt);
            }

            public void fillData(int position){
                txt.setText("测试" + position);
            }
        }
    }

    private void openAnim(){
        if(canOpenAnim && listView.getVisibility() == View.GONE){
            //要将两个标记都置为false,在动画的结束监听器中置为true
            canOpenAnim = false;
            canCloseAnim = false;
            //每次打开都在第一行
            listView.setSelection(0);
            listView.setVisibility(View.VISIBLE);
            //设置ListView的enabled属性使不可滑动
            listView.setEnabled(false);
            listView.setLayoutAnimation(inAnimationController);
            listView.startLayoutAnimation();
        }
    }

    private void closeAnim(){
        if(canCloseAnim && listView.getVisibility() == View.VISIBLE){
            //要将两个标记都置为false,在动画的结束监听器中置为true
            canOpenAnim = false;
            canCloseAnim = false;
            //设置ListView的enabled属性使不可滑动
            listView.setEnabled(false);
            listView.setLayoutAnimation(outAnimationController);
            listView.startLayoutAnimation();
        }
    }
}

4. 动画与ListView的缓存机制的冲突

当你完成了第3步,你是不是心里觉得这样总该差不多了吧?我只能告诉你快成功了,还有一个很关键的地方没有处理到。你也许想说,连滚动时的ListView的缓存机制都处理了,还能有什么情况?其实,滑动冲突只是缓存机制的一种情况,还有另外一种情况。这种情况出现在我项目中很长时间,一直没有找到对应的原因和解决办法。在一次偶然的机会我找到了原因,不看你肯定会后悔。

现在你这样做:首先点击开启动画按钮,将ListView展现出来,此时我的设备上展示了“测试0”到“测试23”一共24个Item,其中“测试23”只显示了部分。关键:现在往上滑动一点,让屏幕显示出“测试0”到“测试24”一共25个Item,其中“测试0”和“测试24”都是只显示了部分。你只需要保证滑动后的Item数会多一个就行。此时你点击关闭动画按钮,再点击开启动画按钮,往上滑动一点点,是不是发现了“测试24”这一项是一个空白呢?

为什么会这样呢?相信有的小伙伴儿已经心里有了答案。在关闭动画时,ListView有24项,在开启动画时,由于此时调用了setSession(0)方法,此时ListView只有23项,所以此时动画只针对这23项进行动画,而对第24项因为内部机制问题导致没有draw。

怎么解决呢?其实也是非常简单,在关闭动画开启前,手动再调用一次setSession(0),保证关闭动画和开启动画时,ListView的Item数是相同的就行

因为改动比较小,只添加了一行代码,因此就只粘贴这一段方法就行了:

private void closeAnim(){
    if(canCloseAnim && listView.getVisibility() == View.VISIBLE){
        //要将两个标记都置为false,在动画的结束监听器中置为true
        canOpenAnim = false;
        canCloseAnim = false;
        //关闭动画时,保证此时的Item数与开启动画时相等,否则会产生最后一项不绘制的问题
        listView.setSelection(0);
        //设置ListView的enabled属性使不可滑动
        listView.setEnabled(false);
        listView.setLayoutAnimation(outAnimationController);
        listView.startLayoutAnimation();
    }
}

5. 点击耗时任务处理时与滑动的冲突

本来完成了第4步,的确算大功告成了,但是在一次偶然的机会,我又玩出了一种冲突情况,这种情况就非常复杂了。

当我点击了Item响应OnItemClick后,先调用setEnabled(false),然后开启关闭动画,然后在动画监听器回调中,在调用了setEnabled(true)时,此时如果我再滑动ListView,由于enabled为true,visibility为VISIBLE,因此是可以滑动的,但是因为会马上执行动画监听器回调的后续代码,也就是调用setVisibility(View.GONE),因此此时的滑动效果是看不见的,但是的确是在滑动。此时如果滑动刚好导致Item数多了一个,那么下次再开启打开动画时,会导致最后一项无法绘制的问题,原因第4步已经讲了

也就是说,如果onItemClick比较耗时,此时点击一个item时,立刻使劲儿滑动ListView,会有一定几率导致下一次打开ListView时有一项无法绘制是空白项。

怎么解决呢?其实也很简单。一切都是在关闭动画监听器中设置了setEnabled(true)导致的,那么可以将这行代码注释掉,因为在开启动画监听器一定会调用setEnabled(true)。屏蔽掉在关闭动画后还有一定几率对ListView进行操作的情况。

//设置动画结束监听器
ListViewAnimationListener outAnimationListener = new ListViewAnimationListener(listView, new ListViewAnimationListener.OnAnimationFinishListener() {
    @Override
    public void onFinish() {
        canOpenAnim = true;
        canCloseAnim = true;
        //关闭动画后不释放enabled,在开启动画后释放,否则会在点击后使劲儿滑动ListView,
        //会有一定几率造成ListView滑动,造成Item数多一个,导致ListView最后一项无法绘制
//      listView.setEnabled(true);
        listView.setVisibility(View.GONE);
    }
});

6. 解决所有冲突的完整的Activity代码

public class MainActivity extends Activity {

    private ListView listView;
    private MyAdapter adapter;
    //动画控制器,针对ViewGroup的动画
    private LayoutAnimationController inAnimationController;
    private LayoutAnimationController outAnimationController;
    //控制解决连续多次动画的冲突
    private boolean canOpenAnim = true;
    private boolean canCloseAnim = true;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initListView();
        initBaseUI();
    }

    private void initBaseUI() {
        findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                openAnim();
            }
        });
        findViewById(R.id.end).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                closeAnim();
            }
        });
    }

    private void initListView() {
        listView = (ListView) findViewById(R.id.listView);
        adapter = new MyAdapter();
        listView.setAdapter(adapter);
        //设置点击监听器
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                closeAnim();
        }
        });
        listView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE){
                    //停止状态,允许开启关闭动画
                    canOpenAnim = true;
                    canCloseAnim = true;
                }else{
                    canOpenAnim = false;
                    canCloseAnim = false;
                }
            }

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

            }
        });
        //入场动画
        Animation inAnimation = AnimationUtils.loadAnimation(this, R.anim.translate_in);
        inAnimation.setFillAfter(true);
        inAnimationController = new LayoutAnimationController(inAnimation);
        inAnimationController.setOrder(LayoutAnimationController.ORDER_NORMAL);
        inAnimationController.setDelay(0.2f);
        //设置动画结束监听器
        ListViewAnimationListener inAnimationListener = new ListViewAnimationListener(listView, new ListViewAnimationListener.OnAnimationFinishListener() {
            @Override
            public void onFinish() {
                canOpenAnim = true;
                canCloseAnim = true;
                //恢复ListView的enabled属性
                listView.setEnabled(true);
            }
        });
        inAnimation.setAnimationListener(inAnimationListener);
        //出场动画
        Animation outAnimation = AnimationUtils.loadAnimation(this, R.anim.translate_out);
        outAnimation.setFillAfter(true);
        outAnimationController = new LayoutAnimationController(outAnimation);
        outAnimationController.setOrder(LayoutAnimationController.ORDER_NORMAL);
        outAnimationController.setDelay(0.2f);
        //设置动画结束监听器
        ListViewAnimationListener outAnimationListener = new ListViewAnimationListener(listView, new ListViewAnimationListener.OnAnimationFinishListener() {
            @Override
            public void onFinish() {
                canOpenAnim = true;
                canCloseAnim = true;
                //关闭动画后不释放enabled,在开启动画后释放,否则会在点击后使劲儿滑动ListView,
                //会有一定几率造成ListView滑动,造成Item数多一个,导致ListView最后一项无法绘制
//                listView.setEnabled(true);
                listView.setVisibility(View.GONE);
            }
        });
        outAnimation.setAnimationListener(outAnimationListener);
    }

    public class MyAdapter extends BaseAdapter{

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

        @Override
        public Object getItem(int position) {
            return null;
        }

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if(convertView == null){
                convertView = View.inflate(MainActivity.this, R.layout.item, null);
                holder = new ViewHolder(convertView);
                convertView.setTag(holder);
            }else{
                holder = (ViewHolder) convertView.getTag();
            }
            holder.fillData(position);
            return convertView;
        }

        public class ViewHolder{
            private TextView txt;

            public ViewHolder(View rootView) {
                txt = (TextView) rootView.findViewById(R.id.txt);
            }

            public void fillData(int position){
                txt.setText("测试" + position);
            }
        }
    }

    private void openAnim(){
        if(canOpenAnim && listView.getVisibility() == View.GONE){
            //要将两个标记都置为false,在动画的结束监听器中置为true
            canOpenAnim = false;
            canCloseAnim = false;
            //每次打开都在第一行
            listView.setSelection(0);
            listView.setVisibility(View.VISIBLE);
            //设置ListView的enabled属性使不可滑动
            listView.setEnabled(false);
            listView.setLayoutAnimation(inAnimationController);
            listView.startLayoutAnimation();
        }
    }

    private void closeAnim(){
        if(canCloseAnim && listView.getVisibility() == View.VISIBLE){
            //要将两个标记都置为false,在动画的结束监听器中置为true
            canOpenAnim = false;
            canCloseAnim = false;
            //关闭动画时,保证此时的Item数与开启动画时相等,否则会产生最后一项不绘制的问题
            listView.setSelection(0);
            //设置ListView的enabled属性使不可滑动
            listView.setEnabled(false);
            listView.setLayoutAnimation(outAnimationController);
            listView.startLayoutAnimation();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值