300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 使用CollapsingToolbarLayout高仿稀土掘金个人中心页

使用CollapsingToolbarLayout高仿稀土掘金个人中心页

时间:2020-08-29 14:16:13

相关推荐

使用CollapsingToolbarLayout高仿稀土掘金个人中心页

注:该文章也同步更新到稀土掘金:链接

前言

CollapsingToolbarLayout是android MaterialDeign提供的一个组件,通过搭配AppBarLayout可实现toolbar的折叠效果。下边就通过仿实现稀土掘金个人中心页来讲解它的具体用法。先上效果图:

实现

我们将toolbar分为两层:

红色圈中的大模块,暂名:A绿色圈中的模块,暂名:B

通过分层再分析下效果:当视图向上滚动时,A会逐渐折叠当A完全折叠后,B的图标会更新状态,并且用户的信息会向上浮动显示在B中 当A没有完全折叠后,用户信息会消失,并且B中的图标会恢复原来的状态

通过效果分析,我们可以这么设计:

将A内容包含在CollapsingToolbarLayout中,再配合CoordinatorLayout+AppBarLayout,即可实现A内容的折叠监听A内容是否折叠,改变B的状态

CollapsingToolbarLayout折叠事件的监听

通过AppBarLayout.OnOffsetChangedListener的回调

Interface definition for a callback to be invoked when an AppBarLayout's vertical offset changes.// TODO(b/76413401): update this interface after the widget migrationpublic interface OnOffsetChangedListener extends BaseOnOffsetChangedListener<AppBarLayout> {void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset);}

查看官方api文档,该回调可以获取到AppBarLayout的垂直偏移量。通过判断偏移量的大小,即可获取到当前toolbar的状态

abstract class AppBarStateChangeListener : AppBarLayout.OnOffsetChangedListener {enum class State {EXPANDED, COLLAPSED, IDLE}private var mCurrentState = State.IDLEoverride fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) {if (verticalOffset == 0) {if (mCurrentState != State.EXPANDED) {onStateChanged(appBarLayout, State.EXPANDED);}mCurrentState = State.EXPANDED} else if (appBarLayout != null) {if (Math.abs(verticalOffset) >= appBarLayout.totalScrollRange) {if (mCurrentState != State.COLLAPSED) {onStateChanged(appBarLayout, State.COLLAPSED);}mCurrentState = State.COLLAPSED} else {if (mCurrentState != State.IDLE) {onStateChanged(appBarLayout, State.IDLE);}mCurrentState = State.IDLE;}}}abstract fun onStateChanged(appBarLayout: AppBarLayout?, state: State)}

通过CollapsingToolbarLayout的setScrimsShown函数

Set whether the content scrim and/or status bar scrim should be shown or not. Any change in the vertical scroll may overwrite this value.Params:shown – whether the scrims should be shownanimate – whether to animate the visibility changeSee Also:getStatusBarScrim(), getContentScrim()public void setScrimsShown(boolean shown, boolean animate) {if (scrimsAreShown != shown) {if (animate) {animateScrim(shown ? 0xFF : 0x0);} else {setScrimAlpha(shown ? 0xFF : 0x0);}scrimsAreShown = shown;}}

查看官方api文档,可以看到当内容发生变化时,该方法会被自动调用,因此我们可以通过扩展该方法

class NestCollapsingToolbarLayout : CollapsingToolbarLayout {private var mIsScrimsShown: Boolean = falseprivate lateinit var scrimsShowListener: OnScrimsShowListenerconstructor(context: Context) : super(context)constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context,attrs,defStyleAttr)override fun setScrimsShown(shown: Boolean, animate: Boolean) {super.setScrimsShown(shown, animate)if (mIsScrimsShown != shown) {mIsScrimsShown = shownif (scrimsShowListener != null) {scrimsShowListener.onScrimsShowChange(this, mIsScrimsShown)}}}fun setOnScrimesShowListener(listener: OnScrimsShowListener){scrimsShowListener = listener}interface OnScrimsShowListener {fun onScrimsShowChange(nestCollapsingToolbarLayout: NestCollapsingToolbarLayout,isScrimesShow: Boolean)}}

当然这两个方法会有区别,方法一监听的offset为整个AppBarLayout包含内容的高度,方法二只针对CollapsingToolbarLayout包含内容的高度。这里我使用方法二

