Tears_fg

导航

安卓Design包之CollapsingToolbarLayout(可折叠的工具栏布局)的简单使用

转自:

CollapsingToolbarLayout的使用

2020.7.8记录:

项目更新为androidX了,里面的包名相应的也要替换,否则会找不到包。

android.support.design.widget.CoordinatorLayout -> androidx.coordinatorlayout.widget.CustomCoordinatorLayout
android.support.design.widget.AppBarLayout -> com.google.android.material.appbar.AppBarLayout
android.support.design.widget.CollapsingToolbarLayout -> com.google.android.material.appbar.CollapsingToolbarLayout
android.support.v7.widget.Toolbar -> androidx.core.widget.Toolbar
android.support.design.widget.TabLayout -> com.google.android.material.appbar.TabLayout
android.support.v4.widget.NestedScrollView -> androidx.core.widget.NestedScrollView

 

注意:使用前需要添加Design依赖包,使用toolbar时需要隐藏标题头

CollapsingToolbarLayout作用是提供了一个可以折叠的Toolbar,它继承至FrameLayout,给它设置layout_scrollFlags,它可以控制包含在CollapsingToolbarLayout中的控件(如:ImageView、Toolbar)在响应layout_behavior事件时作出相应的scrollFlags滚动事件(移除屏幕或固定在屏幕顶端)

NestedScrollView:它是support-v4包提供的控件,继承至FrameLayout, 并实现了NestedScrollingParent,NestedScrollingChild, ScrollingView接口. 

它的作用类似于Android.widget.ScrollView,不同点在于NestedScrollView支持嵌套滑动.

需实现的布局:

 最外层是CoorinatorLayout,然后里面包含了AppBarlayout和NestedScrollView

AppBarLayout里包含了CollspsingToolbar和Tableyout,它的作用是可以将所有的子控件都当成一个整体

CollapsingToolbarLayout里面则包含了一个ImageView和ToolBar作为伸缩的区域。

 

 

1. AppBarLayout的子布局有5种滚动标识(layout_scrollFlags属性):

  • scroll:将此布局和滚动时间关联。这个标识要设置在其他标识之前,没有这个标识则布局不会滚动且其他标识设置无效。
  • enterAlways:任何向下滚动操作都会使此布局可见。这个标识通常被称为“快速返回”模式。
  • enterAlwaysCollapsed:假设你定义了一个最小高度(minHeight)同时enterAlways也定义了,那么view将在到达这个最小高度的时候开始显示,并且从这个时候开始慢慢展开,当滚动到顶部的时候展开完。
  • exitUntilCollapsed:当你定义了一个minHeight,此布局将在滚动到达这个最小高度的时候折叠。
  • snap:当一个滚动事件结束,如果视图是部分可见的,那么它将被滚动到收缩或展开。例如,如果视图只有底部25%显示,它将折叠。相反,如果它的底部75%可见,那么它将完全展开。

  实践证明,scroll和enterAlwaysCollapsed,scroll和exitUntilCollapsed使用时效果无阴影,scroll和enterAlways配合使用时,效果有阴影。

2.CollapsingToolbarLayout中通过layout_collapseMode属性来指定其内部的子控件是折叠还是固定在屏幕上方。


 <!-- layout_collapseMode(折叠模式)-有两个值:
                 1.parallax:在内容滚动时,CollapsingToolbarLayout中的View(比如ImageView)也可以同时滚动,
                 实现视差滚动效果,通常和layout_collapseParallaxMultiplier(设置视差因子)搭配使用。如果不想实现联动效果,可以设置为0,效果就和scrollview一样了
                 2.pin - 当CollapsingToolbarLayout完全收缩后,Toolbar还可以固定在屏幕上。
          -->

xml文件:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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:orientation="vertical"
    tools:context="fanggao.qf.collapsingtoolbarlayout.MainActivity">
   <!-- android:fitsSystemWindow = "true" 表示整个布局展示是整个屏幕出去状态栏,标题栏和导航栏剩下的区域-->
    <android.support.design.widget.AppBarLayout
        android:id="@+id/layout_appbar"
        android:layout_width="match_parent"
        android:layout_height = "wrap_content"
       >
        <!--
         app:expandedTitleMarginStart="10dp"
        设置扩张时候(还没有收缩时)title离屏幕左边的距离

         app:contentScrim="?attr/colorPrimary"
        设置当完全CollapsingToolbarLayout折叠(收缩)后的背景颜色
        -->
        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/ctb"
            android:layout_width="match_parent"
            android:layout_height="250dp"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleMarginStart="10dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">
       
        <ImageView
            android:id="@+id/image"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop"
            app:layout_collapseMode="parallax"
            android:src = "@drawable/cat"
            />
        <!--标题-->
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="30dp"
            app:layout_collapseMode="pin"
            app:title="Toolbar"/>
        </android.support.design.widget.CollapsingToolbarLayout>
        <!--选项卡-->
        <android.support.design.widget.TabLayout
            android:id="@+id/tabLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabIndicatorColor="@color/colorAccent"
            app:tabMode="scrollable"
            app:tabSelectedTextColor="@color/colorAccent"
            app:tabTextColor="@android:color/black"/>

    </android.support.design.widget.AppBarLayout>
    <android.support.v4.widget.NestedScrollView
        android:id="@+id/nestedScrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <android.support.v4.view.ViewPager
            android:id="@+id/viewPager"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>

主程序:

