快來玩下拉刷新動畫

前言

提及下拉刷新和上拉加載你們應該都不陌生。從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 。

相關文章
相關標籤/搜索