Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement drag item to the screen edge feature

I need to implement current tab change when the item is dragged near screen edge (left or right).

There's no problem with drag N drop feature and current tab switches. I just can't handle properly this event - when dragged item is near the edge of the screen.

How would you do that?

What I tried:

<FrameLayout
    android:id="@+id/left_toggle_area"
    android:layout_width="30dp"
    android:layout_height="fill_parent"
    android:layout_alignParentStart="true"
    android:layout_alignParentLeft="true">
</FrameLayout>

<FrameLayout
    android:id="@+id/right_toggle_area"
    android:layout_width="30dp"
    android:layout_height="fill_parent"
    android:layout_alignParentEnd="true"
    android:layout_alignParentRight="true">
</FrameLayout>

<GridView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    ... />

The idea is to change current tab programmatically when the dragged item at toggle area.

But the event DragEvent.ACTION_DRAG_ENTERED on that area never occurs since (I believe so) the GridView takes the entire screen and simply overlaps with toggle areas.

I've tried already bring those areas to the front when the drag start event is triggered. But that doesn't work for some reason.

FrameLayout toggleView = (FrameLayout) v;
        toggleView.bringToFront();
        toggleView.requestLayout();
        toggleView.invalidate();

If anyone faced such problem please share your experience. Or perhaps there's another better solution for this. Please give me a hint.

like image 455
AnZ Avatar asked Dec 18 '15 14:12

AnZ


People also ask

What's the name of the interface that should be implemented to drag on screen objects?

DragShadowBuilder object, so the callbacks can access it to construct the drag shadow. The view doesn't have to be the View (if any) that the user selected to start the drag operation. If you use this constructor, you don't have to extend View. DragShadowBuilder or override its methods.


2 Answers

The current solution I came to which works (more or less) is to use TextView nested in PagerTabStrip title as drag area. The biggest con in this approach is that those toggle areas are too small for some reason. ENTER event triggers only at those areas.

enter image description here

I belive this solution would be good with oldschool tabs. Therefore here's code. Might be useful for someone.

1). Parse TextViews from PagerTabStrip

private ArrayList <TextView> titles;

...

public void setPager(PagerTabStrip mPagerTabStrip) {
    for (int i = 0; i < mPagerTabStrip.getChildCount(); ++i) {
        View nextChild = mPagerTabStrip.getChildAt(i);
        if (nextChild instanceof TextView) {
            TextView textViewToConvert = (TextView) nextChild;

            if (textViewToConvert.getText().toString().contains(TabsFragment.TAG_MONITOR)){ // I take only few tabs 
                if (!titles.contains(textViewToConvert)) {
                    titles.add(textViewToConvert);
                }
            }
        }
    }

}

public ArrayList<TextView> getTitles() {
    return titles;
}

After this operation I have 3 TextViews that need drag'n'drop navigation.

2). Pass your list of TextViews to class where you implement DragListeners:

 public void setUpDragNDropTabMoves(ArrayList<TextView> views) {
    if (views != null) {
        for (TextView tv : views){
            tv.setOnDragListener(TabToggleDragListener);
        }
    }
 }

3). And the listener itself

 View.OnDragListener TabToggleDragListener = new View.OnDragListener() {
    @Override
    public boolean onDrag(View v, DragEvent event) {
        TextView toggleView = (TextView) v;
        String title = toggleView.getText().toString();

        switch (event.getAction()) {
            case DragEvent.ACTION_DROP:
                ...
                break;

            case DragEvent.ACTION_DRAG_ENTERED:
                int tabId = -1;
                //define which tab is destination
                if (title.contains(TabsFragment.TAG_MONITOR1)){
                    tabId = 1;
                } else if (title.contains(TabsFragment.TAG_MONITOR2)){
                    tabId = 2;
                } else if (title.contains(TabsFragment.TAG_MONITOR3)){
                    tabId = 3;
                }

                //send event to tab host fragment/activity and perform tab change to passed index
                //you may use EventBus or Otto for this. I used Local Broadcast
                BroadcastSender.sendMessageToUI(getContext(),
                        BroadcastSender.TABS_FRAGMENT,
                        BroadcastActions.TF_TOGGLE_TAB,
                        tabId 
                );
                break;
            case DragEvent.ACTION_DRAG_EXITED:
                ...
                break;

            case DragEvent.ACTION_DRAG_ENDED:
                ...
                break;
        }
        return true;
    }
};
like image 198
AnZ Avatar answered Oct 06 '22 15:10

AnZ


I take it the parent of the three views (toggle areas and the GridView) is a RelativeLayout. First of all, replace the GridView, to be the first child of its parent. Leave the rest of the layout exactly as they are.

Secondly, in order to catch drop event with View.OnDragListener interface, you need to set the minimum SDK version to 11 in the AndroidManifest.xml. After that you can pass the listener to the toggle areas:

View.OnDragListener dragListener = new View.OnDragListener() {
    @Override
    public boolean onDrag(View v, DragEvent event) {
    // you can check for event.ACTION_DRAG_ENTERED here 
    return true;
    }
};
FrameLayout leftToggleArea = (FrameLayout) findViewById(R.id.left_toggle_area);
leftToggleArea.setOnDragListener(dragListener);

In case you still have problems, post more of your code.

like image 31
mehrdadjg Avatar answered Oct 06 '22 16:10

mehrdadjg