Android滚动布局
newki 人气:0使用 AppbarLayout 和 MotionLayout 实现常用的布局效果
前文我们讲了协调滚动的一些定义方式,我们在开发中常用的几种效果都可用 AppbarLayout 或 MotionLayout 来实现。
这里先上效果图
可能大家都比较会AppbarLayout这种实现方式,这里就直接上代码和效果图了。
一、AppbarLayout + ViewPager
核心代码都在 CoordinatorLayout 闭包中
<androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/app_bar_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" android:orientation="vertical" app:elevation="0dp"> <!-- 顶部的图片 --> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/cl_img_box" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_scrollFlags="scroll|snap"> <ImageView android:id="@+id/image_view_photo_cover" android:layout_width="0dp" android:layout_height="0dp" android:contentDescription="@string/part_time_jobs" android:scaleType="centerCrop" app:layout_constraintDimensionRatio="W,94:129" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ProgressBar android:id="@+id/progress_bar_job_detail" android:layout_width="25dp" android:layout_height="25dp" android:layout_gravity="center" android:indeterminate="true" android:indeterminateTint="@color/colorAccent" android:indeterminateTintMode="src_atop" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <com.youth.banner.Banner android:id="@+id/banner_employer_imgs" android:layout_width="match_parent" android:layout_height="0dp" android:background="@drawable/iv_promotion_merchant_detail_placehold_bg" app:banner_auto_loop="false" app:banner_indicator_height="@dimen/d_5dp" app:banner_indicator_marginBottom="@dimen/d_15dp" app:banner_indicator_normal_color="#66ffffff" app:banner_indicator_normal_width="@dimen/d_4dp" app:banner_indicator_selected_color="#ffffff" app:banner_indicator_selected_width="@dimen/d_6dp" app:banner_indicator_space="@dimen/d_4dp" app:banner_infinite_loop="true" app:layout_constraintDimensionRatio="W,94:129" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <LinearLayout android:id="@+id/ll_gallery_box" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="@dimen/d_15dp" android:layout_marginBottom="@dimen/d_10dp" android:background="@drawable/shape_job_detail_gallery_box" android:gravity="center" android:orientation="horizontal" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:drawableLeft="@drawable/job_detail_gallery_icon" android:drawablePadding="@dimen/d_7dp" android:paddingLeft="@dimen/d_10dp" android:paddingTop="@dimen/d_6dp" android:paddingRight="@dimen/d_10dp" android:paddingBottom="@dimen/d_6dp" android:text="@string/gallery" android:textColor="@color/white" android:textSize="@dimen/d_14sp" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout> <!-- Tab布局 --> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <com.google.android.material.tabs.TabLayout android:id="@+id/tabLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" app:tabIndicator="@drawable/shape_blue_bottom_line_3conner" app:tabIndicatorColor="@color/app_blue" app:tabIndicatorFullWidth="false" app:tabIndicatorHeight="@dimen/d_2dp" app:tabMode="scrollable" app:tabRippleColor="@color/transparent" app:tabSelectedTextColor="@color/app_blue" app:tabTextColor="@color/black" /> <ImageView android:id="@+id/iv_tab_right" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="right" android:src="@drawable/job_detail_tab_right_arrow" /> </FrameLayout> <View android:layout_width="match_parent" android:layout_height="@dimen/d_6dp" android:background="@color/page_background" /> </com.google.android.material.appbar.AppBarLayout> <androidx.viewpager.widget.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> </androidx.coordinatorlayout.widget.CoordinatorLayout>
很平常的效果了,也是用的比较多的效果,定义起来非常简单,在AppbarLayout下面包裹2个View,图片View跟随滚动,而TabBarLayout则吸顶。
效果如下:
二、AppbarLayout + RecyclerView
和上面一样的效果,这里我们可以指定下拉的时候把隐藏的布局显示出来,设置一个核心属性
app:layout_scrollFlags="scroll|snap|enterAlways
定义的布局如下:
<androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 顶部的筛选 --> <com.google.android.material.appbar.AppBarLayout android:id="@+id/app_bar_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" android:orientation="vertical" app:layout_scrollFlags="scroll|snap|enterAlways"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_12dp" android:layout_marginTop="@dimen/d_12dp" android:layout_marginRight="@dimen/d_12dp" android:gravity="center_vertical" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="兼职推荐" android:textColor="@color/black_33" android:textSize="@{viewModel.mSortTypeLD == 1?@dimen/d_17sp:@dimen/d_15sp}" binding:clicks="@{click.switchReCommend}" binding:typefaceMediumOrBold="@{viewModel.mSortTypeLD == 1}" tools:textSize="@dimen/d_17sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_15dp" android:text="最新" android:textColor="@color/black_33" android:textSize="@{viewModel.mSortTypeLD == 2?@dimen/d_17sp:@dimen/d_15sp}" binding:clicks="@{click.switchlatested}" binding:typefaceMediumOrBold="@{viewModel.mSortTypeLD == 2}" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_15dp" android:text="附近" android:textColor="@color/black_33" android:textSize="@{viewModel.mSortTypeLD == 3?@dimen/d_17sp:@dimen/d_15sp}" binding:clicks="@{click.switchNearby}" binding:typefaceMediumOrBold="@{viewModel.mSortTypeLD == 3}" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/d_8dp" android:orientation="horizontal" android:paddingLeft="@dimen/d_15dp" android:paddingRight="@dimen/d_15dp"> <TextView android:layout_width="0dp" android:layout_height="@dimen/d_27dp" android:layout_weight="1" android:background="@{viewModel.MPopupPositionTypeLD==1?@drawable/shape_blue_round2_border:@drawable/shape_white_gray_round2}" android:drawableRight="@{viewModel.MPopupPositionTypeLD==1?@drawable/ic_drop_up_blue:@drawable/ic_drop_down_gray}" android:gravity="center" android:paddingLeft="@dimen/d_8dp" android:paddingRight="@dimen/d_8dp" android:text="全武汉" android:textColor="@{viewModel.MPopupPositionTypeLD==1?@color/app_blue:@color/gray_66}" android:textSize="@dimen/d_12sp" binding:clicks="@{click.searchByLocationPopup}" tools:drawableRight="@drawable/ic_drop_down_gray" /> <TextView android:layout_width="0dp" android:layout_height="@dimen/d_27dp" android:layout_marginLeft="@dimen/d_5dp" android:layout_weight="1" android:background="@{viewModel.MPopupPositionTypeLD==2?@drawable/shape_blue_round2_border:@drawable/shape_white_gray_round2}" android:drawableRight="@{viewModel.MPopupPositionTypeLD==2?@drawable/ic_drop_up_blue:@drawable/ic_drop_down_gray}" android:gravity="center" android:paddingLeft="@dimen/d_8dp" android:paddingRight="@dimen/d_8dp" android:text="职位类型" android:textColor="@{viewModel.MPopupPositionTypeLD==2?@color/app_blue:@color/gray_66}" android:textSize="@dimen/d_12sp" binding:clicks="@{click.searchByPositionPopup}" tools:drawableRight="@drawable/ic_drop_down_gray" /> <TextView android:layout_width="0dp" android:layout_height="@dimen/d_27dp" android:layout_marginLeft="@dimen/d_5dp" android:layout_weight="1" android:background="@{viewModel.MPopupPositionTypeLD==3?@drawable/shape_blue_round2_border:@drawable/shape_white_gray_round2}" android:drawableRight="@{viewModel.MPopupPositionTypeLD==3?@drawable/ic_drop_up_blue:@drawable/ic_drop_down_gray}" android:gravity="center" android:paddingLeft="@dimen/d_8dp" android:paddingRight="@dimen/d_8dp" android:text="日期" android:textColor="@{viewModel.MPopupPositionTypeLD==3?@color/app_blue:@color/gray_66}" android:textSize="@dimen/d_12sp" binding:clicks="@{click.searchByDatePopup}" tools:drawableRight="@drawable/ic_drop_down_gray" /> <TextView android:layout_width="0dp" android:layout_height="@dimen/d_27dp" android:layout_marginLeft="@dimen/d_5dp" android:layout_weight="1" android:background="@{viewModel.MPopupPositionTypeLD==4?@drawable/shape_blue_round2_border:@drawable/shape_white_gray_round2}" android:drawableRight="@{viewModel.MPopupPositionTypeLD==4?@drawable/ic_drop_up_blue:@drawable/ic_drop_down_gray}" android:gravity="center" android:paddingLeft="@dimen/d_8dp" android:paddingRight="@dimen/d_8dp" android:text="筛选" android:textColor="@{viewModel.MPopupPositionTypeLD==4?@color/app_blue:@color/gray_66}" android:textSize="@dimen/d_12sp" binding:clicks="@{click.searchByFilterPopup}" tools:drawableRight="@drawable/ic_drop_down_gray" /> </LinearLayout> <View android:id="@+id/view_popup_divider" android:layout_width="match_parent" android:layout_height="@dimen/d_1dp" android:layout_marginTop="@dimen/d_10dp" android:background="@color/main_divider" /> </LinearLayout> </com.google.android.material.appbar.AppBarLayout> <!-- 刷新+列表 --> <com.scwang.smart.refresh.layout.SmartRefreshLayout android:id="@+id/refresh_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:focusable="true" android:focusableInTouchMode="true" app:layout_behavior="@string/appbar_scrolling_view_behavior" app:srlEnablePreviewInEditMode="true" app:srlPrimaryColor="@color/white" tools:visibility="gone"> <com.scwang.smart.refresh.header.ClassicsHeader android:layout_width="match_parent" android:layout_height="wrap_content" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:overScrollMode="never" android:scrollbars="vertical"> </androidx.recyclerview.widget.RecyclerView> </com.scwang.smart.refresh.layout.SmartRefreshLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
效果如下:
三、MotionLayout
MotionLayout的定义主要看xml中控件的start end 的位置与状态。
页面xml:
<androidx.constraintlayout.motion.widget.MotionLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" app:layoutDescription="@xml/scene_part_job_detail"> <!-- 顶部的图片 --> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/cl_top_img_box" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/iv_job_image" android:layout_width="0dp" android:layout_height="0dp" android:background="@color/gray" android:scaleType="centerCrop" app:layout_constraintDimensionRatio="W,219:375" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> <!-- 相册布局 --> <LinearLayout android:id="@+id/ll_gallery_box" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="@dimen/d_15dp" android:layout_marginBottom="@dimen/d_10dp" android:background="@drawable/shape_job_detail_gallery_box" android:gravity="center" android:orientation="horizontal" app:layout_constraintBottom_toBottomOf="@id/cl_top_img_box" app:layout_constraintRight_toRightOf="@id/cl_top_img_box" binding:clicks="@{click.gotoGalleryPage}"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:drawableLeft="@drawable/job_detail_gallery_icon" android:drawablePadding="@dimen/d_7dp" android:paddingLeft="@dimen/d_10dp" android:paddingTop="@dimen/d_6dp" android:paddingRight="@dimen/d_10dp" android:paddingBottom="@dimen/d_6dp" android:text="相册" android:textColor="@color/white" android:textSize="@dimen/d_14sp" /> </LinearLayout> <!-- 固定的状态栏 用于定位 --> <com.guadou.lib_baselib.view.titlebar.StatusbarGrayView android:id="@+id/status_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> <!-- 返回栏 --> <FrameLayout android:id="@+id/back_layout" android:layout_width="match_parent" android:layout_height="@dimen/d_40dp" android:background="@color/transparent" tools:layout_editor_absoluteY="24dp"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="@dimen/d_14dp" android:layout_marginTop="@dimen/d_2dp" android:src="@mipmap/back_white_icon" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|right" android:layout_marginRight="@dimen/d_15dp" android:src="@drawable/part_detail_job_collect_icon" /> </FrameLayout> <!-- 工作信息等TextView --> <TextView android:id="@+id/tv_job_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="@dimen/d_5dp" android:ellipsize="end" android:gravity="center" android:lines="1" android:singleLine="true" android:text="斗鱼主播招聘" android:textColor="@color/white" android:textSize="@dimen/d_20sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@id/back_layout" /> <TextView android:id="@+id/tv_job_employer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="@dimen/d_10dp" android:text="武汉斗鱼网络科技有限公司" android:textColor="@color/white" android:textSize="@dimen/d_14sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_job_title" /> <TextView android:id="@+id/tv_job_hour" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="@dimen/d_10dp" android:text="100元/小时" android:textColor="@color/text_orange" android:textSize="@dimen/d_16sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_job_employer" /> <TextView android:id="@+id/tv_job_industry" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="@dimen/d_8dp" android:text="江岸 | 主播" android:textColor="@color/white" android:textSize="@dimen/d_14sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_job_hour" /> <!-- Tab布局 --> <FrameLayout android:id="@+id/tab_layout_box" android:layout_width="match_parent" android:layout_height="@dimen/d_40dp"> <com.google.android.material.tabs.TabLayout android:id="@+id/tabLayout" android:layout_width="match_parent" android:layout_height="@dimen/d_40dp" android:background="@color/white" app:tabIndicator="@drawable/shape_blue_bottom_line_3conner" app:tabIndicatorColor="@color/app_blue" app:tabIndicatorFullWidth="false" app:tabIndicatorHeight="@dimen/d_2dp" app:tabMode="scrollable" app:tabRippleColor="@color/transparent" app:tabSelectedTextColor="@color/app_blue" app:tabTextColor="@color/black" /> <ImageView android:id="@+id/iv_tab_right" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="right" android:src="@drawable/job_detail_tab_right_arrow" binding:clicks="@{click.tabNext}" /> </FrameLayout> <!-- Fragment容器 --> <androidx.viewpager.widget.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="0dp" /> </androidx.constraintlayout.motion.widget.MotionLayout>
定义场景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" motion:duration="350"> <OnClick motion:clickAction="toggle" motion:targetId="@id/cl_top_img_box" /> <OnSwipe motion:dragDirection="dragUp" motion:touchAnchorId="@id/view_pager" /> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/cl_top_img_box" android:layout_width="match_parent" android:layout_height="wrap_content" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@id/ll_gallery_box" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="@dimen/d_15dp" android:layout_marginBottom="@dimen/d_10dp" android:alpha="1" motion:layout_constraintBottom_toBottomOf="@id/cl_top_img_box" motion:layout_constraintRight_toRightOf="@id/cl_top_img_box" /> <Constraint android:id="@id/status_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:alpha="0" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@id/back_layout" android:layout_width="match_parent" android:layout_height="@dimen/d_40dp" motion:layout_constraintTop_toBottomOf="@id/status_view" /> <Constraint android:id="@id/tv_job_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="@dimen/d_5dp" android:layout_marginRight="@dimen/d_15dp" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintTop_toBottomOf="@id/back_layout"> <CustomAttribute motion:attributeName="textSize" motion:customFloatValue="20" /> </Constraint> <Constraint android:id="@id/tv_job_employer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="@dimen/d_10dp" android:alpha="1" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintTop_toBottomOf="@id/tv_job_title" /> <Constraint android:id="@id/tv_job_hour" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="@dimen/d_10dp" android:alpha="1" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintTop_toBottomOf="@id/tv_job_employer" /> <Constraint android:id="@id/tv_job_industry" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="@dimen/d_8dp" android:alpha="1" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintTop_toBottomOf="@id/tv_job_hour" /> <Constraint android:id="@id/tab_layout_box" android:layout_width="match_parent" android:layout_height="@dimen/d_40dp" motion:layout_constraintTop_toBottomOf="@id/cl_top_img_box" /> <Constraint android:id="@id/view_pager" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintTop_toBottomOf="@id/tab_layout_box" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@id/cl_top_img_box" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintBottom_toBottomOf="@id/back_layout" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@id/ll_gallery_box" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="@dimen/d_15dp" android:layout_marginBottom="@dimen/d_10dp" android:alpha="0" motion:layout_constraintBottom_toBottomOf="@id/cl_top_img_box" motion:layout_constraintRight_toRightOf="@id/cl_top_img_box" /> <Constraint android:id="@id/status_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:alpha="0" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@id/back_layout" android:layout_width="match_parent" android:layout_height="@dimen/d_40dp" motion:layout_constraintTop_toBottomOf="@id/status_view" /> <Constraint android:id="@id/tv_job_title" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_50dp" android:layout_marginTop="0dp" android:layout_marginRight="@dimen/d_50dp" motion:layout_constraintBottom_toBottomOf="@id/back_layout" motion:layout_constraintLeft_toLeftOf="@id/back_layout" motion:layout_constraintRight_toRightOf="@id/back_layout" motion:layout_constraintTop_toTopOf="@id/back_layout"> <CustomAttribute motion:attributeName="textSize" motion:customFloatValue="20" /> </Constraint> <Constraint android:id="@id/tv_job_employer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="@dimen/d_11dp" android:alpha="0" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintTop_toBottomOf="@id/back_layout" /> <Constraint android:id="@id/tv_job_hour" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="@dimen/d_11dp" android:alpha="0" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintTop_toBottomOf="@id/tv_job_employer" /> <Constraint android:id="@id/tv_job_industry" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="@dimen/d_8dp" android:alpha="0" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintTop_toBottomOf="@id/tv_job_hour" /> <Constraint android:id="@id/tab_layout_box" android:layout_width="match_parent" android:layout_height="@dimen/d_40dp" motion:layout_constraintTop_toBottomOf="@id/back_layout" /> <Constraint android:id="@id/view_pager" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintTop_toBottomOf="@id/tab_layout_box" /> </ConstraintSet> </MotionScene>
效果:
同样的效果我们可以用 MotionLayout 实现一些 Behavior 的效果:
<androidx.constraintlayout.motion.widget.MotionLayout android:id="@+id/motion_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/page_bg" app:layoutDescription="@xml/scene_profile_page"> <!-- 状态栏 --> <com.guadou.lib_baselib.view.titlebar.StatusbarGrayView android:layout_width="match_parent" android:layout_height="wrap_content" /> <!-- Profile顶部 --> <ImageView android:id="@+id/iv_top_bg" android:layout_width="0dp" android:layout_height="245dp" android:scaleType="fitXY" android:src="@drawable/profile_top_img_bg" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <!-- 内部的头像+姓名等信息 --> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/cl_name_info" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="30dp" android:layout_marginTop="50dp" android:layout_marginRight="15dp" android:orientation="horizontal" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"> <com.guadou.lib_baselib.view.CircleImageView android:id="@+id/iv_user_avatar" android:layout_width="74dp" android:layout_height="74dp" android:src="@color/gray" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/iv_user_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_10dp" android:text="邓悠然" android:textColor="@color/black_33" android:textSize="@dimen/d_20sp" app:layout_constraintBottom_toTopOf="@id/tv_user_mobile" app:layout_constraintLeft_toRightOf="@id/iv_user_avatar" app:layout_constraintRight_toLeftOf="@id/tv_user_resume" app:layout_constraintTop_toTopOf="@id/iv_user_avatar" /> <TextView android:id="@+id/tv_user_mobile" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/shape_profile_mobile_bg" android:drawableLeft="@drawable/profile_mobile_icon" android:drawablePadding="4.5dp" android:paddingLeft="@dimen/d_10dp" android:paddingTop="@dimen/d_5dp" android:paddingRight="@dimen/d_10dp" android:paddingBottom="@dimen/d_5dp" android:text="18961023119" android:textColor="@color/black_33" android:textSize="@dimen/d_14sp" app:layout_constraintBottom_toBottomOf="@id/iv_user_avatar" app:layout_constraintLeft_toLeftOf="@id/iv_user_name" app:layout_constraintTop_toBottomOf="@id/iv_user_name" /> <TextView android:id="@+id/tv_user_resume" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/shape_profile_resume_bg" android:drawableLeft="@drawable/profile_resume_icon" android:drawablePadding="@dimen/d_7dp" android:paddingLeft="@dimen/d_15dp" android:paddingTop="@dimen/d_8dp" android:paddingRight="@dimen/d_13dp" android:paddingBottom="@dimen/d_8dp" android:text="我的简历" android:textColor="@color/black_33" android:textSize="@dimen/d_14sp" app:layout_constraintBottom_toBottomOf="@id/iv_user_avatar" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="@id/iv_user_avatar" binding:clicks="@{click.gotoMyResume}" /> </androidx.constraintlayout.widget.ConstraintLayout> <!-- 内部的工作申请统计相关 --> <LinearLayout android:id="@+id/ll_job_info" android:layout_width="match_parent" android:layout_height="117dp" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="180dp" android:layout_marginRight="@dimen/d_15dp" android:background="@drawable/shape_white_round10" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/d_20dp" android:layout_marginTop="14.5dp" android:text="我的投递" android:textColor="@color/black_33" android:textSize="@dimen/d_16sp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:paddingLeft="@dimen/d_5dp" android:paddingRight="@dimen/d_5dp"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/selector_btn_white_gray_bg" binding:clicks="@{click.gotoJobAll}"> ... </androidx.constraintlayout.widget.ConstraintLayout> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/selector_btn_white_gray_bg" binding:clicks="@{click.gotoJobPending}"> ... </androidx.constraintlayout.widget.ConstraintLayout> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/selector_btn_white_gray_bg" binding:clicks="@{click.gotoJobApplied}"> ... </androidx.constraintlayout.widget.ConstraintLayout> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/selector_btn_white_gray_bg" binding:clicks="@{click.gotoJobArrived}"> ... </androidx.constraintlayout.widget.ConstraintLayout> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/selector_btn_white_gray_bg" binding:clicks="@{click.gotoJobComplete}"> ... </androidx.constraintlayout.widget.ConstraintLayout> </LinearLayout> </LinearLayout> <!-- Profile底部列表 --> <androidx.core.widget.NestedScrollView android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="0dp" android:background="@color/page_bg" android:fillViewport="true" android:overScrollMode="never" android:scrollbars="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@id/ll_job_info"> ... </androidx.core.widget.NestedScrollView> </androidx.constraintlayout.motion.widget.MotionLayout>
定义的场景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" motion:duration="350"> <OnSwipe motion:dragDirection="dragUp" motion:touchAnchorId="@id/scrollView" /> <KeyFrameSet> <KeyPosition motion:alpha="0" motion:framePosition="25" motion:keyPositionType="parentRelative" motion:motionTarget="@+id/cl_name_info" /> </KeyFrameSet> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/status_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:alpha="0" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@id/iv_top_bg" android:layout_width="0dp" android:layout_height="245dp" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintRight_toRightOf="parent" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@id/cl_name_info" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="30dp" android:layout_marginTop="50dp" android:layout_marginRight="15dp" android:alpha="1" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintRight_toRightOf="parent" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@id/ll_job_info" android:layout_width="match_parent" android:layout_height="117dp" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="180dp" android:layout_marginRight="@dimen/d_15dp" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintRight_toRightOf="parent" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@id/scrollView" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintTop_toBottomOf="@id/ll_job_info" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@id/status_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:alpha="1" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@id/iv_top_bg" android:layout_width="0dp" android:layout_height="0dp" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintRight_toRightOf="parent" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@id/cl_name_info" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginLeft="30dp" android:layout_marginTop="50dp" android:layout_marginRight="15dp" android:alpha="0" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintRight_toRightOf="parent" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@id/ll_job_info" android:layout_width="match_parent" android:layout_height="117dp" android:layout_marginLeft="@dimen/d_15dp" android:layout_marginTop="10dp" android:layout_marginRight="@dimen/d_15dp" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintRight_toRightOf="parent" motion:layout_constraintTop_toBottomOf="@id/status_view" /> <Constraint android:id="@id/scrollView" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintTop_toBottomOf="@id/ll_job_info" /> </ConstraintSet> </MotionScene>
效果:
总结
类似这样的协调滚动布局,当底部列表滑动的时候,顶部的布局做响应的动作,我们都可以通过 AppBarLayout 和 MotionLayout 来实现。区别只是AppBarLayout实现起来更简单,MotionLayout 的定义可以更细。
需要注意的是 AppBarLayout 定义的方式 可以定义一些相对复杂的页面,如果非常复杂的元素使用 MotionLayout 来作为跟视图,全部的布局一股脑的写在 MotionLayout 中,那么可能导致性能问题的,最直观的感受是启动这个Activity都会很慢。
其实在高刷屏流行的今天,留给我们布局上屏的处理时间越来越少了,如果是特别复杂或是嵌套较深的布局,一定要慎重使用。常见的优化方式是异步加载布局、先展示占位图、优化 MotionLayout 布局,只包裹需要改变的视图。
加载全部内容