Created
May 17, 2018 19:20
-
-
Save Tgo1014/a02a0fc469f6d626afe94b352dcfd81b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<shape xmlns:android="http://schemas.android.com/apk/res/android" | |
android:shape="rectangle"> | |
<solid android:color="#ffffff" /> | |
<padding | |
android:bottom="-105dp" | |
android:left="5dp" | |
android:right="5dp" | |
android:top="5dp" /> | |
<corners | |
android:topLeftRadius="30dp" | |
android:topRightRadius="30dp" /> | |
</shape> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private fun showBottomSheetMenu() { | |
val v = layoutInflater.inflate(R.layout.your_custom_layout_here, null) | |
val bottomSheet = GoogleBottomSheetDialog(this) | |
bottomSheet.setContentView(v) | |
bottomSheet.show() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2015 The Android Open Source Project | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
import android.content.Context; | |
import android.content.res.TypedArray; | |
import android.graphics.Color; | |
import android.graphics.drawable.ColorDrawable; | |
import android.os.Build; | |
import android.os.Bundle; | |
import android.support.annotation.LayoutRes; | |
import android.support.annotation.NonNull; | |
import android.support.annotation.StyleRes; | |
import android.support.design.widget.BottomSheetBehavior; | |
import android.support.design.widget.CoordinatorLayout; | |
import android.support.v4.view.AccessibilityDelegateCompat; | |
import android.support.v4.view.ViewCompat; | |
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; | |
import android.support.v7.app.AppCompatDialog; | |
import android.util.TypedValue; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import android.view.Window; | |
import android.view.WindowManager; | |
import android.widget.FrameLayout; | |
/** | |
* Base class for {@link android.app.Dialog}s styled as a bottom sheet. | |
*/ | |
public class GoogleBottomSheetDialog extends AppCompatDialog { | |
private BottomSheetBehavior<FrameLayout> behavior; | |
boolean cancelable = true; | |
private boolean canceledOnTouchOutside = true; | |
private boolean canceledOnTouchOutsideSet; | |
public GoogleBottomSheetDialog(@NonNull Context context) { | |
this(context, 0); | |
} | |
public GoogleBottomSheetDialog(@NonNull Context context, @StyleRes int theme) { | |
super(context, getThemeResId(context, theme)); | |
// We hide the title bar for any style configuration. Otherwise, there will be a gap | |
// above the bottom sheet when it is expanded. | |
supportRequestWindowFeature(Window.FEATURE_NO_TITLE); | |
} | |
protected GoogleBottomSheetDialog( | |
@NonNull Context context, boolean cancelable, OnCancelListener cancelListener) { | |
super(context, cancelable, cancelListener); | |
supportRequestWindowFeature(Window.FEATURE_NO_TITLE); | |
this.cancelable = cancelable; | |
} | |
@Override | |
public void setContentView(@LayoutRes int layoutResId) { | |
super.setContentView(wrapInBottomSheet(layoutResId, null, null)); | |
} | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
Window window = getWindow(); | |
if (window != null) { | |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | |
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); | |
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); | |
} | |
window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); | |
} | |
} | |
@Override | |
public void setContentView(View view) { | |
super.setContentView(wrapInBottomSheet(0, view, null)); | |
} | |
@Override | |
public void setContentView(View view, ViewGroup.LayoutParams params) { | |
super.setContentView(wrapInBottomSheet(0, view, params)); | |
} | |
@Override | |
public void setCancelable(boolean cancelable) { | |
super.setCancelable(cancelable); | |
if (this.cancelable != cancelable) { | |
this.cancelable = cancelable; | |
if (behavior != null) { | |
behavior.setHideable(cancelable); | |
} | |
} | |
} | |
@Override | |
protected void onStart() { | |
super.onStart(); | |
if (behavior != null) { | |
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED); | |
} | |
} | |
@Override | |
public void setCanceledOnTouchOutside(boolean cancel) { | |
super.setCanceledOnTouchOutside(cancel); | |
if (cancel && !cancelable) { | |
cancelable = true; | |
} | |
canceledOnTouchOutside = cancel; | |
canceledOnTouchOutsideSet = true; | |
} | |
private View wrapInBottomSheet(int layoutResId, View view, ViewGroup.LayoutParams params) { | |
FrameLayout container = | |
(FrameLayout) View.inflate(getContext(), R.layout.design_bottom_sheet_dialog, null); | |
CoordinatorLayout coordinator = container.findViewById(R.id.coordinator); | |
if (layoutResId != 0 && view == null) { | |
view = getLayoutInflater().inflate(layoutResId, coordinator, false); | |
} | |
FrameLayout bottomSheet = coordinator.findViewById(R.id.design_bottom_sheet); | |
bottomSheet.setBackground(new ColorDrawable(Color.TRANSPARENT)); | |
behavior = BottomSheetBehavior.from(bottomSheet); | |
behavior.setBottomSheetCallback(bottomSheetCallback); | |
behavior.setHideable(cancelable); | |
if (params == null) { | |
bottomSheet.addView(view); | |
} else { | |
bottomSheet.addView(view, params); | |
} | |
// We treat the CoordinatorLayout as outside the dialog though it is technically inside | |
coordinator | |
.findViewById(R.id.touch_outside) | |
.setOnClickListener( | |
view1 -> { | |
if (cancelable && isShowing() && shouldWindowCloseOnTouchOutside()) { | |
cancel(); | |
} | |
}); | |
// Handle accessibility events | |
ViewCompat.setAccessibilityDelegate( | |
bottomSheet, | |
new AccessibilityDelegateCompat() { | |
@Override | |
public void onInitializeAccessibilityNodeInfo( | |
View host, AccessibilityNodeInfoCompat info) { | |
super.onInitializeAccessibilityNodeInfo(host, info); | |
if (cancelable) { | |
info.addAction(AccessibilityNodeInfoCompat.ACTION_DISMISS); | |
info.setDismissable(true); | |
} else { | |
info.setDismissable(false); | |
} | |
} | |
@Override | |
public boolean performAccessibilityAction(View host, int action, Bundle args) { | |
if (action == AccessibilityNodeInfoCompat.ACTION_DISMISS && cancelable) { | |
cancel(); | |
return true; | |
} | |
return super.performAccessibilityAction(host, action, args); | |
} | |
}); | |
bottomSheet.setOnTouchListener( | |
(view12, event) -> { | |
// Consume the event and prevent it from falling through | |
return true; | |
}); | |
return container; | |
} | |
boolean shouldWindowCloseOnTouchOutside() { | |
if (!canceledOnTouchOutsideSet) { | |
if (Build.VERSION.SDK_INT < 11) { | |
canceledOnTouchOutside = true; | |
} else { | |
TypedArray a = | |
getContext() | |
.obtainStyledAttributes(new int[]{android.R.attr.windowCloseOnTouchOutside}); | |
canceledOnTouchOutside = a.getBoolean(0, true); | |
a.recycle(); | |
} | |
canceledOnTouchOutsideSet = true; | |
} | |
return canceledOnTouchOutside; | |
} | |
private static int getThemeResId(Context context, int themeId) { | |
if (themeId == 0) { | |
// If the provided theme is 0, then retrieve the dialogTheme from our theme | |
TypedValue outValue = new TypedValue(); | |
if (context.getTheme().resolveAttribute(R.attr.bottomSheetDialogTheme, outValue, true)) { | |
themeId = outValue.resourceId; | |
} else { | |
// bottomSheetDialogTheme is not provided; we default to our light theme | |
themeId = R.style.Theme_Design_Light_BottomSheetDialog; | |
} | |
} | |
return themeId; | |
} | |
private BottomSheetBehavior.BottomSheetCallback bottomSheetCallback = | |
new BottomSheetBehavior.BottomSheetCallback() { | |
@Override | |
public void onStateChanged( | |
@NonNull View bottomSheet, @BottomSheetBehavior.State int newState) { | |
if (newState == BottomSheetBehavior.STATE_HIDDEN) { | |
cancel(); | |
} | |
} | |
@Override | |
public void onSlide(@NonNull View bottomSheet, float slideOffset) { | |
} | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment