NestedScrolling機(jī)制的學(xué)習(xí)筆記(二)

這篇文章會(huì)用NestedScrolling機(jī)制做一個(gè)實(shí)例,此實(shí)例代碼參考自:http://blog.csdn.net/al4fun/article/details/53889075

我在源代碼的基礎(chǔ)上风瘦,刪減了些代碼剃根,加了些注釋

例子效果如下:
1.gif

先貼出布局文件代碼:

<?xml version="1.0" encoding="utf-8"?>
<com.example.nesteddemo.MyParent xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.nesteddemo.MainActivity"
    android:orientation="vertical">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher_round"
        android:layout_gravity="center_horizontal"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="我是不會(huì)隱藏的文字欄,辣雞"
        android:background="#FFB6C1"
        android:textSize="25sp"/>

    <com.example.nesteddemo.MyChild
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/libai"
            android:textSize="25sp"/>

    </com.example.nesteddemo.MyChild>

</com.example.nesteddemo.MyParent>

布局文件很簡(jiǎn)單舒憾,其中MyParent和MyChild分別是實(shí)現(xiàn)了NestedScrollingParent和NestedScrollingChild接口的父容器和子元素,這兩個(gè)View都繼承自LinearLayout。

我們先來看看MyChild是怎樣實(shí)現(xiàn)的:

public class MyChild extends LinearLayout implements NestedScrollingChild {
    NestedScrollingChildHelper nscp;
    int lastY;

    //這兩個(gè)數(shù)組用來接收父容器傳過來的參數(shù)
    int[] consumed;
    int[] offsetWindow;

    int showHeight;

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

    public MyChild(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //第一次測(cè)量香浩,因?yàn)槭莣rap_content,測(cè)量出來的只是父容器除了ImageView和TextView剩余的高度
        //此次測(cè)量只是為了求得剩余的高度
        //如果沒有第二次測(cè)量,那么下面的文字就會(huì)顯示不出來
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        showHeight = getMeasuredHeight();

        //現(xiàn)在我們把MeasureSpec設(shè)置為UNSPECIFIED,這樣MyChild的高度就沒有限制了臼勉,也就能顯示全部的文字了
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);
        super.onMeasure(widthMeasureSpec,heightMeasureSpec);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                lastY = (int) event.getRawY();
                break;

            case MotionEvent.ACTION_MOVE:
                int y = (int) event.getRawY();
                int dy = y-lastY;
                lastY = y;

                //開啟NestedScrolling機(jī)制邻吭,如果找到了匹配的父容器,那么就與父容器配合消費(fèi)掉滑動(dòng)距離
                if(startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)){
                    //dy是我們傳過去的滑動(dòng)的距離囱晴,父容器可以根據(jù)邏輯來選擇要不要消費(fèi)膏蚓,消費(fèi)多少
                    dispatchNestedPreScroll(0,dy,consumed,offsetWindow);
                    scrollBy(0,-dy);
                }

                break;

            case MotionEvent.ACTION_UP:
                break;
        }

        return true;
    }

    //scrollBy內(nèi)部調(diào)用scrollTo,我們不能滑出去,也不能滑的太下面畸写,我們要修正這些情況
    @Override
    public void scrollTo(@Px int x, @Px int y) {
        int maxY = getMeasuredHeight()-showHeight;
        if(y>maxY){
            y=maxY;
        }
        else if(y<0){
            y=0;
        }
        super.scrollTo(x, y);
    }

    //這里使用單例模式提供Helper驮瞧,我發(fā)現(xiàn)如果沒有單例模式,機(jī)制就會(huì)失效
    //原因我大致的猜到了枯芬,但是我還不能具體的表達(dá)出來论笔,如果有人知道,請(qǐng)?jiān)谠u(píng)論區(qū)留下言
    private NestedScrollingChildHelper getNscp(){
        if(nscp == null){
            nscp = new NestedScrollingChildHelper(this);
            nscp.setNestedScrollingEnabled(true);
            return nscp;
        }else {
            return nscp;
        }
    }

    @Override
    public void setNestedScrollingEnabled(boolean enabled) {
        getNscp().setNestedScrollingEnabled(enabled);
    }

    @Override
    public boolean isNestedScrollingEnabled() {
        return getNscp().isNestedScrollingEnabled();
    }

    @Override
    public boolean startNestedScroll(int axes) {
        return getNscp().startNestedScroll(axes);
    }

    @Override
    public void stopNestedScroll() {
        getNscp().stopNestedScroll();
    }

    @Override
    public boolean hasNestedScrollingParent() {
        return getNscp().hasNestedScrollingParent();
    }

    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
        return getNscp().dispatchNestedScroll(dxConsumed,dyConsumed,dxUnconsumed,dyUnconsumed,offsetInWindow);
    }

    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
        return getNscp().dispatchNestedPreScroll(dx,dy,consumed,offsetInWindow);
    }

    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return getNscp().dispatchNestedFling(velocityX,velocityY,consumed);
    }

    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return getNscp().dispatchNestedPreFling(velocityX,velocityY);
    }
}

