13

我发现在 Android 的 Gmail 应用程序中使用了“撤消栏”的实现。“UndoBar”基本上是一个显示在布局顶部的视图。

不幸的是,它并不完整——它没有通过触摸栏外的屏幕来关闭栏的功能。

我已经实现FrameLayout了覆盖onInterceptTouchEvent来处理酒吧解雇,但触摸操作栏什么都不做。

有什么方法可以从操作栏中处理此类事件?

下面有一个带有“UndoBar”的图像。我想在红点表示的操作栏中处理触摸时要实现的目标。

在此处输入图像描述

4

8 回答 8

7

尝试覆盖您的活动的 dispatchTouchEvent。

dispatchTouchEvent(MotionEvent event){

     int x= event.getRawX();
     int y= event.getRawY();


         if(/*check bounds of your view*/){
          // set your views visiblity to gone or what you want. 
         }
      //for prevent consuming the event.
      return super().dispatchTouchEvent(event);    
}

更新

dispatchTouchEvent(MotionEvent event){

     int x= event.getRawX();
     int y= event.getRawY();

      return super().dispatchTouchEvent(event)||[YourView].onTouch(event);    
}
于 2013-05-13T06:31:05.837 回答
4

要捕获包括 ActionBar 在内的整个屏幕的触摸事件,请向 Window 添加一个视图。

View overlayView = new View(this);
WindowManager.LayoutParams p = new WindowManager.LayoutParams();
p.gravity = Gravity.TOP;
p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
p.token = overlayView.getWindowToken();
overlayView.setOnTouchListener(new OnTouchListener() {
    public boolean onTouch(View view, MotionEvent event) {
        // Get the action bar
        int actionBarHeight = actionBar.getHeight();
        if ((event.getAction() == MotionEvent.ACTION_DOWN)
            && (event.getRawY() < actionBarHeight)) {
            // Touch inside the actionBar so let's consume it
            // Do something
            return true;
        }
        return false;
    }
});
WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
mWindowManager.addView(overlayView, p);

希望这可以帮助。

于 2013-05-13T08:41:23.607 回答
2

你应该重写 Activity 的 dispatchTouchEvent(ev: MotionEvent) 方法


看看这个解除无限期 Snackbar 的例子

class MainActivity : AppCompatActivity() {

private var snackbar: Snackbar? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setSupportActionBar(toolbar)

    // show indefinite snackbar
    snackbar = Snackbar.make(coordinator_layout, "Hello world!", Snackbar.LENGTH_INDEFINITE).apply {
        show()
    }
}

/**
 * On each touch event:
 * Check is [snackbar] present and displayed
 * and dismiss it if user touched anywhere outside it's bounds
 */
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
    // dismiss shown snackbar if user tapped anywhere outside snackbar
    snackbar?.takeIf { it.isShown }?.run {
        val touchPoint = Point(Math.round(ev.rawX), Math.round(ev.rawY))
        if (!isPointInsideViewBounds(view, touchPoint)) {
            dismiss()
            snackbar = null // set snackbar to null to prevent this block being executed twice
        }
    }

    // call super
    return super.dispatchTouchEvent(ev)
}

/**
 * Defines bounds of displayed view and check is it contains [Point]
 * @param view View to define bounds
 * @param point Point to check inside bounds
 * @return `true` if view bounds contains point, `false` - otherwise
 */
private fun isPointInsideViewBounds(view: View, point: Point): Boolean = Rect().run {
    // get view rectangle
    view.getDrawingRect(this)

    // apply offset
    IntArray(2).also { locationOnScreen ->
        view.getLocationOnScreen(locationOnScreen)
        offset(locationOnScreen[0], locationOnScreen[1])
    }

    // check is rectangle contains point
    contains(point.x, point.y)
}
}        

或查看完整的要点

于 2018-11-10T23:37:23.987 回答
0

onTouchEvent在你的 FrameLayout 上试过了吗?

我还没有尝试过,但我认为这对我来说是有意义的,如果发生MotionEvent这种布局ACTION_OUTSIDE动作应该被解雇。

试试下面的。

public boolean onTouchEvent(MotionEvent event)  
{  

       if(event.getAction() == MotionEvent.ACTION_OUTSIDE){  

             undoBar.setVisibility(View.GONE);
       }  
       return true;  
}  
于 2013-05-09T15:01:58.940 回答
0

有 2 种可能的解决方案。

正如我在评论中提到的,您可以在您的和最轻微的滚动上实现一个滚动侦听器ListView和简单的调用hideUndoBar(true)

或者

您可以修改UndoBarController. 您会注意到,撤消栏只是在构造函数中的监听器和视图的显示方法 setFocus 中的一个View耳光。OnFocusChangeView

在您OnFocusChange检查视图是否失去焦点并调用hideUndoBar(true).

更新

我在这里创建了一个要点https://gist.github.com/atgheb/5551961 展示了如何更改UndoBarController以添加失去焦点时隐藏的功能。

我没有测试它,但我不明白为什么它不起作用。

于 2013-05-10T01:58:02.553 回答
0
params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSPARENT);

你真正需要的是:

WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH

文档说:

窗口标志:如果你设置了 FLAG_NOT_TOUCH_MODAL,你可以设置这个标志来接收一个带有动作 MotionEvent.ACTION_OUTSIDE 的特殊 MotionEvent,用于在你的窗口之外发生的触摸。

于 2014-02-05T04:02:52.723 回答
0

我对在这里找到的任何答案都不满意。我的解决方案是将触摸侦听器附加到父视图,如下所示:

((View)getParent()).setOnTouchListener((v, event) -> {
    return true;
});
于 2017-10-31T12:19:36.863 回答
-2

您可以Activity使用以下代码获取您的所有触摸事件。我想这段代码也会受到影响ActionBar

public class MainActivity extends Activity {
...
// This example shows an Activity, but you would use the same approach if
// you were subclassing a View.
@Override
public boolean onTouchEvent(MotionEvent event){ 

    int action = MotionEventCompat.getActionMasked(event);

    switch(action) {
        case (MotionEvent.ACTION_DOWN) :
            Log.d(DEBUG_TAG,"Action was DOWN");
            return true;
        case (MotionEvent.ACTION_MOVE) :
            Log.d(DEBUG_TAG,"Action was MOVE");
            return true;
        case (MotionEvent.ACTION_UP) :
            Log.d(DEBUG_TAG,"Action was UP");
            return true;
        case (MotionEvent.ACTION_CANCEL) :
            Log.d(DEBUG_TAG,"Action was CANCEL");
            return true;
        case (MotionEvent.ACTION_OUTSIDE) :
            Log.d(DEBUG_TAG,"Movement occurred outside bounds " +
                    "of current screen element");
            return true;      
        default : 
            return super.onTouchEvent(event);
    }      
}
于 2013-04-22T20:26:23.973 回答