android 为什么视图被拖动时ViewDragHelper重置到布局()上的原始位置?

4urapxun  于 2023-04-04  发布在  Android
关注(0)|答案(6)|浏览(116)

我在我的游戏中实现了一个自定义的可扩展动作栏。我使用ViewDragHelper来处理栏视图的拖动和投掷,它包含在我子类化的LinearLayout中,以附加ViewDragHelper。
以下链接对实现这一目标有很大帮助:

我遇到的唯一问题是父LinearLayout的layout()的行为:在每次调用layout()/onLayout()时,子可拖动/可扩展操作栏重置到其原始位置(在XML布局中设置的位置)。

为什么

(In我的经验layout()从来没有弄乱已经移动的视图的位置)

yzxexxkh

yzxexxkh1#

我使用的解决方法是在每次拖动操作之后记录视图的偏移量,并在onLayout()中重新应用它们,例如。

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);
}
v8wbuo2f

v8wbuo2f2#

我在玩DrawerLayout(一种具有可拖动抽屉的布局)时遇到了同样的问题,最初也很惊讶。
从阅读源代码来看,ViewDragHelper似乎使用了offsetLeftAndRight()/ offsetTopAndBottom()来可视化地移动任何捕获和拖动的视图,大概可以使用到API 4。

回答您的问题:布局并没有扰乱视图的位置,只是通过使用offset..()函数,ViewDragHelper并没有为您确定视图的位置。

因此,在视图的onLayout中,您必须考虑使用ViewDragHelper完成的任何视觉定位。例如,在DrawerLayout中,这是通过一个简单的'lp.onScreen'百分比字段进行内部跟踪的,并且在其onLayout()中,相应的移位被添加到任何拖动的抽屉子视图的child.layout()调用中的参数中。

mwyxok5s

mwyxok5s3#

对不起,我不知道为什么layout()被调用,但也许你的视图之一,如ViewPager调用getView(View parentView)。
无论如何,我假设你使用的是像this blog描述的DragViewHelper。所以有一个相关的方法 OnLayoutChangeListener 将布局移动到前一个位置。

  • 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);
                }
            }
        });

因此,当调用layout()时,方法isMoving应该被启动-但它看起来并没有。因为DragViewHelper处于STATE_IDLE状态。我感谢user3452758的回答技巧。您需要在方法computeScroll()中设置自定义滑动布局中的标志,以便在正确的时间启用isMoving == true。

  • 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);
    }
/* ......... */
}
zdwk9cvp

zdwk9cvp4#

这个答案有点晚了,但是当requestLayout()被调用时,你的内容被中继到它的原始位置的原因是(很可能)因为你在onLayout()中的代码没有考虑到视图位置的变化。
在flavienlaurent示例中,他的布局代码将可拖动视图定位在左侧位置0,但顶部位置使用全局作用域变量mTop,该变量在回调onViewPositionChanged(android.view.View,int,int,int,int)中修改为top值。您需要跟踪更改的值并将其纳入onLayout()中。
将位置更改与拖动视图绑定在一起的一种简单方法是将它们存储在LayoutParams中,并在布局代码中检索主题。如果您希望在布局中有多个可拖动的视图,这一点尤其有用。此模式的一个很好的示例是Android SDK DrawerLayout的source code(NavigationDrawer)。看一下类的底部,在自定义LayoutParams内部类。它有一个类型为float的onScreen字段。此变量用于存储位置差异(相对于视图原点),并在setDrawerViewOffset()中更新。
另一种模式是执行user@user3452758建议的操作,但是如果您希望有多个可拖动的视图,这将很难跟踪。

toiithl6

toiithl65#

你只需要添加这样的代码:

@Override
public void onViewPositionChanged(@NonNull View changedView, int left, int top,
                                              int dx, int dy) {
        int right = getMeasuredWidth() - left - changedView.getMeasuredWidth();
        int bottom = getMeasuredHeight() - top - changedView.getMeasuredHeight();
        setLayout(left, top, right, bottom, changedView);
        super.onViewPositionChanged(changedView, left, top, dx, dy);
}
private void setLayout(int l, int t, int r, int b, View dragView) {
        MarginLayoutParams layoutParams = (MarginLayoutParams) dragView.getLayoutParams();
        layoutParams.setMargins(l, t, r, b);
        dragView.setLayoutParams(layoutParams);
}
hfwmuf9z

hfwmuf9z6#

1、在viewDragHelper中,记录更改时的pos

override fun onViewPositionChanged(
                changedView: View,
                left: Int,
                top: Int,
                dx: Int,
                dy: Int
            ) {
                if (changedView == mFloatView) {
                    mLastDragPos.x = width - mFloatView.right
                    mLastDragPos.y = height - mFloatView.bottom
                }
            }

 override fun onViewReleased(releasedChild: View, xvel: Float, yvel: Float) {
// 2、change when onViewReleased 
                if (releasedChild == mFloatView) {
                    releasedChild.let {
                        LogUtils.d(TAG, "onViewReleased")
                        val mr = width - it.right
                        val mb = height - it.bottom
                             ViewUtils.setMargins(
            mFloatView,
            0,
            0,
           mr ,
           mb ,
        )
                    }
                }
            }

int父视图,在onLayout之前设置子位置

var mLastDragPos = Point(0, 0)
    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
// 3、set child pos first  
        if (mLastDragPos.x > 0 || mLastDragPos.y > 0) {
             ViewUtils.setMargins(
            mFloatView,
            0,
            0,
           mLastDragPos.x,
           mLastDragPos.y,
        )
            mLastDragPos.set(0, 0)
        }
        super.onLayout(changed, left, top, right, bottom)
    }

相关问题