public class MainActivity extends AppCompatActivity {
    private Toolbar toolbar;
    private ImageView image;
    private ViewPager viewpager;
    private TabLayout tabLayout;
    private CollapsingToolbarLayout collapsingToolbarLayout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();
    }
    private void initData() {
        toolbar.setLogo(R.mipmap.ic_launcher);
        setSupportActionBar(toolbar);
        //设置返回按钮
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        //设置收缩展开toolbar字体颜色
        collapsingToolbarLayout.setExpandedTitleColor(Color.WHITE);
        collapsingToolbarLayout.setCollapsedTitleTextColor(Color.BLACK);

        //设置tablayout与viewPager
        viewpager.setAdapter(new TestViewPageAdapter());
        tabLayout.setupWithViewPager(viewpager);
    }
    private void initView() {
        toolbar = (Toolbar) findViewById(R.id.toolbar);
        image = (ImageView) findViewById(R.id.image);
        viewpager = (ViewPager) findViewById(R.id.viewPager);
       tabLayout = (TabLayout) findViewById(R.id.tabLayout);
       collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.ctb);
    }
    class TestViewPageAdapter extends PagerAdapter{
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            TextView textView = new TextView(MainActivity.this);
            textView.setGravity(Gravity.CENTER);
            textView.setText("pager "+(position+1));
            textView.setTextSize(30);
            textView.setTextColor(Color.BLUE);
            container.addView(textView);
            return textView;
        }
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View)object);
        }
        @Override
        public int getCount() {
            return 5;
        }
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
/*获得标题*/
        /*该方法必须写,不然tablayout不能显示标题*/
        @Override
        public CharSequence getPageTitle(int position) {
            return "TAB"+(position+1);
        }
    }
}

效果:

滑动前

向下滑动后:

 

 如果有viewpager嵌套fragment的场景,可以在fragment的根布局中加上NestedScrollView,这样就不会产生滑动冲突事件。如果使用的是ScrollView,会导致上下2个分离,CollapsingToolbarLayout未滑动时,scrollview也可以滑动(不支持嵌套滑动),这样就跟实际需求背离了。

NestedScrollView参考资料:

https://www.jianshu.com/p/f55abc60a879

NestedScrolling机制完全解析 带你玩转嵌套滑动

 

add:

如何监听CollapsingToolbarLayout的展开与折叠?

这里我们使用官方提供的AppBarLayout.OnOffsetChangedListener就可以实现了,不过封装下效果更好。代码如下。

import com.google.android.material.appbar.AppBarLayout

abstract class AppBarStateChangeListener : AppBarLayout.OnOffsetChangedListener {
    private var mCurrentState = State.IDLE

    enum class State {
        EXPANDED,
        COLLAPSED,
        IDLE
    }

    override fun onOffsetChanged(appBarLayout: AppBarLayout?, i: Int) {
        appBarLayout?.let {
            if (i == 0) {
                if (mCurrentState != State.EXPANDED) {
                    onStateChanged(it, State.EXPANDED)
                }
                mCurrentState = State.EXPANDED
            } else if (Math.abs(i) >= it.totalScrollRange) {
                if (mCurrentState != State.COLLAPSED) {
                    onStateChanged(it, State.COLLAPSED)
                }
                mCurrentState = State.COLLAPSED
            } else {
                if (mCurrentState != State.IDLE) {
                    onStateChanged(it, State.IDLE)
                }
                mCurrentState = State.IDLE
            }
        }
    }



    abstract fun onStateChanged(appBarLayout: AppBarLayout?, state: State)
}
View Code

使用:

 appbar_layout.addOnOffsetChangedListener(object : AppBarStateChangeListener() {
            override fun onStateChanged(appBarLayout: AppBarLayout?, state: State) {
                if( state == State.EXPANDED ) {
                    //展开状态

                }else if(state == State.COLLAPSED){
                    //折叠状态
                }else{
                    //中间状态
                }
            }
        })

 

2022.7.20更新:

折叠布局去除阴影

1.drawable文件夹新建appbar_elevation.xml文件

<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
    <!--折叠状态下的阴影-->
    <item app:state_collapsed="true">
        <objectAnimator
            android:propertyName="elevation"
            android:valueTo="0dp"
            android:valueType="floatType" />
    </item>
    <!--展开状态下的阴影-->
    <item app:state_collapsed="false">
        <objectAnimator
            android:propertyName="elevation"
            android:valueTo="0dp"
            android:valueType="floatType" />
    </item>
</selector>

2.appbar增加stateListAnimator属性。

               <com.google.android.material.appbar.AppBarLayout
                    android:id="@+id/mAppBarLayout"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/white"
                    android:stateListAnimator="@drawable/expert_appbar_elevation"
                    >

原理见

AppBarLayout嵌套CollapsingToolbarLayout实现折叠屏去除阴影

FAQ:

1.CoordinatorLayout中的recyclerview下拉刷新卡顿,当从下往上快速滑动时,每次需要等待几秒才能刷新,怎么解决?

  当recyclerview快速滑动时,不能触发下拉刷新,这是因为onSrollStateChanged()回调没有及时调用,当快速滑动时,会调用

SCROLL_STATE_SETTLING(快速滑动)状态,该状态等到fling结束才会调用onSrollStateChanged()方法改变为SCROLL_STATE_IDLE(停止)状态(大概时间为2-3秒)。
 
 recycleviewCars.addOnScrollListener(new RecyclerView.OnScrollListener() {
           @Override
           public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
               super.onScrollStateChanged(recyclerView, newState);
               if(newState == RecyclerView.SCROLL_STATE_SETTLING){
                   recycleviewCars.stopScroll();
               }
           }
       });

 

 

  

posted on 2016-10-08 18:05  Tears_fg  阅读(20674)  评论(0编辑  收藏  举报