我发现在 Android 的 Gmail 应用程序中使用了“撤消栏”的实现。“UndoBar”基本上是一个显示在布局顶部的视图。
不幸的是,它并不完整——它没有通过触摸栏外的屏幕来关闭栏的功能。
我已经实现FrameLayout
了覆盖onInterceptTouchEvent
来处理酒吧解雇,但触摸操作栏什么都不做。
有什么方法可以从操作栏中处理此类事件?
下面有一个带有“UndoBar”的图像。我想在红点表示的操作栏中处理触摸时要实现的目标。
尝试覆盖您的活动的 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);
}
要捕获包括 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);
希望这可以帮助。
你应该重写 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)
}
}
或查看完整的要点
你onTouchEvent
在你的 FrameLayout 上试过了吗?
我还没有尝试过,但我认为这对我来说是有意义的,如果发生MotionEvent
这种布局ACTION_OUTSIDE
动作应该被解雇。
试试下面的。
public boolean onTouchEvent(MotionEvent event)
{
if(event.getAction() == MotionEvent.ACTION_OUTSIDE){
undoBar.setVisibility(View.GONE);
}
return true;
}
有 2 种可能的解决方案。
正如我在评论中提到的,您可以在您的和最轻微的滚动上实现一个滚动侦听器ListView
和简单的调用hideUndoBar(true)
。
或者
您可以修改UndoBarController
. 您会注意到,撤消栏只是在构造函数中的监听器和视图的显示方法 setFocus 中的一个View
耳光。OnFocusChange
View
在您OnFocusChange
检查视图是否失去焦点并调用hideUndoBar(true)
.
我在这里创建了一个要点https://gist.github.com/atgheb/5551961
展示了如何更改UndoBarController
以添加失去焦点时隐藏的功能。
我没有测试它,但我不明白为什么它不起作用。
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,用于在你的窗口之外发生的触摸。
我对在这里找到的任何答案都不满意。我的解决方案是将触摸侦听器附加到父视图,如下所示:
((View)getParent()).setOnTouchListener((v, event) -> {
return true;
});
您可以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);
}
}