Flipboard 内部控件 BottomSheet 今天开源

3,139 阅读4分钟

BottomSheet is an Android component which presents a dismissable view from the bottom of the screen. BottomSheet can be a usefull replacement for dialogs and menus but can hold any view so the use cases are endless. This repository includes the BottomSheet component itself but also includes a set of common view componenets presented in a bottom sheet. These are located in the commons module.

BottomSheet has been used in production at Flipboard for a while now so it is thoroughly tested. Here is a GIF of it in action inside of Flipboard!

FlipUI gif

Installation

If all you want is the BottomSheet component and don't need things from commons you can skip that dependency.

dependencies {
    compile 'com.flipboard:bottomsheet-core:1.0.0'
    compile 'com.flipboard:bottomsheet-commons:1.0.0' // optional
}

Getting Started

Get started by wrapping your layout in a BottomSheet. So if you currently have this:

<LinearLayout
    android:id="@+id/root"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <View
        android:id="@+id/view1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <View
        android:id="@+id/view2"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

LinearLayout>

You would have to update it to look like this:

<com.flipboard.bottomsheet.BottomSheet
    android:id="@+id/bottomsheet"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/root"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <View
            android:id="@+id/view1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

        <View
            android:id="@+id/view2"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    LinearLayout>

com.flipboard.bottomsheet.BottomSheet>

Back in your activity or fragment you would get a reference to the BottomSheet like any other view.

BottomSheet bottomSheet = (BottomSheet) findViewById(R.id.bottomsheet);

Now all you need to do is show a view in the bottomSheet:

bottomSheet.showWithSheetView(LayoutInflater.from(context).inflate(R.layout.my_sheet_layout, bottomSheet, false));

You could also use one of the sheet views from the commons module.

