Android Material 之CoordinatorLay

2018-05-19  本文已影响151人  leo567

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,步骤非常简单:

 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将被固定在屏幕顶部)

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

上一篇下一篇

猜你喜欢

热点阅读