一、CoordinatorLayout基本介紹
CoordinatorLayout作為“super-powered FrameLayout”基本實現兩個功能: (demo是實現 http://blog.csdn.net/huachao1001/article/details/51554608 中的)
1、作為頂層布局
2、調度協調子布局
通過實驗發現它也可以嵌套在其他的布局文件內,但它只能協調它的子布局。且與snakebar連着使用時,來控制彈出消息的位置,這個在后面介紹snakebar時講。
基本用法
1、引入相應的庫
使用CoordinatorLayout可以實現一些炫酷的動態效果,使用它首先得在app的gradle中引入相應的庫:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:design:23.3.0'
}
node: 按網上的大多資料是compile 'com.android.support:design:22.2.1' , 這里我們引入的上面的支持庫版本一致,否則會報錯。
2、將CoordinatorLayout內部子控件或者布局設定behavior行為,該控件設定了監聽其它的子控件或者布局來執行相應動作。下面給出簡單的一個布局:
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.user.coordinatorlayouttest.MainActivity"> <Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#FFCC00" android:text="Hello" app:layout_behavior="com.example.user.coordinatorlayouttest.MyBehavior" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:layout_marginLeft="30dp" android:layout_marginTop="30dp" android:background="#3366CC" android:text="dependency" android:id="@+id/nice_to_me_to_you"/> </android.support.design.widget.CoordinatorLayout>
其中button就設定了behaviour,其實就是來監聽一些控件的動作來執行相應的動作。
3、實現behaviour:
package com.example.user.coordinatorlayouttest; import android.content.Context; import android.support.design.widget.CoordinatorLayout; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.View; import android.widget.Button; import android.widget.TextView; /** * Created by user on 2017/5/3. */ public class MyBehavior extends CoordinatorLayout.Behavior<Button> { private int width; public MyBehavior(Context context, AttributeSet attrs) { super(context, attrs); DisplayMetrics display = context.getResources().getDisplayMetrics(); width = display.widthPixels; } @Override public boolean layoutDependsOn(CoordinatorLayout parent, Button child, View dependency) { //如果dependency是TextView的實例,說明它就是我們所需要的Dependency,只有返回true時候,下面的方法才會執行 return dependency instanceof TextView; } //每次dependency位置發生變化,都會執行onDependentViewChanged方法 @Override public boolean onDependentViewChanged(CoordinatorLayout parent, Button btn, View dependency) { //根據dependency的位置,設置Button的位置 int top = dependency.getTop(); int left = dependency.getLeft(); int x = width - left - btn.getWidth(); int y = top; setPosition(btn, x, y); return true; } private void setPosition(View v, int x, int y) { CoordinatorLayout.MarginLayoutParams layoutParams = (CoordinatorLayout.MarginLayoutParams) v.getLayoutParams(); layoutParams.leftMargin = x; layoutParams.topMargin = y; v.setLayoutParams(layoutParams); } }
這里設定了子布局中TextView的對象是dependency,當TextView的位置發生變化時,button的位置也會相應發生變化(豎直方向移動一致,橫向方向是相反的)
4、定義dependency的位移方式,(其實大多dependency是自定義的控件),這里設計點觸控,總結一下:點觸控時候位移量最好用getRawX函數來獲取,用getX是獲得控件內部坐標,而控件本身在移動,函數執行有一定頻率會導致位移量有丟失且圖片抖動,用下面的方式就不會了:
textView = (TextView) findViewById(R.id.nice_to_me_to_you); textView.setOnTouchListener(new View.OnTouchListener() { int toLeft,toTop,sx ,sy; @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: sx = (int) event.getRawX(); sy = (int) event.getRawY(); toLeft = textView.getLeft(); toTop = textView.getTop(); break; case MotionEvent.ACTION_MOVE: int sx2 = (int) event.getRawX(); int sy2 = (int) event.getRawY(); CoordinatorLayout.MarginLayoutParams layoutParams = (CoordinatorLayout.MarginLayoutParams) v.getLayoutParams(); layoutParams.leftMargin = toLeft + sx2 - sx; layoutParams.topMargin =toTop + sy2 - sy; v.setLayoutParams(layoutParams); break; } return true; } });
綜合上面,CoordinatorLayout的用法介紹完畢了,但CoordinatorLayout通常與一些控件結合起來用,以達到一些特殊的效果,如:FloatingActionButton、snakebar、tabLayout,toolbar 、下面簡單介紹FloatingActionButton、snakebar控件的基本用法:關於后面2個在后面的博客中再說。
二、snakebar 基本用法
snakebar也是與CoordinatorLayout一樣需要引入compile 'com.android.support:design:23.3.0' ,其特點如下:(參考 http://blog.csdn.net/u013320868/article/details/51906896)
1)SnackBars 提供了一個輕量級的反饋操作,他們在屏幕的底部顯示一條簡短的信息,如果是較大的設備就顯示在左下角。SnackBar出現在屏幕中所有其他元素的上方,同一時間只能顯示一條SnackBar。
2)在超時或者用戶在屏幕上完成了交互的時候SnackBar會自動消失,特別是在召喚了新的表層(意思是SnackBar本來是最外層的,然后在SnackBar上又新添加了一層)或者Activity的時候。SnackBar能在屏幕上側滑。
3)SnackBar能包含一個action使用setAction方法
4)你可以通過它的CallBack來得知Snackbar是顯示還是隱藏
與Toast進行比較,SnackBar有優勢:
1.SnackBar可以自動消失,也可以手動取消(側滑取消,但是需要在特殊的布局中,后面會仔細說)
2.SnackBar可以通過setAction()來與用戶進行交互
3.通過CallBack我們可以獲取SnackBar的狀態
下面介紹一下
node:snakeBar通常與CoordinatorLayout連用,出現在CoordinatorLayout布局的下發,彈出一條消息,且可以設置一個action事件,但有時候沒有CoordinatorLayout或者不在CoordinatorLayout布局內。下面的結論是通過實驗得到的:
1、當布局中有CoordinatorLayout,不管引入snakeBar的控件是CoordinatorLayout 子控件還是CoordinatorLayout的子布局的子控件,它都會出現在CoordinatorLayout布局的最下面。
2、當引入snakebar的控件不在CoordinatorLayout內部時候,(即使有CoordinatorLayout,但引入snakebar控件不在其內)則snakebar會出現在最上層的framLayout的底部,即屏幕的底部。
下面給出個簡單的例子:
布局文件:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="200dp" android:id="@+id/f1" android:background="@android:color/darker_gray" > <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end|bottom" android:layout_margin="16dp" android:background="@android:color/holo_red_light" /> </android.support.design.widget.CoordinatorLayout> </RelativeLayout>
然后在activity的onCreate()中寫入下面方法后,當點擊懸浮按鈕的時候就會在200dp的位置出現snakebar,上面說到的引入snakebar按鈕就是fab:
findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar snackBar =Snackbar.make(view,"it is snackbar!",Snackbar.LENGTH_SHORT); //設置SnackBar背景顏色 snackBar.getView().setBackgroundColor(Color.RED); //設置按鈕文字顏色 snackBar.setActionTextColor(Color.WHITE); //設置點擊事件 snackBar.setAction("點擊", new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this,"It is Toast!",Toast.LENGTH_SHORT).show(); } }); //設置回調 snackBar.setCallback(new Snackbar.Callback() { @Override public void onDismissed(Snackbar snackbar, int event) { super.onDismissed(snackbar, event); Toast.makeText(MainActivity.this, "Snackbar dismiss", Toast.LENGTH_SHORT).show(); } @Override public void onShown(Snackbar snackbar) { super.onShown(snackbar); Toast.makeText(MainActivity.this, "Snackbar show", Toast.LENGTH_SHORT).show(); } }).show(); } });
可以看出,我們可以setAction來設定點擊時間,而Snakebar.LEMGTH_SHORT是顯示的時間,它有三種方式:
Snackbar.LENGTH_SHORT// 短時間顯示,然后自動取消
Snackbar.LENGTH_LONG// 長時間顯示,然后自動取消
Snackbar.LENGTH_INDEFINITE// 不消失顯示,除非手動取消
且我們可以設定回調函數,當snakeBar顯示和消失時候完成一些事情
3、FloatingActionButton
關於floatingActionButton介紹網上很多,它繼承自ImageView,關於它的介紹主要集中在於設置背景色和陰影的設置上:
1、它也是來自上面所說的支持庫,在build.grade文件中寫上
compile 'com.android.support:design:23.2.0'
2、關於它的幾個重要屬性的介紹
1、app:borderWidth=""------------------邊框寬度,通常設置為0 ,用於解決Android 5.X設備上陰影無法正常顯示的問題
2、app:backgroundTint=""---------------按鈕的背景顏色,不設置,默認使用theme中colorAccent的顏色 (默認顏色為主題中的colorAccent的顏色,關於如何自定義主題,以后的文章再講)
3、app:rippleColor=""--------------------點擊的邊緣陰影顏色
4、app:elevation=""----------------------邊緣陰影的寬度
5、app:pressedTranslationZ="16dp"-----點擊按鈕時,按鈕邊緣陰影的寬度,通常設置比elevation的數值大
3 可以看出屬性是app開頭的,因此在布局文件前面加上:
xmlns:app="http://schemas.android.com/apk/res-auto"
4、通過網上的設置background然后寫一個drawable文件,寫一個背景的選擇器,在按下時候變色,如下:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="false" android:drawable="@color/floatBack1"></item> <item android:state_pressed="true" android:drawable="@color/floatBackPressed"></item> </selector>
發現將該drawable給background屬性無效,給backgroundTint后變色了但不是我們設定的顏色,通過查看源碼,發現setbackground源碼如下:
@Override public void setBackgroundDrawable(Drawable background) { Log.i(LOG_TAG, "Setting a custom background is not supported."); } @Override public void setBackgroundResource(int resid) { Log.i(LOG_TAG, "Setting a custom background is not supported."); } @Override public void setBackgroundColor(int color) { Log.i(LOG_TAG, "Setting a custom background is not supported."); }
它本身沒有實現這些函數,因此設置background屬性是無效的,它的一些屬性都是靠實現了ShadowViewDelegate接口的類完成的,而屬性設置中的backgournd無效,智能通過backgroundTint來設定背景,而backgroundTint使用drawable里的文件顏色是改變了但不是我們設定的,(原因不是很清楚)因此在代碼中來設定背景了:
比如下面我們設定當點擊懸浮圖標時候背景色跟着變化,點擊完畢時候恢復原來顏色時,主要是設置了按鍵的OntouchListener
floatingActionButton = (FloatingActionButton) findViewById(R.id.fab); floatingActionButton.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#ffff0000")));//設定默認色 floatingActionButton.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: floatingActionButton.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#ffffff00")));//手指按下時候變色 break; case MotionEvent.ACTION_UP: floatingActionButton.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#ffff0000")));//手指離開時候恢復 break; } return true; } });