欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Material Design设计,包含CoordinatorLayout、AppBarLayout、CollapsingToolbarLayout、FloatingActionBar等控件知识梳理

程序员文章站 2022-05-30 23:12:26
...

Material Design 概述

Google I/O 2014 发布的 Material Design 势必将会成为统一 Android Mobile、Android Table、Desktop Chrome 等全平台设计语言规范。然而普及程度并没有想象中的那么好,于是Google提供了一个Design Support库方便开发者开发。在2015年I / O大会上,Google发布了一个新的设计支持库,该库有助于引入大量材料设计组件,包括导航抽屉视图,浮动标签,浮动动作按钮,Snackbars以及用于将运动和滚动事件。 该库支持Android 2.3及更高版本。

添加依赖

implementation 'com.android.support:design:27.1.1'

Toolbar

Toolbar可以理解升级版的Actionbar,由于Actionbar设计原因,只能位于Activity顶端,不能实现Material Design效果,因此官方推荐使用Toolbar来替代Actionbar。

效果(看起来跟Actionbar没有差别)
Material Design设计,包含CoordinatorLayout、AppBarLayout、CollapsingToolbarLayout、FloatingActionBar等控件知识梳理

  • 注意:

    • Activity继承AppCompatActivity
    • 设置一个不带Actionbar的Theme

直接上代码实现一个Toolbar,布局文件、Java代码如下:

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="@color/colorPrimary"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:layout_scrollFlags="scroll|snap|enterAlways"
    app:popupTheme="@style/Theme.AppCompat.Light" />
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

实现一个侧滑菜单

效果
Material Design设计,包含CoordinatorLayout、AppBarLayout、CollapsingToolbarLayout、FloatingActionBar等控件知识梳理

布局文件

material_design_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:orientation="vertical">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:layout_scrollFlags="scroll|snap|enterAlways"
            app:popupTheme="@style/Theme.AppCompat.Light" />

    </FrameLayout>

    <android.support.design.widget.NavigationView
        android:id="@+id/navigationView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/header_layout"
        app:menu="@menu/drawer_layout_menu" />

</android.support.v4.widget.DrawerLayout>
  • DrawerLayout继承ViewGroup,作为布局文件的它,允许放入两个子控件,一个是主屏幕当前显示的内容,另一个即为侧滑菜单的内容。
  • NavigationView是Design Support中提供的一个控件,通过app:headerLayout和app:menu两个属性设置侧滑菜单的头布局与具体的菜单项,这里需要注意的是必须要设置android:layout_gravity=”start”属性,至于方向:从左到右或者从右到左自己选择。

header_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="180dp"
    android:background="?attr/colorPrimary"
    android:padding="10dp">

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/civ_icon"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_centerInParent="true"
        android:src="@drawable/ic_account_circle_gray_24dp" />

    <TextView
        android:id="@+id/tv_username"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/civ_icon"
        android:layout_centerInParent="true"
        android:padding="2dp"
        android:text="username"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/tv_mail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tv_username"
        android:layout_centerInParent="true"
        android:padding="2dp"
        android:text="aaa@qq.com"
        tools:ignore="HardcodedText" />

</RelativeLayout>

drawer_layout_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_call"
            android:icon="@drawable/ic_account_circle_black_24dp"
            android:title="Profile"
            tools:ignore="HardcodedText" />
        <item
            android:id="@+id/nav_friends"
            android:icon="@drawable/ic_people_black_24dp"
            android:title="Friends"
            tools:ignore="HardcodedText" />
        <item
            android:id="@+id/nav_location"
            android:icon="@drawable/ic_place_black_24dp"
            android:title="Location"
            tools:ignore="HardcodedText" />
        <item
            android:id="@+id/nav_mail"
            android:icon="@drawable/ic_email_black_24dp"
            android:title="Email"
            tools:ignore="HardcodedText" />
        <item
            android:id="@+id/nav_task"
            android:icon="@drawable/ic_assignment_black_24dp"
            android:title="Tasks"
            tools:ignore="HardcodedText" />
    </group>
</menu>
Java代码

TestMaterialDesign.java

public class TestMaterialDesign extends DefaultActivity implements NavigationView.OnNavigationItemSelectedListener {

    private DrawerLayout mDrawerLayout;
    private NavigationView mNavigationView;
    private CircleImageView mCivPic;

    @Override
    public void initView() {

        setContentView(R.layout.material_design_layout2);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        mDrawerLayout = findViewById(R.id.drawer_layout);
        mNavigationView = findViewById(R.id.navigationView);
        //导入header_layout布局,获取CircleImageView,并设置点击事件
        View headerView = mNavigationView.inflateHeaderView(R.layout.header_layout);
        mCivPic = headerView.findViewById(R.id.civ_icon);
    }

    @Override
    public void initClickListener() {

        mCivPic.setOnClickListener(this);
        mNavigationView.setNavigationItemSelectedListener(this);
    }

    @Override
    public void onClick(View v) {

        switch (v.getId()) {

            //圆形图片控件点击事件
            case R.id.civ_icon:

                Toast.makeText(this, "点击可换图像", Toast.LENGTH_SHORT).show();
                mDrawerLayout.closeDrawers();
                break;
        }
    }

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {

        switch (item.getItemId()) {

            case R.id.nav_call:

                Toast.makeText(this, item.getTitle(), Toast.LENGTH_SHORT).show();
                break;

            case R.id.nav_friends:

                Toast.makeText(this, item.getTitle(), Toast.LENGTH_SHORT).show();
                break;

            case R.id.nav_location:

                Toast.makeText(this, item.getTitle(), Toast.LENGTH_SHORT).show();
                break;

            case R.id.nav_mail:

                Toast.makeText(this, item.getTitle(), Toast.LENGTH_SHORT).show();
                break;

            case R.id.nav_task:

                Toast.makeText(this, item.getTitle(), Toast.LENGTH_SHORT).show();
                break;
        }
        //这里简单处理一下,调用closeDrawers()方法关闭侧滑菜单
        mDrawerLayout.closeDrawers();

        return false;
    }
}

这时一个简单的侧滑菜单已经完成,可是主页面却空空如也,那么接下来就给主页面添加一些东西吧!

效果展示
Material Design设计,包含CoordinatorLayout、AppBarLayout、CollapsingToolbarLayout、FloatingActionBar等控件知识梳理

修改布局material_design_layout.xml,仅仅增加RecyclerView控件

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:orientation="vertical">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:layout_scrollFlags="scroll|snap|enterAlways"
            app:popupTheme="@style/Theme.AppCompat.Light" />

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </FrameLayout>

适配器HeroAdapter

public class HeroAdapter extends RecyclerView.Adapter<HeroAdapter.HeroViewHolder> {

    private Context mContext;
    private List<Heros> mHeroDatas;
    private LayoutInflater mInflate;
    private ICardViewOnClickListener listener;

    public HeroAdapter(Context context, List<Heros> herosList) {

        mHeroDatas = new ArrayList<>();
        mInflate = LayoutInflater.from(context);
        this.mContext = context;
        this.mHeroDatas = herosList;
    }

    @NonNull
    @Override
    public HeroViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

        View view = mInflate.inflate(R.layout.recyclerview_heros_item, parent, false);

        return new HeroViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull final HeroViewHolder holder, int position) {

        final int adapterPosition = holder.getAdapterPosition();
        Heros heros = mHeroDatas.get(adapterPosition);
        holder.tv_name.setText(heros.getName());
         //Github开源库Glide
        Glide.with(mContext).load(heros.getImageUrl()).into(holder.iv_icon);

        holder.cardView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                listener.onCardViewClick(adapterPosition);
            }
        });
    }

    @Override
    public int getItemCount() {

        return mHeroDatas.size();
    }

    static class HeroViewHolder extends RecyclerView.ViewHolder {

        ImageView iv_icon;
        TextView tv_name;
        CardView cardView;

        HeroViewHolder(View itemView) {
            super(itemView);

            iv_icon = itemView.findViewById(R.id.iv_showIcon);
            tv_name = itemView.findViewById(R.id.tv_name);
            cardView = itemView.findViewById(R.id.card_view);
        }
    }

    public interface ICardViewOnClickListener {

        void onCardViewClick(int position);
    }

    public void setCardViewClickListener(ICardViewOnClickListener listener) {

        this.listener = listener;
    }
}

实体类Hero

public class Heros {

    private String mHeroName;
    private int mHeroImage;

    public Heros(String name, int imageUrl) {

        this.mHeroName = name;
        this.mHeroImage = imageUrl;
    }

    public String getName() {

        return mHeroName;
    }

    public int getImageUrl() {

        return mHeroImage;
    }
}

RecyclerView的Item布局文件 recyclerview_heros_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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/card_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    android:elevation="12dp"
    app:cardCornerRadius="6dp"
    tools:targetApi="lollipop">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:orientation="vertical"
        android:padding="5dp">

        <ImageView
            android:id="@+id/iv_showIcon"
            android:layout_width="196dp"
            android:layout_height="100dp"
            android:layout_gravity="center"
            android:src="@mipmap/ali" />

        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center"
            android:padding="5dp"
            android:text="阿狸"
            android:textColor="@color/colorAccent"
            android:textSize="16sp"
            tools:ignore="HardcodedText" />

    </LinearLayout>

</android.support.v7.widget.CardView>
  • CardView是实现卡片式布局效果的重要控件,查看源码可以发现它也是继承于FramLayout,增加了android:elevation和app:cardCornerRadius两个属性,前者是产生阴影效果,设置后会有立体感,高度越高,投影范围越大,投影颜色越淡,后者则是圆角效果;

修改TestMaterialDesign.java代码 上面代码不变仅展示新增代码

public class TestMaterialDesign extends DefaultActivity implements NavigationView.OnNavigationItemSelectedListener {

    private DrawerLayout mDrawerLayout;
    private NavigationView mNavigationView;
    private CircleImageView mCivPic;
    private List<Heros> mHerosList;
    private RecyclerView mRecyclerView;
    private HeroAdapter mAdapter;
     private Heros[] mHeros =
            {
                    new Heros("艾希", R.mipmap.aixi), new Heros("阿狸", R.mipmap.ali),
                    new Heros("伊泽瑞尔", R.mipmap.ez), new Heros("盖伦", R.mipmap.gailun),
                    new Heros("孙悟空", R.mipmap.houzi), new Heros("剑姬", R.mipmap.jianji),
                    new Heros("剑圣", R.mipmap.jiansheng), new Heros("机器人", R.mipmap.jiqiren),
                    new Heros("蛮子", R.mipmap.manzi), new Heros("诺克", R.mipmap.nuoke),
                    new Heros("女枪", R.mipmap.nvqiang), new Heros("琴女", R.mipmap.qinnv),
                    new Heros("瑞文", R.mipmap.ruiwen), new Heros("螳螂", R.mipmap.tanglang),
                    new Heros("提莫", R.mipmap.timo), new Heros("武器", R.mipmap.wuqi),
                    new Heros("小炮", R.mipmap.xiaopao), new Heros("瞎子", R.mipmap.xiazi),
                    new Heros("妖姬", R.mipmap.yaoji), new Heros("亚索", R.mipmap.yasuo)
            };

    @Override
    public void initView() {

        setContentView(R.layout.material_design_layout);
        mHerosList = new ArrayList<>();
        mRecyclerView = findViewById(R.id.recyclerview);
    }

    @Override
    public void initDatas() {

        Collections.addAll(mHerosList, mHeros);

        mAdapter = new HeroAdapter(this, mHerosList);

        mRecyclerView.setAdapter(mAdapter);

        GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
        mRecyclerView.setLayoutManager(layoutManager);
    }

到现在为止主页面的内容已经有了,可是看着总有一点别扭,到底是哪块别扭呢?发现RecyclerView的Item盖住了Toolbar,还是有点不美观!相信广大猿友也是忍受不了这个缺陷的存在,那么就来解决这个缺陷。很简单,Google为我们提供了解决办法,那就是只需将主布局文件中的FramLayout改写成CoordinatorLayout,再使用AppBarLayout包裹Toolbar,然后在RecyclerView中Toolbar中分别增加如下属性重新运行程序,是不是这个缺陷已经完美的解决了?这里就不上图了。

app:layout_behavior=”@string/appbar_scrolling_view_behavior”
app:layout_scrollFlags=”scroll|snap|enterAlways”

  • CoordinatorLayout实际上是一个升级版的FramLayout,由Design Support库提供的布局文件,它的作用可以监听所有子控件的时间,然后做出最合理的处理
  • AppBarLayout 相当于垂直方向的LinearLayout,由Design Support库提供的布局文件,内部封装了很多滚动事件。
    • scroll:表示RecyclerView向上滑动时候,Toolbar也会向上滑动,并且隐藏
    • enterAlways:表示RecyclerView向下滑动时候,Toolbar也会向下滑动,重新出现
    • snap:根据滑动的距离判断Toolbar显示还是隐藏

先写到这里,会很快补上之后的内容,文末会附上完整的Demo链接,如有不准确的地方,欢迎指正~~~

完整Demo地址

相关标签: MaterialDesign