BottomSheetDialogFragment - 监听被用户事件解除
BottomSheetDialogFragment - listen to dismissed by user event
如何收听 BottomSheetDialogFragment
的最终解雇?我只想在最终解雇时保存用户更改...
我试过以下方法:
方法一
只有在通过向下滑动对话框(不是按下后退或触摸外部)关闭对话框时才会触发
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
Dialog d = super.onCreateDialog(savedInstanceState);
d.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = (FrameLayout) dialog.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior behaviour = BottomSheetBehavior.from(bottomSheet);
behaviour.setState(BottomSheetBehavior.STATE_EXPANDED);
behaviour.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN)
{
// Bottom Sheet was dismissed by user! But this is only fired, if dialog is swiped down! Not if touch outside dismissed the dialog or the back button
Toast.makeText(MainApp.get(), "HIDDEN", Toast.LENGTH_SHORT).show();
dismiss();
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
}
});
return d;
}
方法二
这不允许我区分最终解雇和来自屏幕旋转或 activity 娱乐的解雇...
@Override
public void onDismiss(DialogInterface dialog)
{
super.onDismiss(dialog);
// this works fine but fires one time too often for my use case, it fires on screen rotation as well, although this is a temporarily dismiss only
Toast.makeText(MainApp.get(), "DISMISSED", Toast.LENGTH_SHORT).show();
}
问题
如何收听指示用户已完成对话的事件?
尽管 SO 上的所有类似问题都建议使用 onDismiss
我认为以下是正确的解决方案:
@Override
public void onCancel(DialogInterface dialog)
{
super.onCancel(dialog);
Toast.makeText(MainApp.get(), "CANCEL", Toast.LENGTH_SHORT).show();
}
在以下情况下会触发:
* the user presses back
* the user presses outside of the dialog
这不会触发:
* on screen rotation and activity recreation
解决方案
合并 onCancel 和 BottomSheetBehavior.BottomSheetCallback.onStateChanged,如下所示:
public class Dailog extends BottomSheetDialogFragment
{
@Override
public void onCancel(DialogInterface dialog)
{
super.onCancel(dialog);
handleUserExit();
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
Dialog d = super.onCreateDialog(savedInstanceState);
d.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior behaviour = BottomSheetBehavior.from(bottomSheet);
behaviour.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN)
{
handleUserExit();
dismiss();
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
}
});
return d;
}
private void handleUserExit()
{
Toast.makeText(MainApp.get(), "TODO - SAVE data or similar", Toast.LENGTH_SHORT).show();
}
}
我用这个简单的技巧实现了这个
val bottomSheetDialog = FeedbackFormsFragment.createInstance()
bottomSheetDialog.show((activity as FragmentActivity).supportFragmentManager, BOTTOM_SHEET)
// add some delay to allow the bottom sheet to be visible first so that the dialog is not null
Handler().postDelayed({
bottomSheetDialog.dialog?.setOnDismissListener {
// add code here
}
}, 1000)
虽然@prom85 的方法有效,但还有一个不同的方法。如果您想在一种情况下关闭 BottomSheetDialogFragment
并在另一种情况下保留它,那是行不通的。它会在所有情况下关闭对话框。
例如,如果您在 BottomSheetDialogFragment
的 EditText
内输入文本,偶尔在外部单击,它会在没有任何警告的情况下关闭对话框。我试过 https://medium.com/@anitas3791/android-bottomsheetdialog-3871a6e9d538,它有效,但在另一种情况下。当您向下拖动对话框时,它将关闭。如果您在外部单击,它不会显示任何警告消息,也不会关闭对话框。
所以,我使用了来自 的 @shijo 的一个很好的建议。
将这些行添加到 onActivityCreated
方法(或 onCreateView
之后的任何其他生命周期方法)。
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
View touchOutsideView = getDialog().getWindow()
.getDecorView()
.findViewById(android.support.design.R.id.touch_outside);
touchOutsideView.setOnClickListener(yourClickListener);
}
在我的例子中 yourClickListener
我检查文本并显示警告或关闭对话框:
private fun checkAndDismiss() {
if (newText == oldText) {
dismissAllowingStateLoss()
} else {
showDismissAlert()
}
}
创建 BottomSheetDialogFragment
时,不要像在 中那样调用 setCancelable(false)
,否则这些方法可能不起作用。也许不要在样式中设置 <item name="android:windowCloseOnTouchOutside">false</item>
或在 onCreateDialog
.
中设置 setCanceledOnTouchOutside(false)
我也尝试了几种方法来覆盖取消行为,但都没有成功。
<style name="BottomSheetDialogTheme" parent="Theme.Design.Light.BottomSheetDialog">
<item name="android:windowCloseOnTouchOutside">false</item>
</style>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, R.style.BottomSheetDialogTheme)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setOnShowListener {
val bottomSheet = dialog.findViewById<View>(
android.support.design.R.id.design_bottom_sheet) as? FrameLayout
val behavior = BottomSheetBehavior.from(bottomSheet)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
//showing the different states.
when (newState) {
BottomSheetBehavior.STATE_HIDDEN -> dismiss() //if you want the modal to be dismissed when user drags the bottomsheet down
BottomSheetBehavior.STATE_EXPANDED -> {
}
BottomSheetBehavior.STATE_COLLAPSED -> {
}
BottomSheetBehavior.STATE_DRAGGING -> {
}
BottomSheetBehavior.STATE_SETTLING -> {
}
}
}
})
dialog.setOnCancelListener {
// Doesn't matter what you write here, the dialog will be closed.
}
dialog.setOnDismissListener {
// Doesn't matter what you write here, the dialog will be closed.
}
}
return dialog
}
override fun onCancel(dialog: DialogInterface?) {
// Doesn't matter what you write here, the dialog will be closed.
super.onCancel(dialog)
}
override fun onDismiss(dialog: DialogInterface?) {
// Doesn't matter what you write here, the dialog will be closed.
super.onDismiss(dialog)
}
bottomSheetDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
toast("dismissed");
}
});
如果您从 BottomSheetDialogFragment() 扩展,只需覆盖您的 class
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
//Code here
}
这将在 onBackPress 以及您通过单击对话框外部关闭对话框时触发。
确保不要将对话框设置为可取消,因为这不会触发
在 AppCompatActivity
中,您可以使用以下技巧:
val mgr = supportFragmentManager
val callback = object: FragmentManager.OnBackStackChangedListener {
var count = 0
override fun onBackStackChanged() {
// We come here twice, once when the sheet is opened,
// once when it's closed.
if (++count >= 2) {
mgr.removeOnBackStackChangedListener(this)
doWhatNeedsToBeDoneWhenTheSheetIsClosed()
}
}
}
mgr.addOnBackStackChangedListener(callback)
请务必在 sheet 上调用 show
之前执行上述操作。
这段代码对我有用:
bottomSheetDialogFragment.getDialog().setOnDismissListener(dialog -> {
// code goes here
});
注意,你应该在显示 bottomSheetDialogFragment
之后调用它(在我的例子中是 showNow
),否则 getDialog()
将 return 你无效。
如何收听 BottomSheetDialogFragment
的最终解雇?我只想在最终解雇时保存用户更改...
我试过以下方法:
方法一
只有在通过向下滑动对话框(不是按下后退或触摸外部)关闭对话框时才会触发
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
Dialog d = super.onCreateDialog(savedInstanceState);
d.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = (FrameLayout) dialog.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior behaviour = BottomSheetBehavior.from(bottomSheet);
behaviour.setState(BottomSheetBehavior.STATE_EXPANDED);
behaviour.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN)
{
// Bottom Sheet was dismissed by user! But this is only fired, if dialog is swiped down! Not if touch outside dismissed the dialog or the back button
Toast.makeText(MainApp.get(), "HIDDEN", Toast.LENGTH_SHORT).show();
dismiss();
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
}
});
return d;
}
方法二
这不允许我区分最终解雇和来自屏幕旋转或 activity 娱乐的解雇...
@Override
public void onDismiss(DialogInterface dialog)
{
super.onDismiss(dialog);
// this works fine but fires one time too often for my use case, it fires on screen rotation as well, although this is a temporarily dismiss only
Toast.makeText(MainApp.get(), "DISMISSED", Toast.LENGTH_SHORT).show();
}
问题
如何收听指示用户已完成对话的事件?
尽管 SO 上的所有类似问题都建议使用 onDismiss
我认为以下是正确的解决方案:
@Override
public void onCancel(DialogInterface dialog)
{
super.onCancel(dialog);
Toast.makeText(MainApp.get(), "CANCEL", Toast.LENGTH_SHORT).show();
}
在以下情况下会触发:
* the user presses back
* the user presses outside of the dialog
这不会触发:
* on screen rotation and activity recreation
解决方案
合并 onCancel 和 BottomSheetBehavior.BottomSheetCallback.onStateChanged,如下所示:
public class Dailog extends BottomSheetDialogFragment
{
@Override
public void onCancel(DialogInterface dialog)
{
super.onCancel(dialog);
handleUserExit();
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
Dialog d = super.onCreateDialog(savedInstanceState);
d.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior behaviour = BottomSheetBehavior.from(bottomSheet);
behaviour.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN)
{
handleUserExit();
dismiss();
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
}
});
return d;
}
private void handleUserExit()
{
Toast.makeText(MainApp.get(), "TODO - SAVE data or similar", Toast.LENGTH_SHORT).show();
}
}
我用这个简单的技巧实现了这个
val bottomSheetDialog = FeedbackFormsFragment.createInstance()
bottomSheetDialog.show((activity as FragmentActivity).supportFragmentManager, BOTTOM_SHEET)
// add some delay to allow the bottom sheet to be visible first so that the dialog is not null
Handler().postDelayed({
bottomSheetDialog.dialog?.setOnDismissListener {
// add code here
}
}, 1000)
虽然@prom85 的方法有效,但还有一个不同的方法。如果您想在一种情况下关闭 BottomSheetDialogFragment
并在另一种情况下保留它,那是行不通的。它会在所有情况下关闭对话框。
例如,如果您在 BottomSheetDialogFragment
的 EditText
内输入文本,偶尔在外部单击,它会在没有任何警告的情况下关闭对话框。我试过 https://medium.com/@anitas3791/android-bottomsheetdialog-3871a6e9d538,它有效,但在另一种情况下。当您向下拖动对话框时,它将关闭。如果您在外部单击,它不会显示任何警告消息,也不会关闭对话框。
所以,我使用了来自 的 @shijo 的一个很好的建议。
将这些行添加到 onActivityCreated
方法(或 onCreateView
之后的任何其他生命周期方法)。
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
View touchOutsideView = getDialog().getWindow()
.getDecorView()
.findViewById(android.support.design.R.id.touch_outside);
touchOutsideView.setOnClickListener(yourClickListener);
}
在我的例子中 yourClickListener
我检查文本并显示警告或关闭对话框:
private fun checkAndDismiss() {
if (newText == oldText) {
dismissAllowingStateLoss()
} else {
showDismissAlert()
}
}
创建 BottomSheetDialogFragment
时,不要像在 中那样调用 setCancelable(false)
,否则这些方法可能不起作用。也许不要在样式中设置 <item name="android:windowCloseOnTouchOutside">false</item>
或在 onCreateDialog
.
setCanceledOnTouchOutside(false)
我也尝试了几种方法来覆盖取消行为,但都没有成功。
<style name="BottomSheetDialogTheme" parent="Theme.Design.Light.BottomSheetDialog">
<item name="android:windowCloseOnTouchOutside">false</item>
</style>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, R.style.BottomSheetDialogTheme)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setOnShowListener {
val bottomSheet = dialog.findViewById<View>(
android.support.design.R.id.design_bottom_sheet) as? FrameLayout
val behavior = BottomSheetBehavior.from(bottomSheet)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
//showing the different states.
when (newState) {
BottomSheetBehavior.STATE_HIDDEN -> dismiss() //if you want the modal to be dismissed when user drags the bottomsheet down
BottomSheetBehavior.STATE_EXPANDED -> {
}
BottomSheetBehavior.STATE_COLLAPSED -> {
}
BottomSheetBehavior.STATE_DRAGGING -> {
}
BottomSheetBehavior.STATE_SETTLING -> {
}
}
}
})
dialog.setOnCancelListener {
// Doesn't matter what you write here, the dialog will be closed.
}
dialog.setOnDismissListener {
// Doesn't matter what you write here, the dialog will be closed.
}
}
return dialog
}
override fun onCancel(dialog: DialogInterface?) {
// Doesn't matter what you write here, the dialog will be closed.
super.onCancel(dialog)
}
override fun onDismiss(dialog: DialogInterface?) {
// Doesn't matter what you write here, the dialog will be closed.
super.onDismiss(dialog)
}
bottomSheetDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
toast("dismissed");
}
});
如果您从 BottomSheetDialogFragment() 扩展,只需覆盖您的 class
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
//Code here
}
这将在 onBackPress 以及您通过单击对话框外部关闭对话框时触发。
确保不要将对话框设置为可取消,因为这不会触发
在 AppCompatActivity
中,您可以使用以下技巧:
val mgr = supportFragmentManager
val callback = object: FragmentManager.OnBackStackChangedListener {
var count = 0
override fun onBackStackChanged() {
// We come here twice, once when the sheet is opened,
// once when it's closed.
if (++count >= 2) {
mgr.removeOnBackStackChangedListener(this)
doWhatNeedsToBeDoneWhenTheSheetIsClosed()
}
}
}
mgr.addOnBackStackChangedListener(callback)
请务必在 sheet 上调用 show
之前执行上述操作。
这段代码对我有用:
bottomSheetDialogFragment.getDialog().setOnDismissListener(dialog -> {
// code goes here
});
注意,你应该在显示 bottomSheetDialogFragment
之后调用它(在我的例子中是 showNow
),否则 getDialog()
将 return 你无效。