Как мне реализовать onDragListener в объекте внутри фрагмента? - PullRequest
0 голосов
/ 15 декабря 2018

У моего приложения есть класс MarkedLine, который расширяет View.Экземпляр этого класса показан во фрагменте.Я хочу, чтобы пользователи могли выполнять следующие 3 вещи:

  1. Увеличивать линию, выполняя жесты «щепотка» и «растяжение»
  2. Дотронуться до любой точки линии и получить еекоординаты
  3. Переместить линию вокруг

У меня есть первые два работающих, но не могу понять третий (перетаскивание).

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

Базовая компоновка фрагмента (fragment_marked_line) выглядит следующим образом (Iудалены нерелевантные биты, отступы, поля и т. д.):

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

    <packagepath.models.ToolboxButton
        android:id="@+id/toolbarNext"
        android:layout_width="@dimen/toolbar_icon_size"
        android:layout_height="@dimen/toolbar_icon_size"
        android:src="@drawable/next_line"
        res:layout_constraintTop_toTopOf="parent" />

    <packagepath.models.MarkedLine
        android:id="@+id/markedLine"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        res:layout_constraintStart_toStartOf="parent"
        res:layout_constraintEnd_toEndOf="parent"
        res:layout_constraintTop_toBottomOf="@+id/toolbarNext" />

</android.support.constraint.ConstraintLayout>

(так что в основном это кнопка с линией полной ширины под ней. Кнопка позволяет пользователю вызвать следующую строку).

Код фрагмента (MarkedLineFragment) выглядит следующим образом (примечание. LineSet - это просто массив MarkedLines с несколькими дополнительными переменными, такими как, например, при создании, размерами строки и т. Д.):

public class MarkedLineFragment extends Fragment {

    LineSet       mLineSet
    MarkedLine    mMarkedLine;
    ToolboxButton btn_next;

    int     mItemNumber, mMaxItems;

    public MarkedLineFragment() {}

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater,
                     ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);

        View rootView = inflater.inflate(
                R.layout.fragment_marked_line, container, false);

        // Get view objects
        btn_next = rootView.findViewById(R.id.toolbarNext);
        mMarkedLine = rootView.findViewById(R.id.markedLine);

        // Initialise the button
        initialise_button();

        // If the LineSet has already been set, 
        //  pass it through to the MarkedLine
        if(mLineSet != null) {
            mMarkedLine.setLineSet(mLineSet);
            mMaxItems = mLineSet.getNum_items();
        }

        // Initialise at line 1
        mItemNumber = 1;
        mMarkedLine.setCurrentItem(mItemNumber);

        // Draw the MarkedLine
        drawLine();

        return rootView;
    }

    // Initialise the button so that it moves to the next line on clicking
    public void initialise_button() {

        btn_next.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mItemNumber == mMaxItems) return;
                else mItemNumber += 1;
                set_new_item_number();
            }
        });

    }

    private void set_new_item_number() {
        mMarkedLine.setCurrentItem(mItemNumber);
    }

    public void drawChart() {
      if(mMarkedLine != null) mMarkedLine.postInvalidate();
    }

}

Наконец, класс MarkedLine (я упустил детали того, как нарисована линия, потому что я не думаю, что она уместна, и она довольно длинная - но я могу добавить ее в случае необходимости):

public class MarkedLine extends View {

    private LineSet mLineSet;
    private int currentItem;
    private int numBoxes;
    private float canvas_height, canvas_width;
    private float box_size;
    private float minX, maxX, minY, maxY;

    // Scaling (pinch & zoom) variables
    private float scaleFactor = 1.0f; // Current scale factor
    private ScaleGestureDetector detectorScale;// Detector for gestures
    private GestureDetector detectorTouch; // Detector for tap gestures

    public MarkedLine(Context thisContext, AttributeSet attrs) {
        super(thisContext, attrs);

        detectorScale = new ScaleGestureDetector(thisContext, new MarkedLine.ScaleListener());
        detectorTouch = new GestureDetector(thisContext, new MarkedLine.TouchListener());

    }

