最近在學習android材料設計的新控件,前面一篇文章講到 CoordinatorLayout 結合幾個新控件可以實現的幾個效果。其中第一個是,Coordinatorlayout + FloatingActionButton,配合使用,當彈出 Snackbar 的時候,FloatingActionBar會跟隨上移和下移。這次再針對 FloatingActionButton 具體分析一下。先貼出布局文件和java代碼:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
<Button android:id="@+id/btnSnackBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="點擊彈出SnackBar"/>
<android.support.design.widget.FloatingActionButton android:id="@+id/floatingActionBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|right" android:src="@mipmap/ic_launcher" />
</android.support.design.widget.CoordinatorLayout>
public class MainActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sixth); ((Button) findViewById(R.id.btnSnackBar)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Snackbar.make(v,"這是一個snackBar",Snackbar.LENGTH_SHORT).show(); } }); } }
代碼很簡潔,效果如下:
這個效果只有在CoordinatorLayout下,FloatingActionButton 配合 Snackbar 使用才會有。
我們知道,FloatingActionButton 間接繼承自 ImagButton, 如果將 FloatingActionButton 換成常用的 ImagButton 或者 ImageView,效果將不存在。為什么會這樣呢。在 FloatingActionButton 的定義中發現,通過反射,利用注解注入了一個 behavior 屬性。FloatingActionButton 類中定義了一個內部類——Behaivor,繼承自 CoordinatorLayout.Behavior<FloatingActionButton> 。里面有兩個方法如下:
@Override public boolean layoutDependsOn(CoordinatorLayout parent,FloatingActionButton child, View dependency) { // We're dependent on all SnackbarLayouts (if enabled)
return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout; } @Override public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child,View dependency) { if (dependency instanceof Snackbar.SnackbarLayout) { updateFabTranslationForSnackbar(parent, child, dependency); } else if (dependency instanceof AppBarLayout) { // If we're depending on an AppBarLayout we will show/hide it automatically // if the FAB is anchored to the AppBarLayout
updateFabVisibility(parent, (AppBarLayout) dependency, child); } return false; } private float getFabTranslationYForSnackbar(CoordinatorLayout parent,FloatingActionButton fab) { float minOffset = 0; final List<View> dependencies = parent.getDependencies(fab); for (int i = 0, z = dependencies.size(); i < z; i++) { final View view = dependencies.get(i); if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(fab, view)) { minOffset = Math.min(minOffset,ViewCompat.getTranslationY(view) - view.getHeight()); } } return minOffset; }
layoutDependsOn 方法,判斷底下彈出的是不是snackbar,如果是,floatingactionbutton才會聯動。
當需要聯動的snackbar向上移動的過程中,會不斷調用 onDependentViewChanged 方法。
getFabTranslationYForSnackbar 用來獲取SnackBar逐漸彈出來的時候變化的高度。
明白了這三個方法之后,我們就可以對這個效果進行修改寫。下面我們自己寫一個類,讓其繼承自 CoordinatorLayout.Behavior<FloatingActionButton>,
讓FloatingActionButton在上升過程中實現隨高度變化旋轉效果。
代碼如下:
public class MyBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> { /** * Default constructor for instantiating Behaviors. */
public MyBehavior() { } /** * Default constructor for inflating Behaviors from layout. The Behavior will have * the opportunity to parse specially defined layout parameters. These parameters will * appear on the child view tag. * * @param context * @param attrs */
public MyBehavior(Context context, AttributeSet attrs) { super(context, attrs); } /** * 判斷底下彈出的是不是snackbar,如果是,floatingactionbutton才會聯動 * * @param parent * @param child * @param dependency * @return
*/ @Override public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) { return dependency instanceof Snackbar.SnackbarLayout; } /** * 當需要聯動的snackbar向上移動的過程中,會不斷調用這個方法 * * @param parent * @param child * @param dependency * @return
*/ @Override public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) { float fTransY = getFabTranslationYForSnackbar(parent, child); float iRate = -fTransY / dependency.getHeight(); child.setRotation(iRate * 90); child.setTranslationY(fTransY); return false; } /** * 用來獲取SnackBar逐漸彈出來的時候變化的高度 * * @param parent * @param fab * @return
*/
private float getFabTranslationYForSnackbar(CoordinatorLayout parent, FloatingActionButton fab) { float minOffset = 0; final List<View> dependencies = parent.getDependencies(fab); for (int i = 0, z = dependencies.size(); i < z; i++) { final View view = dependencies.get(i); if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(fab, view)) { minOffset = Math.min(minOffset, ViewCompat.getTranslationY(view) - view.getHeight()); } } return minOffset; } }
然后修改布局文件,為 FloatingActionButton 設置 layout_behavior 屬性為自定義的MyBehavior,
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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">
<Button android:id="@+id/btnSnackBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="點擊彈出SnackBar"/>
<android.support.design.widget.FloatingActionButton android:id="@+id/floatingActionBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|right" app:layout_behavior="com.example.joy.coordinatorlayouttest.MyBehavior" android:src="@mipmap/ic_launcher" />
</android.support.design.widget.CoordinatorLayout>
效果如下: