Skip to content

Commit

Permalink
Added a BottomSheetFragment to bottomsheet-commons that works like a …
Browse files Browse the repository at this point in the history
…DialogFragment.
  • Loading branch information
evant committed Nov 5, 2015
1 parent 5473cd7 commit b5500d5
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package android.support.v4.app;

public class AccessFragmentInternals {
private AccessFragmentInternals() {
}

public static int getContainerId(Fragment fragment) {
return fragment.mContainerId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package com.flipboard.bottomsheet.commons;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.CallSuper;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.support.v4.app.AccessFragmentInternals;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;

import com.flipboard.bottomsheet.BottomSheetLayout;
import com.flipboard.bottomsheet.OnSheetDismissedListener;
import com.flipboard.bottomsheet.ViewTransformer;

/**
* A fragment that shows itself in a {@link BottomSheetLayout}. Like a {@link
* android.support.v4.app.DialogFragment}, you can show this either in a bottom sheet by using
* {@link #show(FragmentManager, int)} or attach it to a view with the normal fragment transaction
* methods.
*/
public class BottomSheetFragment extends Fragment implements OnSheetDismissedListener, BottomSheetLayout.OnSheetStateChangeListener {

private static final String SAVED_SHOWS_BOTTOM_SHEET = "bottomsheet:savedBottomSheet";
private static final String SAVED_BACK_STACK_ID = "bottomsheet:backStackId";
private static final String SAVED_BOTTOM_SHEET_LAYOUT_ID = "bottomsheet:bottomSheetLayoutId";

@IdRes
private int bottomSheetLayoutId = View.NO_ID;
private BottomSheetLayout bottomSheetLayout;
private boolean dismissed;
private boolean shownByMe;
private boolean viewDestroyed;
private boolean showsBottomSheet = true;
private int backStackId = -1;

public BottomSheetFragment() {

}

/**
* Display the bottom sheet, adding the fragment to the given FragmentManager. This does
* <em>not</em> add the transaction to the back stack. When teh fragment is dismissed, a new
* transaction will be executed to remove it from the activity.
*
* @param manager The FragmentManager this fragment will be added to.
* @param bottomSheetLayoutId The bottom sheet layoutId in the parent view to attach the
* fragment to.
*/
public void show(FragmentManager manager, @IdRes int bottomSheetLayoutId) {
dismissed = false;
shownByMe = true;
this.bottomSheetLayoutId = bottomSheetLayoutId;
manager.beginTransaction()
.add(this, String.valueOf(bottomSheetLayoutId))
.commit();
}

/**
* Display the bottom sheet, adding the fragment using an excising transaction and then
* committing the transaction.
*
* @param transaction An existing transaction in which to add the fragment.
* @param bottomSheetLayoutId The bottom sheet layoutId in the parent view to attach the
* fragment to.
*/
public int show(FragmentTransaction transaction, @IdRes int bottomSheetLayoutId) {
dismissed = false;
shownByMe = true;
this.bottomSheetLayoutId = bottomSheetLayoutId;
transaction.add(this, String.valueOf(bottomSheetLayoutId));
viewDestroyed = false;
backStackId = transaction.commit();
return backStackId;
}

/**
* Dismiss the fragment and it's bottom sheet. If the fragment was added to the back stack, all
* back stack state up to and including this entry will be popped. Otherwise, a new transaction
* will be committed to remove this fragment.
*/
public void dismiss() {
dismissInternal(/*allowStateLoss=*/false);
}

/**
* Version of {@link #dismiss()} that uses {@link FragmentTransaction#commitAllowingStateLoss()}.
* See linked documentation for further details.
*/
public void dismissAllowingStateLoss() {
dismissInternal(/*allowStateLoss=*/true);
}

private void dismissInternal(boolean allowStateLoss) {
if (dismissed) {
return;
}
dismissed = true;
shownByMe = false;
if (bottomSheetLayout != null) {
bottomSheetLayout.dismissSheet();
bottomSheetLayout = null;
}
viewDestroyed = true;
if (backStackId >= 0) {
getFragmentManager().popBackStack(backStackId, FragmentManager.POP_BACK_STACK_INCLUSIVE);
backStackId = -1;
} else {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.remove(this);
if (allowStateLoss) {
ft.commitAllowingStateLoss();
} else {
ft.commit();
}
}
}

public BottomSheetLayout getBottomSheetLayout() {
return bottomSheetLayout;
}

/**
* Override this to proved a custom {@link ViewTransformer}.
*/
public ViewTransformer getViewTransformer() {
return null;
}

@Override
public void onAttach(Context context) {
super.onAttach(context);
if (!shownByMe) {
dismissed = false;
}
}

@Override
public void onDetach() {
super.onDetach();
if (!shownByMe && !dismissed) {
dismissed = true;
}
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showsBottomSheet = AccessFragmentInternals.getContainerId(this) == 0;

if (savedInstanceState != null) {
showsBottomSheet = savedInstanceState.getBoolean(SAVED_SHOWS_BOTTOM_SHEET, showsBottomSheet);
backStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, -1);
bottomSheetLayoutId = savedInstanceState.getInt(SAVED_BOTTOM_SHEET_LAYOUT_ID, View.NO_ID);
}
}

@Override
public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
if (!showsBottomSheet) {
return super.getLayoutInflater(savedInstanceState);
}
bottomSheetLayout = (BottomSheetLayout) findBottomSheetLayout();
if (bottomSheetLayout != null) {
return LayoutInflater.from(bottomSheetLayout.getContext());
}
return LayoutInflater.from(getContext());
}

@Nullable
private View findBottomSheetLayout() {
Fragment parentFragment = getParentFragment();
if (parentFragment != null) {
View view = parentFragment.getView();
if (view != null) {
return view.findViewById(bottomSheetLayoutId);
} else {
return null;
}
}
Activity parentActivity = getActivity();
if (parentActivity != null) {
return parentActivity.findViewById(bottomSheetLayoutId);
}
return null;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);

if (!showsBottomSheet) {
return;
}

View view = getView();
if (view != null) {
if (view.getParent() != null) {
throw new IllegalStateException("BottomSheetFragment can not be attached to a container view");
}
bottomSheetLayout.setOnSheetStateChangeListener(this);
}
}

