在ListView中处理显示的视图

时间:2017-03-21 16:19:28

标签: java android listview animation

我正在尝试在Android上实现一个ListView子类,允许手动重新排序其内容。作为该过程的第一步,我将“长按”监听器附加到ListView绘制的每个子视图,如:

@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {

    //listen for long-click events as the trigger for reordering the list
    child.setOnLongClickListener(new OnLongClickListener() {
        @Override
        public boolean onLongClick(View view) {
            if (! SortableListView.this.isSortEnabled()) {
                return false;
            }

            //capture some state about the initial position of the list
            //[...]

            //note the view that we're moving around
            dragView = view;
            dragView.setBackgroundColor(Color.argb(128, 200, 200, 255));
            dragView.setElevation(2.0f);

            //disable the list's default scrolling behavior
            SortableListView.this.requestDisallowInterceptTouchEvent(true);

            return true;
        }
    });

    //also add a general touch listener for when we're actually sorting things
    child.setOnTouchListener(this);

    return super.drawChild(canvas, child, drawingTime);
}

然后还有一个普通的'touch'事件监听器,用于在我们实际拖动某些东西时处理ListView的更新:

@Override
public boolean onTouch(final View view, MotionEvent motionEvent) {
    //XXX:  (0,0) is at the top-left of the screen
    if (dragView == null) {
        //not dragging anything yet, just note the touch location for if/when we start
        dragStartX = motionEvent.getRawX();
        dragStartY = motionEvent.getRawY();

        return false;
    }
    if (view != dragView) {
        //not interested in this event
        return false;
    }

    if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
        dragCurrentX = motionEvent.getRawX();
        dragCurrentY = motionEvent.getRawY();

        //make the cell the user tapped follow their touch
        dragView.animate().xBy(dragCurrentX - dragStartX).yBy(dragCurrentY - dragStartY).setDuration(0).start();

        //now look at how far the view has moved, and reposition the displayed views if necessary (this is the broken part)
        //[...]

        return true;
    }
    if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
        //done, put the view back
        dragView.setTranslationX(0);
        dragView.setTranslationY(0);
        dragView.setElevation(0.0f);
        dragView = null;

        //enable scrolling
        this.requestDisallowInterceptTouchEvent(false);
        return true;
    }

    return false;
}

问题在于,虽然拖动视图通常会起作用,但我还想在移动时将其“占位符”点向上/向下移动。也许最好用一些照片来说明这一点:

初始状态 Initial State

开始拖动后 After starting to drag

拖动位后 enter image description here

因此,当用户将其拖动到列表中时,目标是让“占位符”单元格跟随浮动蓝色单元格。我试图通过在浮动单元移动时操纵列表的子视图来完成此操作,但到目前为止没有任何工作。

我当前的代码(即“破碎的部分”)尝试这个:

int dragViewIndex = this.indexOfChild(dragView);
if (dragView.getTranslationY() > dragView.getHeight() && dragViewIndex < this.getChildCount() - 1) {
    //move down 1 spot (towards bottom of list), reduce tY by height
    this.detachViewFromParent(dragView);
    this.attachViewToParent(dragView, dragViewIndex + 1, dragView.getLayoutParams());

    dragView.setTranslationY(dragView.getTranslationY() - dragView.getHeight());
}
if (dragView.getTranslationY() < -1 * dragView.getHeight() && dragViewIndex > 0) {
    //move up 1 spot (towards top of list), reduce tY by height
    this.detachViewFromParent(dragView);
    this.attachViewToParent(dragView, dragViewIndex - 1, dragView.getLayoutParams());

    dragView.setTranslationY(dragView.getTranslationY() + dragView.getHeight());
}

但这似乎几乎没有任何结果(除了混淆列表之外;正如您在第三张图片中看到的那样,拖动的条目实际上在UI中显示两次)。

是否有一种正确/优雅的方式让空单元格跟踪列表中的内容?或者我是否需要查看更激烈的措施,例如翻译列表中的每个其他单元格以适应空白空间的位置?

1 个答案:

答案 0 :(得分:0)

看来我在正确的轨道上有关于翻译列表中其他单元格的想法。这段代码符合我的要求:

if (dragView.getTranslationY() - dragView.getHeight() * numShifts > dragView.getHeight() && dragViewIndex < this.getChildCount() - 1) {
    //move down 1 spot (towards bottom of list)
    View displacedView = this.getChildAt(dragViewIndex + 1);
    if (displacedView.getTranslationY() != 0) {
        displacedView = this.getChildAt(dragViewIndex);
        displacedView.animate().translationY(0).setDuration(100).start();
    }
    else {
        displacedView.animate().yBy(-1 * view.getHeight()).setDuration(100).start();
    }
    dragViewIndex++;
    numShifts++;
}
else if (dragView.getTranslationY() - dragView.getHeight() * numShifts < -1 * dragView.getHeight() && dragViewIndex > 0) {
    //move up 1 spot (towards top of list)
    View displacedView = this.getChildAt(dragViewIndex - 1);
    if (displacedView.getTranslationY() != 0) {
        displacedView = this.getChildAt(dragViewIndex);
        displacedView.animate().translationY(0).setDuration(100).start();
    }
    else {
        displacedView.animate().yBy(view.getHeight()).setDuration(100).start();
    }
    dragViewIndex--;
    numShifts--;
}

可能会被清理一下。但基本上你可以看到细胞被拖动了多远,每次它上下移动一个完整的增量(根据它的高度),你可以根据需要上下移动细胞。这会创建“占位符”单元格在列表中移动的外观。

请注意,此代码仅在列表中的每个项目具有相同高度时才有效。这对我的用例来说已经足够了,但不适用于每个用例。