中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android嵌套滾動與協調滾動如何實現

發布時間:2022-06-17 13:48:07 來源:億速云 閱讀:135 作者:iii 欄目:開發技術

本文小編為大家詳細介紹“Android嵌套滾動與協調滾動如何實現”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Android嵌套滾動與協調滾動如何實現”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

一、CoordinatorLayout + Behavior

CoordinatorLayout 顧名思義是協調布局,其原理很簡單,在onMeasure()的時候保存childView,通過 PreDrawListener監聽childView的變化,最終通過雙層for循環找到對應的Behavior,分發任務即可。CoordinatorLayout實現了NestedScrollingParent2,那么在childView實現了NestedScrollingChild方法時候也能解決滑動沖突問題。

而Behavior就是一個應用于View的觀察者模式,一個View跟隨者另一個View的變化而變化,或者說一個View監聽另一個View。

在Behavior中,被觀察View 也就是事件源被稱為denpendcy,而觀察View,則被稱為child。

一般自定義Behavior來說分兩種情況:

  • 監聽另一個view的狀態變化,例如大小、位置、顯示狀態等

  • 監聽CoordinatorLayout里的滑動狀態

這里我們以之前的效果為主來實現自定義的Behavior,先設置NestedScrollView在ImageView下面:

public class MyScrollBehavior extends ViewOffsetBehavior<NestedScrollView> {
    private int topImgHeight;
    private int topTextHeight;
    public MyScrollBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull NestedScrollView child,
                                   @NonNull View dependency) {
        return dependency instanceof ImageView ;
    }
    @Override
    protected void layoutChild(CoordinatorLayout parent, NestedScrollView child, int layoutDirection) {
        super.layoutChild(parent, child, layoutDirection);
        if (topImgHeight == 0) {
            final List<View> dependencies = parent.getDependencies(child);
            for (int i = 0, z = dependencies.size(); i < z; i++) {
                View view = dependencies.get(i);
                if (view instanceof ImageView) {
                    topImgHeight = view.getMeasuredHeight();
                } 
            }
        }
        child.setTop(topImgHeight);
        child.setBottom(child.getBottom() + topImgHeight);
    }
}

然后設置監聽CoordinatorLayout里的滑動狀態,ImageView做同樣的滾動

public class MyImageBehavior extends CoordinatorLayout.Behavior<View> {
    private int topBarHeight = 0;  //負圖片高度
    private int downEndY = 0;   //默認為0
    public MyImageBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
                                       @NonNull View child, @NonNull View directTargetChild,
                                       @NonNull View target, int axes, int type) {
        //監聽垂直滾動
        return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
    }
    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child,
                                  @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        if (topBarHeight == 0) {
            topBarHeight = -child.getMeasuredHeight();
        }
        float transY = child.getTranslationY() - dy;
        //處理上滑
        if (dy > 0) {
            if (transY >= topBarHeight) {
                translationByConsume(child, transY, consumed, dy);
                translationByConsume(target, transY, consumed, dy);
            } else {
                translationByConsume(child, topBarHeight, consumed, (child.getTranslationY() - topBarHeight));
                translationByConsume(target, topBarHeight, consumed, (child.getTranslationY() - topBarHeight));
            }
        }
        if (dy < 0 && !target.canScrollVertically(-1)) {
            //處理下滑
            if (transY >= topBarHeight && transY <= downEndY) {
                translationByConsume(child, transY, consumed, dy);
                translationByConsume(target, transY, consumed, dy);
            } else {
                translationByConsume(child, downEndY, consumed, (downEndY - child.getTranslationY()));
                translationByConsume(target, downEndY, consumed, (downEndY - child.getTranslationY()));
            }
        }
    }
    @Override
    public boolean onNestedFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, float velocityX, float velocityY, boolean consumed) {
        return super.onNestedFling(coordinatorLayout, child, target, velocityX,
                velocityY, consumed);
    }
    private void translationByConsume(View view, float translationY, int[] consumed, float consumedDy) {
        consumed[1] = (int) consumedDy;
        view.setTranslationY(translationY);
    }
}