大多數(shù)的函數(shù)我們都用幫助類的同名函數(shù)處理了千所,其余的代碼我已經(jīng)寫上了詳細(xì)的注釋狂魔,不難看懂。

接下看下MyParent的實(shí)現(xiàn):

public class MyParent extends LinearLayout implements NestedScrollingParent {
    NestedScrollingParentHelper nsp;
    ImageView iv;
    MyChild nsc;
    int ivHeight;

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

    public MyParent(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        //創(chuàng)建一個(gè)Helper類
        nsp = new NestedScrollingParentHelper(this);
    }

    //拿到父容器里面的三個(gè)子View
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        iv = (ImageView) getChildAt(0);
        nsc = (MyChild) getChildAt(2);

        //拿到ImageView的高度
        iv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if(ivHeight<=0){
                    ivHeight = iv.getMeasuredHeight();
                }
            }
        });

    }

    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        //參數(shù)里target是實(shí)現(xiàn)了NestedScrolling機(jī)制的子元素淫痰,這個(gè)子元素可以不是父容器的直接子元素
        //child是包含了target的View最楷,這個(gè)View是父容器的直接子元素
        if(target instanceof MyChild){
            return true;
        }

        return false;
    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        //dy是子View傳過來的,來詢問父容器是不是要消費(fèi)他待错,要的話籽孙,就把dy放進(jìn)consumed數(shù)組,表示我消費(fèi)了
        //其中consumed數(shù)組,consumed[0]表示x方向的距離火俄,consumed[1]表示y方向的距離
        if(showImg(dy)||hideImg(dy)/*這里根據(jù)業(yè)務(wù)邏輯來判斷*/){
            scrollBy(0,-dy);
            consumed[1] = dy;
        }
    }

    private boolean hideImg(int dy) {
        //上拉的時(shí)候犯建,判斷是不是要隱藏圖片
        if(dy<0){
            if(getScrollY()<ivHeight){
                //判斷只要上移的部分,沒有超過ImageView烛占,那么就讓父容器繼續(xù)滑動(dòng)
                return true;
            }
        }

        return false;
    }

    private boolean showImg(int dy) {
        //下拉的時(shí)候胎挎,判斷是不是要顯示圖片
        if(dy>0){
            if(nsc.getScrollY()==0){
                return true;
            }
        }

        return false;
    }

    //scrollBy內(nèi)部調(diào)用scrollTo,我們父容器不能滑出去,也不能滑的太下面忆家,我們要修正這些情況
    @Override
    public void scrollTo(@Px int x, @Px int y) {
        if(y>ivHeight){
            y = ivHeight;
        }
        else if(y<0){
            y=0;
        }

        super.scrollTo(x,y);
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
        nsp.onNestedScrollAccepted(child,target,nestedScrollAxes);
    }

    @Override
    public void onStopNestedScroll(View target) {
        nsp.onStopNestedScroll(target);
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {

    }

    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        return false;
    }

    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
        return false;
    }

    @Override
    public int getNestedScrollAxes() {
        return 0;
    }
}

