拖放,ListView和项目意见指出错过ACTION_DRAG_STARTED事件 [英] Drag and drop, ListView and item Views that miss the ACTION_DRAG_STARTED event

查看:333
本文介绍了拖放,ListView和项目意见指出错过ACTION_DRAG_STARTED事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Android上,我使用了一个的ListView ,我希望能够使用拖放重新排序的项目和下降。我知道有不同的实现的拖放列表视图,不过,我想使用的拖放框架,因为未来的 API级别11

它开始很顺利,直到我想我的滚动的ListView 而做一个拖放。因为它是写在下面的例子中,现在,我检查我该列表元素之上,所以如果它的位置不是在 ListView.getLastVisiblePosition() ListView.getFirstVisiblePosition()我用 ListView.smoothScrollToPosition()键查看其他列表项。

这是第一个实现,但它工作得很好。

滚动时,问题出现了:一些元素不回答的拖放事件 - DragEvent.ACTION_DRAG_ENTERED 和其他人 - 当我在它们之上。这是由于ListView控件管理其项目意见的方式:它试图回收项目的看法是不可见的任何更多的

这是所有权利,它的工作原理,但有时 getView() ListAdapter 返回一个新对象。既然是新的,这个对象无缘 DragEvent.ACTION_DRAG_STARTED 所以不回答其他的dragEvent 事件!

下面是一个例子。在这种情况下,如果我开始一拖,用长按拖放一个列表项,如果我拖动它,大部分项目都会有一个绿色的背景,如果我在它们之上;但有些却没有。

有关使他们订阅拖放事件机制,即使他们错过任何想法 DragEvent.ACTION_DRAG_STARTED

  //带我有使用MyViewAdapter一个ListView
// MyListView _myListView = ...
// _myListView.setAdapter(新MyViewAdapter(getActivity(),...));
_myListView.setOnItemLongClickListener(新AdapterView.OnItemLongClickListener(){
    @覆盖
    公共布尔onItemLongClick(适配器视图<>母公司视图中查看,INT位置,长的id){
        DragShadowBuilder shadowBuilder =新View.DragShadowBuilder(视图);
        view.startDrag(空,shadowBuilder,_myListView.getItemAtPosition(位置),0);
        返回true;
    }
});

类MyViewAdapter扩展ArrayAdapter< MyElement> {

    公共MyViewAdapter(上下文的背景下,名单,其中,TimedElement>对象){
        超(背景下,0,对象);
    }

    @覆盖
    公共查看getView(INT位置,查看convertView,ViewGroup中父){
        查看myElementView = convertView;
        如果(myElementView == NULL){
            / *如果code为在此执行,而与拖拽滚动,
             *新的观点并没有关联到当前的拖放事件* /
            Log.d(应用程序,对象创建!);
            //创建视图
            // myElementView = ...
            // prepare拖放
            myElementView.setOnDragListener(新MyElementDragListener());
        }
        // Associates的看法和立场,在ListAdapter,需要拖放
        myElementView.setTag(R.id.item_position,位置);
        //继续prepare视图
        // ...
        返回timedElementView;
    }

    私有类MyElementDragListener实现View.OnDragListener {
        @覆盖
        公共ondrag当布尔(视图V,的dragEvent事件){
            最终诠释行动= event.getAction();
            开关(动作){
            案例DragEvent.ACTION_DRAG_STARTED:
                返回true;
            案例DragEvent.ACTION_DRAG_ENTERED:
                v.setBackgroundColor(Color.GREEN);
                v.invalidate();
                返回true;
            案例DragEvent.ACTION_DRAG_LOCATION:
                INT targetPosition =(整数)v.getTag(R.id.item_position);
                如果(event.getY()&其中; v.getHeight()/ 2){
                    Log.i(应用程序,顶+ targetPosition);
                }
                其他 {
                    Log.i(应用程序,底部+ targetPosition);
                }
                //滚动的ListView的,而这样做的拖放
                如果(targetPosition> _myListView.getLastVisiblePosition() -  2){
                    _myListView.smoothScrollToPosition(targetPosition + 2);
                }
                否则,如果(targetPosition< _myListView.getFirstVisiblePosition()+ 2){
                    _myListView.smoothScrollToPosition(targetPosition-2);
                }
                返回true;
            案例DragEvent.ACTION_DRAG_EXITED:
                v.setBackgroundColor(Color.BLUE);
                v.invalidate();
                返回true;
            案例DragEvent.ACTION_DROP:
            案例DragEvent.ACTION_DRAG_ENDED:
            默认:
               打破;
            }
            返回false;
        }
    }
}
 

解决方案

