gpt4 book ai didi

android - 父 View 没有得到长按事件

转载 作者:行者123 更新时间:2023-11-30 01:34:29 28 4
gpt4 key购买 nike

我正在尝试编写一个类似启动器的应用程序,它可以将小部件添加到其屏幕。

我正在使用 Leonardo Fischer 的教程 (http://leonardofischer.com/hosting-android-widgets-my-appwidgethost-tutorial/),它很棒。

为了删除小部件,用户应该长按小部件,这就是我遇到麻烦的地方;一些小部件(例如 WhatsApp Messagelist、Evernote 列表)允许您滚动它们。出于某种原因,如果您滚动,Android 会触发一个 LongClick 事件,该事件会错误地删除小部件...

我的代码:(创建小部件并设置 LongClickListener)

public void createWidget(Intent data) {
Bundle extras = data.getExtras();
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);

final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
hostView.setAppWidget(appWidgetId, appWidgetInfo);

// relative layout
//RelativeLayout.LayoutParams lp = new RelativeLayout()
//mainlayout.addView(hostView, lp);
mainlayout.addView(hostView);

// [COMMENTED OUT] hostView.setOnLongClickListener(new AppWidgetLongClickListener(hostView));

}

更新

无数个小时后,我想我部分明白发生了什么,但我仍然无法得到正确的行为。

根据 http://balpha.de/2013/07/android-development-what-i-wish-i-had-known-earlier/ ,您需要在父容器(在我的例子中是 mainlayout)中实现 onInterceptTouchEvent 以在事件到达子容器(widgets)之前拦截和处理事件以我为例)。

所以我搜索了以下代码并尝试适应我的需求:

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Consume any touch events for ourselves after longpress is triggered
//Log.i(TAG,"OnIntercept: "+ev.toString());
if (mHasPerformedLongPress) {
Log.i(TAG,"Longpress OK!: "+ev.toString());
mHasPerformedLongPress = false;
return true;
}

// Watch for longpress events at this level to make sure
// users can always pick up this widget
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
postCheckForLongClick();
break;
}

case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress != null) {
removeCallbacks(mPendingCheckForLongPress);
}
break;
}

// Otherwise continue letting touch events fall through to children
return false;
}

class CheckForLongPress implements Runnable {
private int mOriginalWindowAttachCount;

public void run() {
Log.i(TAG,"Inside RUN");
if (getParent()!= null) {
Log.i(TAG,"getParent:"+getParent().toString());
}
if ((getParent() != null) && hasWindowFocus()
&& (mOriginalWindowAttachCount == getWindowAttachCount())
&& !mHasPerformedLongPress) {
if (performLongClick()) { // <-- DOESN'T WORK :(
mHasPerformedLongPress = true;
}
}
}

public void rememberWindowAttachCount() {
mOriginalWindowAttachCount = getWindowAttachCount();
}
}

private void postCheckForLongClick() {
mHasPerformedLongPress = false;

if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout());
}

@Override
public void cancelLongPress() {
super.cancelLongPress();

mHasPerformedLongPress = false;
if (mPendingCheckForLongPress != null) {
removeCallbacks(mPendingCheckForLongPress);
}
}

当我点击一个小部件时,上面的代码确实拦截了触摸事件,但它的逻辑似乎旨在拦截(并直接用于进一步处理)对小部件的长按。我实际需要的是在父 View 中拦截长按。

诀窍似乎在于 if (performLongClick()),据我所知,它向小部件触发了一个 LongClick 事件...

...所以我想我现在的问题是如何在父 View 中跟踪长按。

对于处理 Android UI 事件的冗长(看似基本的)问题,我深表歉意,但从我用谷歌搜索来看,这似乎是一个非常复杂的话题..

最佳答案

所以它完成了...!我不确定这是否是一个优雅的解决方案,但它确实有效。

onInterceptTouchEvent 允许父 View 在事件发送给最终发送者之前对其进行操作。请注意,如果您触摸实际 View ,它不会触发。所以如果你有一个带有一些“空白”和一些元素的布局,onInterceptTouchEvent 不会触发如果你触摸布局的“空白”(你需要布局的onTouchEvent 在这种情况下)。

因为我们基本上只能跟踪ACTION_UPACTION_MOVEACTION_DOWN 事件,所以我们需要计时持续时间ACTION_DOWN/ACTION_UP事件对来决定这是否是长按,所以我所做的如下:

public class time_counter {
private long begin_time;
private long end_time;


public time_counter(long i, long f) {
this.begin_time = i;
this.end_time = f;
}

public long getDuration() {
return (this.end_time - this.begin_time);
}

}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {

// Consume any touch events for ourselves after longpress is triggered

// Watch for longpress events at this level to make sure
// users can always pick up this widget
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
cnt = new time_counter(ev.getEventTime(), (long)0);
break;
}

case MotionEvent.ACTION_MOVE: {
if (cnt != null) {
cnt.end_time = ev.getEventTime();
}
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (cnt != null) {
cnt.end_time = ev.getEventTime();
}
Log.i(TAG, "DURATION: " + cnt.getDuration());
if (cnt.getDuration() > ViewConfiguration.getLongPressTimeout()) {
Log.i(TAG, "it's a longpress: " + this.toString());
if (processClick) {
processClick = false;
this.doRemoveWidget();
}
cancelLongPress();
return true;
}
break;
}

// Otherwise continue letting touch events fall through to children
return false;
}

每当 Android 发送一个 ACTION_DOWN 事件时,代码就会开始使用一个简单的“时间计数器”对象来跟踪它的持续时间。计数器的结束时间戳在整个 ACTION_MOVE 事件中不断更新,当 Android 发送 ACTION_UPACTION_CANCEL 时,代码会检查最终持续时间。如果超过 ViewConfiguration.getLongPressTimeout()(默认 = 500 毫秒),它会触发操作。

请注意,在我的例子中,我需要一个 bool 变量来防止触发多个事件,因为我想使用 LongClick 来删除一个小部件。几乎总是会发生的第二次意外触发会触发空指针异常,因为小部件已被删除。

我用几个小部件(大的、小的、可配置的、有和没有 ScrollView 等等)测试了它,我没有发现任何故障。

同样,不确定这是一个优雅的还是“Android 明智”的解决方案,但它解决了我的问题。

希望这对您有所帮助!

引用:如果您需要关于触摸事件的优秀文章,请查看http://balpha.de/2013/07/android-development-what-i-wish-i-had-known-earlier/ .它给了我正确的“心态”来解决我的问题。

关于android - 父 View 没有得到长按事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35279659/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com