@Override
public void onStart() {
super.onStart();
if (bottomSheetLayout != null) {
viewDestroyed = false;
bottomSheetLayout.showWithSheetView(getView(), getViewTransformer(), this);
}
}

@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (!showsBottomSheet) {
outState.putBoolean(SAVED_SHOWS_BOTTOM_SHEET, false);
}
if (backStackId != -1) {
outState.putInt(SAVED_BACK_STACK_ID, backStackId);
}
if (bottomSheetLayoutId != View.NO_ID) {
outState.putInt(SAVED_BOTTOM_SHEET_LAYOUT_ID, bottomSheetLayoutId);
}
}

/**
* Remove bottom sheet.
*/
@Override
public void onDestroyView() {
super.onDestroyView();
if (bottomSheetLayout != null) {
viewDestroyed = true;
bottomSheetLayout.dismissSheet();
bottomSheetLayout = null;
}
}

@Override
@CallSuper
public void onDismissed(BottomSheetLayout bottomSheetLayout) {
if (!viewDestroyed) {
dismissInternal(true);
}
}

@Override
public void onSheetStateChanged(BottomSheetLayout.State state) {

}
}
7 changes: 7 additions & 0 deletions bottomsheet-sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".sample.MainActivity" />
</activity>
<activity android:name=".sample.BottomSheetFragmentActivity"
android:label="@string/bottomsheet_fragment"
android:parentActivityName=".sample.MainActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".sample.MainActivity" />
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.flipboard.bottomsheet.sample;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

import com.flipboard.bottomsheet.BottomSheetLayout;
import com.flipboard.bottomsheet.R;
import com.flipboard.bottomsheet.commons.ImagePickerSheetView;

/**
* Activity demonstrating the use of {@link ImagePickerSheetView}
*/
public final class BottomSheetFragmentActivity extends AppCompatActivity {

protected BottomSheetLayout bottomSheetLayout;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bottom_sheet_fragment);
bottomSheetLayout = (BottomSheetLayout) findViewById(R.id.bottomsheet);
findViewById(R.id.bottomsheet_fragment_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new MyFragment().show(getSupportFragmentManager(), R.id.bottomsheet);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,12 @@ public void onClick(View v) {
startActivity(new Intent(MainActivity.this, ImagePickerActivity.class));
}
});

findViewById(R.id.bottomsheet_fragment_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, BottomSheetFragmentActivity.class));
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.flipboard.bottomsheet.sample;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.flipboard.bottomsheet.R;
import com.flipboard.bottomsheet.commons.BottomSheetFragment;

public class MyFragment extends BottomSheetFragment {

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_my, container, false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<com.flipboard.bottomsheet.BottomSheetLayout
android:id="@+id/bottomsheet"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
>

<Button
android:id="@+id/bottomsheet_fragment_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top"
android:layout_marginBottom="16dp"
android:text="@string/show"
/>
</RelativeLayout>
</com.flipboard.bottomsheet.BottomSheetLayout>
6 changes: 6 additions & 0 deletions bottomsheet-sample/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@
android:text="@string/image_picker"
/>

<Button
android:id="@+id/bottomsheet_fragment_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/bottomsheet_fragment"
/>
</LinearLayout>
15 changes: 15 additions & 0 deletions bottomsheet-sample/src/main/res/layout/fragment_my.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:colorBackground"
android:orientation="vertical"
>

<TextView
android:layout_width="match_parent"
android:layout_height="400dp"
android:padding="16dp"
android:text="Hello, World!"
/>
</LinearLayout>
Loading

0 comments on commit b5500d5

Please sign in to comment.