下拉刷新

這篇文章適合看了眾多講解下拉刷新、視圖測量與繪制、事件分發(fā)仍然模糊不清的同學(xué),android下拉刷新控件不知從何時起已經(jīng)成為項(xiàng)目標(biāo)配,所以熟悉下拉刷新控件變得尤為重要,本文將從下拉刷新控件入手,順便學(xué)習(xí)下自定義控件和事件分發(fā)機(jī)制。
我們可以點(diǎn)進(jìn)當(dāng)下可拓展性最高、最流行的下拉刷新項(xiàng)目:android-Ultra-Pull-To-Refresh,發(fā)現(xiàn)里面有個核心類PtrFrameLayout,乍一看1368行,不管你暈不暈,反正我是暈的。好了,我們來個簡易版的(Ps:引用NsRefreshLayout項(xiàng)目的代碼來講解,并且都只是偽代碼或部分代碼):

一、定義些可拓展的屬性

像這樣寫在values/attrs.xml里(ps:其實(shí)你寫在strings.xml里都沒問題,xml的名字也可以自己取,只需要保證根標(biāo)簽是declare-styleaqle即可,而且這樣分開寫更方便查找):

<declare-styleable name="PtrFrameLayout">
     <attr name="ptr_pull_to_fresh" format="boolean" />
</declare-styleable>
二、獲取到這些屬性

確保在每三個構(gòu)造函數(shù)里都可以拿到這些屬性

public PtrFrameLayout(Context context) {
        this(context, null);
    }

    public PtrFrameLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PtrFrameLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.PtrFrameLayout, 0, 0);
        mPullToRefresh = arr.getBoolean(R.styleable.PtrFrameLayout_ptr_pull_to_fresh, mPullToRefresh);
        arr.recycle();
    }
三、給布局添加頭布局或底布局

在方法onFinishInflate()里以addView的形式添加自定義的頭布局或者底布局,并使用第二部接收的值來填充屬性(比如顏色,字體什么的)。

四、重寫事件分發(fā)

目前主要有2種方式來重寫事件分發(fā)。

  1. 重寫onInterceptTouchEvent和onTouchEvent的方式
  • 重寫onInterceptTouchEvent
public boolean onInterceptTouchEvent(MotionEvent ev) {
      case MotionEvent.ACTION_MOVE: {
               //判斷是下拉刷新還是上拉加載更多
            if (disY > 0 && !canChildScrollUp() && mPullRefreshEnable) {
                        mCurrentAction = ACTION_PULL_DOWN_REFRESH;
                        return true;
            } else {
               return super.onInterceptTouchEvent(ev);
            }
}

對touch事件為MOVE類型的進(jìn)行判斷處理,如果滿足攔截條件,進(jìn)行攔截并返回true,如不滿足條件或是類型不是MOVE的其他touch事件,執(zhí)行super.onInterceptTouchEvent(ev),代表不攔截,由系統(tǒng)幫我們向下傳遞,遇到需要消費(fèi)該事件的content(比如listview滑動)消費(fèi)掉就完事了

  • 重寫onTouchEvent

public boolean onTouchEvent(MotionEvent event){
case MotionEvent.ACTION_MOVE: {
handleScroll(dy);//處理頭試圖和內(nèi)容視圖的下滑
return true;//return super 或者false都沒事
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
return releaseTouch();//處理釋放操作,這里的返回值同上
}
}
```
原著在move那里返回的是true,其實(shí)無論返回什么都是可以的,因?yàn)橐呀?jīng)重寫了onInterceptTouchEvent,當(dāng)遇到滿足下拉刷新的情況自然會走自己的OnTouchEvent(),如果沒有不滿足條件,由于你是parentView,而且沒有攔截事件,所以子布局優(yōu)先消費(fèi),也跟你無關(guān)了,哈哈

  1. 重寫dispatchTouchEvent的方式
    android-Ultra-Pull-To-Refresh就是用的這種方式,當(dāng)滿足刷新條件時return true表示自己處理了,如果沒滿足條件,執(zhí)行super.dispatchTouchEventSupper(e)繼續(xù)分發(fā)給孩子,個人覺得這種方式干預(yù)了系統(tǒng)的分發(fā)事件,畢竟孩子的所有觸摸事件都是通過這個方法得來的,說沒就沒了,不像onInterceptTouchEvent攔截了還會丟給孩子一個cancel事件,所以這種系統(tǒng)級的事情就要我們自己去寫了,不過癮。。。
五、布局位移

上個部分有個偽代碼handleScroll(),是用來位移孩子的方法,下面統(tǒng)計(jì)下位移視圖的幾種方式:

  1. layout()
  2. bringToFront()(需配合requestLayout使用)
  3. LayoutParams
  4. padding(設(shè)為負(fù)值就隱藏了)
  5. transactionY,transactionX
  6. offsetLeftAndRight() offsetTopAndBottom()
  7. Scroller
  8. scrollTo() scrollBy()
    以上8種其實(shí)可以分為2類,1234需要requestLayout(),所以當(dāng)頻繁調(diào)用時會卡,5678只是影響drawable部分,頻繁調(diào)用也很順滑。

總結(jié)

如果你想通過看完上面的內(nèi)容,然后自己寫一個完美的下拉刷新控件,我想還是需要很長時間的,畢竟里面還有很多的小細(xì)節(jié)沒有涉及到,我只是幫大家把輪子分解得簡單點(diǎn),對輪子有一個大體的認(rèn)知,小細(xì)節(jié)的地方不明白也不影響大局觀嘛,畢竟寫代碼還是要有一個上帝視角,誰也不能一口吃個大胖子。

相關(guān)鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容