在Android 3.x中使用拖放功能进行少量拖动后会导致IllegalStateException异常。

14

在Android 3.x中存在拖放机制的问题:在执行一些拖动操作后(比如30次),会出现异常(参见附加链接)。

https://groups.google.com/forum/#!msg/android-platform/2APvO248NNY/rKI-5dCT8XcJ (我在日志中得到了与该帖子所附异常相同的内容。)

Android技术人员在那里回答说这是API中的错误,并且说避免该问题的唯一方法是调用垃圾收集器。

我做到了,不再抛出异常,但是经过一段时间(比如说更多的30-40次拖动操作)后,Android出于某种原因停止调用“放置”事件。

我试图通过释放所有资源/画布/绘图缓存/回收位图并重新创建它们来“刷新”所有视图,但没有帮助(不再抛出异常 - 但仍然无法使用“放置”事件)。

唯一“有用”的事情就是关闭活动并重新启动它。

是否有人以某种方式解决了这个问题,或者有一个好的简单替代方案?(除了实现自己的拖放功能..)

我希望能够得到解决方案,而不会强制我重新启动或重新创建任何不该这样做的内容。

以下是演示该Bug的示例代码(未演示我所说的使用System.GC后“放置”事件问题的部分):

public class DragandDropExampleActivity extends Activity {

private boolean mIsBeenDragged = false;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    final ImageView imageViewToDRag = (ImageView) findViewById(R.id.image_view_to_drag);

    imageViewToDRag.setClickable(true);

    imageViewToDRag.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                mIsBeenDragged = true;
                DragShadowBuilder shadowBuilder = new DragShadowBuilder(imageViewToDRag);
                imageViewToDRag.startDrag(null, shadowBuilder, imageViewToDRag, 0);
            } else if (event.getAction() == MotionEvent.ACTION_UP) {

                mIsBeenDragged = false; 
            }
            return false;
        }
    });

}
}

这是XML:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_frame"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<ImageView
    android:id="@+id/image_view_to_drag"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_launcher" >

</ImageView>

以下是堆栈跟踪信息:

06-04 13:34:32.730: E/View(8061):
java.lang.IllegalArgumentException
    at android.view.Surface.lockCanvasNative(Native Method)
    at android.view.Surface.lockCanvas(Surface.java:350)
    at android.view.View.startDrag(View.java:11489)
    at com.show.dragandrop.DragandDropExampleActivity$1.onTouch(DragandDropExampleActivity.java:32)
    at android.view.View.dispatchTouchEvent(View.java:4617)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1560)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1291)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1560)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1291)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1560)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1291)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1560)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1291)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java: 1862)
    at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1286)
    at android.app.Activity.dispatchTouchEvent(Activity.java:2315)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1835)
    at android.view.View.dispatchPointerEvent(View.java:4689)
    at android.view.ViewRoot.deliverPointerEvent(ViewRoot.java:2415)
    at android.view.ViewRoot.handleMessage(ViewRoot.java:2077)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:132)
    at android.app.ActivityThread.main(ActivityThread.java:4126)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:491)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
    at dalvik.system.NativeStart.main(Native Method)

要让异常出现 - 只需将图像拖动到屏幕上的某个点,并松开手指。重复这个动作30次,就会抛出异常。 我创建了这个非常简单的示例,以证明异常是由于我的应用程序没有任何额外开销而抛出的。

TIA


你能添加一些示例代码来进行测试吗?我认为这与取消事件有关,导致对象处于无效状态。 - rekire
可以的,但我现在不在工作站,所以明天再发。谢谢。 - Tal Kanel
@rekire:异常也从模拟器中抛出,我刚刚再次检查了一下。 - Tal Kanel
1
这有帮助到您吗?https://dev59.com/9Gox5IYBdhLWcg3w8ItJ - Vrashabh Irde
@Vrashabh:看起来似乎有一些关联,我稍后会更认真地检查一下。谢谢。 - Tal Kanel
显示剩余3条评论
5个回答

1

试试这个:

private OnTouchListener drag = new OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            setViewPosition(v, Math.round(event.getRawX()),
                    Math.round(event.getRawY()));
        }
        return false;
    }
};


private void setViewPosition(View v, int x, int y) {
    if (v != null) {
        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT);
        params.leftMargin = (x - v.getMeasuredWidth() / 2);
        params.topMargin = (y - v.getMeasuredHeight() / 2);
        v.setLayoutParams(params);
        v.invalidate();
    }
}

0
自从我提出那个问题以来已经过了很长时间。 当Android ICS发布时,这个错误已经不存在了。 因此,自从Android ICS以来,真的没有理由编写针对Honeycomb的应用程序。 无论如何,只有0.3%的Android设备运行此版本的操作系统,

所以对我来说,答案就是-不要使用Honeycomb! :->


0
如果重新启动活动有帮助,那么尝试重新启动视图,通过删除它并再次添加它(可能是新实例)。此外,您可以尝试常规操作(通常不会有帮助),如invalidate(),覆盖onDraw()并查看为什么调用或未调用。

很好的想法,但如果不够清晰 - 我也尝试过了,没有帮助。无论如何 - 我希望有一个解决方案,不需要强制我重新启动/删除任何东西... - Tal Kanel

0

试试这个:

imageViewToDRag.startDrag(null, shadowBuilder, imageViewToDRag, 0);
System.gc();

在我的Android 3.2上运行良好!


已经尝试过了。我在问题描述中写了关于它的内容。在简单的演示应用程序中,它可以解决问题 - 但是当我使用它很多次后,拖动事件会在一段时间后停止被调用。此外,调用GC并不是一个真正的解决方案。 - Tal Kanel

0

注释中,可以看出您发布的问题已经涉及到了问题。以下是来自Google Groups讨论区的相关内容:

这个问题是由于GC没有对拖拽操作进行垃圾回收所致。但是,如果窗口被重绘,GC将对拖拽操作进行垃圾回收。例如,在软键盘打开或某个ImageView更新后,窗口将会被重绘。

因此,通过强制重绘包含View的对象,似乎可以让垃圾回收器回收拖拽对象。虽然我手头没有3.0设备,但也许可以尝试一下类似如下的方法:

        } else if (event.getAction() == MotionEvent.ACTION_UP) {
           findViewById(R.id.main_frame).invalidate();
        }

你可能还需要在其中加入建议的垃圾回收提示:

        } else if (event.getAction() == MotionEvent.ACTION_UP) {
           findViewById(R.id.main_frame).invalidate();
           System.gc();
        }

但我认为如果可能的话,你是对的要避免那样做,所以只有在没有它无法工作时才尝试。

另外,我认为onTouch()应该返回true,因为你已经消耗了触摸事件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接