Why is view dragged with ViewDragHelper reset to its original position on layout()?

与世无争的帅哥 提交于 2019-11-28 23:33:53

The workaround I'm using is to record the view's offsets after each drag operation, and reapply them in onLayout(), e.g.

View mVdhView;
int mVdhXOffset;
int mVdhYOffset;

@Override
public void computeScroll() {
    if (dragHelper.continueSettling(true)) {
        postInvalidateOnAnimation();
    } else {
        mVdhXOffset = mVdhView.getLeft();
        mVdhYOffset = mVdhView.getTop();
    }
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);

      // Reapply VDH offsets
    mVdhView.offsetLeftAndRight(mVdhXOffset);
    mVdhView.offsetTopAndBottom(mVdhYOffset);
}

I encountered the same issue when playing with a DrawerLayout (a layout having draggable drawers), and was initially also surprised.

From reading the source, it seems ViewDragHelper uses offsetLeftAndRight() / offsetTopAndBottom() to visually shift any captured and dragged View, presumably to be able to be usable down to API 4.

To answer your question: layout is NOT messing with the positions of the View, it is just that by using the offset..() functions that ViewDragHelper is not hammering down the positioning of the View for you.

So, in your view's onLayout you'll have to account for any visual positioning done using ViewDragHelper. For example, in DrawerLayout this is internally tracked through a simple 'lp.onScreen' percentage field and in its onLayout() the corresponding shift is added to the parameters in a call to child.layout() for any dragged drawer child View.

This answer is a little late, but the reason why your content gets relayed out to its original position when requestLayout() is called, is (mostly likely) because your code in onLayout() is not taking in to account the changes to the views position.

In flavienlaurent example,his layout code positions the draggable view at a left position of 0 but the top position uses the global scoped variable mTop, which is modified inside of callback onViewPositionChanged(android.view.View, int, int, int, int) to the value of top. You need to track the values of your change and take them into to account inside of onLayout().

A simple way of keeping the position changes bundled with the dragging view is to store them inside of the LayoutParams, and retrieve theme inside of the layout code. This is especially useful if you want to have several draggable views inside of your layout. A excellent example of this pattern is the source code of the Android SDK DrawerLayout (NavigationDrawer). Take a look at the bottom of the class, at the custom LayoutParams inner class. It has a field onScreen that is type float. This variable is used to store the difference in position (relative to the view origin) of the draggable view, and is updated in setDrawerViewOffset().

Another pattern would be to do what user @user3452758 recommended, but again this will get difficult to keep track of if your ever desire to have several draggable views.

murt

Sorry I don't know why the layout() is called, but maybe one of your view like ViewPager call getView(View parentView).

Anyway I assume that your using DragViewHelper like this blog describes. So there is the revelant method OnLayoutChangeListener that moves layout to previous position.

MyActivity.java

//parent
rlOuterView = (SlidingLayout) findViewById(R.id.sl_sliding_view);
//child - slides up/down
rlAppBarFooter = (RelativeLayout) findViewById(R.id.rl_footer);
        rlMainView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                if( rlOuterView.isMoving() ){
                    v.setTop(oldTop);
                    v.setBottom(oldBottom);
                    v.setLeft(oldLeft);
                    v.setRight(oldRight);
                }
            }
        });

So when is called layout() the method isMoving should be fired up - but it looks like it doesn't. Because DragViewHelper is in STATE_IDLE. I thank for trick to answer by user3452758 . You need to set flag in you custom sliding layout in method computeScroll() , that enable isMoving == true in the right time.

SlidingLayout.java

public class SlidingLayout extends RelativeLayout {
/* ........... */
@Override
    public void computeScroll() { // needed for automatic settling.
        if (mDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
            preventPositionReset = false;
        }else{
            preventPositionReset = true;
        }
    }

    public boolean isMoving() {
        return (/*mDraggingState == ViewDragHelper.STATE_IDLE ||*/
                preventPositionReset ||
                mDraggingState == ViewDragHelper.STATE_DRAGGING ||
                mDraggingState == ViewDragHelper.STATE_SETTLING);
    }
/* ......... */
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!