我不确定这是设备问题还是代码问题,但我使用相对布局和图像视图创建了一个简单的可拖动可排序列表。我有非常具体的理由这样做,这不是我的问题。
我遇到的问题是有时它会完全冻结我的应用程序。项目将卡在拖动状态。如果我抬起手指,阴影(拖动)对象仍在屏幕上,如果我触摸屏幕,它将移动到那个位置。这将持续大约一分钟,然后我会得到一个错误,说应用程序没有响应,可以选择终止它或等待。logcat中唯一有用的位如下:
12-09 14:23:13.157: W/WindowManager(16415): Drag is in progress but there is no drag window handle.
然后当应用程序超时时,我会将其视为一个错误
12-09 14:59:09.782: E/ActivityManager(16415): ANR in com.appname.appname (com.appname.appname/.MainActivity)
12-09 14:59:09.782: E/ActivityManager(16415): Reason: keyDispatchingTimedOut
我在谷歌上搜索了这条错误信息,唯一的信息是某人没有拖动监听器,另一个人说是设备触摸传感器没有跟上。
理想情况下,我很乐意修复这个错误,并从一开始就防止它发生。如果我拖得很快的话,这种情况似乎确实会发生,但我不能很好地要求我的用户不要拖得很快…正确的?
或者,有没有一种方法可以检测到拖动已经冻结了应用程序并中断了拖动。比如在触摸监听器上设置一个定时器,如果在一两秒钟内没有拖拽位置信息,那么拖拽会中断吗?计时器的事我知道怎么做,但我不知道我会如何强迫阻力停止时,它冻结。有什么想法吗?
代码如下:
设置
//happens once when the app loads
RelativeLayout trackList = (RelativeLayout) findViewById(R.id.nsTrackList1);
trackList.setOnDragListener(new MyTrackDragListener(this));
//happens in a loop for each "track" (image view)
trackButton = new ImageView(this);
trackButton.setImageDrawable(nsPackages[trackId].banner[bannerSizeSelector]);
trackButton.setOnTouchListener(new MyTrackTouchListener());
关于触摸
public class MyTrackTouchListener implements OnTouchListener {
boolean isDragging=false;
float prevX, prevY;
public boolean onTouch(View view, MotionEvent motionEvent) {
if(motionEvent.getPointerCount() < 2 && !isDragging) return false;
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
isDragging=false;
prevX=0;
prevY=0;
return true;
} else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
if(isDragging) return true;
boolean wasFirst = (prevX == 0 || prevY == 0);
float theTotalDiff = Math.abs(prevX - motionEvent.getX()) + Math.abs(prevY - motionEvent.getY());
prevX=motionEvent.getX();
prevY=motionEvent.getY();
if(wasFirst) return true;
if(theTotalDiff <3) return true;
ClipData data = ClipData.newPlainText("", "");
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDrag(data, shadowBuilder, view, 0);
int thisViewId = view.getId();
//hide view
view.setVisibility(View.GONE);
isDragging=true;
return true;
} else if (motionEvent.getAction() == MotionEvent.ACTION_UP || motionEvent.getAction() == MotionEvent.ACTION_CANCEL) {
isDragging=false;
return true;
}else {
Integer thisAction = motionEvent.getAction();
Log.d("looper","Motion action: "+thisAction.toString());
return false;
}
}
}
关于阻力
class MyTrackDragListener implements OnDragListener {
public static boolean isDragging=false;
private MainActivity parent;
public MyTrackDragListener(MainActivity myAct){
parent=myAct;
}
@Override
public boolean onDrag(View v, DragEvent event) {
int action = event.getAction();
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
isDragging=true;
// do nothing
return true;
case DragEvent.ACTION_DROP:
View view = (View) event.getLocalState();
parent.doDropSort(view,(int) event.getY());
return true;
case DragEvent.ACTION_DRAG_ENDED:
if(isDragging && event.getResult()==false){
View view2 = (View) event.getLocalState();
parent.doDropSort(view2,(int) event.getY(),true);
return true;
}
isDragging=false;
break;
case DragEvent.ACTION_DRAG_LOCATION:
parent.doDragHover((int) event.getY());
return true;
default:
Log.d("looper","drag other... "+String.valueOf(event.getAction()));
}
return false;
}
}
一些我已经试过的东西
完全删除拖动侦听器
总是从ondrag返回true
总是从ondrag返回false
基本上,在拖动和触摸中返回true/false的每个组合
移除2个手指和动作移动部分并触发拖动动作
同样的结果。拖放在大约70%的时间内都能完美地工作,然后突然出现上面描述的冻结行为。有时是第一次拖,有时是几次拖。我注意到除了可能的拖拽速度之外,其他模式都是一致的。当我快速拖动时,似乎通常会发生这种情况,但拖动方向或拖动到哪里似乎无关紧要。
最佳答案
哎呀!我想出来了。这是将来帮助别人的答案。
最后,我把它缩小到只有最上面的列表项(不知道我是怎么漏掉的),然后开始注释行,直到它没有断开,缩小到这一行:
view.setVisibility(View.GONE);
我的列表的设置方式是在相对布局中使用下面的布局属性(顶部项除外)。一旦我从视图中删除了顶部的项,第二个到顶部的项就在一个可见性消失的项下面,所以它不知道该怎么做。解决方案是设置视图中下一项的参数。幸运的是,我手动设置了我的轨迹按钮的视图ID(还有其他视图也可以滚动)。所以它们是连续的。所以我的修复方法是简单地找到id+1的项,并通过如下设置将其设置为零使其成为顶级项…
// the top item in my list is ALWAYS view id 1
if(thisViewId == 1){
ImageView nextTrackButton = (ImageView) trackList.findViewById(thisViewId+1);
LayoutParams ntLP = (LayoutParams) nextTrackButton.getLayoutParams();
ntLP.addRule(RelativeLayout.BELOW,0);
nextTrackButton.setLayoutParams(ntLP);
}
//hide view
view.setVisibility(View.GONE);