Android Support Design 库 之 Snackbar使用及源码分析

在谷歌提出 material design 之后,终于推出了 android.support.design 这个官方的material design库,这几天我也简单浏览了下这个库,基本上我们常用的组件都有了,从今天开始,就可以一步步替换掉

以前使用的github上的那些开源控件了,毕竟谷歌出品 才属精品~~另外分析这个design库的源码我认为是非常有意义的,android上的app 在以前各家都有各家的风格,但是在谷歌出了material design这门新的

设计语言以及官方的库以后,相信越来越多的app 会逐步优化自己的ui 来符合官方的标准,学习这个design库的源码可以让我们以后改写自定义控件的时候更加柔韧有余。

首先,来看一下这个官方的介绍。http://www.google.com/design/spec/components/snackbars-toasts.html#

这个文章系统的阐述了 snackbar和toast的区别和正确使用snackbar的方式。

我简单归纳如下:

1.比toast更加好,毕竟snackbar 可以响应点击事件

2.snackbar 同一时间有且只有一个在显示。

3.snackbar 上不要有图标

4.snackbar上action 只能有一个。

5.如果有悬浮按钮 floating action button的话,snackbar 在弹出的时候 不要覆盖这个button.

6.此外我个人认为snackbar 在一定程度上可以替代dialog的某些应用场景。比如以前网络不通的情况下 我们登陆失败,会给一个dialog提示,现在就可以用snackbar 来做这个有action的提示 更加方便快捷。

使用snackbar:

1.导入support design 库 (这一步在以后的design库的 控件文章里都会舍去)

首先找到你app的build gradle文件

然后增加一个compile语句即可

compile ‘com.android.support:design:22.2.0‘

2.编写xml文件以及java文件

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:paddingBottom="@dimen/activity_vertical_margin"
 6     android:paddingLeft="@dimen/activity_horizontal_margin"
 7     android:paddingRight="@dimen/activity_horizontal_margin"
 8     android:paddingTop="@dimen/activity_vertical_margin"
 9     tools:context=".MainActivity"
10     android:id="@+id/layout">
11
12
13     <!-- 因为snackbar 需要有一个父控件所以 我们暂时就用tv 来做他的父控件-->
14     <TextView
15         android:id="@+id/tv"
16         android:layout_width="match_parent"
17         android:layout_height="30dp"
18         android:layout_centerVertical="true"
19         android:gravity="center"
20         android:text="Bottom layout" />
21
22 </RelativeLayout>
 1 package com.example.burning.myapplication;
 2
 3 import android.os.Bundle;
 4 import android.support.design.widget.Snackbar;
 5 import android.support.v7.app.ActionBarActivity;
 6 import android.view.Menu;
 7 import android.view.MenuItem;
 8 import android.view.View;
 9 import android.widget.TextView;
10
11
12 public class MainActivity extends ActionBarActivity {
13
14     private TextView tv;
15
16     @Override
17     protected void onCreate(Bundle savedInstanceState) {
18         super.onCreate(savedInstanceState);
19         setContentView(R.layout.activity_main);
20         tv = (TextView) this.findViewById(R.id.tv);
21         tv.setOnClickListener(new View.OnClickListener() {
22             @Override
23             public void onClick(View v) {
24                 //这个地方第一个参数 传进去的是tv 但是实际上你无论传进去什么值 snackbar都一定是从屏幕的最底端出现的 原因在源码
25                 //分析那边可以看到
26                 Snackbar.make(tv, "connection error", Snackbar.LENGTH_LONG).setAction("retry", new View.OnClickListener() {
27                     @Override
28                     public void onClick(View v) {
29                         tv.setText("aleady click snackbar");
30                     }
31                 }).show();
32
33             }
34         });
35     }
36
37     @Override
38     public boolean onCreateOptionsMenu(Menu menu) {
39         // Inflate the menu; this adds items to the action bar if it is present.
40         getMenuInflater().inflate(R.menu.menu_main, menu);
41         return true;
42     }
43
44     @Override
45     public boolean onOptionsItemSelected(MenuItem item) {
46         // Handle action bar item clicks here. The action bar will
47         // automatically handle clicks on the Home/Up button, so long
48         // as you specify a parent activity in AndroidManifest.xml.
49         int id = item.getItemId();
50
51         //noinspection SimplifiableIfStatement
52         if (id == R.id.action_settings) {
53             return true;
54         }
55
56         return super.onOptionsItemSelected(item);
57     }
58 }

最后我们来看下效果

然后我们来看一下 如果和正常的FAB(悬浮按钮)在一起会有什么效果(注意这里的悬浮按钮我们也使用design库里的并不使用github上开源的)

先看一下xml文件

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:app="http://schemas.android.com/apk/res-auto"
 3     xmlns:tools="http://schemas.android.com/tools"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent"
 6     android:paddingBottom="@dimen/activity_vertical_margin"
 7     android:paddingLeft="@dimen/activity_horizontal_margin"
 8     android:paddingRight="@dimen/activity_horizontal_margin"
 9     android:paddingTop="@dimen/activity_vertical_margin"
10     tools:context=".MainActivity">
11
12
13     <FrameLayout
14         android:id="@+id/layout"
15         android:layout_width="match_parent"
16         android:layout_height="match_parent">
17
18         <android.support.design.widget.FloatingActionButton
19             android:id="@+id/btnFloatingAction"
20             android:layout_width="wrap_content"
21             android:layout_height="wrap_content"
22             android:layout_gravity="bottom|right"
23             android:src="@drawable/ic_plus"
24             app:borderWidth="0dp"
25             app:fabSize="normal" />
26     </FrameLayout>
27 </RelativeLayout>

activity代码

 1 package com.example.burning.myapplication;
 2
 3 import android.os.Bundle;
 4 import android.support.design.widget.Snackbar;
 5 import android.support.v7.app.ActionBarActivity;
 6 import android.view.Menu;
 7 import android.view.MenuItem;
 8 import android.view.View;
 9 import android.view.ViewGroup;
10
11
12 public class MainActivity extends ActionBarActivity {
13
14     private ViewGroup layout;
15
16
17     @Override
18     protected void onCreate(Bundle savedInstanceState) {
19         super.onCreate(savedInstanceState);
20         setContentView(R.layout.activity_main);
21         layout = (ViewGroup) this.findViewById(R.id.layout);
22         layout.setOnClickListener(new View.OnClickListener() {
23             @Override
24             public void onClick(View v) {
25                 Snackbar.make(layout, "connection error", Snackbar.LENGTH_LONG).setAction("retry", new View.OnClickListener() {
26                     @Override
27                     public void onClick(View v) {
28                     }
29                 }).show();
30
31             }
32         });
33     }
34
35     @Override
36     public boolean onCreateOptionsMenu(Menu menu) {
37         // Inflate the menu; this adds items to the action bar if it is present.
38         getMenuInflater().inflate(R.menu.menu_main, menu);
39         return true;
40     }
41
42     @Override
43     public boolean onOptionsItemSelected(MenuItem item) {
44         // Handle action bar item clicks here. The action bar will
45         // automatically handle clicks on the Home/Up button, so long
46         // as you specify a parent activity in AndroidManifest.xml.
47         int id = item.getItemId();
48
49         //noinspection SimplifiableIfStatement
50         if (id == R.id.action_settings) {
51             return true;
52         }
53
54         return super.onOptionsItemSelected(item);
55     }
56 }

来看一下运行效果

大家可以看到当我们的snackbar在弹出的时候 会覆盖到我们的FAB,那这体验是非常糟糕的,这里也给出一个完美的解决方案

其实也很简单用一下design库里的layout即可 java代码不需要改变 只要稍微改一下布局文件即可

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:app="http://schemas.android.com/apk/res-auto"
 3     xmlns:tools="http://schemas.android.com/tools"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent"
 6     android:paddingBottom="@dimen/activity_vertical_margin"
 7     android:paddingLeft="@dimen/activity_horizontal_margin"
 8     android:paddingRight="@dimen/activity_horizontal_margin"
 9     android:paddingTop="@dimen/activity_vertical_margin"
10     tools:context=".MainActivity">
11
12
13     <android.support.design.widget.CoordinatorLayout
14         android:id="@+id/layout"
15         android:layout_width="match_parent"
16         android:layout_height="match_parent">
17
18         <android.support.design.widget.FloatingActionButton
19             android:id="@+id/btnFloatingAction"
20             android:layout_width="wrap_content"
21             android:layout_height="wrap_content"
22             android:layout_gravity="bottom|right"
23             android:src="@drawable/ic_plus"
24             app:borderWidth="0dp"
25             app:fabSize="normal" />
26     </android.support.design.widget.CoordinatorLayout>
27 </RelativeLayout>

就是换了一个新的layout而已。

来看下运行的效果。

当然了 你要改变这个snackbar的背景色也是可以的 只需要

1  Snackbar sb=Snackbar.make(layout, "connection error", Snackbar.LENGTH_LONG).setAction("retry", new View.OnClickListener() {
2                     @Override
3                     public void onClick(View v) {
4                     }
5                 });
6                 //红色
7                 sb.getView().setBackgroundColor(0xfff44336);
8                 sb.show();

基本的用法 要点就是这些,我们来看一下这个snackbar的源码   看看谷歌官方是如何编写 material design 风格控件的

这里我无法贴上全部源码 因为太多,只挑重要的流程说 可以从make开始。

 1  //这个地方就是构造函数
 2     Snackbar(ViewGroup parent) {
 3         this.mParent = parent;
 4         this.mContext = parent.getContext();
 5         LayoutInflater inflater = LayoutInflater.from(this.mContext);
 6         this.mView = (Snackbar.SnackbarLayout)inflater.inflate(layout.layout_snackbar, this.mParent, false);
 7     }
 8
 9     //这个地方就是我们调用的make函数 也就是整个类的一个入口 在这里可以看到 我们的代码转入了snackbar的构造函数
10     public static Snackbar make(View view, CharSequence text, int duration) {
11         //注意看这个地方调用了 findsuitableparent这个函数
12         Snackbar snackbar = new Snackbar(findSuitableParent(view));
13         snackbar.setText(text);
14         snackbar.setDuration(duration);
15         return snackbar;
16     }
17
18     public static Snackbar make(View view, int resId, int duration) {
19         return make(view, view.getResources().getText(resId), duration);
20     }
21
22     //这个函数其实作用就是无论你传进去的是什么view 我最终都会遍历到framlayout为止,因为activity的最外层实际上就是一个framlayout
23     //所以你在调用make函数的时候无论传什么值进去 snackabr都会从最底部弹出来 就是因为这个函数做了这样的工作 但是!!!!    //如果在遍历到最顶部的framlayout之前 遇到了一个framelayout 那么就会从这个framlayout的底部弹出,而不会从屏幕的最下方弹出了。
24     //这个地方CoordinatorLayout的优先级比framlayout还要高 所以你如果穿进去的view是CoordinatorLayout的话 这个snackbar 就一定会从
25     //CoordinatorLayout 底部弹出了。如果你CoordinatorLayout的最底部恰好在屏幕中间 那么snackbar 就会从屏幕中间弹出  而不会从底部弹出 这一点一定要注意
26     @Nullable
27     private static ViewGroup findSuitableParent(View view) {
28         ViewGroup fallback = null;
29
30         do {
31             if(view instanceof CoordinatorLayout) {
32                 return (ViewGroup)view;
33             }
34
35             if(view instanceof FrameLayout) {
36                 if(view.getId() == 16908290) {
37                     return (ViewGroup)view;
38                 }
39
40                 fallback = (ViewGroup)view;
41             }
42
43             if(view != null) {
44                 ViewParent parent = view.getParent();
45                 view = parent instanceof View?(View)parent:null;
46             }
47         } while(view != null);
48
49         return fallback;
50     }

大家可以看一下第六行。实际上这个mView就是一个内部类的对象

1     private final Snackbar.SnackbarLayout mView;

