Jake大神都去Google搞Kotlin了~
ISwipeRefreshLayout是基于SwipeRefreshLayout源碼基礎(chǔ)上修改掌桩,便于使用自定義loading樣式的下拉刷新組件遣蚀。
為什么寫這個組件矾麻?
原生的SwipeRefreshLayout好歸好,但它不能自定義動畫效果芭梯,只能簡單改下color险耀、alpha等,往往實(shí)際開發(fā)中都是自家的loading效果玖喘。所以就有了ISwipeRefreshLayout甩牺。
這里貼一個現(xiàn)在項(xiàng)目使用的loading。
錄屏的問題動畫太快了
還不錯吧累奈,說實(shí)話雀氏有點(diǎn)low贬派。但它不是畫出來了,是幀動畫在切換费尽,而且內(nèi)存控制的很好赠群,推薦下這個組件FrameAnimDrawable。
用法
和SwipeRefreshLayout幾乎一樣旱幼,并且適用于所有view查描。
- xml布局
<io.jiantao.android.uikit.refresh.ISwipeRefreshLayout
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</io.jiantao.android.uikit.refresh.ISwipeRefreshLayout>
- 代碼中
refreshLayout = (ISwipeRefreshLayout) findViewById(R.id.refresh_layout);
refreshLayout.setOnRefreshListener(new ISwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
// do refresh ...
handler.sendEmptyMessageDelayed(1, 3000);
}
});
refreshLayout.setRefreshHeaderView(new AvLoadingRefreshView(this));
//------------------------------
//手動刷新和取消(ture/false)
ISwipeRefreshLayout.setRefreshing(true);
//另外一種方式,當(dāng)然就是下拉刷新了,內(nèi)部實(shí)現(xiàn)下文會提到冬三。
實(shí)現(xiàn)過程
- 剔除原有效果
// 這兩個類就是原生那個轉(zhuǎn)的圈圈
CircleImageView mCircleView;
MaterialProgressDrawable mProgress;
把SwipeRefreshLayout代碼爬出來匀油,發(fā)現(xiàn)就這兩個依賴類,剔除簡單方便勾笆。
- 自定義loading
//定義成員變量view
private View mRefreshView;
...
//并添加設(shè)置入口
public void setRefreshHeaderView(View view) {
if(view == null){//為了安全敌蚜,這里還應(yīng)該判斷下是否正在執(zhí)行動畫
return;
}
removeView(mRefreshView);//移除舊的
this.mRefreshView = view;
view.setMinimumHeight(HEADER_VIEW_MIN_HEIGHT);
addView(view);//添加新的
getRefreshTrigger().init();
}
以上代碼很簡單,暴露自定義view方法窝爪。
// onMeasure 方法計(jì)算view高度
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
... 省略代碼 ...
if(mRefreshView != null){
//計(jì)算view寬高
measureChild(mRefreshView, widthMeasureSpec, heightMeasureSpec);
//緩存一些變量
updateBaseValues(mRefreshView.getMeasuredHeight());
mRefreshViewIndex = -1;
// Get the index of the mRefreshView. 這里是用于改變view的繪制順序的弛车。
for (int index = 0; index < getChildCount(); index++) {
if (getChildAt(index) == mRefreshView) {
mRefreshViewIndex = index;
break;
}
}
}
}
// onLayout 方法布局
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int width = getMeasuredWidth();
final int childTop = getPaddingTop();
... 省略代碼 ...
if(mRefreshView != null){
int refreshWidth = mRefreshView.getMeasuredWidth();
//使mRefreshView相對父控件水平方向居中,豎直方向在父控件的上面位置蒲每,剛好看不見纷跛。mRefreshViewHeight就是onMeasure得到的值。
mRefreshView.layout((width/2 - refreshWidth/2), childTop - mRefreshViewHeight, (width/2 + refreshWidth/2), childTop);
}
}
以上為計(jì)算和布局過程修改邀杏,下面說下滑動過程處理贫奠,更新百分比,view滑動望蜡,觸發(fā)回調(diào)刷新等等唤崭。
//手指move事件觸發(fā)此方法
@SuppressLint("NewApi")
private void moveSpinner(float overscrollTop) {
... 省略代碼 ...
if (mScale) {//大小縮放動畫
setAnimationProgress(Math.min(1f, overscrollTop / mTotalDragDistance));
}
float progress = overscrollTop / mTotalDragDistance;
if (progress < 1.0f) {//下拉至觸發(fā)點(diǎn)的百分比
getRefreshTrigger().onPullDownState(progress);
} else {//釋放即可觸發(fā)刷新
getRefreshTrigger().onReleaseToRefresh();
}
//下拉過程view的移動距離
final float tranlationY = overscrollTop > mTotalDragDistance ? mTotalDragDistance : overscrollTop;
//執(zhí)行移動動畫
translateContentViews(tranlationY);
}
畢竟是在源碼基礎(chǔ)上改動,整個過程還是比較簡單脖律。
感興趣的朋友可參考修改后的代碼谢肾。
最后,推薦一個動畫庫,效果很棒状您,代碼簡潔勒叠,本文ISwipeRefreshLayout組件可直接使用。代碼在手膏孟,想怎么搞都行。
avi.gif
loading_test.gif
文中難免有錯誤之處拌汇,還望指出柒桑,歡迎評論交流。