美文网首页
Android Material 之CoordinatorLay

Android Material 之CoordinatorLay

作者: leo567 | 来源:发表于2018-05-19 13:06 被阅读151次

    1. CoordinatorLayout

    我们通常把CoordinatorLayout作为顶层布局来协调其子布局之间的动画效果。

    子view1在布局中通过设置behavior属性与子view2关联,当移动view2的时候view1产生相应的效果,而这个效果具体是怎么样的由behavior类来决定。我们把view1叫做Child,view2叫做Dependency,Child跟随Dependency的变化而变化(CoordinatorLayout通过设置子View的 Behaviors来调度子View,使两个互相关联的view之间高度解耦)。

    系统控件中往往把behavior作为内部类自己实现,比如AppBarLayout.Behavior, AppBarLayout.ScrollingViewBehavior,和FloatingActionButton.Behavior等。

    当然我们也可以自定义Behavior,步骤非常简单:

    • 首先在代码中自定义Behavior,一般实现如下两个方法,注意泛型参数传Child的类型。
     public class MyBehavior extends CoordinatorLayout.Behavior<View>{
            /**
             * Determine whether the supplied child view has another specific sibling view as a
             * layout dependency.
             * @param parent the parent view of the given child
             * @param child the child view to test
             * @param dependency the proposed dependency of child
             * @return true if child's layout depends on the proposed dependency's layout,
             *         false otherwise
             */
            @Override
            public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
            //如果dependency是xxView的实例,说明它就是我们所需要的Dependency
            return dependency instanceof xxView;
            }
    
            /**
             * Respond to a change in a child's dependent view
             * @return true if the Behavior changed the child view's size or position, false otherwise
             *
             * 当dependency发生改变时(位置、宽高等),执行这个函数
             * 返回true表示child的位置或者是宽高要发生改变,否则就返回false
             */
            @Override
            public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
            
             // do something
                return true;
            }
        }
    
    • 在布局中使用
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout 
     .......
      >
    
     <View
        .......        
         app:layout_behavior="xxx.xx.xxx.MyBehavior" />
    
        <xxView
            ....... 
             />
    </>
    

    1.1 FloatingActionButton & Snackbar

    FloatingActionButton把它当成一个自带阴影和填充颜色的ImageView来使用皆可.
      Snackbar是Android Support Design Library库支持的来替代Toast的一个控件。Snackbar使用的时候需要一个控件容器用来容纳Snackbar.官方推荐使用CoordinatorLayout。
    像Toast一样使用:

    Snackbar.make(view, "SnackbarTest", Snackbar.LENGTH_LONG).show();
    

    Snackbar还支持添加一个按钮的,其可以如下构造:

    Snackbar.make(view, "SnackbarTest",Snackbar.LENGTH_LONG).setAction("Action", new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Snackbar.make(view,"ActionClick",Snackbar.LENGTH_LONG).show();
                }
            }).show();
    

    1.2 CoordinatorLayout +FloatingActionButton+snackbar

    FloatingActionButton默认使用FloatingActionButton.Behavior,FloatingActionButton会随着snackbar向上移动,下面看效果:
      

       这里写图片描述

    布局:

    <android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/coordinatorLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <android.support.design.widget.FloatingActionButton
            android:id="@+id/floatingActionButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end|bottom"
            android:layout_margin="20dp"
            android:src="@drawable/ic_right"
            >
        </android.support.design.widget.FloatingActionButton>
    </android.support.design.widget.CoordinatorLayout>
    

    代码:

     @InjectView(R.id.floatingActionButton)
        FloatingActionButton floatingActionButton;
        @InjectView(R.id.coordinatorLayout)
        CoordinatorLayout coordinatorLayout;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.inject(this);
        }
        @OnClick(R.id.floatingActionButton)
        public void onClick() {
            Snackbar.make(coordinatorLayout, "SnackbarTest",Snackbar.LENGTH_LONG).setAction("Action", new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Snackbar.make(coordinatorLayout,"ActionClick",Snackbar.LENGTH_LONG).show();
                }
            }).show();
        }
    

    2. AppBarLayout

    AppBarLayout继承自LinearLayout,布局方向为垂直方向。所以你可以把它当成垂直布局的LinearLayout来使用。当CoordinatorLayout发生滚动手势的时候,AppBarLayout的子View通过在布局中设置app:layout_scrollFlags属性,来发生相应的滚动。

    简单的来说,AppBarLayout可以协调其子view随着同为CoordinatorLayout子view的兄弟view发生滚动手势的时候发生相应滚动。app:layout_scrollFlags属性有四个枚举值:

    • scroll:
         this flag should be set for all views that want to scroll off the screen - for views that do not use this flag, they’ll remain pinned to the top of the screen (所有想滚动出屏幕的view都需要设置这个flag, 没有设置这个flag的view将被固定在屏幕顶部)
    • enterAlways:
        this flag ensures that any downward scroll will cause this view to become visible, enabling the ‘quick return’ pattern (设置这个flag时,向下的滚动都会导致该view变为可见,启用快速“返回模式”)
    • enterAlwaysCollapsed:
        When your view has declared a minHeight and you use this flag, your View will only enter at its minimum height , only re-expanding to its full height when the scrolling view has reached it’stop.(当你的视图已经设置minHeight属性又使用此标志时,你的视图只能已最小高度进入,只有当滚动视图到达顶部时才扩大到完整高度)
    • exitUntilCollapsed:
        this flag causes the view to scroll off until it is ‘collapsed’ (its minHeight) before exiting(滚动退出屏幕,最后折叠在顶端)

    2.1CoordinatorLayout+AppBarLayout+Toolbar+TabLayout+ViewPager+RecyclerView+CardView

    效果图:
      


    1.gif

    在AppBarLayout中嵌套Toolbar和TabLayout,与ViewPager协调滚动。

    activity_main.xml

    <android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/coordinatorLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <android.support.design.widget.AppBarLayout
            android:id="@+id/appBarLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"  
                     android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:layout_scrollFlags="scroll|enterAlways"
                />
                <android.support.design.widget.TabLayout
                android:id="@+id/tabLayout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
    
        </android.support.design.widget.AppBarLayout>
    
        <android.support.v4.view.ViewPager
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />
    </android.support.design.widget.CoordinatorLayout>
    

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
    
        @InjectView(R.id.toolbar)
        Toolbar toolbar;
        @InjectView(R.id.tabLayout)
        TabLayout tabLayout;
        @InjectView(R.id.viewpager)
        ViewPager viewpager;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.inject(this);
    
            toolbar.setTitle("This is Title");
            toolbar.setSubtitle("subTitle");
            setSupportActionBar(toolbar);
    
            viewpager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
                @Override
                public int getCount() {
                    return 3;
                }
    
                @Override
                public Fragment getItem(int position) {
                    switch (position) {
                        case 0:
                            return new Fragment_a();
                        case 1:
                            return new Fragment_a();
                        case 2:
                            return new Fragment_a();
                        default:
                            return new Fragment_a();
                    }
                }
    
                @Override
                public CharSequence getPageTitle(int position) {
                    switch (position) {
                        case 0:
                            return "page one";
                        case 1:
                            return "page two";
                        case 2:
                            return "page three";
                        default:
                            return "page one";
                    }
                }
            });
            tabLayout.setupWithViewPager(viewpager);
        }
    }
    

    Fragment_a.java

    public class Fragment_a extends Fragment {
    
        @InjectView(R.id.recyclerView)
        RecyclerView recyclerView;
        List<String> datas;
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment, container, false);
            ButterKnife.inject(this, view);
    
            recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL));
            initData();
            RecyclerViewAdapter adapter=new RecyclerViewAdapter(datas);
            recyclerView.setAdapter(adapter);
    
            SpacesItemDecoration decoration=new SpacesItemDecoration(16);
            recyclerView.addItemDecoration(decoration);
    
            adapter.setOnItemClickListener(new RecyclerViewAdapter.OnRecyclerViewItemClickListener() {
                @Override
                public void onItemClick(View v) {
                    Snackbar.make(v, "Click Item "+v.getTag(), Snackbar.LENGTH_LONG).show();
                }
            });
            return view;
        }
        public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
            private int space;
            public SpacesItemDecoration(int space) {
                this.space=space;
            }
            @Override
            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
                outRect.left=space;
                outRect.right=space;
                outRect.bottom=space;
                if(parent.getChildAdapterPosition(view)==0){
                    outRect.top=space;
                }
            }
        }
        public void initData(){
            datas = new ArrayList<String>();
            for(int i =0;i<17;i++){
                datas.add("item "+i);
            }
        }
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            ButterKnife.reset(this);
        }
    }
    

    RecyclerViewAdapter.java

    public  class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>{
    
        private List<String> datas;
    
        public static interface OnRecyclerViewItemClickListener {
            void onItemClick(View view);
        }
        private OnRecyclerViewItemClickListener mOnItemClickListener = null;
        public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
            mOnItemClickListener = listener;
        }
        public RecyclerViewAdapter(List<String> datas) {
            this.datas=datas;
        }
    
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
        {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item,parent,false);
            v.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v) {
                    mOnItemClickListener.onItemClick(v);
                }
            });
            ViewHolder vh = new ViewHolder(v);
            return vh;
        }
    
        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            holder.item.setTag(position);
            holder.tv.setText(datas.get(position));
        }
    
        class ViewHolder extends RecyclerView.ViewHolder
        {
            public View item;
            public TextView tv;
            public ViewHolder(View view){
                super(view);
                item = view;
                tv = (TextView) view.findViewById(R.id.text);
            }
        }
        @Override
        public int getItemCount()
        {
            return datas.size();
        }
    
        public void addItem(String s, int position) {
            datas.add(position, s);
            notifyItemInserted(position);
        }
    
        public void removeItem(final int position) {
            datas.remove(position);
            notifyItemRemoved(position);
        }
    }
    

    recyclerview_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:foreground="?android:attr/selectableItemBackground"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        app:cardCornerRadius="7dp"
        app:cardElevation="7dp"
        android:clickable="true"
        >
            <TextView
                android:text="TextView"
                android:textColor="#7A67EE"
                android:layout_gravity="center"
                android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="20sp" />
    
    </android.support.v7.widget.CardView>
    

    Toolbar可以看做是android 5.0以后用来替代ActionBar的控件,所以我们在使用Toolbar的时候,需要先在styles.xml文件中的AppTheme标签中设置不使用主题自带的ActionBar,加入如下两行代码:

    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
    

    CoordinatorLayout中带有滚动属性的子View(这里的ViewPager)需要设置app:layout_behavior属性。

    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    

    把ViewPager的滚动和AppBarLayout相关联起来。然后给AppBarLayout中需要产生相应滚动的子view(这里的Toolbar)设置app:layout_scrollFlags属性。

    本demo下载: http://download.csdn.net/detail/amazing7/9577677

    3. CollapsingToolbarLayout

    CollapsingToolbarLayout主要是用于实现折叠效果。它需要放在AppBarLayout布局里面,并且作为AppBarLayout的直接子View。它继承至FrameLayout,给它设置layout_scrollFlags,它可以控制包含在CollapsingToolbarLayout中的控件在响应layout_behavior事件时作出相应的scrollFlags滚动事件(通过给CollapsingToolbarLayout的子view设置app:layout_collapseMode属性)。

    CollapsingToolbarLayout的几个属性:

    app:collapsedTitleTextAppearance 在收缩时Title文字外形设置
    app:expandedTitleTextAppearance 展开时Title文字外形的设置
    app:contentScrim 标题文字停留在顶部时候背景的设置
    app:expandedTitleMarginStart 展开时title向左填充的距离
    app:expandedTitleMarginEnd 收缩时title向左填充的距离

    CollapsingToolbarLayout的子View中通过设置layout_collapseMode属性来响应折叠模式,该属性有两个枚举值:

    “pin”:固定模式,在折叠的时候最后固定在顶端
    “parallax”:视差模式,在折叠的时候会有个视差折叠的效果

    在FloatingActionButton中通过设置以下两个属性,可以使FloatingActionButton跟随CollapsingToolbarLayout的收缩而显示隐藏(所依赖的AppBarLayout的id和相对AppBarLayout的摆放位置)。

    app:layout_anchor="@id/appBarLayout"
    app:layout_anchorGravity="bottom|right|end" 
    

    3.1CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout+Toolbar+NestedScrollView+RecyclerView+FloatingActionButton

    效果图:

    1.gif

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    
    <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/coordinatorLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">
    
        <android.support.design.widget.AppBarLayout
            android:id="@+id/appBarLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fitsSystemWindows="true"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
    
            <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/collapsing_toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:contentScrim="?attr/colorPrimary"
                app:expandedTitleMarginEnd="70dp"
                app:expandedTitleMarginStart="50dp"
                app:layout_scrollFlags="scroll|exitUntilCollapsed">
    
                <ImageView
                    android:layout_width="match_parent"
                    android:layout_height="300dp"
                    android:fitsSystemWindows="true"
                    android:scaleType="centerCrop"
                    android:src="@drawable/ic_scu"
                    app:layout_collapseMode="parallax" />
    
                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    app:layout_collapseMode="pin"
                    app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
    
            </android.support.design.widget.CollapsingToolbarLayout>
    
        </android.support.design.widget.AppBarLayout>
    
        <android.support.v4.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
            <android.support.v7.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </android.support.v4.widget.NestedScrollView>
    
    
        <android.support.design.widget.FloatingActionButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:clickable="true"
            android:src="@drawable/ic_done"
            app:layout_anchor="@id/appBarLayout"
            app:layout_anchorGravity="bottom|right|end" />
    </android.support.design.widget.CoordinatorLayout>
    

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
    
        @InjectView(R.id.toolbar)
        Toolbar toolbar;
        @InjectView(R.id.collapsing_toolbar)
        CollapsingToolbarLayout collapsingToolbar;
        @InjectView(R.id.recyclerView)
        RecyclerView recyclerView;
        List<String> datas;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.inject(this);
    
            setSupportActionBar(toolbar);
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    
            collapsingToolbar.setTitle("Collapsing");
    
            InitRecyclerView();
         }
        public void InitRecyclerView(){
            recyclerView.setLayoutManager(new StaggeredGridLayoutManager(1,StaggeredGridLayoutManager.VERTICAL));
            initData();
            RecyclerViewAdapter adapter=new RecyclerViewAdapter(datas);
            recyclerView.setAdapter(adapter);
    
            SpacesItemDecoration decoration=new SpacesItemDecoration(16);
            recyclerView.addItemDecoration(decoration);
    
            adapter.setOnItemClickListener(new RecyclerViewAdapter.OnRecyclerViewItemClickListener() {
                @Override
                public void onItemClick(View v) {
                    Snackbar.make(v, "Click Item "+v.getTag(), Snackbar.LENGTH_LONG).show();
                }
            });
        }
        public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
            private int space;
            public SpacesItemDecoration(int space) {
                this.space=space;
            }
            @Override
            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
                outRect.left=space;
                outRect.right=space;
                outRect.bottom=space;
                if(parent.getChildAdapterPosition(view)==0){
                    outRect.top=space;
                }
            }
        }
        public void initData(){
            datas = new ArrayList<String>();
            for(int i =0;i<17;i++){
                datas.add("item "+i);
            }
        }
    }
    
    

    NestedScrollView也可以与AppBarLayout协调滚动,可以把它看出自带Behavior的ScrollView来使用,但是它并没有继承ScrollView,而是继承的FrameLayout。
     
     NestedScrollView通过设置layout_behavior来使AppBarLayout产生滚动关联,CollapsingToolbarLayout作为AppBarLayout的子view通过设置layout_scrollFlags属性来产生滚动响应,CollapsingToolbarLayout的子view通过设置layout_collapseMode属性来响应滚动模式。

    本例demo地址:http://download.csdn.net/detail/amazing7/9577751

    相关文章

      网友评论

          本文标题:Android Material 之CoordinatorLay

          本文链接:https://www.haomeiwen.com/subject/aaiodftx.html