Android: использование MotionEvent для перетаскивания представлений в настраиваемой ViewGroup

У меня возникают проблемы с позиционированием элементов представления в пользовательской группе просмотра, которую я создал, особенно в ситуациях перетаскивания. Я нацелен на Android 2.2 и выше, поэтому я не могу использовать API перетаскивания, который появился в Android 3.

Моя пользовательская ViewGroup называется «NodeGrid» и расширяет «RelativeLayout». Его метод onLayout переопределен и выглядит следующим образом:

@Override
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) 
{
    //pxConversion is a multiplier to convert 1 dip to x pixels.
    //We will use it to convert dip units into px units.
    Resources r = getResources();
    float pxConversion = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, r.getDisplayMetrics());

    //Layout the children of this viewgroup
    int childCount = this.getChildCount();
    for (int i = 0; i < childCount; i++)
    {
        NodeView view = (NodeView) this.getChildAt(i);

        if (view.DataContext != null)
        {
            int left = (int) (view.DataContext.get_left() * pxConversion);
            int top = (int) (view.DataContext.get_top() * pxConversion);
            int right = left + (int) (view.DataContext.get_width() * pxConversion);
            int bottom = top + (int) (view.DataContext.get_height() * pxConversion);

            view.layout(left, top, right, bottom);
        }
    }   
}

Дочерние элементы «NodeGrid» имеют тип «NodeView» (как вы можете видеть в приведенном выше коде). NodeView — это просто пользовательский класс View, который я создал. Он содержит член с именем «DataContext», который является классом модели представления, который содержит некоторые геттеры/сеттеры с информацией о позиции экземпляра NodeView в NodeGrid.

Мой класс «NodeView» фиксирует события касания пользователя, поэтому пользователь может просто перетащить узел в любое место сетки. Обработчик события выглядит так:

@Override
public boolean onTouchEvent(MotionEvent event) {

    if (event.getAction() == MotionEvent.ACTION_DOWN)
    {
        isBeingDragged = true;
    }
    else if (event.getAction() == MotionEvent.ACTION_UP)
    {
        isBeingDragged = false;
    }
    else if (event.getAction() == MotionEvent.ACTION_MOVE)
    {
        if (DataContext != null && isBeingDragged)
        {
            float xPosition = event.getRawX();
            float yPosition = event.getRawY();

            DataContext.set_left(xPosition);
            DataContext.set_top(yPosition);

            this.requestLayout();
        }
    }

    return true;
}

Моя проблема в том, что перетаскиваемое представление не позиционируется так, как я ожидал, в моем «NodeGrid». Кажется, что положение оси X правильное, когда я перетаскиваю его, но положение оси Y смещается на некоторое постоянное количество пикселей в любое время. Что может быть причиной этого? Я пробовал использовать методы getX() и getY(), а не getRawX() и getRawY(), но это только ухудшило ситуацию.

Я не был уверен, возвращают ли мне getRawX() и getRawY() единицы измерения px или единицы наклона, поэтому, ожидая, что он вернет мне единицы px, я попытался преобразовать их в единицы наклона, прежде чем назначать новые значения x и y для узел, но это только уменьшило смещение, но не устранило его.

Что может быть причиной разницы в том, где я касаюсь и где находится узел?


person David    schedule 26.11.2011    source источник


Ответы (1)


Очень удивлен, что видимо больше никто не сталкивался с такой ситуацией...

После некоторых исследований мне частично удалось решить проблему.

Причина, по которой getRawX() и getRawY() не работают, заключается в том, что они работают с абсолютной позицией экрана без учета чего-либо на экране, не являющегося частью вашего приложения (например, меню Android, которое постоянно находится в верхней части экрана). если вы не находитесь в полноэкранном режиме). Использование этих координат казалось хакерским, особенно без очевидного способа оправдать их и преобразовать в координаты «моего приложения».

Насколько я могу судить, getX() и getY() относятся к объекту, к которому прикасаются. Принимая это во внимание, я изменил код в onTouchEvent следующим образом:

float xPosition = event.getX();
float yPosition = event.getY();

DataContext.set_left(DataContext.get_left() + xPosition);
DataContext.set_top(DataContext.get_top() + yPosition);

this.requestLayout();

Это определенно помогает... но все еще не кажется идеальным решением. В общем, это заставляет объект представления тянуться вместе с моим курсором/пальцем мыши, но это очень нервно, и все еще есть моменты, когда он может стать странным и исчезнуть с экрана.

Затем я подумал, что, может быть, это потому, что я не включаю в метод «исторические координаты». Я попытался включить исторические координаты в метод, поместив этот код непосредственно перед кодом, который я только что показал выше:

int historySize = event.getHistorySize();
for (int i = 0; i < historySize; i++)
{
    float xPosition = event.getHistoricalX(i);
    float yPosition = event.getHistoricalY(i);

    DataContext.set_left(DataContext.get_left() + xPosition);
    DataContext.set_top(DataContext.get_top() + yPosition);

    this.requestLayout();
}

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

person David    schedule 24.12.2011
comment
Привет, ты решил проблему? У вас есть полный код для загрузки? - person Ricardo; 28.03.2013