Прокрутка по большому холсту

Мне нужна помощь, чтобы понять основы прокрутки элементов, которые нарисованы на холсте в Android. Предположим, я хочу создать временную шкалу, где время в 0 является вершиной визуализации, и по мере увеличения времени шкала продолжает отображаться ниже предыдущей точки. Если я хочу сделать это на Android, я знаю, что могу просто создать кучу элементов на холсте, переопределив onDraw(). Однако предположим, что визуализация больше, чем позволяет экран.

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

Before_Scroll

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

After_Scroll

Я считаю, что мне нужно использовать прокрутки, но я совершенно потерял, как это сделать. Я прочитал эту страницу http://developer.android.com/training/custom-views/custom-drawing.html, объяснив, как создать свои собственные изображения клиентов, и эта страница http://developer.android.com/training/custom-views/making-interactive.html, объясняя, как сделать интерактивный интерфейс интерактивным, но я думаю, что что-то не хватает.

Образец кода, который иллюстрирует эту проблему (это основной, предположим, что существует логика, диктующая WHERE, строки/линии идут и т.д.) выглядит следующим образом:

package com.example.scrolltest;

import com.example.scrolltest.Draw;

import android.os.Bundle;
import android.app.Activity;
import android.graphics.Color;

public class MainActivity extends Activity {
    Draw draw;

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

    draw = new Draw(this);
    draw.setBackgroundColor(Color.WHITE);
    setContentView(draw);
    }
}

и

package com.example.scrolltest;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;


public class Draw extends View {
    Paint paint = new Paint();

    public Draw(Context context) {
        super(context);            
    }

    @Override
    public void onDraw(Canvas canvas) {

        paint.setColor(Color.GREEN);
        canvas.drawRect(30, 30, 90, 200, paint);
        paint.setColor(Color.BLUE);

        canvas.drawLine(100, 20, 100, 1900, paint);

        paint.setColor(Color.GREEN);
        canvas.drawRect(200, 2000, 400, 3000, paint);
        }
}

Что я не могу понять, так это то, как я использую прокручиваемую прокрутку вниз до прямоугольников, которые находятся вне экрана. Я также не уверен, что я начал это правильно или должен использовать drawables вместо...

Ответ 1

Простой метод (если требуемая высота не очень большая).

Используйте ScrollView и добавьте в него вид Draw. Вычислите требуемую высоту для этого вида в onMeasure.

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

draw = new Draw(this);
draw.setBackgroundColor(Color.WHITE);
ScrollView scrollView = new ScrollView(this);
scrollView.addView(draw);
setContentView(scrollView);
}

public class Draw extends View {
        Paint paint = new Paint();

        public Draw(Context context) {
            super(context);            
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            // Compute the height required to render the view
            // Assume Width will always be MATCH_PARENT.
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = 3000 + 50; // Since 3000 is bottom of last Rect to be drawn added and 50 for padding.
            setMeasuredDimension(width, height);
        }

        @Override
        public void onDraw(Canvas canvas) {

            paint.setColor(Color.GREEN);
            canvas.drawRect(30, 30, 90, 200, paint);
            paint.setColor(Color.BLUE);

            canvas.drawLine(100, 20, 100, 1900, paint);

            paint.setColor(Color.GREEN);
            canvas.drawRect(200, 2000, 400, 3000, paint);
            }
    }

Ответ 2

Альтернативой может быть использование значений смещения X/Y.

Как вы обрабатываете значения смещения, зависит от вас, все, хотя я предпочитаю использовать класс I, вызывающий Camera. Доступ должен быть статичным.

public void render(Canvas c){
    c.drawRect(100 - offsetX, 100 - offsetY, 120 - offsetX, 120 - offsetY, Paints.BLUE);

}

И для панорамирования холста:

float x, y;
@Override
public boolean onTouchEvent(MotionEvent ev) {
    if(ev.getAction() == MotionEvent.ACTION_DOWN){
        x = ev.getX();
        y = ev.getY();
    }else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
        float currX = ev.getX();
        float currY = ev.getY();

        float newOffsetX = (x   - currX),
                newOffsetY = (y - currY);
        //The next two if-statements are to add sensitivity to the scrolling.
        //You can drop these if you don't plan on having touch events
        //with pressing. Pressing works without this as well, but the canvas may move slightly 
        //I use DP to handle different screen sizes but it can be replaced with pixels. c is a context-instance
        if (newOffsetY < Maths.convertDpToPixel(2, c) && newOffsetY > -Maths.convertDpToPixel(2, c))
            newOffsetY = 0;

        if (newOffsetX < Maths.convertDpToPixel(2, c) && newOffsetX > -Maths.convertDpToPixel(2, c))
            newOffsetX = 0;
        offsetX += newOffsetX;
        offsetY += newOffsetY;
        x = ev.getX();
        y = ev.getY();
    }

    return true;

}

И образец класса камеры:

public class Camera{

    public static float offsetX, offsetY;
    //The constructor. ix and iy is the initial offset. Useful if you are creating a game and need to change the initial offset to center around a starting position.
    //Most of the time it will be enough to set the values to 0
    public Camera(float ix, float iy){
        this.offsetX = ix;
        this.offsetY = iy;
    }

}