Android Animation动画实战(一): 从布局动画引入ListView滑动时,每一Item项的显示动画

简介:

前言:

  之前,我已经写了两篇博文,给大家介绍了Android的基础动画是如何实现的,如果还不清楚的,可以点击查看:Android Animation动画详解(一): 补间动画 及 Android Animation动画详解(二): 组合动画特效 。 已经熟悉了基础动画的实现后,便可以试着去实现常见APP中出现过的那些精美的动画。今天我主要给大家引入一个APP的ListView的动画效果: 当展示ListView时,Listview的每一个列表项都按照规定的动画显示出来。

  说起来比较抽象,先给大家看一个动画效果,这是APP窝牛装修的ListView显示动画:


    有木有觉得很酷炫?有木有啊!?


一、Layout Animation

    所谓的布局动画,其实就是为ViewGroup添加显示动画效果,主要用过LayoutAnimationController来控制实现。LayoutAnimationController用于为一个Layout里面的控件,或者是一个ViewGroup里面的控件设置动画效果,可以在XML文件中设置,亦可以在Java代码中设置。


1.1 在XML文件中设置布局动画


  首先,我们在res/anim文件夹下建立一个list_anim_layout.xml文件,该文件就是布局动画控制器。

<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:delay="30%"
    android:animationOrder="random"
    android:animation="@anim/slide_right" />

android:delay  子类动画时间间隔 (延迟)   70% 也可以是一个浮点数 如“1.2”等。
android:animationOrder="random"   子类的显示方式 random表示随机。
android:animationOrder 的取值有 
     normal 0   默认
     reverse 1 倒序
     random 2  随机
android:animation="@anim/slide_right" 表示列表项显示时的具体动画是什么!

下面,我们定义每一个列表项显示时的动画效果吧,及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>

显示的效果为ListView第一次出现的时候为 item随机出现 每个Item都是从右边的区域向左滑动到显示的地方。

接下来,你只需要把这个布局动画,指定到ViewGroup上就好了:

<ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layoutAnimation="@anim/list_anim_layout"
        >
</ListView>
就这么简单就完成了,快来看下效果吧:




1.2 在Java代码中实现布局动画

    在Java代码中实现布局动画并无难度,只要熟悉几个API的使用即可。 关于动画的定义和上文一致,只是,你不需要再在把控制动画应用到listview中了,即android:layoutAnimation="@anim/list_anim_layout"这行代码可以删除。

    接下来,需要在Java代码中进行配置:

    private void startLayoutAnim() {
        //通过加载XML动画设置文件来创建一个Animation对象;
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.slide_right);
        //得到一个LayoutAnimationController对象;
        LayoutAnimationController lac = new LayoutAnimationController(animation);
        //设置控件显示的顺序;
        lac.setOrder(LayoutAnimationController.ORDER_REVERSE);
        //设置控件显示间隔时间;
        lac.setDelay(1);
        //为ListView设置LayoutAnimationController属性;
        listView.setLayoutAnimation(lac);
    }


    观察下,效果和之前使用xml文件肯定是一致的了。

    

    介绍到这里,可能有人会有疑问了,博文一开始介绍的“窝牛装修”的那种效果,是每一个列表项显示的时候才会显示动画。我们这个确实所有的列表项的动画一起都显示了,只是显示顺序不同而已。 通过我们这种方法,怎么可能会达到那种效果呢?

    确实,通过布局动画,没办法控制每一个Item在加载时才显示动画。那该如何是好呢?

    兄弟们,换个思路吧,如果布局动画完成不了,何必不直接用简单的补间动画,再结合每个列表项的显示控制,来实现窝牛装修列表显示的效果呢? 

    那有人会问了,怎么知道每一个列表项何时才加载么?

    你难道忘了BaseAdapter的getView()方法了么?

    没错,每当一个列表项显示时,都会主动调用BaseAdaper的getView()方法。


二、仿窝牛装修List列表的动画效果

    首先,我们定义一个动画资源,该动画即是列表项显示时的动画: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>

    该平移动画表示,从下往上,垂直平移100px,时间为500毫秒。

    接下来,我们需要在BaseAdapter的getView()方法里,去使用该动画:

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) {

            // 下拉项布局
            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;
    }

}

    我们来简要分析应用动画的地方: 1、我们Adapter的构造方法里加载了之前定义的动画,活的Animation对象。 2、 我们在getView方法里,为convertView设置并启动Animation,即convertView.startAnimation(animation)。

    够简单吧,只是这么两行代码,就可以实现在加载每一个View Item时启动动画效果。

    可是,我们发现,这并不是非常完美的实现,为啥这么说呢?

    因为你此刻往上滑动列表,会发现,已经加载过的Item的动画还会再次启动执行一次。这个体验太糟糕了。为啥会出现这种情况啊?

    因为getVIew方法的调用时机会对动画产生影响。Adapter中的getView方法,会在每一个item处于可见状态时调用,所以无论你上滑还是下滑,都会重复调用getView方法(这也是ListView为啥在使用时要进行优化的地方)。

    所以,为了解决刚刚发生的问题,我们可以设置标识,进行判断,已经加载过的view的动画不再进行启动加载。

    完整的代码如下:

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) {

            // 下拉项布局
            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,则使用动画
        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;
    }

}

     看到了么,加了一个isFirst进行判断,这样,就可以有效控制动画的显示了。效果如下:


   

结束:

     本文我主要介绍了两个部分,一个是Layout Animation布局动画,使用布局动画可以控制VIew Groups中的每一个数据的显示动画; 还一个就是实战,仿“窝牛装修”ListView滑动时每一个Item滑动进入可见状态的动画效果。通过这两个动画示例,我相信可以帮助大家更好的处理动画,克服“动画恐惧症”。


源码下载地址(免费):http://download.csdn.net/detail/zuiwuyuan/9051895

相关文章
|
26天前
|
ARouter Android开发
Android不同module布局文件重名被覆盖
Android不同module布局文件重名被覆盖
|
20天前
|
缓存 前端开发 Android开发
Android实战之如何截取Activity或者Fragment的内容?
本文首发于公众号“AntDream”,介绍了如何在Android中截取Activity或Fragment的屏幕内容并保存为图片。包括截取整个Activity、特定控件或区域的方法,以及处理包含RecyclerView的复杂情况。
18 3
|
2月前
|
Android开发 开发者 索引
Android实战经验之如何使用DiffUtil提升RecyclerView的刷新性能
本文介绍如何使用 `DiffUtil` 实现 `RecyclerView` 数据集的高效更新,避免不必要的全局刷新,尤其适用于处理大量数据场景。通过定义 `DiffUtil.Callback`、计算差异并应用到适配器,可以显著提升性能。同时,文章还列举了常见错误及原因,帮助开发者避免陷阱。
148 9
|
29天前
|
Android开发
Android实战之如何快速实现自动轮播图
本文介绍了在 Android 中使用 `ViewPager2` 和自定义适配器实现轮播图的方法,包括添加依赖、布局配置、创建适配器及实现自动轮播等步骤。
21 0
|
2月前
|
开发工具 Android开发 git
Android实战之组件化中如何进行版本控制和依赖管理
本文介绍了 Git Submodules 的功能及其在组件化开发中的应用。Submodules 允许将一个 Git 仓库作为另一个仓库的子目录,有助于保持模块独立、代码重用和版本控制。虽然存在一些缺点,如增加复杂性和初始化时间,但通过最佳实践可以有效利用其优势。
33 3
|
30天前
|
Android开发
Android开发显示头部Bar的需求解决方案--Android应用实战
Android开发显示头部Bar的需求解决方案--Android应用实战
19 0
|
2月前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
在Android应用开发中,追求卓越性能是不变的主题。本文介绍如何利用Android NDK(Native Development Kit)结合Java与C++进行混合编程,提升应用性能。从环境搭建到JNI接口设计,再到实战示例,全面展示NDK的优势与应用技巧,助你打造高性能应用。通过具体案例,如计算斐波那契数列,详细讲解Java与C++的协作流程,帮助开发者掌握NDK开发精髓,实现高效计算与硬件交互。
119 1
|
1月前
|
ARouter Android开发
Android不同module布局文件重名被覆盖
Android不同module布局文件重名被覆盖
98 0
|
前端开发 程序员 开发工具
|
前端开发 程序员 开发工具