分別為ImageView和NestedScrollView設置對應的 Behavior。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:background="@color/white"
    android:orientation="vertical">
    <com.guadou.lib_baselib.view.titlebar.EasyTitleBar
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:Easy_title="CoordinatorLayout+Behavior" />
    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ImageView
            android:layout_width="150dp"
            android:layout_height="150dp"
            app:layout_behavior="com.google.android.material.appbar.MyImageBehavior"
            android:layout_gravity="center_horizontal"
            android:contentDescription="我是測試的圖片"
            android:src="@mipmap/ic_launcher" />
        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#ccc"
            android:gravity="center"
            android:text="我是測試的分割線"
            android:visibility="gone" />
        <androidx.core.widget.NestedScrollView
            android:id="@+id/nestedScroll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_behavior="com.google.android.material.appbar.MyScrollBehavior">
            <TextView
                android:id="@+id/nestedScrollLayout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/scroll_content" />
        </androidx.core.widget.NestedScrollView>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>

我們先把TextView隱藏先不處理TextView。

這樣我們就實現了自定義 Behavior 監聽滾動的實現。那么我們加上TextView 的 Behavior 監聽ImageView的滾動,做對應的滾動。

先修改 MyScrollBehavior 讓他在ImageView和TextView下面

public class MyScrollBehavior extends ViewOffsetBehavior<NestedScrollView> {
    private int topImgHeight;
    private int topTextHeight;
    public MyScrollBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull NestedScrollView child,
                                   @NonNull View dependency) {
        return dependency instanceof ImageView || dependency instanceof TextView ;
    }
    @Override
    protected void layoutChild(CoordinatorLayout parent, NestedScrollView child, int layoutDirection) {
        super.layoutChild(parent, child, layoutDirection);
        if (topImgHeight == 0) {
            final List<View> dependencies = parent.getDependencies(child);
            for (int i = 0, z = dependencies.size(); i < z; i++) {
                View view = dependencies.get(i);
                if (view instanceof ImageView) {
                    topImgHeight = view.getMeasuredHeight();
                } else if (view instanceof TextView) {
                    topTextHeight = view.getMeasuredHeight();
                    view.setTop(topImgHeight);
                    view.setBottom(view.getBottom() + topImgHeight);
                }
            }
        }
        child.setTop(topImgHeight + topTextHeight);
        child.setBottom(child.getBottom() + topImgHeight + topTextHeight);
    }
}

然后設置監聽ImageView的滾動:

public class MyTextBehavior extends CoordinatorLayout.Behavior<View> {
    private int imgHeight;
    public MyTextBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
        return dependency instanceof ImageView;
    }
    @Override
    public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
        //跟隨ImageView滾動,ImageView滾動多少我滾動多少
        float translationY = dependency.getTranslationY();
        if (imgHeight == 0) {
            imgHeight = dependency.getHeight();
        }
        float offsetTranslationY = imgHeight + translationY;
        child.setTranslationY(offsetTranslationY);
        return true;
    }
}

xml修改如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:background="@color/white"
    android:orientation="vertical">
    <com.guadou.lib_baselib.view.titlebar.EasyTitleBar
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:Easy_title="CoordinatorLayout+Behavior" />
    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ImageView
            android:layout_width="150dp"
            android:layout_height="150dp"
            app:layout_behavior="com.google.android.material.appbar.MyImageBehavior"
            android:layout_gravity="center_horizontal"
            android:contentDescription="我是測試的圖片"
            android:src="@mipmap/ic_launcher" />
        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#ccc"
            app:layout_behavior="com.google.android.material.appbar.MyTextBehavior"
            android:gravity="center"
            android:text="我是測試的分割線"
            android:visibility="visible" />
        <androidx.core.widget.NestedScrollView
            android:id="@+id/nestedScroll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_behavior="com.google.android.material.appbar.MyScrollBehavior">
            <TextView
                android:id="@+id/nestedScrollLayout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/scroll_content" />
        </androidx.core.widget.NestedScrollView>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>

看到上面的示例,我們把常用的幾種 Behavior 都使用了一遍,系統的ViewOffsetBehavior 和監聽滾動的 Behavior 監聽View的 Behavior。

為了實現這么一個簡單的效果就用了這么多類,這么復雜。我分分鐘就能實現!

行行,我知道你厲害,這不是為了演示同樣的效果,使用不同的方式實現嘛。通過 Behavior 可以實現一些嵌套滾動不能完成的效果,比如鼎鼎大名的支付寶首頁效果,美團詳情效果等。Behavior 更加的靈活,控制的粒度也更加的細。

但是如果只是簡單實現上面的效果,我們可以用 AppBarLayout + 內部自帶的 Behavior 也能實現類似的效果,AppBarLayout內部已經封裝并使用了 Behavior 。我們看看如何實現。

二、CoordinatorLayout + AppBarLayout

其實內部也是基于 Behavior 實現的,內部實現為 HeaderBehavior 和 HeaderScrollingViewBehavior 。

