快来玩下拉刷新动画

前言

提及下拉刷新和上拉加载你们应该都不陌生。从ListView时代的PulltoRefreshView到RecycleView时代的SwipeRefreshLayout,再到Github上封装好的适应各类View,而且同时支持下拉刷新和上拉加载的库;对于实现这样的一个功能已是轻车熟路了。可是正是因为这种状况,致使了如下几个问题。javascript

  • 实现的效果单一,没有自身的特性

这一点使用过Google提供的SwipeRefreshLayout的同窗应该深有体会,这个控件的确好用,把它套在RecycleView的外面,实现相应的接口就能够很方便的实现下拉刷新的效果。可是它可以实现的交互效果也很单一,甚至说有点无聊,基本上没有可定制的内容,咱们能作的最多就是改一改旋转的ProgressBar的颜色及其背景颜色。java

  • 集成三方控件,过于冗余

相信如今大部分人遇到列表都是用RecycleView来实现,可是因为SwipeRefreshLayout没有自带上拉加载的功能,因此咱们只能曲线救国;要么复写SwipeRefreshLayout本身加上上拉加载的功能;要么放弃使用SwipeRefreshLayout而是从github上找其余的实现方式,后者应该是不少人的选择;可是使用第三方控件,也会带来额外的一些问题,好比三方控件过于的重和庞大,功能过于复杂,有时候咱们为了某一个简单的功能,可能要引入一些其余无用的代码,这是很差的。android

所以,咱们须要一款轻量级的库,他可以让咱们更自由的定制下拉加载和上拉刷新时的效果,同时又不会过于的繁重。git

UltimateRefreshView

一个让你关注于刷新动画效果的下拉刷新,上拉加载的控件。
老规矩,先来看看效果。github

美团下拉刷新动画

QQ 下拉动画

WebView下拉刷新动画

这里放了几张能看清的动画,这些动画效果彻底不一样;可是只用一个UltimateRefreshView就够了。更多动画效果请到Github查看网络

功能

  • 支持ListView,RecycleView,ScrollView,WebView
  • 一行代码指定是否支持上拉加载,下拉刷新
  • 自由定制刷新时头部和尾部的动画效果

使用方式

首先,是引入库app

compile 'com.reoobter:ultrapullview:1.0.0'复制代码

其次,实现各自的动画效果ide

这里咱们就以美团APP顶部下拉刷新的动画为例来看看如何实现动画效果布局

meituan_header_refresh_layout.xmlpost

<?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" android:background="@color/white" android:orientation="vertical"> <ImageView android:id="@+id/loading" android:layout_width="50dp" android:layout_height="50dp" android:layout_margin="10dp" android:scaleX="0" android:scaleY="0" android:src="@drawable/pull_image"/> </LinearLayout>复制代码

这个布局文件很简单,整个只有一个ImageView。咱们的实现思路,就是在不一样的结点修改ImageView的内容,从而呈现出整个下拉刷新时全部的动画效果。那么这些结点是哪些呢?

public class MeiTuanHeaderAdapter extends BaseHeaderAdapter {

    private ImageView loading;
    private int viewHeight;
    private float pull_distance=0;

    public MeiTuanHeaderAdapter(Context context) {
        super(context);
    }

    @Override
    public View getHeaderView() {
        View mView = mInflater.inflate(R.layout.meituan_header_refresh_layout, null, false);
        loading = (ImageView) mView.findViewById(R.id.loading);
        MeasureTools.measureView(mView);
        viewHeight = mView.getMeasuredHeight();
        return mView;
    }

    @Override
    public void pullViewToRefresh(int deltaY) {
        //这里乘以0.3 是由于UltimateRefreshView 源码中对于滑动有0.3的阻尼系数,为了保持一致
        pull_distance=pull_distance+deltaY*0.3f;
        float scale = pull_distance / viewHeight;
        loading.setScaleX(scale);
        loading.setScaleY(scale);

    }


    @Override
    public void releaseViewToRefresh(int deltaY) {
        loading.setImageResource(R.drawable.mei_tuan_loading_pre);
        AnimationDrawable mAnimationDrawable= (AnimationDrawable) loading.getDrawable();
        mAnimationDrawable.start();
    }

