Android GridView 通过拖放重新排序元素

Posted

技术标签:

【中文标题】Android GridView 通过拖放重新排序元素【英文标题】:Android GridView reorder elements via Drag and Drop 【发布时间】:2011-11-01 01:51:07 【问题描述】:

我正在处理的应用程序中有一个 GridView。我希望能够通过拖放对 GridView 中的项目进行重新排序。我在 ListViews 上找到了很多帮助,但在 GridViews 上却一无所获。我想在这个启动器应用程序http://www.youtube.com/watch?v=u5LISE8BU_E&t=5m30s 中实现类似的行为。有什么想法吗?

【问题讨论】:

嘿,你解决了这个问题吗,我有一个类似的问题要解决。你能分享一些代码sn-p吗? Chandra,如果还需要,请看下面我的回答。 你能把其中一个标记为答案吗?这将帮助许多寻求帮助的人快速找到答案。 【参考方案1】:

如果您不解决此问题,我将提供我的代码。但它适用于android 3.0及更高版本,因为我使用android drag-n-drop framework

grid = (GridView) findViewById(R.id.grid);
grid.setAdapter(new DragGridAdapter(items, getActivity()));

....

grid.setOnTouchListener(new View.OnTouchListener() 

        @Override
        public boolean onTouch(View v, MotionEvent event) 

            if (event.getAction() == MotionEvent.ACTION_DOWN) 
                GridView parent = (GridView) v;

                int x = (int) event.getX();
                int y = (int) event.getY();

                int position = parent.pointToPosition(x, y);
                if (position > AdapterView.INVALID_POSITION) 

                    int count = parent.getChildCount();
                    for (int i = 0; i < count; i++) 
                        View curr = parent.getChildAt(i);
                        curr.setOnDragListener(new View.OnDragListener() 

                            @Override
                            public boolean onDrag(View v, DragEvent event) 

                                boolean result = true;
                                int action = event.getAction();
                                switch (action) 
                                case DragEvent.ACTION_DRAG_STARTED:
                                    break;
                                case DragEvent.ACTION_DRAG_LOCATION:
                                    break;
                                case DragEvent.ACTION_DRAG_ENTERED:
                                    v.setBackgroundResource(R.drawable.shape_image_view_small_gallery_selected);
                                    break;
                                case DragEvent.ACTION_DRAG_EXITED:
                                    v.setBackgroundResource(R.drawable.shape_image_view_small_gallery_unselected);
                                    break;
                                case DragEvent.ACTION_DROP:
                                    if (event.getLocalState() == v) 
                                        result = false;
                                     else 
                                        View droped = (View) event.getLocalState();
                                        GridItem dropItem = ((DragGridItemHolder) droped.getTag()).item;

                                        GridView parent = (GridView) droped.getParent();
                                        DragGridAdapter adapter = (DragGridAdapter) parent.getAdapter();
                                        List<GridItem> items = adapter.getItems();

                                        View target = v;
                                        GridItem targetItem = ((DragGridItemHolder) target.getTag()).item;
                                        int index = items.indexOf(targetItem);
                                        items.remove(dropItem);
                                        items.add(index, dropItem);
                                        adapter.notifyDataSetChanged();
                                    
                                    break;
                                case DragEvent.ACTION_DRAG_ENDED:
                                    v.setBackgroundResource(R.drawable.shape_image_view_small_gallery_unselected);
                                    break;
                                default:
                                    result = false;
                                    break;
                                
                                return result;
                            
                        );
                    

                    int relativePosition = position - parent.getFirstVisiblePosition();


                    View target = (View) parent.getChildAt(relativePosition);

                    DragGridItemHolder holder = (DragGridItemHolder) target.getTag();
                    GridItem currentItem = holder.item;
                    String text = currentItem.getFile().getAbsolutePath();

                    ClipData data = ClipData.newPlainText("DragData", text);
                    target.startDrag(data, new View.DragShadowBuilder(target), target, 0);
                
            
            return false;

和 DragGridAdapter

public class DragGridAdapter extends BaseAdapter
private Context context;
private List<GridItem> items;

public DragGridAdapter(List<GridItem> items, Context context)
    this.context = context;
    this.items = items;


@Override
public int getCount() 
    return items.size();


@Override
public Object getItem(int position) 
    return items.get(position);


@Override
public long getItemId(int position) 
    return position;


@Override
public View getView(int position, View convertView, ViewGroup parent) 
    DragGridItemHolder holder;
    if (convertView == null) 
        holder = new DragGridItemHolder();

        ImageView img = new ImageView(context);
        holder.image = img;
        convertView = img;
        convertView.setTag(holder);
     else 
        holder = (DragGridItemHolder) convertView.getTag();
    
    holder.item = items.get(position);
    holder.image.setImageBitmap(items.get(position).getBitmap());
    return convertView;


public List<GridItem> getItems() 
    return items;

希望对你有帮助

【讨论】:

View.OnDragListener 是 Android-3.0 的一部分,上面的代码不起作用 thnx 为例.. 我只有一个错误.. 第一次我将一个项目拖到 gridview 中的其他项目上时,项目的图像视图源丢失了。当我放下它时,源将被修复。在第一次拖放活动后,一切正常。对这种奇怪的行为有什么想法吗? 对不起,我没有回答你的问题。也许这是您设备上的错误 @Dmitriy_Boichenko 。我喜欢你的代码,但如果你提供 DragGridItemHolder 的代码会更好 @Abhilash 不幸的是我今天没有这个代码。但我记得这很简单。请考虑标准android的viewholder模式【参考方案2】:

我的拖放网格视图版本https://github.com/askerov/DynamicGrid。 它扩展了原始 GridView,支持拖放以重新排序项目,如果拖出屏幕则自动滚动。它在 3.0+ api 上完全可用,但支持 2.2 和 2.3 有限制(无动画)。

【讨论】:

感谢您抽出宝贵时间进行编辑,但您似乎有点忽略了重点。这基本上仍然是一个仅链接的答案;不鼓励这样的答案。这有两个主要原因:直接回答用户的具体问题通常更有帮助,即使是好的资源的链接有时也会变坏。详情请参阅this meta post。 Alex,您的网格不能很好地处理两列,特别是在从下到上拖动时。你能帮忙吗?【参考方案3】:

看看 thquinn 的 DraggableGridView,这是针对 Android 2.2(API 级别 8)开发的。 希望这可以帮助某人:)

