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文件

Android Support Design 库 之 Snackbar使用及源码分析_第1张图片

然后增加一个compile语句即可

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

Android Support Design 库 之 Snackbar使用及源码分析_第2张图片

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
< 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 = "match_parent"
     android:paddingBottom = "@dimen/activity_vertical_margin"
     android:paddingLeft = "@dimen/activity_horizontal_margin"
     android:paddingRight = "@dimen/activity_horizontal_margin"
     android:paddingTop = "@dimen/activity_vertical_margin"
     tools:context = ".MainActivity"
     android:id = "@+id/layout" >
 
 
    
     < TextView
         android:id = "@+id/tv"
         android:layout_width = "match_parent"
         android:layout_height = "30dp"
         android:layout_centerVertical = "true"
         android:gravity = "center"
         android:text = "Bottom layout" />
 
RelativeLayout >

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

最后我们来看下效果

Android Support Design 库 之 Snackbar使用及源码分析_第3张图片

 

 

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

先看一下xml文件

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

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

来看一下运行效果

Android Support Design 库 之 Snackbar使用及源码分析_第4张图片

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

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

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

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

来看下运行的效果。

 

 Android Support Design 库 之 Snackbar使用及源码分析_第5张图片

 

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

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

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

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

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

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

1 private final Snackbar.SnackbarLayout mView;

Android Support Design 库 之 Snackbar使用及源码分析_第6张图片

 

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
"1.0" encoding= "utf-8" ?>
 
"http://schemas.android.com/apk/res/android"
       class = "android.support.design.widget.Snackbar$SnackbarLayout"
       android:layout_width= "match_parent"
       android:layout_height= "wrap_content"
       android:layout_gravity= "bottom"
       style= "@style/Widget.Design.Snackbar" />

继续看下内部类

Android Support Design 库 之 Snackbar使用及源码分析_第7张图片

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
xml version = "1.0" encoding = "utf-8" ?>
 
< merge xmlns:android = "http://schemas.android.com/apk/res/android" >
 
     < TextView
             android:id = "@+id/snackbar_text"
             android:layout_width = "wrap_content"
             android:layout_height = "wrap_content"
             android:layout_weight = "1"
             android:paddingTop = "@dimen/snackbar_padding_vertical"
             android:paddingBottom = "@dimen/snackbar_padding_vertical"
             android:paddingLeft = "@dimen/snackbar_padding_horizontal"
             android:paddingRight = "@dimen/snackbar_padding_horizontal"
             android:textAppearance = "@style/TextAppearance.Design.Snackbar.Message"
             android:maxLines = "@integer/snackbar_text_max_lines"
             android:layout_gravity = "center_vertical|left|start"
             android:ellipsize = "end" />
 
     < TextView
             android:id = "@+id/snackbar_action"
             android:layout_width = "wrap_content"
             android:layout_height = "wrap_content"
             android:layout_marginLeft = "@dimen/snackbar_extra_spacing_horizontal"
             android:layout_marginStart = "@dimen/snackbar_extra_spacing_horizontal"
             android:layout_gravity = "center_vertical|right|end"
             android:background = "?attr/selectableItemBackground"
             android:paddingTop = "@dimen/snackbar_padding_vertical"
             android:paddingBottom = "@dimen/snackbar_padding_vertical"
             android:paddingLeft = "@dimen/snackbar_padding_horizontal"
             android:paddingRight = "@dimen/snackbar_padding_horizontal"
             android:visibility = "gone"
             android:textAppearance = "@style/TextAppearance.Design.Snackbar.Action" />
 
merge >

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

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

Android Support Design 库 之 Snackbar使用及源码分析_第8张图片

 

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


你可能感兴趣的:(Android应用)