然后接着看第六行的xml文件(到这里其实我们也能猜到了 真正自定义view的snackbar是由snackbar的内部类snackbarlayout来完成的)

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <!--
 3   ~ Copyright (C) 2015 The Android Open Source Project
 4   ~
 5   ~ Licensed under the Apache License, Version 2.0 (the "License");
 6   ~ you may not use this file except in compliance with the License.
 7   ~ You may obtain a copy of the License at
 8   ~
 9   ~      http://www.apache.org/licenses/LICENSE-2.0
10   ~
11   ~ Unless required by applicable law or agreed to in writing, software
12   ~ distributed under the License is distributed on an "AS IS" BASIS,
13   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   ~ See the License for the specific language governing permissions and
15   ~ limitations under the License.
16 -->
17
18 <!--注意看class那边的写法  那个$就是代表内部类的一个符号 这个技巧 以后我们自己自定义控件的时候也可以学习-->
19 <view xmlns:android="http://schemas.android.com/apk/res/android"
20       class="android.support.design.widget.Snackbar$SnackbarLayout"
21       android:layout_width="match_parent"
22       android:layout_height="wrap_content"
23       android:layout_gravity="bottom"
24       style="@style/Widget.Design.Snackbar" /><!-- From: file:/usr/local/google/buildbot/repo_clients/https___googleplex-android.googlesource.com_a_platform_manifest.git/lmp-mr1-supportlib-release/frameworks/support/design/res/layout/layout_snackbar.xml -->

继续看下内部类

找到我们真正的snackbar的布局文件 注意这个地方讨巧的使用了merge标签 这是一个比较好的优化xml的 写法

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <!--
 3   ~ Copyright (C) 2015 The Android Open Source Project
 4   ~
 5   ~ Licensed under the Apache License, Version 2.0 (the "License");
 6   ~ you may not use this file except in compliance with the License.
 7   ~ You may obtain a copy of the License at
 8   ~
 9   ~      http://www.apache.org/licenses/LICENSE-2.0
10   ~
11   ~ Unless required by applicable law or agreed to in writing, software
12   ~ distributed under the License is distributed on an "AS IS" BASIS,
13   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   ~ See the License for the specific language governing permissions and
15   ~ limitations under the License.
16 -->
17
18 <merge xmlns:android="http://schemas.android.com/apk/res/android">
19
20     <TextView
21             android:id="@+id/snackbar_text"
22             android:layout_width="wrap_content"
23             android:layout_height="wrap_content"
24             android:layout_weight="1"
25             android:paddingTop="@dimen/snackbar_padding_vertical"
26             android:paddingBottom="@dimen/snackbar_padding_vertical"
27             android:paddingLeft="@dimen/snackbar_padding_horizontal"
28             android:paddingRight="@dimen/snackbar_padding_horizontal"
29             android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"
30             android:maxLines="@integer/snackbar_text_max_lines"
31             android:layout_gravity="center_vertical|left|start"
32             android:ellipsize="end"/>
33
34     <TextView
35             android:id="@+id/snackbar_action"
36             android:layout_width="wrap_content"
37             android:layout_height="wrap_content"
38             android:layout_marginLeft="@dimen/snackbar_extra_spacing_horizontal"
39             android:layout_marginStart="@dimen/snackbar_extra_spacing_horizontal"
40             android:layout_gravity="center_vertical|right|end"
41             android:background="?attr/selectableItemBackground"
42             android:paddingTop="@dimen/snackbar_padding_vertical"
43             android:paddingBottom="@dimen/snackbar_padding_vertical"
44             android:paddingLeft="@dimen/snackbar_padding_horizontal"
45             android:paddingRight="@dimen/snackbar_padding_horizontal"
46             android:visibility="gone"
47             android:textAppearance="@style/TextAppearance.Design.Snackbar.Action"/>
48
49 </merge><!-- From: file:/usr/local/google/buildbot/repo_clients/https___googleplex-android.googlesource.com_a_platform_manifest.git/lmp-mr1-supportlib-release/frameworks/support/design/res/layout/layout_snackbar_include.xml -->

到这里 主要的snackbar的一个加载流程就分析完毕了,很多诸如动画的代码部分 我就暂时不去分析他了,大家可以自己仔细分析。

