Почему представление перетаскивается с помощью ViewDragHelper reset в исходное положение на макете()?

Я использую пользовательскую расширяемую панель действий в своей игре. Я использую ViewDragHelper, чтобы обрабатывать перетаскивание и покачивание представления на панели, которое содержится в подклассе LinearLayout я для присоединения ViewDragHelper.

Следующие ссылки оказали большую помощь для достижения этой цели:

Единственная проблема, с которой я сталкиваюсь, - это поведение layout() моего родительского LinearLayout: при каждом вызове layout()/onLayout() дочерняя перетаскиваемая/расширяемая панель действий reset в исходное положение (тот, который установлен в макете XML).

Почему это?

(По моему опыту layout() никогда не смешивается с позициями, которые уже были перемещены)

Ответ 1

Обходной путь, который я использую, заключается в записи смещений вида после каждой операции перетаскивания и повторного применения их в 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);
}

Ответ 2

Я столкнулся с той же проблемой при игре с DrawerLayout (макет с перетаскиваемыми ящиками) и был также удивлен.

Из чтения источника кажется, что ViewDragHelper использует offsetLeftAndRight()/offsetTopAndBottom(), чтобы визуально сдвинуть любой захваченный и перетаскиваемый View, предположительно, чтобы быть в состоянии использоваться до API 4.

Чтобы ответить на ваш вопрос: макет НЕ возится с позициями View, это просто с помощью функций offset..(), которые ViewDragHelper не забивает позиционирование View для вас.

Итак, по вашему мнению onLayout вам придется учитывать любое визуальное позиционирование, сделанное с помощью ViewDragHelper. Например, в DrawerLayout это внутренне отслеживается через простое поле "lp.onScreen" и в onLayout() соответствующий сдвиг добавляется к параметрам в вызове child.layout() для любого перетаскиваемого дочернего представления ящика.

Ответ 3

Этот ответ немного запоздалый, но причина, по которой ваш контент передается в исходное положение, когда вызывается requestLayout(), (по большей части), потому что ваш код в onLayout() не принимает во внимание изменения к позиции взглядов.

В примере flavienlaurent его код компоновки позиционирует перетаскиваемый вид в левой позиции 0, но верхняя позиция использует глобальную переменную mTop, которая модифицируется внутри обратного вызова onViewPositionChanged (android.view.View, int, int, int, int) до значения top. Вам нужно отслеживать значения вашего изменения и принимать их во внимание внутри onLayout().

Простым способом сохранения изменений позиции в комплекте с представлением перетаскивания является сохранение их внутри LayoutParams и извлечение темы внутри кода макета. Это особенно полезно, если вы хотите иметь несколько перетаскиваемых представлений внутри вашего макета. Отличным примером этого шаблона является исходный код Android SDK DrawerLayout (NavigationDrawer). Взгляните на нижнюю часть класса на собственный класс LayoutParams. Он имеет поле onScreen, которое является типом float. Эта переменная используется для хранения разницы в позиции (относительно начала представления) перетаскиваемого вида и обновляется в setDrawerViewOffset().

Еще один пример - сделать то, что пользователь @user3452758 рекомендовал, но снова это будет сложно отслеживать, если вы когда-либо захотите иметь несколько перетаскиваемых представлений.

Ответ 4

Извините, я не знаю, почему вызывается layout(), но, возможно, одно из ваших представлений, таких как ViewPager call getView (View parentView).

В любом случае, я предполагаю, что ваш использование DragViewHelper, например этот блог описывает. Таким образом, существует метод revelant 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);
    }
/* ......... */
}