【问题标题】:android BottomSheet how to collapse when clicked outside?android BottomSheet 在外部单击时如何折叠?
【发布时间】:2016-11-06 06:08:28
【问题描述】:

我已经使用 NestedScrollView 实现了底页行为。并且想知道是否可以在外部触摸时隐藏底部视图。

【问题讨论】:

  • 这是一个自我回答的问题吗?因为你同时添加了问答,所以我在问。
  • 是的,这样其他人在遇到同样问题时不会感到沮丧。
  • 对于BottomSheetDialogFragment,请参阅stackoverflow.com/questions/40616833/…

标签: android bottom-sheet


【解决方案1】:

有两种方法可以使对话框可取消:

设置此对话框是否可以使用 BACK 键取消。

Java

dialog.setCancelable(true);

科特林

dialog.setCancelable(true)

设置在窗口边界外触摸时是否取消此对话框。

Java

dialog.setCanceledOnTouchOutside(true);

科特林

dialog.setCanceledOnTouchOutside(true)

【讨论】:

    【解决方案2】:

    我终于可以做到了,

    使用了以下代码行:

    @Override public boolean dispatchTouchEvent(MotionEvent event){
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            if (mBottomSheetBehavior.getState()==BottomSheetBehavior.STATE_EXPANDED) {
    
                Rect outRect = new Rect();
                bottomSheet.getGlobalVisibleRect(outRect);
    
                if(!outRect.contains((int)event.getRawX(), (int)event.getRawY()))
                    mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
            }
        }
    
        return super.dispatchTouchEvent(event);
    }
    

    希望它能拯救某人的一整天!

    【讨论】:

    • 像魅力一样工作!
    • Fragment 中呢?
    • @RajeshNasit 在您的活动中实现 DispatchTouchEvent 方法,并使用 FragmentManager 调用您的 Fragment 中的方法。
    • 我这样做了,但问题是触摸事件在点击每个视图的滑动时调用。我只希望它用于特定视图滑动
    • 它有效,但这是一个好的解决方案吗?因为每次我们触摸或滚动屏幕时,dispatchTouchEvent 方法都会被调用多次,这可能对电池使用和应用程序性能不利。
    【解决方案3】:

    对我来说这是一个简单的setCancelable(true);

    @Override
    public void setupDialog(Dialog dialog, int style) {
        super.setupDialog(dialog, style);
    
        View contentView = View.inflate(getContext(), R.layout.layout_additional_prices, null);
        unbinder = ButterKnife.bind(this, contentView);
        dialog.setContentView(contentView);
        dialog.setOnKeyListener(new BottomSheetBackDismissListener());
        //makeBottomSheetFullScreen(getActivity(), mBottomSheetBehaviorCallback, contentView);
        setCancelable(true);
    }
    

    【讨论】:

      【解决方案4】:

      我发现使用 OP 的答案或 AutoCloseBottomSheetBehavior.java 版本(来自 Budius)的用户体验问题。我更改了 AutoCloseBottomSheetBehavior 代码,这种方式对我来说似乎更干净并且不会导致任何 UX 问题(代码在 Kotlin 中):

      class AutoCloseBottomSheetBehavior<V : View>(
              context: Context,
              attrs: AttributeSet
      ) : BottomSheetBehavior<V>(context, attrs) {
      
          private val gestureDetector: GestureDetectorCompat =
                  GestureDetectorCompat(context, object : GestureDetector.SimpleOnGestureListener() {
      
                      override fun onDown(event: MotionEvent?): Boolean {
                          return true
                      }
      
                      override fun onSingleTapUp(e: MotionEvent?): Boolean =
                              when {
                                  state == STATE_EXPANDED -> {
                                      state = STATE_COLLAPSED
                                      true
                                  }
                                  state == STATE_COLLAPSED && isHideable -> {
                                      state = STATE_HIDDEN
                                      true
                                  }
                                  else -> false
                              }
                  })
      
          @SuppressLint("ClickableViewAccessibility")
          override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
              parent.setOnTouchListener { _, event ->
                  gestureDetector.onTouchEvent(event)
              }
              return super.onLayoutChild(parent, child, layoutDirection)
          }
      }
      

      每当用户在父 CoordinatorLayout 上执行单击时,这会折叠/隐藏底部工作表,并且还处理底部工作表可隐藏的情况(如果它处于 COLLAPSED 状态,我们希望隐藏它)。

      【讨论】:

        【解决方案5】:

        您可以调用以下代码在点击外部时关闭底部工作表对话框。

        BottomSheetDialog dialog = new BottomSheetDialog(context);
        dialog.setContentView(R.layout.bottom_sheet);
        dialog.setCanceledOnTouchOutside(true);
        dialog.show(); 
        

        【讨论】:

          【解决方案6】:

          对于活动:

          @Override 
          public boolean dispatchTouchEvent(MotionEvent event){
              if (event.getAction() == MotionEvent.ACTION_DOWN) {
                  if (mBottomSheetBehavior.getState()==BottomSheetBehavior.STATE_EXPANDED) {
          
                      Rect outRect = new Rect();
                      bottomSheet.getGlobalVisibleRect(outRect);
          
                      if(!outRect.contains((int)event.getRawX(), (int)event.getRawY()))
                          mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                  }
              }
          
              return super.dispatchTouchEvent(event);
          }
          

          对于 Fragment:Activity 中使用相同的方法,例如,

          @Override
          public boolean dispatchTouchEvent(MotionEvent event) {
              if (event.getAction() == MotionEvent.ACTION_DOWN) {
                  if (fragment != null && fragment instanceof HomeFragment) {
                      ((HomeFragment) fragment).hideBottomSheetFromOutSide(event);
                  }
              }
              return super.dispatchTouchEvent(event);
          }
          

          并在片段中创建方法,例如:

             /**
               * Calling from Dashboard Activity
               *
               * @param event Motion Event
               */
              public void hideBottomSheetFromOutSide(MotionEvent event) {
                  if (isBottomSheetMenuExpanded()) {
                      Rect outRect = new Rect();
                      mBinding.homeBottomSheetLayout.getGlobalVisibleRect(outRect);
                      if (!outRect.contains((int) event.getRawX(), (int) event.getRawY()))
                          mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                  }
              }
          

          希望对您有所帮助。

          谢谢。

          【讨论】:

          • 嗨,这可以用吗? bottomsheetdialogfragment?
          • 我没有检查。你可以试试看。
          • 现在检查,不工作,没有触发断点
          • 它有效,但这是一个好的解决方案吗?因为每次我们触摸或滚动屏幕时,dispatchTouchEvent 方法都会被调用多次,这可能对电池使用和应用程序性能不利。
          • 是的,没错。如果您找到任何其他好的解决方案,请告诉我。
          【解决方案7】:
          someViewToClickOn.setOnClickListener(v -> 
              behavior.setState(BottomSheetBehavior.STATE_HIDDEN));
          

          这也有效!我第一次使用BottomSheetBehavior.STATE_COLLAPSED 不起作用

          【讨论】:

            【解决方案8】:

            为您的主布局设置点击监听器(在本例中为坐标布局)

            @OnClick(R.id.coordinateLayout)
            public void onClickView(View view) {
                if (sheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
                    sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                }
            }
            

            注意:Butterknife 用于点击,否则在活动的 onCreate 中使用下面的代码。

            CoordinateLayout layout = (CoordinateLayout) findViewById(R.id. coordinateLayout);
            layout.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (sheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
                        sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                    }
                }
            });
            

            【讨论】:

              【解决方案9】:

              感谢 OP 的问题/答案。我使用了他的代码,但改进了它的清洁度并想分享。您可以直接在 BottomSheetBehavior 中编写代码,而不是扩展 View 并添加接口。像这样:

              AutoCloseBottomSheetBehavior.java

              import android.content.Context;
              import android.graphics.Rect;
              import android.support.design.widget.BottomSheetBehavior;
              import android.support.design.widget.CoordinatorLayout;
              import android.util.AttributeSet;
              import android.view.MotionEvent;
              import android.view.View;
              
              public class AutoCloseBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
              
                  public AutoCloseBottomSheetBehavior(Context context, AttributeSet attrs) {
                      super(context, attrs);
                  }
              
                  @Override
                  public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
                      if (event.getAction() == MotionEvent.ACTION_DOWN &&
                          getState() == BottomSheetBehavior.STATE_EXPANDED) {
              
                          Rect outRect = new Rect();
                          child.getGlobalVisibleRect(outRect);
              
                          if (!outRect.contains((int) event.getRawX(), (int) event.getRawY())) {
                              setState(BottomSheetBehavior.STATE_COLLAPSED);
                          }
                      }
                      return super.onInterceptTouchEvent(parent, child, event);
                  }
              }
              

              然后您只需将其添加到您的 XML 布局中:

              <?xml version="1.0" encoding="utf-8"?>
              <android.support.design.widget.CoordinatorLayout
                  xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:app="http://schemas.android.com/apk/res-auto"
                  xmlns:tools="http://schemas.android.com/tools"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
              
                 ... your normal content here ...
                 <SomeLayout... />
              
                  ... the bottom sheet with the behavior
                  <LinearLayout
                      android:layout_width="match_parent"
                      android:layout_height="wrap_content"
                      android:orientation="vertical"
                      app:layout_behavior="<com.package.name.of.the.class>.AutoCloseBottomSheetBehavior">
              
                      ... the bottom sheet views
              
                  </LinearLayout>
              
              </android.support.design.widget.CoordinatorLayout>
              

              【讨论】:

              • 这很好用!小建议...在调用“setState”后添加“return true”将阻止点击通过。在我的用例中,如果在对话框外单击,我只是希望它折叠而不传递单击事件以在其后面查看:)
              • @Psest328 添加return true 导致这对我停止工作有时。如果我在里面有return true,表格只会偶尔关闭。
              • 非常适合我。这应该是公认的答案。
              【解决方案10】:

              很多人都在寻找一种在片段上实现 dispatchTouchEvent 的方法。所以他们可以这样做:

              按照定义创建自定义布局:

              public class DispatchTouchEvent extends LinearLayout {
              
                  public interface onDispatchEvent
                  {
                      void dispatchEvent(MotionEvent e);
                  }
              
                  private onDispatchEvent dispatchEvent;
              
                  public DispatchTouchEvent(Context context) {
                      super(context);
                  }
              
                  public DispatchTouchEvent(Context context, AttributeSet attrs) {
                      super(context, attrs);
                  }
              
                  public DispatchTouchEvent(Context context, AttributeSet attrs, int defStyleAttr) {
                      super(context, attrs, defStyleAttr);
                  }
              
                  public void setDispatchEvent(onDispatchEvent dispatchEvent)
                  {
                      this.dispatchEvent=dispatchEvent;
                  }
              
                  @Override
                  public boolean dispatchTouchEvent(MotionEvent ev) {
                      if(dispatchEvent!=null)
                      {
                          dispatchEvent.dispatchEvent(ev);
                      }
                      return super.dispatchTouchEvent(ev);
                  }
              
              }
              

              现在使用这个布局作为片段布局的基础。在片段内部将此布局初始化为:

              public class ABC extends fragment implements DispatchTouchEvent.onDispatchEvent
              {
              
              DispatchTouchEvent dispatchTouchEvent;
              
              @Override
              public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                       Bundle savedInstanceState) {
              ....
                  dispatchTouchEvent = (DispatchTouchEvent)rootView.findViewById(R.id.dispatch_event);
                  dispatchTouchEvent.setDispatchEvent(this);
              ....
              }
              
              @Override
              public void dispatchEvent(MotionEvent event) {
                  if (event.getAction() == MotionEvent.ACTION_DOWN) {
                  if (mBottomSheetBehavior.getState()==BottomSheetBehavior.STATE_EXPANDED) 
                  {
              
                      Rect outRect = new Rect();
                      bottomSheet.getGlobalVisibleRect(outRect);
              
                      if(!outRect.contains((int)event.getRawX(), (int)event.getRawY()))   
                       mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                  }
                  }
              
                  }
              
              }
              

              【讨论】:

                猜你喜欢
                • 2020-02-29
                • 1970-01-01
                • 2015-11-18
                • 1970-01-01
                • 1970-01-01
                • 2014-07-15
                • 1970-01-01
                • 2016-02-22
                • 1970-01-01
                相关资源
                最近更新 更多