Material Design实战

记得最早接触MaterialDesign还是在去年我刚自学android的时候,当时迫切的想尝试一下这种新的设计语言,但由于一些原因搁浅到现在。趁这个机会写个小demo,感受一下这种设计语言的魅力。先来看下效果:

这里写图片描述

这个demo主要实现了:Material design的设计风格,Toolbar制作顶栏,RecyclerView制作列表。

主要用到的知识点:

1.Material design基本知识;

2.Toolbar的用法;

3.RecyclerView的基本用法;

然后说下具体的实现思路。

Material design

什么是Material design?

We challenged ourselves to create a visual language for our users that synthesizes the classic principles of good design with the innovation and possibility of technology and science. This is material design. This spec is a living document that will be updated as we continue to develop the tenets and specifics of material design.

从官方介绍里我们了解到,这是一门崭新的视觉设计语言。它除了遵循经典设计定则,还汲取了最新的科技,秉承了创新的设计理念。这就是材料化设计(Material Design)。

Material design的核心

Material design的核心思想,就是把物理世界的体验带进屏幕。去掉现实中的杂质和随机性,保留其最原始纯净的形态、空间关系、变化与过渡,配合虚拟世界的灵活特性,还原最贴近真实的体验,达到简洁与直观的效果。

创建使用Material design的应用

官方文档中的步骤

1.The material theme(使用materialdesign主题)
2.Widgets for cards and lists(使用列表和卡片组件)
3.Custom shadows and view clipping(定义shadows和clipping视图)
4.Vector drawables(矢量drawables)
5.Custom animations(自定义动画)

本demo的实现过程

使用materialdesign主题

Material主题被定义在:

 @android:style/Theme.Material (暗色版本)
 @android:style/Theme.Material.Light (亮色版本)
 @android:style/Theme.Material.Light.DarkActionBar

为了使我们的应用可以兼容低版本,可以使用兼容主题

 Theme.AppCompat
 Theme.AppCompat.Light
 Theme.AppCompat.Light.DarkActionBar

关于主题,我们可以自定义调色板,反馈动画和 Activity 切换动画。同时XML layout 中的元素可以定义 android:theme 属性, 用于引用主题资源。这个属性修改了自己和子元素的主题,通过这个我们可以修改局部主题颜色。

这里是我用的主题,是Pink色系。

<!-- inherit from the material theme -->
<style name="MyTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Main theme colors -->
    <!--   your app branding color for the app bar -->
    <item name="android:colorPrimary">@color/myColorPrimary</item>
    <!--   darker variant for the status bar and contextual app bars -->
    <item name="android:colorPrimaryDark">@color/myColorPrimaryDark</item>
    <!--   theme UI controls like checkboxes and text fields -->
    <item name="android:colorAccent">@color/myColorAccent</item>
    <!--  textcolor  -->
    <item name="android:textColor">@color/myColorText</item>
</style>

AndroidStudio还提供了可视化操作的工具,用来设置这些颜色。

这里写图片描述

工具界面如下图,可以点击特定属性选择符合自己品牌的颜色。

这里写图片描述

使用Toolbar

5.0以后使用了Toolbar这个控件来替换以前的ActionBar。并且提供了supprot library用于向下兼容。使用方法与ActionBar基本类似。

隐藏ActionBar使用ToolBar有两种方法:

1.继承主题:Theme.AppCompat.Light.NoActionBar

2.在主题中使用以下属性:

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

为了兼容低版本,我们使用support v7 里的 toolbar。

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="55dp"
    android:background="@color/myColorPrimary"
    android:elevation="3dp"
    android:navigationIcon="@mipmap/menu"
    android:title="@string/app_name"
    app:titleTextColor="@color/myColorText"></android.support.v7.widget.Toolbar>

然后是一些基本的设置:

/**
     * 初始化toolbar
     */
    private void initToolBar() {
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        // App Logo
//        toolbar.setLogo(R.mipmap.logo);
        // Title
//        toolbar.setTitle("WaKaKa");
        // Sub Title
//        toolbar.setSubtitle("Sub title");
        toolbar.setOverflowIcon(getResources().getDrawable(R.mipmap.more));
        setSupportActionBar(toolbar);

        //导航按钮
        toolbar.setNavigationIcon(R.mipmap.menu);
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "Click navigation", Toast.LENGTH_SHORT).show();
            }
        });

        toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                String msg = "";
                switch (item.getItemId()) {
                    case R.id.action_delete:
                        msg += "Click delete";
                        break;
                    case R.id.action_favorite:
                        msg += "Click favorite";
                        break;
                    case R.id.action_settings:
                        msg += "Click settings";
                        break;
                }
                if (!msg.equals("")) {
                    Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
                }
                return true;
            }
        });
    }

要注意的是setNavigationIcon需要放在 setSupportActionBar之后才会生效。

这个demo中的图标大部分是从谷歌官方制作的icon中下载的。

