Перетаскивание в android 3.x вызывает незаконное появлениеStateException после небольшого числа при перетаскивании
есть проблема с механизмом перетаскивания в android 3.x:
после выполнения некоторых перетаскиваний (скажем, 30 перетаскиваний) начисляется исключение (см. прикрепленную ссылку)
https://groups.google.com/forum/#!msg/android-platform/2APvO248NNY/rKI-5dCT8XcJ
(Я получаю в журнале то же самое, что и прикрепленное к этому сообщению..)
Технический специалист android отвечает там, что это ошибка в API, и говорит, что единственный способ избежать этой проблемы - вызвать сборщик мусора.
Я сделал это. исключение больше не было выбрано, но через некоторое время (скажем, более 30-40 перетаскиваний) андроид по какой-то причине перестает вызывать событие drop.
Я попытался "обновить" весь вид, освободив все ресурсы/холсты/чертежи кеша/переработку растровых изображений и заново их создав, и это не помогло (больше не выдавало исключение, но все же после некоторого перетаскивания событие не работает)
единственное, что "помогает" - это закрыть активность и снова перезапустить ее.
кто-нибудь решил эту проблему как-то или имел хорошую простую альтернативу??? (помимо реализации моей собственной функции перетаскивания).
Я хотел бы получить решение, которое не заставит меня перезапустить или воссоздать все, что не предполагается..
здесь пример кода, который демонстрирует ошибку (не демонстрирует часть, о которой я говорил о проблеме с событием drop после использования 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
Ответы
Ответ 1
прошло много времени с тех пор, как я задал этот вопрос.
когда была выпущена Android-версия Android, эта ошибка больше не существовала.
поэтому, поскольку у Android Android нет причин для написания приложений, ориентированных на Honeycomb.
в любом случае только 0,3% от общего числа Android-устройств, работающих с этой версией ОС,
поэтому для меня ответ - просто не использовать Honeycomb!: →
Ответ 2
Попробуйте следующее: -
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();
}
}
Ответ 3
Если перезапуск активности помогает, попробуйте перезапустить представление, удалив его и добавив снова (может быть новый экземпляр). Также вы можете попробовать обычный штат (что обычно не помогает), например invalidate(), переопределить onDraw() и посмотреть, почему он вызывается или не вызван.
Ответ 4
Попробуйте следующее:
imageViewToDRag.startDrag(null, shadowBuilder, imageViewToDRag, 0);
System.gc();
Это работало для меня на Android 3.2!
Ответ 5
Из comment в question, охватывающий проблему, которую вы отправили в группы Google обсуждение:
Эта проблема возникает, потому что 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
, так как вы использовали событие касания.