不妨碍拖放功能的Android弹出窗口

13
我一直在为安卓打造类似于Nova Launcher的启动器应用程序。我已经设置好了OnItemLongClickListener和OnDragListener。当我长按图标时,会显示一个弹出菜单,如“删除”,“更改图标”等。下面的图片展示了弹出菜单打开时的应用程序进展情况。 enter image description here 问题是,当弹出菜单打开时,拖动可以工作,但是放下却无法工作。似乎一旦弹出菜单打开,我就不能记录x、y位置。而且当执行放下操作时,在logcat中会显示以下消息。
I/ViewRootImpl: Reporting drop result: false

我在OnDragListener中的代码大致如下:

public boolean onDrag(View v, DragEvent event) {
int dragEvent = event.getAction();
switch (dragEvent)
    {
        case DragEvent.ACTION_DRAG_LOCATION:
        //Open popup here; note: its opened only once. popup.show();
        //Log.i("Position x : ", Float.toString(event.getX())); log x or y

        /*code to detect x any y change amount and close the popup
          once user drags the icon little further and app knows that
          user is trying to drag instead of opening the popup
          and hence close the popup. popup.dismiss();
        */

        // other case like ACTION_DROP etx goes after this
    }
 }

但是似乎在弹出窗口打开后,我无法记录x或y的值;并且确定行为是“拖动”还是“弹出窗口打开”的代码也无法运行。

那么我该如何解决这个问题呢?我想在任何情况下,只要拖动量足够大,就关闭弹出窗口,以此判断用户想要拖动。如果不够大,则停止拖动并仅显示弹出窗口。

编辑

我通过同时使用OnTouchListener和OnDragListener解决了弹出窗口的问题。以下是我的OnDragListener代码。

//bottomAppDrawer is a GridView
bottomAppDrawer.setOnDragListener(new View.OnDragListener() {
        @Override
        public boolean onDrag(View v, DragEvent event) {
            int dragEvent = event.getAction();
            LinearLayout draggedItem = (LinearLayout) event.getLocalState(); //dragged LinearLayout

            GridView targetItem = (GridView) v; /* How do i get this drop target as LinearLayout so that i can delete or swap data */

            switch (dragEvent)
            {
                case DragEvent.ACTION_DRAG_LOCATION:
                    if(reset==false) {
                        dragPositionStart = event.getX();
                        reset= true;
                    }

                    if(Math.abs(dragPositionStart - event.getX())>=20) {
                        Log.i("Position close : ", Float.toString(dragPositionStart));

                        if(isPopupOpen) {
                            popupMenu.dismiss();
                            v.startDrag(data, dragShadow, itemView, 0);
                            Toast.makeText(mContext, "popup closed", Toast.LENGTH_SHORT).show();
                            isPopupOpen = false;
                        }

                        reset = false;
                    }

                    break;

                case DragEvent.ACTION_DROP:



                    Toast.makeText(mContext, "drop" + Integer.toString(targetItem.getChildCount()), Toast.LENGTH_SHORT).show();

                    break;


            }

            return true;
        }
    });

现在的问题是,当我将LinearLayout放入“Gridview”中时,我得到的放置目标是“Gridview”。而这个“LinearLayout”是“Gridview”的子元素。我希望放置目标是同一“GridView”内的另一个“LinearLayout”,这样我就可以交换数据或重新排序。如下图所示。

enter image description here


谢谢回复。我使用了OnTouchListner和OnDragListner两个方法解决了问题。现在我可以将GridView作为目标元素进行拖放操作。但是,如何从其父级GridView中获取目标LinearLayout呢? - Redone
2个回答

3

据我所了解,您想要做两件事。1)拖动后重新排序视图。2)在重新排序后更改视图类型。

对于问题1,由于它是网格视图,因此听起来我们实际上只是想重新排列适配器中的数据,并可能更改数据以使其显示不同。但是我们需要确定原始项目的位置和目标位置。

我们可以扩展GridView来实现这一点:

public class DragAndDropGridView extends GridView {

    public void handleMove(int x, int y, int originalPosition) {


        Rect rect = new Rect();
        PushbackAdapter adapter = (PushbackAdapter) getAdapter();

        for (int visiblePosition = getFirstVisiblePosition(); visiblePosition <= getLastVisiblePosition(); visiblePosition++) {
            // TODO verify that there are no edge cases not covered by this

            View view = getChildAt(visiblePosition);
            int left = view.getLeft();
            int top = view.getTop();
            getChildVisibleRect(view, rect, null);
            rect.offsetTo(left, top);
            if (rect.contains(x, y)) {
                // yay the user tried drop the view at this location
                // determine if they wanted to drop it here or after this side
                int centerX = rect.centerX();
                if (x <= centerX) {
                    // we want to drop it here
                    adapter.move(originalPosition, visiblePosition);
                    adapter.notifyDataSetInvalidated();
                    break;
                } else {
                    // we want to drop it behind
                    adapter.move(originalPosition, visiblePosition + 1);
                    adapter.notifyDataSetInvalidated();
                    break;
                }
            }

        }
    }
}  

那么我们就需要调用handleMoveMethod方法。我们在ACTION_DROP方法中执行此操作。
                 case DragEvent.ACTION_DROP:


                        handleMove((int)event.getX(), (int)event.getY(), getPositionForView(draggedItem));


                        Toast.makeText(mContext, "drop" + Integer.toString(targetItem.getChildCount()), Toast.LENGTH_SHORT).show();

                        break;

最后(问题2),看起来您可能想要更改位置处对象的内容或包含它的视图类型。如果您需要不同类型的视图,我建议使用getItemViewType和getItemViewTypeCount方法。例如,以下类似的代码:

    private static class PushbackAdapter extends ArrayAdapter {

    ArrayList<Object> mItems;

    public void move(int originalPosition, int targetPosition){
        // TODO verify that this move logic is correct
        Object item = mItems.remove(originalPosition);
        item.useLinearLayoutType(true);
        mItems.add(targetPosition, item);
    }

    ...

    @Override
    public int getItemViewType(int i) {
        return mItems.get(i).isLeanearLayoutType()? 1 : 0;
    }

可能存在漏洞,请彻底测试


2

使用targetItem.pointToPosition(..)查找在Gridview中拖动的LinerLayout的位置。

使用以下代码滑动LinerLayout:

int i =targetItem.pointToPosition((int)event.getX(), (int)event.getY());
int j = Integer.parseInt(event.getClipData().getItemAt(0).getText().toString());
Collections.swap(targetItem, i, j);//swap Linerlayout
Log.i(TAG, "Swapped " + i+ " with " + j);

代码未经测试。希望它能对您有所帮助。:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接