    public void setCallback(OnBoxTouched callback) { mCallback = callback; }

    public void setLineSet(LineSet lineSet) {
        mLineSet = lineSet;
        numBoxes = mLineSet.getNum_boxes();
        invalidate();
    }

    public void setCurrentItem(int newItemNumber) {
        currentItem = newItemNumber;
        invalidate();
    }

    protected void onDraw(Canvas canvas) {

        if (mLineSet == null) return;

        // Set up canvas
        canvas.save();
        canvas.scale(scaleFactor, scaleFactor);
        canvas.translate(translateX / scaleFactor, translateY / scaleFactor);

        // draw_boxes reads how many boxes make up the MarkedLine,
        //  calculates what size they need to be to fit on the canvas, 
        //  and then draws them
        draw_boxes();

        // fill_in_line adds in the appropriate colours to the 
        //  boxes in the line
        fill_in_line();

        canvas.restore();
    }

    // GRID EVENT FUNCTIONS - respond to User touching screen

    // onTouchEvent
    // User has touched the screen - trigger listeners
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        detectorScale.onTouchEvent(event);
        detectorTouch.onTouchEvent(event);
        invalidate();

        return true;

    }

    // LISTENERS

    /*
     * Respond to user touching the screen
     */
    private class TouchListener extends GestureDetector.SimpleOnGestureListener {

        public boolean onSingleTapUp(MotionEvent event) {

            // Determine where the screen was touched
            float xTouch = event.getX();
            float yTouch = event.getY();

            // Check that the touch was within the line; return if not
            if(!touch_in_line(xTouch, yTouch)) return false;

            // Figure out which Box was tapped
            int xCell = getTouchedBox(xTouch);

            // Now the box which was tapped is coloured in
            colour_box(xCell);

            return true;
        }

    }

    /*
     * Determine scale factor for zoom mode
     * This can be called in View and Edit Activities
     */
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

        @Override
        public boolean onScale(ScaleGestureDetector detector) {

            float MIN_ZOOM = 1f;     // Minimum zoom level
            float MAX_ZOOM = 5f;     // Maximum zoom level

            scaleFactor *= detector.getScaleFactor();
            scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
            return true;
        }

    }
}

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

Я предполагаю, что мне нужно добавить onDragListener к чему-то, но я не могу понять, что.Я попытался использовать класс DragListener, похожий на классы ScaleListener и TouchListener, с методом onDrag (у меня просто есть несколько фиктивных строк, чтобы я мог прикрепить точку останова).Затем я объявил экземпляр этого класса (dragListener).Я попытался прикрепить его в конструкторе MarkedLine, используя this.onDragListener(dragListener), но он не реагировал на перетаскивание.

Затем я попытался сделать что-то похожее во фрагменте, прикрепив его к классу mMarkedLine в onCreateView, но опять-таки неНе могу ответить, когда пытаюсь перетащить.

Я прочитал документацию по Android, в которой предлагался класс onDragListener, но я явно что-то не так делаю.

1 Ответ

0 голосов
/ 10 января 2019

В случае, если это кому-то поможет, я исправил это, добавив проверку на перетаскивание в onTouchEvent класса MarkedLine:

@Override
public boolean onTouchEvent(MotionEvent event) {

    detectorScale.onTouchEvent(event);
    detectorTouch.onTouchEvent(event);

    // Check for drag gestures
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            startX = event.getX() - previousTranslateX;
            startY = event.getY() - previousTranslateY;
            break;
        case MotionEvent.ACTION_UP:
            previousTranslateX = translateX;
            previousTranslateY = translateY;
            break;
        case MotionEvent.ACTION_MOVE:
            translateX = event.getX() - startX;
            translateY = event.getY() - startY;
            break;
    }

    invalidate();

    return true;

}
...