提及下拉刷新和上拉加載你們應該都不陌生。從ListView時代的PulltoRefreshView到RecycleView時代的SwipeRefreshLayout,再到Github上封裝好的適應各類View,而且同時支持下拉刷新和上拉加載的庫;對於實現這樣的一個功能已是輕車熟路了。可是正是因爲這種狀況,致使瞭如下幾個問題。javascript
這一點使用過Google提供的SwipeRefreshLayout的同窗應該深有體會,這個控件的確好用,把它套在RecycleView的外面,實現相應的接口就能夠很方便的實現下拉刷新的效果。可是它可以實現的交互效果也很單一,甚至說有點無聊,基本上沒有可定製的內容,咱們能作的最多就是改一改旋轉的ProgressBar的顏色及其背景顏色。java
相信如今大部分人遇到列表都是用RecycleView來實現,可是因爲SwipeRefreshLayout沒有自帶上拉加載的功能,因此咱們只能曲線救國;要麼複寫SwipeRefreshLayout本身加上上拉加載的功能;要麼放棄使用SwipeRefreshLayout而是從github上找其餘的實現方式,後者應該是不少人的選擇;可是使用第三方控件,也會帶來額外的一些問題,好比三方控件過於的重和龐大,功能過於複雜,有時候咱們爲了某一個簡單的功能,可能要引入一些其餘無用的代碼,這是很差的。android
所以,咱們須要一款輕量級的庫,他可以讓咱們更自由的定製下拉加載和上拉刷新時的效果,同時又不會過於的繁重。git
一個讓你關注於刷新動畫效果的下拉刷新,上拉加載的控件。
老規矩,先來看看效果。github
這裏放了幾張能看清的動畫,這些動畫效果徹底不一樣;可是隻用一個UltimateRefreshView就夠了。更多動畫效果請到Github查看網絡
首先,是引入庫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個重要的結點
爲了實現美團頂部刷新動畫的效果,在第一個結點咱們便開始執行動畫,根據刷新的位移比,使用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 。