給SwipeRefreshLayout換個免費的皮膚

![origin.gif](https://upload-images.jianshu.io/upload_images/5928293-6adbc13b242e4af1.gif?imageMogr2/auto-orient/strip)

SwipeRefreshLayout是Androidx提供了提供的下拉刷新組件聂儒,具體如何使用就不說了虎锚,相信大家也都經(jīng)常用。

1衩婚,效果

首先看一下SwipeRefreshLayout的默認(rèn)效果:

origin.gif

為了不耽誤你的時間窜护,先看一下最終效果:

GIF.gif

2,常用方法

方法 解釋
setColorSchemeResources(int…colorReslds) 設(shè)置下拉進(jìn)度條的顏色主題非春,參數(shù)可變柱徙,并且是資源id,最多設(shè)置四種不同的顏色奇昙。
setProgressBackgroundSchemeResource(int coloRes) 設(shè)置下拉進(jìn)度條的背景顏色,默認(rèn)白色护侮。
isRefreshing() 判斷當(dāng)前的狀態(tài)是否是刷新狀態(tài)。
setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener) 設(shè)置監(jiān)聽储耐,需要重寫onRefresh()方法羊初,頂部下拉時會調(diào)用這個方法,在里面實現(xiàn)請求數(shù)據(jù)的邏輯什湘,設(shè)置下拉進(jìn)度條消失等等长赞。
setRefreshing(boolean refreshing) 設(shè)置刷新狀態(tài),true表示正在刷新闽撤,false表示取消刷新得哆。

盡管SwipeRefreshLayout提供了setColorSchemeResources()方法可以設(shè)置幾個'圈圈'的顏色,但效果還是差強(qiáng)人意哟旗,作為高級代碼搬運(yùn)工的我實在看不下去了贩据。

于是幫SwipeRefreshLayout換個免費的皮膚吧栋操。

3,分析

SwipeRefreshLayout的代碼很簡單乐设,就三個類

  • SwipeRefreshLayout

    就是我們在xml中使用的布局,根據(jù)情況攔截并消費手勢事件绎巨,更新Loading視圖的位置近尚,控制其轉(zhuǎn)動角度等,具體邏輯不再這里展開场勤,有需要的大佬自己去看吧戈锻。

  • CircleImageView

    下拉時拉出來的圓圈

  • CircularProgressDrawable

    下拉及刷新時轉(zhuǎn)動的圈圈

他們?nèi)齻€關(guān)系非常緊密,在SwipeRefreshLayout中創(chuàng)建了CircleImageViewCircularProgressDrawable和媳,將CircularProgressDrawable設(shè)置給CircleImageView格遭,然后又將CircleImageView添加到了SwipeRefreshLayout中,代碼如下:

public class SwipeRefreshLayout {
    // 1留瞳,聲明一個CircleImageView
    CircleImageView mCircleVew;
    // 1拒迅,聲明一個CircularProgressDrawable
    CircularProgressDrawable mProgress;

    public SwipeRefreshLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        ...
        // 構(gòu)造方法中初始化
        createProgressView();
        ...
    }

    private void createProgressView() {
        // 2,初始化mCircleView
        mCircleView = new CircleImageView(getContext());
        // 2她倘,初始化mProgress
        mProgress = new CircularProgressDrawable(getContext());
        // 3璧微,將mProgress設(shè)置為mCircleView的Drawable
        mCircleView.setImageDrawable(mProgress);
        mCircleView.setVisibility(View.GONE);
        // 4,添加mCircleView在當(dāng)前ViewGroup中
        addView(mCircleView);
    }
}
結(jié)論

1硬梁,由于CircularProgressDrawable是控制這個'皮膚'的關(guān)鍵前硫,因此需要先定義一個Drawable,并實現(xiàn)相應(yīng)的效果荧止。

2屹电,由于SwipeRefreshLayout沒有提供擴(kuò)展的接口,無法直接使用自定義的Drawable跃巡,因此需要將SwipeRefreshLayout拷貝一份危号,修改其中的代碼。

