帮助解决 Android UI ListView 问题
Posted
技术标签:
【中文标题】帮助解决 Android UI ListView 问题【英文标题】:Help with Android UI ListView problems 【发布时间】:2011-11-14 14:16:32 【问题描述】:要理解这个问题,first read how this method works.
我正在尝试实现拖放ListView,一切正常,但遇到了 一个路障。所以我不必处理所有事情,我正在拦截(但返回 false)MotionEvents 发送到 ListView,让它处理滚动和东西。当我想开始拖动一个项目时,我返回 true 并处理所有拖动的东西。除了一件事,一切都很好。当确定发生长按时(在 onInterceptTouchEvent 中)开始拖动(拖放)。我得到了Bitmap 的图像,我像这样拖动。 itemPositition 是被选中项目的索引。
(省略不相关的部分)
...
View dragItem = mListView.getChildAt(itemPosition);
dragItem.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(dragItem.getDrawingCache());
mDragImage = new ImageView(mContext);
mDragImage.setImageBitmap(bitmap);
...
问题是,mDragImage 是这样的纯黑色。
但是,如果我不让 ListView 处理任何事情。如,我在ACTION_DOWN 上开始拖动并在ACTION_UP 上停止,mDragImage 看起来符合预期(但我显然失去了滚动能力)。
由于拖动是通过长按开始的,因此 ListView 有机会在长按发生之前做一些事情。这是我对为什么会发生这种情况的猜测。当一个项目被按下时,它被 ListView 突出显示。在这样做的某个地方,它弄乱了位图。所以当我去拿它时,它处于一种奇怪的状态(全黑)。
我看到了两种解决此问题的方法,但我都不知道该怎么做。
从头开始创建图像。
自己处理突出显示(如果这是问题所在)。
选项 2 对我来说似乎更好,但我查看了文档和源代码却不知道该怎么做。以下是我做过/尝试过的一些事情。
我设置了setOnItemClickListener(...) 和 setOnItemSelectedListener(...) 使用空方法(突出显示 仍然发生)。 (在有人建议之前,打电话给 setOnClickListener 导致运行时错误。)
我还研究过尝试让 ListView 创建一个新项目 (对于选项 2),但找不到方法。
花了 45 分钟浏览源代码和 试图确定突出显示位置的文档 发生了(我从来没有找到过)。
任何解决此问题的帮助将不胜感激。
(EDIT1 开始)
所以我实际上不知道 onLongClickListener 是否工作,我在认为它之前犯了一个错误。我正在尝试设置它,当我发现它时会更新。
(编辑1结束)
发帖前的最后一分钟编辑。我刚才尝试使用onLongClickListener,图像很好。我仍然想知道是否有其他方法。我必须如何使用 onLongClickListener 才能让事情正常工作是丑陋的,但它确实有效。我也花了很多时间试图弄清楚这一点,很高兴找到答案。我仍然希望能够更改/处理突出显示的颜色,默认的橙色并不漂亮。哦,对帖子的长度感到抱歉。在提供我认为需要的所有信息的同时,我想不出办法让它更短。
【问题讨论】:
【参考方案1】:使用此代码,它允许操作药物并放入ListView:
public class DraggableListView extends ListView
private static final String LOG_TAG = "tasks365";
private static final int END_OF_LIST_POSITION = -2;
private DropListener mDropListener;
private int draggingItemHoverPosition;
private int dragStartPosition; // where was the dragged item originally
private int mUpperBound; // scroll the view when dragging point is moving out of this bound
private int mLowerBound; // scroll the view when dragging point is moving out of this bound
private int touchSlop;
private Dragging dragging;
private GestureDetector longPressDetector;
public DraggableListView(Context context, AttributeSet attrs)
this(context, attrs, android.R.attr.listViewStyle);
public DraggableListView(final Context context, AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
longPressDetector = new GestureDetector(getContext(), new SimpleOnGestureListener()
@Override
public void onLongPress(final MotionEvent e)
int x = (int) e.getX();
final int y = (int) e.getY();
int itemnum = pointToPosition(x, y);
if (itemnum == AdapterView.INVALID_POSITION)
return;
if (dragging != null)
dragging.stop();
dragging = null;
final View item = getChildAt(itemnum - getFirstVisiblePosition());
item.setPressed(false);
dragging = new Dragging(getContext());
dragging.start(y, ((int) e.getRawY()) - y, item);
draggingItemHoverPosition = itemnum;
dragStartPosition = draggingItemHoverPosition;
int height = getHeight();
mUpperBound = Math.min(y - touchSlop, height / 3);
mLowerBound = Math.max(y + touchSlop, height * 2 / 3);
);
setOnItemLongClickListener(new OnItemLongClickListener()
@SuppressWarnings("unused")
public boolean onItemLongClick(AdapterView<?> paramAdapterView, View paramView, int paramInt, long paramLong)
// Return true to let AbsListView reset touch mode
// Without this handler, the pressed item will keep highlight.
return true;
);
/* pointToPosition() doesn't consider invisible views, but we need to, so implement a slightly different version. */
private int myPointToPosition(int x, int y)
if (y < 0)
return getFirstVisiblePosition();
Rect frame = new Rect();
final int count = getChildCount();
for (int i = 0; i < count; i++)
final View child = getChildAt(i);
child.getHitRect(frame);
if (frame.contains(x, y))
return getFirstVisiblePosition() + i;
if ((x >= frame.left) && (x < frame.right) && (y >= frame.bottom))
return END_OF_LIST_POSITION;
return INVALID_POSITION;
@Override
public boolean onTouchEvent(MotionEvent ev)
if (longPressDetector.onTouchEvent(ev))
return true;
if ((dragging == null) || (mDropListener == null))
// it is not dragging, or there is no drop listener
return super.onTouchEvent(ev);
int action = ev.getAction();
switch (ev.getAction())
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
dragging.stop();
dragging = null;
if (mDropListener != null)
if (draggingItemHoverPosition == END_OF_LIST_POSITION)
mDropListener.drop(dragStartPosition, getCount() - 1);
else if (draggingItemHoverPosition != INVALID_POSITION)
mDropListener.drop(dragStartPosition, draggingItemHoverPosition);
resetViews();
break;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
int x = (int) ev.getX();
int y = (int) ev.getY();
dragging.drag(x, y);
int position = dragging.calculateHoverPosition();
if (position != INVALID_POSITION)
if ((action == MotionEvent.ACTION_DOWN) || (position != draggingItemHoverPosition))
draggingItemHoverPosition = position;
doExpansion();
scrollList(y);
break;
return true;
private void doExpansion()
int expanItemViewIndex = draggingItemHoverPosition - getFirstVisiblePosition();
if (draggingItemHoverPosition >= dragStartPosition)
expanItemViewIndex++;
// Log.v(LOG_TAG, "Dragging item hovers over position " + draggingItemHoverPosition + ", expand item at index "
// + expanItemViewIndex);
View draggingItemOriginalView = getChildAt(dragStartPosition - getFirstVisiblePosition());
for (int i = 0;; i++)
View itemView = getChildAt(i);
if (itemView == null)
break;
ViewGroup.LayoutParams params = itemView.getLayoutParams();
int height = LayoutParams.WRAP_CONTENT;
if (itemView.equals(draggingItemOriginalView))
height = 1;
else if (i == expanItemViewIndex)
height = itemView.getHeight() + dragging.getDraggingItemHeight();
params.height = height;
itemView.setLayoutParams(params);
/**
* Reset view to original height.
*/
private void resetViews()
for (int i = 0;; i++)
View v = getChildAt(i);
if (v == null)
layoutChildren(); // force children to be recreated where needed
v = getChildAt(i);
if (v == null)
break;
ViewGroup.LayoutParams params = v.getLayoutParams();
params.height = LayoutParams.WRAP_CONTENT;
v.setLayoutParams(params);
private void resetScrollBounds(int y)
int height = getHeight();
if (y >= height / 3)
mUpperBound = height / 3;
if (y <= height * 2 / 3)
mLowerBound = height * 2 / 3;
private void scrollList(int y)
resetScrollBounds(y);
int height = getHeight();
int speed = 0;
if (y > mLowerBound)
// scroll the list up a bit
speed = y > (height + mLowerBound) / 2 ? 16 : 4;
else if (y < mUpperBound)
// scroll the list down a bit
speed = y < mUpperBound / 2 ? -16 : -4;
if (speed != 0)
int ref = pointToPosition(0, height / 2);
if (ref == AdapterView.INVALID_POSITION)
//we hit a divider or an invisible view, check somewhere else
ref = pointToPosition(0, height / 2 + getDividerHeight() + 64);
View v = getChildAt(ref - getFirstVisiblePosition());
if (v != null)
int pos = v.getTop();
setSelectionFromTop(ref, pos - speed);
public void setDropListener(DropListener l)
mDropListener = l;
public interface DropListener
void drop(int from, int to);
class Dragging
private Context context;
private WindowManager windowManager;
private WindowManager.LayoutParams mWindowParams;
private ImageView mDragView;
private Bitmap mDragBitmap;
private int coordOffset;
private int mDragPoint; // at what offset inside the item did the user grab it
private int draggingItemHeight;
private int x;
private int y;
private int lastY;
public Dragging(Context context)
this.context = context;
windowManager = (WindowManager) context.getSystemService("window");
/**
* @param y
* @param offset - the difference in y axis between screen coordinates and coordinates in this view
* @param view - which view is dragged
*/
public void start(int y, int offset, View view)
this.y = y;
lastY = y;
this.coordOffset = offset;
mDragPoint = y - view.getTop();
draggingItemHeight = view.getHeight();
mDragView = new ImageView(context);
mDragView.setBackgroundResource(android.R.drawable.alert_light_frame);
// Create a copy of the drawing cache so that it does not get recycled
// by the framework when the list tries to clean up memory
view.setDrawingCacheEnabled(true);
mDragBitmap = Bitmap.createBitmap(view.getDrawingCache());
mDragView.setImageBitmap(mDragBitmap);
mWindowParams = new WindowManager.LayoutParams();
mWindowParams.gravity = Gravity.TOP;
mWindowParams.x = 0;
mWindowParams.y = y - mDragPoint + coordOffset;
mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
mWindowParams.format = PixelFormat.TRANSLUCENT;
mWindowParams.windowAnimations = 0;
windowManager.addView(mDragView, mWindowParams);
public void drag(int x, int y)
lastY = this.y;
this.x = x;
this.y = y;
mWindowParams.y = y - mDragPoint + coordOffset;
windowManager.updateViewLayout(mDragView, mWindowParams);
public void stop()
if (mDragView != null)
windowManager.removeView(mDragView);
mDragView.setImageDrawable(null);
mDragView = null;
if (mDragBitmap != null)
mDragBitmap.recycle();
mDragBitmap = null;
public int getDraggingItemHeight()
return draggingItemHeight;
public int calculateHoverPosition()
int adjustedY = (int) (y - mDragPoint + (Math.signum(y - lastY) + 2) * draggingItemHeight / 2);
// Log.v(LOG_TAG, "calculateHoverPosition(): lastY=" + lastY + ", y=" + y + ", adjustedY=" + adjustedY);
int pos = myPointToPosition(0, adjustedY);
if (pos >= 0)
if (pos >= dragStartPosition)
pos -= 1;
return pos;
【讨论】:
以上是关于帮助解决 Android UI ListView 问题的主要内容,如果未能解决你的问题,请参考以下文章