    @Override
    public void headerRefreshing() {
        loading.setImageResource(R.drawable.mei_tuan_loading);
        AnimationDrawable mAnimationDrawable= (AnimationDrawable) loading.getDrawable();
        mAnimationDrawable.start();
    }

    @Override
    public void headerRefreshComplete() {
        loading.setImageResource(R.drawable.pull_image);
        loading.setScaleX(0);
        loading.setScaleY(0);
        pull_distance=0;
    }
}复制代码

经过代码咱们能够总结出有4个重要的结点

  • 下拉进行时,这个时候随着手指滑动,整个顶部的view逐渐显示出来
  • 顶部view彻底被下拉出来,这个时候顶部view已经彻底显示出来了,手指释放(抬起)后将进入下一个结点。
  • 正在刷新进行时,刷新进行时,这个结点就是刷新动画执行的时候。
  • 刷新完成,在这个结点触发了刷新完成的动做

为了实现美团顶部刷新动画的效果,在第一个结点咱们便开始执行动画,根据刷新的位移比,使用scale动画逐渐放大初始图片(绿色椭圆);在第二个结点,这个结点通常都很短暂,这个时候顶部已经彻底展现,执行了小人偶翻转出现的动画;在第三个结点,这个结点通常是比较耗时的,在这里用帧动画播放了一个小人偶左右摇摆的动画;最后,在第四个结点,将全部内容初始化到下拉以前的状态,方便下次下拉刷星动画的执行。这样就完成了一次下拉刷新的动画效果。

下面就能够很是方便的把这个动画效果运用到View上。

最后,将动画效果适配到UltimateRefreshView之上

这里就以ListView为例。

首先是布局实现:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" tools:context=".subfragment.ListViewFragment" >

    <com.sak.ultilviewlib.UltimateRefreshView android:id="@+id/refreshView" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" android:orientation="vertical">

        <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="none"/>

    </com.sak.ultilviewlib.UltimateRefreshView>

</FrameLayout>复制代码

布局文件很简单,将所要实现的下拉刷新的控件放在UltimateRefreshView控件内便可。

public class ListViewFragment extends Fragment {
    private UltimateRefreshView mUltimateRefreshView;

    private int page = 0;
    private int PER_PAGE_NUM = 15;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_list_view, container, false);
        initView(view);
        return view;
    }

    private void initView(View view) {
        View headview = LayoutInflater.from(getContext()).inflate(R.layout.list_headview_layout,
                null, false);
        ListView listView = (ListView) view.findViewById(R.id.listView);
        final List<String> datas = new ArrayList<>();
        for (int i = 0; i < PER_PAGE_NUM; i++) {
            datas.add("this is item " + i);
        }
        final ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, datas);
        listView.setAdapter(adapter);
        listView.addHeaderView(headview);
        mUltimateRefreshView = (UltimateRefreshView) view.findViewById(R.id.refreshView);
        mUltimateRefreshView.setBaseHeaderAdapter(new MeiTuanHeaderAdapter(getContext()));
        mUltimateRefreshView.setBaseFooterAdapter();
        mUltimateRefreshView.setOnHeaderRefreshListener(new OnHeaderRefreshListener() {
            @Override
            public void onHeaderRefresh(UltimateRefreshView view) {
                page = 0;
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        datas.clear();
                        for (int i = page * PER_PAGE_NUM; i < PER_PAGE_NUM; i++) {
                            datas.add("this is item " + i);
                        }
                        adapter.notifyDataSetChanged();
                        mUltimateRefreshView.onHeaderRefreshComplete();
                    }
                }, 2000);
            }
        });

        mUltimateRefreshView.setOnFooterRefreshListener(new OnFooterRefreshListener() {
            @Override
            public void onFooterRefresh(UltimateRefreshView view) {
                page++;
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = page * PER_PAGE_NUM; i < (page + 1) * PER_PAGE_NUM; i++) {
                            datas.add("this is item " + i);
                        }
                        adapter.notifyDataSetChanged();
                        mUltimateRefreshView.onFooterRefreshComplete();
                    }
                }, 200);
            }
        });
    }

}复制代码

这里最关键的代码就是:

mUltimateRefreshView.setBaseHeaderAdapter(new MeiTuanHeaderAdapter(getContext()));
        mUltimateRefreshView.setBaseFooterAdapter();复制代码

这样,就为ListView设定了下拉刷新和上拉加载时的动画效果。

为了方便使用,同时提供了无参的setAdapter方法,当不提供参数时,将使用默认的动画效果。

这里,下拉刷新使用了咱们刚才定义的MeiTuanHeaderAdapter这个效果,上拉加载的动画效果则使用了默认的效果;固然,若是你不须要上拉加载效果或下拉刷新效果的话,不设定对应的Adapter便可。即不执行(setBaseFooterAdapter和setBaseHeaderAdapter方法)

最后,为mUltimateRefreshView设置属性动画执行时的监听器,这个回调方法会在刷新动画执行时(也就是上面所说的第三个结点时开始执行时)被调用,在这个方法里咱们通常会进行网络请求或一些耗时操做,这里咱们用Handler模拟了一个简单的耗时任务,当耗时操做完成时,调用对应的刷新完成方法,这样一次下拉刷新或者是上拉加载就执行完了(也就是上面所说的第四个结点)。

这里重点讨论整个刷新过程当中动画效果的实现,下拉或上拉时数据如何刷新不作重点分析。

当咱们须要下拉刷新动画时,继承BaseHeaderAdapter类并在各个方法(即下拉事件的各个结点)按动画所需的效果,作不一样的实现便可。

同理,当须要上拉加载动画时,继承BaseFooterAdapter便可。BaseFooterAdapter类中结点的定义和BaseHeaderAdapter类中彻底一致,只不过滑动方向从下拉变成了上拉而已。这里以简单模仿一下京东上拉加载时的动画效果。

package com.sak.app.adapter;

import android.content.Context;
import android.view.View;
import android.widget.ImageView;

import com.bumptech.glide.Glide;
import com.sak.app.R;
import com.sak.ultilviewlib.adapter.BaseFooterAdapter;

/** * Created by engineer on 2017/4/30. */

public class JDAppFooterAdapter extends BaseFooterAdapter {
    private ImageView loading;
    private Context mContext;

    public JDAppFooterAdapter(Context context) {
        super(context);
        mContext=context;
    }

    @Override
    public View getFooterView() {
        View footerView = mInflater.inflate(R.layout.jd_footer_refresh_layout, null, false);
        loading = (ImageView) footerView.findViewById(R.id.loading);
        return footerView;
    }

    @Override
    public void pullViewToRefresh(int deltaY) {
        Glide.with(mContext).load(R.drawable.jd_loading_logo).into(loading);
    }

    @Override
    public void releaseViewToRefresh(int deltaY) {

    }

    @Override
    public void footerRefreshing() {

    }

    @Override
    public void footerRefreshComplete() {
        loading.setImageDrawable(null);
    }
}复制代码

这里咱们继承了BaseFooterAdapter,能够看到他和BaseHeaderAdapter 十分类似。这里为了方便,没有作很复杂的实现,在上拉开始执行的时候,取巧的用Glide加载了一张gif 的动画,这样上拉时就会呈现一个简单的动画。如今用户在上拉时通常都会很快完成分页数据的加载,上拉动画的实现其实不用太复杂。

更多内容能够参考源码中demo的实现。

总结

看以看到,咱们设置动画刷新的方法是在mUltimateRefreshView上执行的,也就说不管在mUltimateRefreshView内部嵌套的是ListView,仍是RecycleView或者ScrollView都同样。头部和尾部的动画效果彻底不受嵌套子View的影响。所以,咱们能够将更多的精力用于实现头部和尾部刷新时绚丽的动画效果,而再也不纠结于各类滑动事件的处理。

头部和尾部动画的实现,彻底和mUltimateRefreshView自己是分离的,极大的减小了耦合。须要什么样的动画,经过setBaseFooterAdapter和setBaseHeaderAdapter方法进行设置便可,十分灵活,不一样的头部和尾部动画能够自由搭配。


因此,赶忙来玩吧!

最后,再次给出Github 源码。欢迎感兴趣的同窗 star & fork 。

相关文章
相关标签/搜索