3素邪,CircleImageView貌似是可以使用的葱色,但是其中添加了一個半透明背景,無法通過設(shè)置的方式去除娘香,因此也需要重寫一下苍狰。

4,擼碼

1烘绽,自定義LoadingDrawable

自定義一個Drawable并實現(xiàn)Animatable接口淋昭,在其中繪制圖形及動畫,具體如何實現(xiàn)可以參考LoadingDrawable安接,效果如下:

dialog.gif
2翔忽,重寫SwipeRefreshLayout

SwipeRefreshLayout的代碼拷貝一份,重命名為MySwipeRefreshLayout,如下:

public class MySwipeRefreshLayout extends ViewGroup implements NestedScrollingParent3,
        NestedScrollingParent2, NestedScrollingChild3, NestedScrollingChild2, NestedScrollingParent,
        NestedScrollingChild {
    ...
    CircularProgressDrawable mProgress;
    ...
}

只需要將其中的mProgress改為我們上面定義的LoadingDrawable即可歇式,然后刪除一些無用的代碼即可驶悟。看一下效果:

loading0.gif

這樣就有了我們想要的效果材失,但是發(fā)現(xiàn)動畫中有一個圓形的陰影背景痕鳍。分析一下,首先這個背景我不是我們定義的Drawable中的龙巨,那就看看Drawable所在的CircleImageView吧笼呆,代碼如下:

class CircleImageView extends ImageView {
    ...
    CircleImageView(Context context) {
        super(context);
        // 創(chuàng)建一個OvalShape的ShapeDrawable
        ShapeDrawable circle = new ShapeDrawable(new OvalShape());
        // 繪制
        ViewCompat.setBackground(this, circle);
    }
    
    private static class OvalShadow extends OvalShape {
        ...
        @Override
        public void draw(Canvas canvas, Paint paint) {
            // 繪制圓形
            canvas.drawCircle(x, y, x, mShadowPaint);
            canvas.drawCircle(x, y, x - mShadowRadius, paint);
        }
    }
    
}   

可見這個背景是在CircleImageView中繪制的,由于CircleImageView沒有提供對應(yīng)的方法控制是否繪制背景旨别,因此要想去掉這個背景就需要自定義一個CircleImageView诗赌。

3,自定義CircleImageView

由于我們的目的是要去掉背景秸弛,因此這個就非常簡單了铭若,只需要把繪制背景的代碼刪掉即可:

public class MyCircleImageView extends androidx.appcompat.widget.AppCompatImageView {

    private Animation.AnimationListener mListener;

    public MyCircleImageView(Context context) {
        super(context);
    }
    ...

    public void setAnimationListener(Animation.AnimationListener listener) {
        mListener = listener;
    }

    @Override
    public void onAnimationStart() {
        super.onAnimationStart();
        if (mListener != null) {
            mListener.onAnimationStart(getAnimation());
        }
    }

    @Override
    public void onAnimationEnd() {
        super.onAnimationEnd();
        if (mListener != null) {
            mListener.onAnimationEnd(getAnimation());
        }
    }
}

然后將第二步創(chuàng)建的MySwipeRefreshLayout中的CircleImageView換成MyCircleImageView即可,直接看效果吧:

loading1.gif
4递览,進(jìn)一步優(yōu)化

SwipeRefreshLayout的換膚已經(jīng)完成奥喻,但是感覺哪里總是不對勁,再往下拖動時讓布局跟著一起動效果會不會更好呢非迹?試試吧

思路也很簡單环鲤,就是再下拉時根據(jù)拉動的距離讓內(nèi)部的子View ScrollTo到指定位置,最后松手時ScrollTo到原來的位置即可憎兽。

具體代碼分兩步:

1冷离,跟著滑動

onTouchEvent()ACTION_MOVE事件中,如果時Dragged狀態(tài)就會調(diào)用moveSpinner(overscrollTop)纯命,在其中添加代碼即可:

private void moveSpinner(float overscrollTop) {
    float originalDragPercent = overscrollTop / mTotalDragDistance;

    float dragPercent = Math.min(1f, Math.abs(originalDragPercent));
    float adjustedPercent = (float) Math.max(dragPercent - .4, 0) * 5 / 3;
    float extraOS = Math.abs(overscrollTop) - mTotalDragDistance;
    float slingshotDist = mCustomSlingshotDistance > 0
        ? mCustomSlingshotDistance
        : (mUsingCustomStart
           ? mSpinnerOffsetEnd - mOriginalOffsetTop
           : mSpinnerOffsetEnd);
    float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2)
                                             / slingshotDist);
    float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow(
        (tensionSlingshotPercent / 4), 2)) * 2f;
    float extraMove = slingshotDist * tensionPercent * 2;

    int targetY = mOriginalOffsetTop + (int) ((slingshotDist * dragPercent) + extraMove);
    ...
    // 增加以下代碼即可
    if (targetY >= 0) {
        ((ViewGroup) mTarget).getChildAt(0).scrollTo(0, -targetY / 2);
    }
}

只需要在targetY>=0時執(zhí)行scrollTo()即可西剥。

2,返回原處

onTouchEvent()ACTION_UP事件中亿汞,在Dragged狀態(tài)松手時會執(zhí)行finishSpinner()瞭空,在其中將子View回復(fù)到原位即可:

private void finishSpinner(float overscrollTop) {
    // 返回原處
    ((ViewGroup) mTarget).getChildAt(0).scrollTo(0, 0);
    ...
}

如果你覺得scrollTo(0, 0)太突兀,可以搞個屬性動畫讓回彈速度變慢一點疗我,這里就不多說了咆畏,看下效果吧:

GIF.gif

5,最后

如果你想進(jìn)一步擴(kuò)展吴裤,比如加上一些文字旧找,最近更新時間等也是可以實現(xiàn)的。

最后安利一波我的開源項目:風(fēng)云天氣(https://github.com/wdsqjq/FengYunWeather)麦牺,以上效果就在這里哦~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钮蛛,一起剝皮案震驚了整個濱河市鞭缭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌魏颓,老刑警劉巖岭辣,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異甸饱,居然都是意外死亡沦童,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門柜候,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搞动,“玉大人躏精,你說我怎么就攤上這事渣刷。” “怎么了矗烛?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵辅柴,是天一觀的道長。 經(jīng)常有香客問我瞭吃,道長碌嘀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任歪架,我火速辦了婚禮股冗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘和蚪。我一直安慰自己止状,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布攒霹。 她就那樣靜靜地躺著怯疤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪催束。 梳的紋絲不亂的頭發(fā)上集峦,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機(jī)與錄音抠刺,去河邊找鬼塔淤。 笑死,一個胖子當(dāng)著我的面吹牛速妖,可吹牛的內(nèi)容都是我干的凯沪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼买优,長吁一口氣:“原來是場噩夢啊……” “哼妨马!你這毒婦竟也來了挺举?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤烘跺,失蹤者是張志新(化名)和其女友劉穎湘纵,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滤淳,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡梧喷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了脖咐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铺敌。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖屁擅,靈堂內(nèi)的尸體忽然破棺而出偿凭,到底是詐尸還是另有隱情,我是刑警寧澤派歌,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布弯囊,位于F島的核電站,受9級特大地震影響胶果,放射性物質(zhì)發(fā)生泄漏匾嘱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一早抠、第九天 我趴在偏房一處隱蔽的房頂上張望霎烙。 院中可真熱鬧,春花似錦蕊连、人聲如沸悬垃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盗忱。三九已至,卻和暖如春羊赵,著一層夾襖步出監(jiān)牢的瞬間趟佃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工昧捷, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留闲昭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓靡挥,卻偏偏與公主長得像序矩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子跋破,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容