和MyChild一樣犹菇,大多數(shù)函數(shù)都用幫助類的同名函數(shù)處理了,其余的也寫上了詳細(xì)的注釋芽卿。

好了揭芍,沒了。

完整代碼地址:https://github.com/ChenTianSaber/NestedScrollingDemo

結(jié)束:

這篇文章先到此結(jié)束卸例。

感覺寫的好干称杨,因?yàn)榇蠖鄶?shù)地方都被幫助類實(shí)現(xiàn)了,而且例子也簡(jiǎn)單筷转,沒有什么一步一步的步驟姑原。大家可以看懂了之后,自己寫一遍呜舒。

接下來我們會(huì)分析一下NestedScrolling的源碼锭汛,來看看它們是怎么做到相互配合的。

如果有問題,請(qǐng)?jiān)谠u(píng)論區(qū)留言唤殴,才疏學(xué)淺般婆,歡迎大家批評(píng)指正。

最后的最后:

感謝我可愛的女朋友朵逝。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蔚袍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子配名,更是在濱河造成了極大的恐慌啤咽,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件段誊,死亡現(xiàn)場(chǎng)離奇詭異闰蚕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)连舍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涩哟,“玉大人索赏,你說我怎么就攤上這事√耍” “怎么了潜腻?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)器仗。 經(jīng)常有香客問我融涣,道長(zhǎng),這世上最難降的妖魔是什么精钮? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任威鹿,我火速辦了婚禮,結(jié)果婚禮上轨香,老公的妹妹穿的比我還像新娘忽你。我一直安慰自己,他們只是感情好臂容,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布科雳。 她就那樣靜靜地躺著,像睡著了一般脓杉。 火紅的嫁衣襯著肌膚如雪糟秘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天球散,我揣著相機(jī)與錄音尿赚,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吼畏,可吹牛的內(nèi)容都是我干的督赤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼泻蚊,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼躲舌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起性雄,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤没卸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后秒旋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體约计,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年迁筛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了煤蚌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡细卧,死狀恐怖尉桩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贪庙,我是刑警寧澤蜘犁,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站止邮,受9級(jí)特大地震影響这橙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜导披,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一屈扎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盛卡,春花似錦助隧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至滓技,卻和暖如春哩牍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背令漂。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工膝昆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留丸边,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓荚孵,卻偏偏與公主長(zhǎng)得像妹窖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子收叶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,116評(píng)論 25 707
  • 一骄呼、溫故而知新 1. 內(nèi)存不夠怎么辦 內(nèi)存簡(jiǎn)單分配策略的問題地址空間不隔離內(nèi)存使用效率低程序運(yùn)行的地址不確定 關(guān)于...
    SeanCST閱讀 7,808評(píng)論 0 27
  • 終于見到你,圓了一個(gè)心愿判没。一暑假念念不忘的美景蜓萄,也終于去感受了一遍。一片花海澄峰,背后的付出嫉沽,人前的開放!都在花海之中...
    王麗燕199閱讀 394評(píng)論 0 2
  • 看大家都在說俏竞,告訴你個(gè)秘密绸硕,我在簡(jiǎn)書上撿個(gè)妹妹、哥哥魂毁。我說臣咖,你會(huì)在簡(jiǎn)書上撿個(gè)姐姐,順帶送個(gè)妹妹漱牵。哈哈哈…… ...
    心覺堂閱讀 668評(píng)論 8 9
  • 身處1700米海拔的黃土高原上,我們沿著泥濘的鄉(xiāng)間小路來到空曠的玉米地疚漆。學(xué)著老師的樣子酣胀,我們以小組為單位分工掰玉米...
    習(xí)習(xí)晨風(fēng)閱讀 148評(píng)論 0 0