我没有解决这个问题,回收利用,但我发现了一个可能的解决方法仍然使用拖放放大器;删除框架。我们的想法是改变观点:不是使用 OnDragListener 每个查看在列表中,它可以用来在的ListView 直接

然后这个想法是找到在其顶部项目的手指同时做的拖&放;降,并写了相关的显示code。在 ListAdapter 的ListView 的。诀窍是到后来发现在其顶部的项目来看,我们是,而且那里的下降就完成了。

为了做到这一点,我设置为 ID 通过适配器的的ListView 位置创建的每个视图 - 以 View.setId(),这样我就可以找到它以后使用)的组合ListView.pointToPosition( ListView.findViewById()

由于一拖侦听器示例(这是,我提醒大家,应用在的ListView ),也可以是这样的事情:

  // Initalize您的ListView
私人的ListView _myListView =新的ListView(的getContext());

//开始拖拽时,长按上一个ListView项目
_myListView.setOnItemLongClickListener(新AdapterView.OnItemLongClickListener(){
    @覆盖
    公共布尔onItemLongClick(适配器视图<>母公司视图中查看,INT位置,长的id){
        DragShadowBuilder shadowBuilder =新View.DragShadowBuilder(视图);
        view.startDrag(空,shadowBuilder,_myListView.getItemAtPosition(位置),0);
        返回true;
    }
});

//设置适配器和阻力监听器
_myListView.setOnDragListener(新MyListViewDragListener());
_myListView.setAdapter(新MyViewAdapter(getActivity()));

上面使用//类

私有类MyViewAdapter扩展ArrayAdapter<对象> {
    公共MyViewAdapter(上下文的背景下,名单,其中,TimedElement>对象){
        超(背景下,0,对象);
    }
    @覆盖
    公共查看getView(INT位置,查看convertView,ViewGroup中父){
        查看MyView的= convertView;
        如果(MyView的== NULL){
            //实例化视图
        }
        // Associates的看法和立场,在ListAdapter,需要拖放
        myView.setId(位置);
        返回MyView的;
    }
}


私有类MyListViewDragListener实现View.OnDragListener {
    @覆盖
    公共ondrag当布尔(视图V,的dragEvent事件){
        最终诠释行动= event.getAction();
        开关(动作){
            案例DragEvent.ACTION_DRAG_STARTED:
                返回true;
            案例DragEvent.ACTION_DRAG_DROP:
                //我们拖累其一是在itemPosition顶部的项目
                INT itemPosition = _myListView.pointToPosition((int)的event.getX(),(int)的event.getY());
                //我们甚至可以认为,在itemPosition由于获得/ SETID
                查看ItemView控件= _myListView.findViewById(itemPosition);
                / *如果你尝试同样的事情在ACTION_DRAG_LOCATION,ItemView控件
                 *有时是空;如果你需要这个观点,只返回如果为null。
                 *由于同一个事件,然后被开除,只处理事件
                 * ItemView控件时不为空。
                 *可在ACTION_DRAG_DROP但现在出现更多的问题
                 *我从来没有ItemView控件在这种情况下空。 * /
                //处理的下降,只要你喜欢
                返回true;
         }
    }
}
 

On Android, I use a ListView and I want to be able to reorder its items using drag and drop. I know there are different implementation of a "drag and drop listview", however I want to use the Drag and Drop framework coming since API level 11.

It started very well until I wanted to scroll my ListView while doing a drag and drop. As it is written in the example below, for now, I check on top of which list element I am, so if its position is not between ListView.getLastVisiblePosition() and ListView.getFirstVisiblePosition() I use a ListView.smoothScrollToPosition() to view the other list items.

It is a first implementation but it works quite well.

The problem arises while scrolling: some elements do not answer to the drag and drop events - DragEvent.ACTION_DRAG_ENTERED and the others - when I am on top of them. It is due to the way the ListView manages its item views: it tries to recycle the item views that are not visible any more.

It is all right and it works, but sometimes the getView() of the ListAdapter returns a new object. Since it is new, this object missed the DragEvent.ACTION_DRAG_STARTED so it does not answer to the other DragEvent events!

Here is an example. In this case, if I start a drag and drop with a long click on a list item and if I drag it, the majority of items will have a green background if I am on top of them ; but some don't.

Any idea about making them subscribe to the Drag and drop event mechanism even if they missed DragEvent.ACTION_DRAG_STARTED?

// Somewhere I have a ListView that use the MyViewAdapter
// MyListView _myListView = ...
// _myListView.setAdapter(new MyViewAdapter(getActivity(), ...));
_myListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
    @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
        DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
        view.startDrag(null, shadowBuilder, _myListView.getItemAtPosition(position), 0);
        return true;
    }
});

class MyViewAdapter extends ArrayAdapter<MyElement> {

    public MyViewAdapter(Context context, List<TimedElement> objects) {
        super(context, 0, objects);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View myElementView = convertView;
        if (myElementView == null) {
            /* If the code is executed here while scrolling with a drag and drop,
             * the new view is not associated to the current drag and drop events */
            Log.d("app", "Object created!");
            // Create view
            // myElementView = ...
            // Prepare drag and drop
            myElementView.setOnDragListener(new MyElementDragListener());
        }
        // Associates view and position in ListAdapter, needed for drag and drop
        myElementView.setTag(R.id.item_position, position);
        // Continue to prepare view
        // ...
        return timedElementView;
    }

    private class MyElementDragListener implements View.OnDragListener {
        @Override
        public boolean onDrag(View v, DragEvent event) {
            final int action = event.getAction();
            switch(action) {
            case DragEvent.ACTION_DRAG_STARTED:
                return true;
            case DragEvent.ACTION_DRAG_ENTERED:
                v.setBackgroundColor(Color.GREEN);
                v.invalidate();
                return true;
            case DragEvent.ACTION_DRAG_LOCATION:
                int targetPosition = (Integer)v.getTag(R.id.item_position);
                if (event.getY() < v.getHeight()/2 ) {
                    Log.i("app", "top "+targetPosition);        
                }
                else {
                    Log.i("app", "bottom "+targetPosition);
                }
                // To scroll in ListView while doing drag and drop
                if (targetPosition > _myListView.getLastVisiblePosition()-2) {
                    _myListView.smoothScrollToPosition(targetPosition+2);
                }
                else if (targetPosition < _myListView.getFirstVisiblePosition()+2) {
                    _myListView.smoothScrollToPosition(targetPosition-2);
                }
                return true;
            case DragEvent.ACTION_DRAG_EXITED:
                v.setBackgroundColor(Color.BLUE);
                v.invalidate();
                return true;
            case DragEvent.ACTION_DROP:
            case DragEvent.ACTION_DRAG_ENDED:
            default:
               break;
            }
            return false;
        }       
    }
}

解决方案

I did not solved this recycling problem, but I found a possible workaround still using the Drag & Drop framework. The idea is to change of perspective: instead of using a OnDragListener on each View in the list, it can be used on the ListView directly.

Then the idea is to find on top of which item the finger is while doing the Drag & Drop, and to write the related display code in the ListAdapter of the ListView. The trick is then to find on top of which item view we are, and where the drop is done.

In order to do that, I set as an id to each view created by the adapter its ListView position - with View.setId(), so I can find it later using a combination of ListView.pointToPosition() and ListView.findViewById().

As a drag listener example (which is, I remind you, applied on the ListView), it can be something like that:

// Initalize your ListView
private ListView _myListView = new ListView(getContext());

// Start drag when long click on a ListView item
_myListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
    @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
        DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
        view.startDrag(null, shadowBuilder, _myListView.getItemAtPosition(position), 0);
        return true;
    }
});

// Set the adapter and drag listener
_myListView.setOnDragListener(new MyListViewDragListener());
_myListView.setAdapter(new MyViewAdapter(getActivity()));

// Classes used above

private class MyViewAdapter extends ArrayAdapter<Object> {
    public MyViewAdapter (Context context, List<TimedElement> objects) {
        super(context, 0, objects);
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View myView = convertView;
        if (myView == null) {
            // Instanciate your view
        }
        // Associates view and position in ListAdapter, needed for drag and drop
        myView.setId(position);
        return myView;
    }
}


private class MyListViewDragListener implements View.OnDragListener {
    @Override
    public boolean onDrag(View v, DragEvent event) {
        final int action = event.getAction();
        switch(action) {
            case DragEvent.ACTION_DRAG_STARTED:
                return true;
            case DragEvent.ACTION_DRAG_DROP:
                // We drag the item on top of the one which is at itemPosition
                int itemPosition = _myListView.pointToPosition((int)event.getX(), (int)event.getY());
                // We can even get the view at itemPosition thanks to get/setid
                View itemView = _myListView.findViewById(itemPosition );
                /* If you try the same thing in ACTION_DRAG_LOCATION, itemView
                 * is sometimes null; if you need this view, just return if null.
                 * As the same event is then fired later, only process the event
                 * when itemView is not null.
                 * It can be more problematic in ACTION_DRAG_DROP but for now
                 * I never had itemView null in this event. */
                // Handle the drop as you like
                return true;
         }
    }
}

这篇关于拖放,ListView和项目意见指出错过ACTION_DRAG_STARTED事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