Nghiên cứu về FloatingActionButton trong Android

Trong quá trình học về các thành phần mới của thiết kế Material Design cho Android, tôi đã tìm hiểu về cách sử dụng CoordinatorLayout kết hợp với các thành phần khác để tạo ra các hiệu ứng. Trong bài viết này, chúng ta sẽ tập trung vào FloatingActionButton và cách nó tương tác với Snackbar. Đầu tiên, hãy xem xét layout XML và mã Java:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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="Nhấn để hiển thị Snackbar"/>

    <com.google.android.material.floatingactionbutton.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" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btnSnackBar = findViewById(R.id.btnSnackBar);
        btnSnackBar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Snackbar.make(v, "Đây là một Snackbar", Snackbar.LENGTH_SHORT).show();
            }
        });
    }
}
Mã trên rất đơn giản và tạo ra hiệu ứng như sau: khi Snackbar xuất hiện, FloatingActionButton sẽ di chuyển lên hoặc xuống để tránh che lấp Snackbar. Hiệu ứng này chỉ xảy ra khi FloatingActionButton được đặt trong CoordinatorLayout và sử dụng cùng với Snackbar. FloatingActionButton kế thừa từ ImageButton, nhưng nếu thay thế FloatingActionButton bằng ImageButton hoặc ImageView, hiệu ứng sẽ không còn. Điều này là do FloatingActionButton có một hành vi (behavior) được định nghĩa thông qua phản chiếu và chú thích. Hành vi này được định nghĩa trong lớp nội bộ Behavior, kế thừa từ CoordinatorLayout.Behavior. Dưới đây là hai phương thức quan trọng:
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
    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) {
        updateFabVisibility(parent, (AppBarLayout) dependency, child);
    }
    return false;
}

private float getFabTranslationYForSnackbar(CoordinatorLayout parent, FloatingActionButton fab) {
    float minOffset = 0;
    List<View> dependencies = parent.getDependencies(fab);
    for (int i = 0, z = dependencies.size(); i < z; i++) {
        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;
}
Phương thức `layoutDependsOn` kiểm tra xem view phụ thuộc có phải là Snackbar hay không. Nếu đúng, FloatingActionButton sẽ di chuyển theo. Phương thức `onDependentViewChanged` được gọi liên tục khi Snackbar di chuyển. Phương thức `getFabTranslationYForSnackbar` tính toán độ dịch chuyển của FloatingActionButton dựa trên vị trí của Snackbar. Bây giờ, chúng ta sẽ tạo một lớp tự định nghĩa MyBehavior kế thừa từ CoordinatorLayout.Behavior để thêm hiệu ứng xoay cho FloatingActionButton khi Snackbar xuất hiện:
public class MyBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
    public MyBehavior() {
    }

    public MyBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
        return dependency instanceof Snackbar.SnackbarLayout;
    }

    @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;
    }

    private float getFabTranslationYForSnackbar(CoordinatorLayout parent, FloatingActionButton fab) {
        float minOffset = 0;
        List<View> dependencies = parent.getDependencies(fab);
        for (int i = 0, z = dependencies.size(); i < z; i++) {
            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;
    }
}
Sau đó, sửa đổi layout XML để sử dụng hành vi mới:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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="Nhấn để hiển thị Snackbar"/>

    <com.google.android.material.floatingactionbutton.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" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>
Hiệu ứng sau khi áp dụng hành vi mới:

Thẻ: FloatingActionButton CoordinatorLayout Snackbar Android Material Design

Đăng vào ngày 1 tháng 6 lúc 23:38