Справка по проблемам с Android UI ListView - PullRequest
5 голосов
/ 11 сентября 2011

Чтобы понять этот вопрос, сначала прочитайте, как работает этот метод.

Я пытаюсь реализовать перетаскивание ListView , все идет хорошо, но натолкнулся на дорожный блок. Поэтому мне не нужно обрабатывать все, я перехватываю (но возвращаю false) MotionEvents , отправленные в ListView, позволяя ему обрабатывать прокрутку и прочее. Когда я хочу начать перетаскивание элемента, я возвращаю true и обрабатывал все элементы перетаскивания. Все работает нормально, кроме одной вещи. Перетаскивание (drag and drop) начинается, когда определено, что длительное нажатие произошло (в 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 - сплошной черный цвет, подобный этому. A solid back bitmap

Но, если я не позволю ListView обрабатывать что-либо. Например, я запускаю перетаскивание на ACTION_DOWN и останавливаюсь на ACTION_UP , mDragImage выглядит ожидаемым (но я, очевидно, теряю способности прокрутки).

A good bitmap

Поскольку перетаскивание начинается с длинного нажатия, ListView предоставляется возможность сделать что-то до того, как произойдет длительное нажатие. Это мое предположение относительно того, почему это происходит. Когда элемент нажимается, он выделяется ListView. Где-то при этом он портит растровое изображение. Поэтому, когда я иду, чтобы получить это, это в странном состоянии (все черное).

Я вижу два варианта исправления, но я не знаю, как это сделать.

  1. Создание изображения с нуля.

  2. Работаю с подсветкой самостоятельно (если в этом проблема).

Второй вариант кажется мне лучшим, за исключением того, что я посмотрел документацию и исходный код и не смог выяснить, как это сделать. Вот некоторые вещи, которые я сделал / попробовал.

  • Я установил setOnItemClickListener (...) и setOnItemSelectedListener (...) с пустым методом (выделение все еще бывает). (Прежде чем кто-либо предлагает setOnClickListener приводит к ошибке во время выполнения.)

  • Я также пытался заставить ListView создать новый элемент (для варианта 2), но не смог найти способ.

  • Потратил 45 минут на просмотр исходного кода и документация пытается определить, где выделение было происходит (я так и не нашел).

Любая помощь, исправляющая это, будет оценена.

(EDIT1 START)

Так что на самом деле я не знаю, работает ли onLongClickListener, я сделал ошибку, прежде чем подумать, что это так. Я пытаюсь настроить его прямо сейчас, обновлю, когда узнаю, работает ли он.

(EDIT1 END)

Последнее редактирование перед публикацией. Я пытался использовать onLongClickListener только сейчас, и изображение хорошее. Я все еще хотел бы знать, есть ли другой путь. То, как я должен использовать onLongClickListener, чтобы заставить вещи работать, ужасно, но это работает. Я также потратил так много времени, пытаясь выяснить это, было бы неплохо узнать ответ. Я все еще хочу иметь возможность изменять / обрабатывать цвет подсветки, по умолчанию оранжевый цвет не очень хорош. Ох и прости насчет длины поста. Я не мог придумать способ сделать его короче, предоставляя всю информацию, которая, по моему мнению, была необходима.

1 Ответ

0 голосов
/ 13 сентября 2012

используйте этот код, он позволяет операции наркотиков и падение в 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;
        }

    }
}
...