另外有心的同学可能发现了这么一个代码

实际上Behavior 我个人认为是这次support design库里面最重要的一个东西,以后我会单独出来讲一下。基本上support design包里 每一个控件都有她的身影出没。

时间: 2024-10-29 14:33:14

Android Support Design 库 之 Snackbar使用及源码分析的相关文章

Android Support Design 库 之 FloatingActionButton使用及源码分析

先来看一段谷歌官方对FAB的解释http://www.google.co.in/design/spec/components/buttons-floating-action-button.html#buttons-floating-action-button-floating-action-button 我简单归纳一下文中所说的几个重点: 1.不是每个app 都需要FAB,如果需要的话最多也是只要一个FAB即可. 2.FAB的icon应该是表示一个动词,而不能是一个名词. 比如我们可以这样: 但

Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)

1 背景 还记得前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事件疑惑吗?当时说了,在那一篇咱们只讨论View的触摸事件派发机制,这个疑惑留在了这一篇解释,也就是ViewGroup的事件派发机制. PS:阅读本篇前建议先查看前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>,这一篇承接上一篇. 关于View与ViewGroup的区别在前一篇的A

Android触摸屏事件派发机制详解与源码分析

请看下面三篇博客,思路还是蛮清晰的,不过还是没写自定义控件系列哥们的思路清晰: Android触摸屏事件派发机制详解与源码分析一(View篇) http://blog.csdn.net/yanbober/article/details/45887547 Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇) http://blog.csdn.net/yanbober/article/details/45912661 Android触摸屏事件派发机制详解与源码分析三(Activi

Android事件传递机制详解及最新源码分析——ViewGroup篇

在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴,强烈建议先阅读上一篇. 好了,废话还是少说,直奔主题,开始本篇的ViewGroup事件传递机制探索之旅. 依然从简单的Demo例子现象开始分析 新建安卓工程,首先自定义一个Button以及一个RelativeLayout,很简单,只是重写了主要与事件传递机制相关的方法,代码如下: 自定义WLButton类: 1 public class WLButton e

Android触摸屏事件派发机制详解与源码分析三(Activity篇)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 该篇承接上一篇<Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)>,阅读本篇之前建议先阅读. 1 背景 还记得前面两篇从Android的基础最小元素控件(View)到ViewGroup控件的触摸屏事件分发机制分析吗?你可能看完会有疑惑,View的事件是ViewGro

【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五)Android事件分发机制(上)Touch三个重要方法的处理逻辑][下文简称(五),请先阅读完(五)再阅读本文],我们通过示例和log来分析了Android的事件分发机制.这些,我们只是看到了现象,如果要进一步了解事件分发机制,这是不够的,我们还需要透过现象看本质,去研究研究源码.本文将从源码(基

Android消息机制Handler、Looper、MessageQueue源码分析

1. Handler Looper MessageQueue的关系 2.源码分析 下图表示了Handler.Looper.MessageQueue.Message这四个类之间的关系. Handler必须与一个Looper关联,相关Looper决定了该Handler会向哪个MessageQueue发送Message 每一个Looper中都包含一个MessageQueue Handler中的mQueue引用的就是与之关联的Looper的MessageQueue 不管Handler在哪个线程发送Mes

Android应用AsyncTask处理机制详解及源码分析

[工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handler异步机制原理(不了解的可以阅读我的<Android异步消息处理机制详解及源码分析>文章),这里继续分析Android的另一个异步机制AsyncTask的原理. 当使用线程和Handler组合实现异步处理时,当每次执行耗时操作都创建一条新线程进行处理,性能开销会比

Android查看私有库android-spport-v4.jar &amp; android-support-v7-appcompat.jar源码

一.非私有库查看源码 非私有库添加源码很简单,在这里只说一种.右击jar文件Properties Java Source Attachment,如下图,添加External Folder即可. 二.私有库查看源码 Android-support-v4.jar和Android-support-v7-appcompat.jar被放在Android Private Libraries目录下,属于私有库,无法采用上述方法. 可在jar文件所在目录下,建立其对为应的properties文件,文件中添加一行