對一些場景使用進行了封裝,滾動效果,吸頂效果,折疊效果等。我們看看同樣的效果,使用 AppBarLayout 如何實現吧:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:background="@color/white"
    android:orientation="vertical">
    <com.guadou.lib_baselib.view.titlebar.EasyTitleBar
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:Easy_title="CoordinatorLayout+AppBarLayout" />
    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:elevation="0dp"
            android:background="@color/white"
            android:orientation="vertical">
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:contentDescription="我是測試的圖片"
                android:src="@mipmap/ic_launcher"
                app:layout_scrollFlags="scroll" />
            <TextView
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:background="#ccc"
                android:gravity="center"
                android:text="我是測試的分割線"
                app:layout_scrollFlags="noScroll" />
        </com.google.android.material.appbar.AppBarLayout>
        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/scroll_content" />
        </androidx.core.widget.NestedScrollView>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>

So Easy ! 真的是太方便了,類似的效果我們都能使用 AppbarLayout 來實現,比如一些詳情頁面頂部圖片,下面列表或ViewPager的都可以使用這種方式,更加的便捷。

三、MotionLayout

不管怎么說,AppbarLayout 只能實現一些簡單的效果,如果想要一些粒度比較細的效果,我們還得使用自定義 Behavior 來實現,但是它的實現確實是有點復雜,2019年谷歌推出了 MotionLayout 。

淘寶的出現可以說讓世上沒有難做的生意,那么 MotionLayout 的出現可以說讓 Android 沒有難實現的動畫了。不管是動畫效果,滾動效果,MotionLayout 絕殺!能用 Behavior 實現的 MotionLayout 幾乎是都能做。

使用 MotionLayout 我們只需要定義起始點和結束點就行了,我們這里不需要根據百分比Fram進行別的操作,所以只定義最簡單的使用。

我們看看如何用 MotionLayout 實現同樣的效果:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@color/white"
    android:orientation="vertical">
    <com.guadou.lib_baselib.view.titlebar.EasyTitleBar
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:Easy_title="MotionLayout的動作" />
    <androidx.constraintlayout.motion.widget.MotionLayout
        android:layout_width="match_parent"
        android:layout_weight="1"
        app:layoutDescription="@xml/scene_scroll_13"
        android:layout_height="0dp">
        <ImageView
            android:id="@+id/iv_img"
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:scaleType="centerCrop"
            android:contentDescription="我是測試的圖片"
            android:src="@mipmap/ic_launcher" />
        <TextView
            android:id="@+id/tv_message"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#ccc"
            android:gravity="center"
            android:text="我是測試的分割線"
            tools:layout_editor_absoluteY="150dp" />
        <androidx.core.widget.NestedScrollView
            android:id="@+id/nestedScroll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:id="@+id/nestedScrollLayout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/scroll_content" />
        </androidx.core.widget.NestedScrollView>
    </androidx.constraintlayout.motion.widget.MotionLayout>
</LinearLayout>

定義的scene_scroll_13.xml

<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">
    <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/start">
        <OnSwipe
            motion:dragDirection="dragUp"
            motion:touchAnchorId="@id/nestedScroll" />
    </Transition>
    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@id/iv_img"
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:translationY="0dp"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
        <Constraint
            android:id="@id/tv_message"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            motion:layout_constraintTop_toBottomOf="@id/iv_img" />
        <Constraint
            android:id="@id/nestedScroll"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintTop_toBottomOf="@id/tv_message" />
    </ConstraintSet>
    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@id/iv_img"
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:translationY="-150dp"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
        <Constraint
            android:id="@id/tv_message"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
        <Constraint
            android:id="@id/nestedScroll"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintTop_toBottomOf="@id/tv_message" />
    </ConstraintSet>
</MotionScene>

讀到這里,這篇“Android嵌套滾動與協調滾動如何實現”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

晋中市| 富民县| 白玉县| 宁乡县| 灌阳县| 栾川县| 太仓市| 克什克腾旗| 乾安县| 夹江县| 白山市| 岗巴县| 辽阳县| 富宁县| 磐石市| 隆安县| 麻阳| 阿鲁科尔沁旗| 伊春市| 峨眉山市| 扎兰屯市| 马山县| 喜德县| 密云县| 柳河县| 定远县| 铜鼓县| 徐闻县| 兰考县| 安阳县| 三明市| 政和县| 浮山县| 财经| 周至县| 广安市| 龙门县| 高邮市| 唐山市| 赤壁市| 航空|