帮助解决 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 问题的主要内容,如果未能解决你的问题,请参考以下文章

Android开发学习之路--UI之ListView

按下返回按钮后,listview会隐藏UI IOS

在android中定制设计ui一个listview

java [ListView] #Android #UI

Android UI:ListView -- SimpleAdapter

Android入门UI-单位与尺寸ListView