使用RecycleView

RecycleView组件是一个更高级和伸缩性更强的 ListView。这个组件是一个显示大量数据的容器,通过维护有限量的View,来达到滚动时的高效。当你的数据会在运行过程中根据用户行为或网络事件动态改变时,使用RecyclerView是一个不错的选择。

RecyclerView 通过以下方式简化显示流程,并操作大量数据:

1.使用 Layout manager 来定位元素

2.为常用操作定义默认动画,比如添加或移除元素

你也可以为 RecyclerView 自定义 Layout manager 和动画。

要使用 RecyclerView 组件,你需要定义一个 adapter 和 layout manager。创建 adapter,要继承 RecyclerView.Adapter 类。Layout manager把元素视图放在 RecyclerView,并决定什么时候重用不可见的元素视图。要重用(或回收)视图时,layout manager 会让 adapter 用另外的元素内容替换视图内的内容。回收 View 这个方法能提高性能,因为它避免了创建不必要的view对象,或执行昂贵的 findViewById() 查找。RecyclerView 提供三种内建的 layout manager:LinearLayoutManager 用于显示横向或纵向的滚动列表;GridLayoutManager 用于显示方格元素;StaggeredGridLayoutManager 在 staggered 方格中显示元素。创建一个自定义的 layout manager,要继承于 RecyclerView.LayoutManager 类。

在xml文件中使用RecyclerView之后,初始化:

private void initRecyclerView() {
        mRecyclerView = (RecyclerView) findViewById(R.id.recycleview);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);

        //set divider
        mRecyclerView.addItemDecoration(new MyItemDecoration(MainActivity.this));

        // specify an adapter (see also next example)
        String[] myDataset = {"a", "b", "c", "d", "e", "a", "b", "c", "d", "e", "a", "b", "c", "d", "e", "a", "b", "c", "d", "e"};
        mAdapter = new MyAdapter(myDataset);
        mRecyclerView.setAdapter(mAdapter);

        mAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, position + "", Toast.LENGTH_SHORT).show();
            }
        });
    }

适配器的代码和使用listview大同小异:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private String[] mDataset;

    private OnItemClickListener listener;

    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    public class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public TextView mTitle;
        public RelativeLayout root;

        public ViewHolder(View v) {
            super(v);
            mTitle = (TextView) v.findViewById(R.id.item_title);
            root = (RelativeLayout) v.findViewById(R.id.item_rl);
        }
    }

    // Provide a suitable constructor (depends on the kind of dataset)
    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // create a new view
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_item_view, parent, false);
        // set the view's size, margins, paddings and layout parameters
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        // - get element from your dataset at this position
        // - replace the contents of the view with that element
        holder.mTitle.setText(mDataset[position]);

        if(listener != null){
            holder.root.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    listener.onItemClick(v,position);
                }
            });
        }
    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return mDataset.length;
    }

    public interface OnItemClickListener {
        void onItemClick(View view, int position);
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.listener = listener;
    }
}

这里的两个比较大的问题是:

1.RecyclerView的分割线要自己定义。

2.RecyclerView Item的点击事件和长按事件都要自己定义。

对于第一个问题我们需要去继承RecyclerView.ItemDecoration类,在里边绘制分割线。

/**
 * 绘制item分割线
 */
public class MyItemDecoration extends RecyclerView.ItemDecoration {

    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
    private Drawable mDivider;

    public MyItemDecoration(Context context) {
        final TypedArray array = context.obtainStyledAttributes(ATTRS);
        mDivider = array.getDrawable(0);
        array.recycle();
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        drawHorizontal(c,parent);
    }

    // 水平线
    public void drawHorizontal(Canvas c, RecyclerView parent) {

        final int childCount = parent.getChildCount();

        // 在每一个子控件的底部画线
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);

            final int left = child.getLeft() + child.getPaddingLeft();
            final int right = child.getWidth() + child.getLeft() - child.getPaddingRight();
            final int top = child.getBottom() - mDivider.getIntrinsicHeight() - child.getPaddingBottom();
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
    }
}

对于第二个问题,我们可以写一个回调方法,来设置点击事件,长按事件类似。

public interface OnItemClickListener {
  void onItemClick(View view, int position);
 }

 public void setOnItemClickListener(OnItemClickListener listener) {
     this.listener = listener;
 }

在onBindViewHolder中使用接口对象处理点击事件。

if(listener != null){
 holder.root.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View v) {
           listener.onItemClick(v,position);
       }
   });
}

然后在activity中进行设置

mAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, position + "", Toast.LENGTH_SHORT).show();
            }
        });

到这里基本就完成了,这个demo可以在低版本中运行,但是5.0的新特性是不会显示的。源码戳这里

参考链接:
Material icon https://design.google.com/icons/
MaterialDesign官方介绍:https://www.google.com/design/spec/material-design/introduction.html
Training:http://developer.android.com/training/material/index.html

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值