2

I've got this canvas which the user can add images/text etc to. If the user drags one of the items to either side of the canvas, it should expand if needed. I googled, but couldn't find any reasonable solution. Also, the canvas is about 90% of the screen width, and 70% of the height.. I'm not asking for an entire solution.. I just need a tip on how to do this (Links, docs, whatever)

4

1 回答 1

2

嗯,很难猜出你想要达到的目标。当你说“如果需要它应该扩展”时,你是什么意思?展开以填充父视图?扩展到它的内在大小?

这是我在自定义视图类中使用的一些(不完整)代码。其中大部分是从这里的多个解决方案中收集的,我感谢原作者。onDraw 是最有趣的。当您要绘制时(此处为自定义绘图),您无需担心平移或缩放,因为画布本身已被平移和缩放。换句话说,您的 x 和 y 坐标与视图大小相关 - 只需将它们乘以比例即可。

public class LightsViewer extends ImageView {

private float scale;

// minimum and maximum zoom
private float MIN_ZOOM = 1f;
private float MAX_ZOOM = 5f;

// mid point between fingers to centre scale
private PointF mid = new PointF();

private float scaleFactor = 1.f;
private ScaleGestureDetector detector;

// drag/zoom mode
private final static int NONE = 0;

// current mode
private int mode ;

private float startX = 0f;
private float startY = 0f;

private float translateX = 0f;
private float translateY = 0f;

private float previousTranslateX = 0f;
private float previousTranslateY = 0f;

public LightsViewer(Context context) {

    super(context);
    detector = new ScaleGestureDetector(getContext(), new ScaleListener());

    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();

    displayWidth = display.getWidth();
    displayHeight = display.getHeight();

}


public LightsViewer(Context context, AttributeSet attrs) {

    super(context,attrs);
    detector = new ScaleGestureDetector(getContext(), new ScaleListener());

    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();

    displayWidth = display.getWidth();
    displayHeight = display.getHeight();

}

@Override
public boolean onTouchEvent(MotionEvent event) {

    int ZOOM = 2;
    int DRAG = 1;

    if (!allowZooming){return true;}

    switch (event.getAction() & MotionEvent.ACTION_MASK) {

        case MotionEvent.ACTION_DOWN:

            mode = DRAG;

            //We assign the current X and Y coordinate of the finger to startX and startY minus the previously translated
            //amount for each coordinates This works even when we are translating the first time because the initial
            //values for these two variables is zero.
            startX = event.getX() - previousTranslateX;
            startY = event.getY() - previousTranslateY;

            break;

        case MotionEvent.ACTION_MOVE:

            if (mode == ZOOM){
                Log.d("LIGHTS","ACTION_MOVE:Move but ZOOM, breaking");
                break;}

            translateX = event.getX() - startX;
            translateY = event.getY() - startY;

            //We cannot use startX and startY directly because we have adjusted their values using the previous translation values.
            //This is why we need to add those values to startX and startY so that we can get the actual coordinates of the finger.
            double distance = Math.sqrt(Math.pow(event.getX() - (startX + previousTranslateX), 2) +
                    Math.pow(event.getY() - (startY + previousTranslateY), 2)
            );

            if(distance > 0) {
                dragged = true;
            }

            break;

        case MotionEvent.ACTION_POINTER_DOWN:

            midPoint(mid, event);

            mode = ZOOM;

            break;

        case MotionEvent.ACTION_UP:

            mode = NONE;
            dragged = false;

            //All fingers went up, so let's save the value of translateX and translateY into previousTranslateX and
            //previousTranslate
            previousTranslateX = translateX;
            previousTranslateY = translateY;

            break;

        case MotionEvent.ACTION_POINTER_UP:

            mode = NONE;

            //This is not strictly necessary; we save the value of translateX and translateY into previousTranslateX
            //and previousTranslateY when the second finger goes up
            previousTranslateX = translateX;
            previousTranslateY = translateY;

            break;
    }

    detector.onTouchEvent(event);

    //We redraw the canvas only in the following cases:
    //
    // o The mode is ZOOM
    //        OR
    // o The mode is DRAG and the scale factor is not equal to 1 (meaning we have zoomed) and dragged is
    //   set to true (meaning the finger has actually moved)
    if ((mode == DRAG && scaleFactor != 1f && dragged) || mode == ZOOM) {            
        this.invalidate();
    }

    return true;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (this.getImageMatrix().isIdentity()){return;}

    if (allowZooming){
        this.applyMatrix(this.getImageMatrix());
    }

}

@Override
public void onDraw(Canvas canvas) {

    canvas.save();

    // scale the canvas
    canvas.scale(scaleFactor, scaleFactor, mid.x, mid.y);

    canvas.translate(translateX / scaleFactor, translateY / scaleFactor);

    super.onDraw(canvas);

    // do custom drawing here...e.g.
    canvas.drawCircle(100,100, 3 / scaleFactor,light.paint);


    canvas.restore();
}

private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        scaleFactor *= detector.getScaleFactor();
        scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
        return true;
    }
}

// calculate the mid point of the first two fingers
private void midPoint(PointF point, MotionEvent event) {
    // ...
    float x = event.getX(0) + event.getX(1);
    float y = event.getY(0) + event.getY(1);
    point.set(x / 2, y / 2);

    Log.d("LIGHTS", x/2 + "," + y/2);

}

public void setMinZoom(float minZoom){
    this.MIN_ZOOM = minZoom;
}

public void applyMatrix(Matrix matrix){

    float[] matrixValues = new float[9];
    matrix.getValues(matrixValues);

    int x = (int)matrixValues[Matrix.MTRANS_X];
    int y = (int)matrixValues[Matrix.MTRANS_Y];
    float scale = matrixValues[Matrix.MSCALE_X];

    if (lights!=null){
        for (Light light:lights){
            light.setX((int)((light.originalX * scale) + x));
            light.setY((int)((light.originalY * scale) + y));
        }
    }

    // if either the x or y translations are less than 0, then the image has been cropped
    // so set the min zoom to the ratio of the displayed size and the actual size of the image
    if (matrixValues[Matrix.MTRANS_X] < 0 || matrixValues[Matrix.MTRANS_Y] <0){
        MIN_ZOOM = displayWidth / this.getDrawable().getIntrinsicWidth();
    }else{
        MIN_ZOOM = 1;
    }

}

public void enableZooming(boolean enable){
    allowZooming = enable;
}

public void setScale(float scale){

    for (Light light:lights){
        light.setX((int)(light.originalX * scale));
        light.setY((int)(light.originalY * scale));
    }
}

}

于 2012-04-13T09:13:40.207 回答