Прокрутка галереи с помощью кнопок - PullRequest
3 голосов
/ 15 ноября 2011

У меня есть галерея, адаптер которой создает несколько экземпляров LinearLayout. Однако у этих экземпляров линейного макета есть кнопки, и когда кто-то нажимает на кнопки, он не может перетащить галерею.

Моя идея состоит в том, чтобы иметь меню, которое пользователь может прокручивать. То, что обычно делается с ScrollView, но поскольку я хочу, чтобы прокручиваемое представление «привязывалось» к текущим страницам кнопок, галерея работает лучше.

Этот вопрос похож на этот: Галерея Android LinearLayouts

Однако, хотя я исправил «кнопки, по-видимому, нажаты» при перетаскивании, я не могу заставить его работать так же, как ScrollView, поскольку кнопки работают как часть области перетаскивания.

Есть намеки?

Не уверен, что код уместен, но вот он.

Макет, содержащий галерею:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

        <Gallery
            android:id="@+id/gallery"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" android:fadingEdge="none"
            android:spacing="0dp"/>

</FrameLayout>

Тестовое задание, которое заполняет галерею:

import com.zehfernando.display.widgets.ZGallery;

public class ScrollTestActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.scrolltest);

        Gallery gallery = (Gallery) findViewById(R.id.gallery);
        gallery.setAdapter(new LayoutAdapter(this));
    }

    public class LayoutAdapter extends BaseAdapter {
        private Context mContext;

        public LayoutAdapter(Context c) {
            mContext = c;
        }

        public int getCount() {
            return 3;
        }

        public Object getItem(int position) {
            return position;
        }

        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            LayoutInflater vi = (LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View v = vi.inflate(R.layout.scrolllayout, null);
            v.setMinimumWidth(getWindowManager().getDefaultDisplay().getWidth());
            return v;
        }
    }
}

Макет для рамок, которые идут внутри галереи:

<?xml version="1.0" encoding="utf-8"?>
<com.zehfernando.display.widgets.ScrollableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/buttonRegister"
        android:layout_width="200dp"
        android:layout_height="72dp"
        android:text="REGISTER"/>

    <Button
        android:id="@+id/buttonUnregister"
        android:layout_width="match_parent"
        android:layout_height="72dp"
        android:text="UNREGISTER" />

</com.zehfernando.display.widgets.ScrollableLinearLayout>

"ScrollableLinearLayout" - это просто мой класс, который расширяет LinearLayout для переопределения onPressed.

1 Ответ

10 голосов
/ 16 ноября 2011

Хорошо, я думаю, что понял, так что вот в случае, если кто-то столкнется с этим в будущем.

Я не очень хорошо знал, как события касания распространялись по списку отображения, поэтому потребовалось гораздо больше проб и ошибок, чем я хотел бы признать, но в основном: можно перехватить события касания у родителя и не допустить он распространяется на детей, в основном превращая кнопки в бесполезные (позволяя пользователям щелкать и перетаскивать их, вместо этого отправляя события родительскому onTouchEvent). Это делается методом onInterceptTouchEvent.

Таким образом, вместо Gallery я расширил его (на данный момент он называется ZGallery). Этого достаточно, чтобы сделать содержащиеся кнопки бесполезными:

@Override
public boolean onInterceptTouchEvent(MotionEvent __e) {
    return true;
}

Но, конечно, я хотел убедиться, что кнопки работают (чтобы они были активными), но также позволяли перетаскивать.

Возможно, есть более умный способ сделать это, но я перехватываю событие касания в моей новой Галерее (как выше), но позволяет проходить (возвращая false) до пользователь переместил «курсор» на заданное пороговое значение - затем я интерпретирую его как намерение перетаскивания и начинаю правильно перехватывать событие касания. Это приводит к тому, что сенсорные события отправляются в мою собственную галерею, работающую по назначению.

Вы можете изменить его, чтобы он работал только для вертикальных или горизонтальных перетаскиваний.

Так или иначе, это упрощенная версия класса Gallery, которая позволяет перетаскивать любой элемент внутри него:

public class ZGallery extends Gallery {

    // Constants
    protected static final float DRAG_THRESHOLD = 10; // If dragging for more than this amount of pixels, means it's a scroll

    // Properties
    protected boolean isPressed;
    protected float startPressX;
    protected float startPressY;
    protected boolean isDragging;

    // ================================================================================================================
    // CONSTRUCTOR ----------------------------------------------------------------------------------------------------

    public ZGallery(Context context) {
        this(context, null);
    }

    public ZGallery(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.galleryStyle);
    }

    public ZGallery(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    // ================================================================================================================
    // EVENT INTERFACE ------------------------------------------------------------------------------------------------

    @Override
    public boolean onInterceptTouchEvent(MotionEvent __e) {
        // Intercepts all touch screen motion events.  This allows you to watch events as they are dispatched to your children, and take ownership of the current gesture at any point.
        // Return true to steal motion events from the children and have them dispatched to this ViewGroup through onTouchEvent().
        // The current target will receive an ACTION_CANCEL event, and no further messages will be delivered here.

        //return super.onInterceptTouchEvent(__e); // super always returns false

        // If this function returns TRUE, NO children get dragging events. This only happens
        // the first interception (mouse down); if true is returned, nothing is intercepted anymore, and
        // events are passed to onTouchEvent directly.
        // If FALSE is returned, this may be called again, but only if there's a children receiving the
        // events instead of this.
        // In sum, once onTouchEvent is called here, onInterceptTouchEvent is not called anymore.

        // Interprets drag data
        return evaluateTouchEvent(__e);

    }

    @Override
    public boolean onTouchEvent(MotionEvent __e) {
        // Interprets drag data
        evaluateTouchEvent(__e);

        // Properly lets superclass interpret touch events (for dragging, fling, etc)
        return super.onTouchEvent(__e);
    }

    protected boolean evaluateTouchEvent(MotionEvent __e) {
        // Interprets motion to see if the user is dragging the View
        // This will run in parallel with the children events
        float dragDeltaX;
        float dragDeltaY;

        switch (__e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // Pressing...
                isPressed = true;
                startPressX = __e.getX();
                startPressY = __e.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                // Moving...
                if (isPressed && !isDragging) {
                    dragDeltaX = __e.getX() - startPressX;
                    dragDeltaY = __e.getY() - startPressY;

                    if (Math.abs(dragDeltaX) > DRAG_THRESHOLD || Math.abs(dragDeltaY) > DRAG_THRESHOLD) {
                        // Moved too far, means it's dragging!

                        // Inject click from correct position so superclass code knows where to drag from
                        MotionEvent me = MotionEvent.obtain(__e);
                        me.setAction(MotionEvent.ACTION_DOWN);
                        me.setLocation(__e.getX() - dragDeltaX, __e.getY() - dragDeltaY);
                        super.onTouchEvent(me);

                        isDragging = true;
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                // Releasing...
                if (isPressed) {
                    isPressed = false;
                    // Let go while pressed
                    if (isDragging) {
                        // Was dragging, so just go back
                        isDragging = false;
                    } else {
                        // Was not dragging, this will trigger a click
                    }
                }
                break;
        }


        // If not dragging, event should be passed on
        // If dragging, the event should be intercepted and interpreted by this gallery's onTouchEvent instead
        return isDragging;
    }
}

Кажется, это работает хорошо. Надеюсь, это будет полезно кому-то еще!

...