【讨论】:

这是很酷的解决方案。但它没有任何滚动监听器。所以如果你有很多图片,你可以取出内存不足错误 不是AdapterView,所以没有实现视图回收【参考方案4】:

使用拖放框架,而不是循环子项和设置 Draglistener,我使用作为网格项目布局容器的 DragableLinearLayout,它扩展了 LinearLayout 并实现了 onDragEvent(DragEvent) 方法。

因此您可以像往常一样使用适配器填充网格,并且大部分拖放代码都在 DragableLinearLayout 的 onDragEvent 上

public class DragableLinearLayout extends LinearLayout 


    public DragableLinearLayout(Context context, AttributeSet attrs,
            int defStyle) 
        super(context, attrs, defStyle);

    

    public DragableLinearLayout(Context context, AttributeSet attrs) 
        super(context, attrs);

    

    public DragableLinearLayout(Context context) 
        super(context);

    

    @Override
    public boolean onDragEvent(DragEvent event) 
        //in wich grid item am I?
        GridView parent = (GridView) getParent();
        Object item = parent.getAdapter().getItem(
                parent.getPositionForView(this));
            //if you need the database id of your item...
        Cursor cur = (Cursor) item;
        long l_id = cur.getLong(cur.getColumnIndex("youritemid"));

        switch (event.getAction()) 
        case DragEvent.ACTION_DRAG_STARTED:

            return true;
        case DragEvent.ACTION_DRAG_ENTERED:

            setBackgroundColor(Color.GREEN);
            invalidate();
            return true;
        case DragEvent.ACTION_DRAG_EXITED:
            setBackgroundColor(Color.WHITE);
            invalidate();
            return false;
        case DragEvent.ACTION_DROP:
ClipData cd = event.getClipData();
            long l_id_start = Long.valueOf(cd.getItemAt(0).getText()
                    .toString());
            //
            Toast.makeText(getContext(), "DROP FROM " + l_id_start
                    + " TO " + l_id, Toast.LENGTH_LONG);
            //do your stuff  
                    ........
                    //the db requery will be on the onDragEvent.drop of the container
                    //see the listener


            return false;
        case DragEvent.ACTION_DRAG_ENDED:
            setBackgroundColor(Color.WHITE);
            invalidate();
            //
            return false;

        

        return true;

    






private View.OnDragListener listenerOnDragEvent = new View.OnDragListener() 

    public boolean onDrag(View v, DragEvent event) 
        // Defines a variable to store the action type for the incoming
        // event
        final int action = event.getAction();
        switch (action) 

        case DragEvent.ACTION_DROP:

            // REQUERY
            updateDbView();
            return false;
            // break;

        
        return true;
    
;

【讨论】:

【参考方案5】:

几个月前,Google 最近发布了几个代码实验室。 https://codelabs.developers.google.com/codelabs/android-training-adaptive-layouts/index.html?index=..%2F..%2Fandroid-training#0

您可以在此处查看解决方案 https://github.com/google-developer-training/android-fundamentals-apps-v2/tree/master/MaterialMe-Resource

他们使用可移动卡片制作网格布局,这些卡片可以使用 itemTouchHandler 拖放到布局中的任何位置。

关于如何进行拖放的更详细代码是here 您需要查看任务 3:使您的 CardView 可滑动、可移动和可点击部分

【讨论】:

【参考方案6】:

我最近找到了一个解决方案,它的作者在这里花了很长时间http://blahti.wordpress.com/2012/03/03/improved-drag-drop-for-gridview/ 之前有 4 篇相关的博客文章更详细地解释了那里发生的事情。

【讨论】:

【参考方案7】:

可能 PagedDragDropGrid 项目是你需要的:https://github.com/mrKlar/PagedDragDropGrid

它提供自定义ViewGroup(类似于GridView),具有平滑重新排序的功能(就像您的视频中一样)。可能需要进行一些自定义,但这是值得的。

希望对你有所帮助。

【讨论】:

在上述库中,拖拽时无法垂直滚动...还有其他选项吗?? 是的,如果你想滚动,你应该对它进行一些自定义。尝试在内部使用 DragDropGrid(不是 PagedDragDropGrid)实现 ScrollView,它对我有帮助。抱歉,我现在没有时间找到确切的解决方案【参考方案8】:

这是来自 h6ah4i 的 library,它利用 Recycler 视图提供具有重新排序功能的拖放网格视图。

【讨论】:

以上是关于Android GridView 通过拖放重新排序元素的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Jquery 重新排序 Gridview 行?

通过拖放可重新排序的 ListView

QML:如何通过拖放重新排序中继器项目?里面有一些工作代码

通过拖放功能重新排序 Xamarin 表单中的 ListView

WPF 中的 ItemsControl 是不是有一个好的解决方案,可以通过垂直拖放对其元素进行重新排序? [关闭]

UICollectionView 与 SwiftUI + 拖放重新排序可能吗?