Android: использование MotionEvent для перетаскивания позиций видов в пользовательской группе ViewGroup. - PullRequest
4 голосов
/ 26 ноября 2011

У меня проблемы с размещением элементов вида в созданной мной пользовательской группе, особенно в ситуациях перетаскивания. Я нацеливаюсь на 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-единицы измерения или dip-единицы, поэтому, ожидая, что он возвращает мне px-единицы, я попытался преобразовать их в dip-единицы перед назначением нового x и значения y для узла, но это только уменьшило смещение, но не устранило его.

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

1 Ответ

1 голос
/ 24 декабря 2011

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

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

Причина, по которой 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();
}

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

...