bottomSheet.showWithSheetView(new IntentPickerSheetView(this, shareIntent, "Share with...", new IntentPickerSheetView.OnIntentPickedListener() {
    @Override
    public void onIntentPicked(Intent intent) {
        bottomSheet.dismissSheet();
        startActivity(intent);
    }
});

That's it for the simplest of use cases. Check out the API documentation below to find out how to customize BottomSheet to fit your use cases.

API

BottomSheet

/**
 * Set the presented sheet to be in an expanded state.
 */
public void expandSheet();

/**
 * Set the presented sheet to be in a peeked state.
 */
public void peekSheet();

/**
 * @return The peeked state translation for the presented sheet view. Translation is counted from the bottom of the view.
 */
public float getPeekSheetTranslation();

/**
 * @return The maximum translation for the presented sheet view. Translation is counted from the bottom of the view.
 */
public float getMaxSheetTranslation();

/**
 * @return The currently presented sheet view. If no sheet is currently presented null will returned.
 */
public View getContentView();

/**
 * @return The currently presented sheet view. If no sheet is currently presented null will returned.
 */
public View getSheetView();

/**
 * Set the content view of the bottom sheet. This is the view which is shown under the sheet
 * being presented. This is usually the root view of your application.
 *
 * @param contentView The content view of your application.
 */
public void setContentView(View contentView);

/**
 * Convenience for showWithSheetView(sheetView, null, null)
 */
public void showWithSheetView(View sheetView);

/**
 * Convenience for showWithSheetView(sheetView, viewTransformer, null)
 */
public void showWithSheetView(View sheetView, ViewTransformer viewTransformer);

/**
 * Present a sheet view to the user.
 *
 * @param sheetView The sheet to be presented.
 * @param viewTransformer The view transformer to use when presenting the sheet.
 * @param onSheetDismissedListener The listener to notify when the sheet is dismissed.
 */
public void showWithSheetView(View sheetView, ViewTransformer viewTransformer, OnSheetDismissedListener onSheetDismissedListener);

/**
 * Dismiss the sheet currently being presented.
 */
public void dismissSheet();

/**
 * @return The current state of the sheet.
 */
public State getState();

/**
 * @return Whether or not a sheet is currently presented.
 */
public boolean isSheetShowing();

/**
 * Set the default view transformer to use for showing a sheet. Usually applications will use
 * a similar transformer for most use cases of bottom sheet so this is a convenience instead of
 * passing a new transformer each time a sheet is shown. This choice is overridden by any
 * view transformer passed to showWithSheetView().
 *
 * @param defaultViewTransformer The view transformer user by default.
 */
public void setDefaultViewTransformer(ViewTransformer defaultViewTransformer);

/**
 * Enable or disable dimming of the content view while a sheet is presented. If enabled a
 * transparent black dim is overlayed on top of the content view indicating that the sheet is the
 * foreground view. This dim is animated into place is coordination with the sheet view.
 * Defaults to true.
 *
 * @param shouldDimContentView whether or not to dim the content view.
 */
public void setShouldDimContentView(boolean shouldDimContentView);

/**
 * @return whether the content view is being dimmed while presenting a sheet or not.
 */
public boolean shouldDimContentView();

/**
 * Enable or disable the use of a hardware layer for the presented sheet while animating.
 * This settings defaults to true and should only be changed if you know that putting the
 * sheet in a layer will negatively effect performance. One such example is if the sheet contains
 * a view which needs to frequently be re-drawn.
 *
 * @param useHardwareLayerWhileAnimating whether or not to use a hardware layer.
 */
public void setUseHardwareLayerWhileAnimating(boolean useHardwareLayerWhileAnimating);

/**
 * Set a OnSheetStateChangeListener which will be notified when the state of the presented sheet changes.
 *
 * @param onSheetStateChangeListener the listener to be notified.
 */
public void setOnSheetStateChangeListener(OnSheetStateChangeListener onSheetStateChangeListener);

OnSheetDismissedListener

/**
 * Called when the presented sheet has been dismissed.
 *
 * @param bottomSheet The bottom sheet which contained the presented sheet.
 */
void onDismissed(BottomSheet bottomSheet);

ViewTransformer

/**
 * Called on every frame while animating the presented sheet. This method allows you to coordinate
 * other animations (usually on the content view) with the sheet view's translation.
 *
 * @param translation The current translation of the presented sheet view.
 * @param maxTranslation The max translation of the presented sheet view.
 * @param peekedTranslation The peeked state translation of the presented sheet view.
 * @param parent The BottomSheet presenting the sheet view.
 * @param view The content view to transform.
 */
void transformView(float translation, float maxTranslation, float peekedTranslation, BottomSheet parent, View view);

Common Components

These are located in the optional bottomsheet-commons dependency and implement common use cases for bottom sheet.

IntentPickerSheetView

This component presents an intent chooser in the form of a BottomSheet view. Give it an intent such as a share intent and let the user choose what activity they want to share the intent with in a BottomSheet. Here is a GIF of it in action!

IntentPickerSheetView gif

Here is a sample use case of this component taken from the sample application.

IntentPickerSheetView intentPickerSheet = new IntentPickerSheetView(MainActivity.this, shareIntent, "Share with...", new IntentPickerSheetView.OnIntentPickedListener() {
    @Override
    public void onIntentPicked(Intent intent) {
        bottomSheet.dismissSheet();
        startActivity(intent);
    }
});
// Filter out built in sharing options such as bluetooth and beam.
intentPickerSheet.setFilter(new IntentPickerSheetView.Filter() {
    @Override
    public boolean include(IntentPickerSheetView.ActvityInfo info) {
        return !info.componentName.getPackageName().startsWith("com.android");
    }
});
// Sort activities in reverse order for no good reason
intentPickerSheet.setSortMethod(new Comparator<IntentPickerSheetView.ActvityInfo>() {
    @Override
    public int compare(IntentPickerSheetView.ActvityInfo lhs, IntentPickerSheetView.ActvityInfo rhs) {
        return rhs.label.compareTo(lhs.label);
    }
});
bottomSheet.showWithSheetView(intentPickerSheet);

Contributing

We welcome pull requests for bug fixes, new features, and improvements to BottomSheet. Contributors to the main BottomSheet repository must accept Flipboard's Apache-style Individual Contributor License Agreement (CLA) before any changes can be merged.