布局设计

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/white"android:orientation="vertical"tools:context=".MainActivity"><androidx.coordinatorlayout.widget.CoordinatorLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><com.google.android.material.appbar.AppBarLayoutandroid:id="@+id/appbar"android:layout_width="match_parent"android:layout_height="wrap_content"><com.example.behaviordemo.NestCollapsingToolbarLayoutandroid:id="@+id/toolbarLayout"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@android:color/white"app:layout_scrollFlags="scroll|exitUntilCollapsed"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:layout_width="match_parent"android:layout_height="150dp"android:scaleType="centerCrop"android:src="@drawable/user_profile_header" /><ImageViewandroid:id="@+id/ivHead"android:layout_width="60dp"android:layout_height="60dp"android:layout_marginLeft="10dp"android:layout_marginTop="125dp"android:background="@drawable/empty_avatar_user" /><LinearLayoutandroid:id="@+id/llAuthor"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/ivHead"android:layout_marginLeft="10dp"android:layout_marginTop="10dp"android:gravity="center_vertical"android:orientation="horizontal"><TextViewandroid:id="@+id/author_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="PG_KING"android:textColor="@android:color/black"android:textSize="16sp" /><ImageViewandroid:id="@+id/ivLevel"android:layout_width="27dp"android:layout_height="15dp"android:layout_marginLeft="5dp"android:background="@drawable/ic_user_big_lv1" /></LinearLayout><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/llAuthor"android:layout_marginLeft="8dp"android:text="Android"android:textColor="@android:color/darker_gray"android:textSize="13sp" /><TextViewandroid:layout_width="80dp"android:layout_height="35dp"android:layout_alignTop="@+id/llAuthor"android:layout_alignParentRight="true"android:layout_marginRight="10dp"android:background="@drawable/bg_edit"android:gravity="center"android:text="编辑"android:textColor="@color/theme_blue"android:textSize="15sp" /></RelativeLayout></com.example.behaviordemo.NestCollapsingToolbarLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="50dp"android:background="@android:color/white"android:orientation="horizontal"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center_vertical"android:orientation="vertical"android:paddingLeft="10dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="10000"android:textColor="@android:color/black"android:textSize="14sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="关注"android:textColor="@android:color/darker_gray"android:textSize="13sp" /></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center_vertical"android:orientation="vertical"android:paddingLeft="10dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="10000"android:textColor="@android:color/black"android:textSize="14sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="关注"android:textColor="@android:color/darker_gray"android:textSize="13sp" /></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center_vertical"android:orientation="vertical"android:paddingLeft="10dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="10000"android:textColor="@android:color/black"android:textSize="14sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="关注"android:textColor="@android:color/darker_gray"android:textSize="13sp" /></LinearLayout></LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="10dp"android:background="@android:color/darker_gray" /><com.google.android.material.tabs.TabLayoutandroid:id="@+id/tabLayout"android:layout_width="match_parent"android:layout_height="60dp"app:tabIndicatorColor="@color/theme_blue"app:tabIndicatorFullWidth="false"app:tabSelectedTextColor="@color/theme_blue"app:tabTextColor="@android:color/darker_gray" /></com.google.android.material.appbar.AppBarLayout><androidx.viewpager.widget.ViewPagerandroid:id="@+id/viewPager"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/white"app:layout_behavior="@string/appbar_scrolling_view_behavior" /></androidx.coordinatorlayout.widget.CoordinatorLayout><RelativeLayoutandroid:id="@+id/rlTitle"android:layout_width="match_parent"android:layout_height="60dp"android:paddingLeft="15dp"android:paddingRight="15dp"><ImageViewandroid:id="@+id/ivBack"android:layout_width="25dp"android:layout_height="25dp"android:layout_centerVertical="true"android:src="@drawable/ic_back" /><LinearLayoutandroid:id="@+id/llSmallAuthor"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_marginLeft="30dp"android:layout_toRightOf="@+id/ivBack"android:gravity="center_vertical"android:orientation="horizontal"android:visibility="invisible"><ImageViewandroid:layout_width="35dp"android:layout_height="35dp"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:src="@drawable/empty_avatar_user" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:text="PG_KING"android:textColor="@android:color/black"android:textSize="16sp" /><ImageViewandroid:layout_width="27dp"android:layout_height="15dp"android:layout_marginLeft="5dp"android:background="@drawable/ic_user_big_lv1" /></LinearLayout><ImageViewandroid:id="@+id/ivShare"android:layout_width="25dp"android:layout_height="25dp"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:src="@drawable/ic_share" /><ImageViewandroid:id="@+id/ivUserData"android:layout_width="25dp"android:layout_height="25dp"android:layout_centerVertical="true"android:layout_marginRight="30dp"android:layout_toLeftOf="@+id/ivShare"android:src="@drawable/ic_userdata" /></RelativeLayout></FrameLayout>

AppBarLayout+CollapsingToolbarLayout需要实现折叠效果,必须选择CoordinatorLayout作为根布局CollapsingToolbarLayout需要设置app:layout_scrollFlags=“scroll|exitUntilCollapsed”定义AppBarLayout与滚动视图之间的联系:在RecyclerView或者任意支持嵌套滚动的view比如NestedScrollView上添加app:layout_behavior属性配置

实现滚动效果

appBarLayout.setOnScrimesShowListener(object :NestCollapsingToolbarLayout.OnScrimsShowListener {override fun onScrimsShowChange(nestCollapsingToolbarLayout: NestCollapsingToolbarLayout,isScrimesShow: Boolean) {if (isScrimesShow) {rlTitle.setBackgroundColor(Color.WHITE)ivBack.setImageResource(R.drawable.ic_back_blue)ivShare.setImageResource(R.drawable.ic_share_blue)ivUserData.setImageDrawable(tintDrawable(resources.getDrawable(R.drawable.ic_userdata),ColorStateList.valueOf(Color.parseColor("#B6B6B6"))))showSmallAuthor()} else {rlTitle.setBackgroundColor(Color.TRANSPARENT)ivBack.setImageResource(R.drawable.ic_back)ivShare.setImageResource(R.drawable.ic_share)ivUserData.setImageDrawable(tintDrawable(ivUserData.drawable,ColorStateList.valueOf(Color.parseColor("#FFFFFF"))))if (objectAnimator.isRunning) {objectAnimator.cancel()}llSmallAuthor.visibility = View.INVISIBLE}}})

补充

1、这里还使用了TabLayout+ViewPager的组合,用于实现主内容区的逻辑。tablayout和viewpager的关联绑定

pagerAdapter = object : ListPagerAdapter(supportFragmentManager, fragments) {override fun getPageTitle(position: Int): CharSequence? {if (tabList.size > position) {return tabList.get(position)}return ""}}viewPager.adapter = pagerAdaptertabLayout.setupWithViewPager(viewPager)

2、动态改变drawable的颜色

看上图,统计柱状图由白色变成灰色~这里没有使用两张图片进行切换,而是使用了系统提供的DrawableCompat.setTintList函数

fun tintDrawable(drawable: Drawable, colors: ColorStateList): Drawable {val wrappedDrawable = DrawableCompat.wrap(drawable)DrawableCompat.setTintList(wrappedDrawable, colors)return wrappedDrawable}// 使用ivUserData.setImageDrawable(tintDrawable(resources.getDrawable(R.drawable.ic_userdata),ColorStateList.valueOf(Color.parseColor("#B6B6B6"))))

拓展

app:layout_scrollFlags属性介绍

<attr name="layout_scrollFlags"><!-- Disable scrolling on the view. This flag should not be combined with any of the otherscroll flags. --><flag name="noScroll" value="0x0"/><!-- The view will be scroll in direct relation to scroll events. This flag needs to beset for any of the other flags to take effect. If any sibling viewsbefore this one do not have this flag, then this value has no effect. --><flag name="scroll" value="0x1"/><!-- When exiting (scrolling off screen) the view will be scrolled until it is'collapsed'. The collapsed height is defined by the view's minimum height. --><flag name="exitUntilCollapsed" value="0x2"/><!-- When entering (scrolling on screen) the view will scroll on any downwardsscroll event, regardless of whether the scrolling view is also scrolling. Thisis commonly referred to as the 'quick return' pattern. --><flag name="enterAlways" value="0x4"/><!-- An additional flag for 'enterAlways' which modifies the returning view toonly initially scroll back to it's collapsed height. Once the scrolling view hasreached the end of it's scroll range, the remainder of this view will be scrolledinto view. --><flag name="enterAlwaysCollapsed" value="0x8"/><!-- Upon a scroll ending, if the view is only partially visible then it will besnapped and scrolled to it's closest edge. --><flag name="snap" value="0x10"/><!-- An additional flag to be used with 'snap'. If set, the view will be snapped to itstop and bottom margins, as opposed to the edges of the view itself. --><flag name="snapMargins" value="0x20"/></attr>

查看官方api文档,我们整体梳理下:

其他属性的使用需要配合scroll,否则会没有效果,正确用法举例:app:layout_scrollFlags=“scroll|exitUntilCollapsed”enterAlways:不管向下还是向上滚动,优先滚动的是AppBarLayout中的childViewexitUntilCollapsed:向上滚动,优先滚动的是AppBarLayout中的childView;向下滚动,优先滚动的是其他视图,直到其他视图已经滚动到顶部再滚动AppBarLayoutsnap:可配合其他属性一起使用,可以保证AppBarLayout完全显示或者完全隐藏而不会显示部分

具体的效果,可以通过写例子进行体验。跑过效果理解起来才没那么枯燥~

最后附上demo地址:gitee-demo

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。