Create a scalable / panoramic container

I am trying to create a ViewGroup that supports panning and scaling of its contents. All I could find on the Internet were ideas and implementations that could be done in ImageView, but not in the container. I want to display a map, and in addition, I want to display several markers, which are ImageButtons, so the user can use them for more information. This is achieved on iOS using UIScrollView, but I could not find an alternative on Android.

I decided to use FrameView, so I could set ImageView with the image as a background, and on top of it add RelativeLayout, on which I could add ImageButtons and put them using fields.

I borrowed part of the TouchImageView implementation here , but I ran into complications. I started with panning, and I partially succeeded, it pans around the container, but panning works terribly, it is very exciting. Here is my code:

public class ScrollingViewGroup extends FrameLayout {

    private int x = 0;
    private int y = 0;

    // We can be in one of these 3 states
    static final int NONE = 0;
    static final int DRAG = 1;
    static final int ZOOM = 2;
    int mode = NONE;

    // Remember some things for zooming
    PointF last = new PointF();
    PointF start = new PointF();

public ScrollingViewGroup(Context context) {
        super(context);
        sharedConstructing(context);
    }

    public ScrollingViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        sharedConstructing(context);
    }

    private void sharedConstructing(Context context) {
        super.setClickable(true);
        this.context = context;
        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());

    setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                mScaleDetector.onTouchEvent(event);

                PointF curr = new PointF(event.getX(), event.getY());

                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    last.set(event.getX(), event.getY());
                    start.set(last);
                    mode = DRAG;
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (mode == DRAG) {
                        float deltaX = curr.x - last.x;
                        float deltaY = curr.y - last.y;
                        Log.d("ScrollingViewGroup", Float.toString(deltaX));
                        Log.d("ScrollingViewGroup", Float.toString(deltaY));
                        float scaleWidth = Math.round(origWidth * saveScale);
                        float scaleHeight = Math.round(origHeight * saveScale);


                        x += deltaX;
                        y += deltaY;



                        last.set(curr.x, curr.y);

                    }
                    break;

                case MotionEvent.ACTION_UP:
                    mode = NONE;
                    int xDiff = (int) Math.abs(curr.x - start.x);
                    int yDiff = (int) Math.abs(curr.y - start.y);
                    if (xDiff < CLICK && yDiff < CLICK)
                        performClick();
                    break;

                case MotionEvent.ACTION_POINTER_UP:
                    mode = NONE;
                    break;
                }
                // setImageMatrix(matrix);
                setTranslationX(x);
                setTranslationY(y);
                invalidate();
                return true; // indicate event was handled
            }

        });
    }

Any ideas are greatly appreciated.

edit: jitter seems to be the reason because when moving deltaX and deltaY alternate between positive and negative numbers, the LogCat check ... is still not sure why. This is caused by a current variable that gives different values ​​each time, but instead of being consistent, they look as if the finger will move back and forth, and not just forward. For example, instead of curr.x equal to 0,1,2,3,4 etc., it is 0,1,0,5,2,1,5 etc. Not sure why.

+5
1

, :

  • ImageView
  • , .

( ). , , , . ImageViews ArrayList FrameLayout . ZDNet (), Android- " , Android".

. . XML. initializeViews(), , , . "ArrayList sampleBitmaps = new ArrayList();" , , addImageView ( ImageView), .

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;


public class TouchContainer extends FrameLayout implements OnTouchListener {

    // constants
    private static final Config DEFAULT_COLOR_DEPTH = Bitmap.Config.ARGB_4444;
    private static final String TAG = "TouchContainer";

    // fields
    private ArrayList<ImageView> items;

    public TouchContainer(Context ctx) {
        this(ctx, null);
    }

    public TouchContainer(Context ctx, AttributeSet attrs) {
        super(ctx, attrs);

        initializeViews();  // initialize some sample Bitmaps
    }

    /**
     * This method is just to make an example 
     */
    protected void initializeViews() {
        ScaleType scaleType = ScaleType.MATRIX;

        // array needs to be created here if used in XML
        items = new ArrayList<ImageView>();


        ArrayList<Bitmap> sampleBitmaps = new ArrayList<Bitmap>();
        // here you should add some bitmaps to the Array that will then be displayed in the container
        // e.g. sampleBitmaps.add(blabla I'm a bitmap) :-)

        ImageView iv = null;
        boolean firstLoop = true;

        for (Bitmap bitmap : sampleBitmaps) {
            // Load the bitmaps into imageviews
            iv = new ImageView(getContext());
            iv.setImageBitmap(bitmap);
            iv.setScaleType(scaleType);
            if (firstLoop) {
                // add the touch listener to the first image view that is stored in the ArrayList
                iv.setOnTouchListener(this);
                firstLoop = false;
            }

            // add view to the FrameLayout
            this.addView(iv);

            // add the imageview to the array
            items.add(iv);
        }

    }



    protected void transformImages(Matrix matrix) {
        for (ImageView image : items) {
            image.setImageMatrix(matrix);
        }

    }




    Matrix matrix = new Matrix();
    Matrix savedMatrix = new Matrix();

    // states
    static final int NONE = 0;
    static final int DRAG = 1;
    static final int ZOOM = 2;
    int mode = NONE;
    static final int CLICK = 3;


    PointF start = new PointF();
    PointF mid = new PointF();
    float oldDist = 1f;

    public boolean onTouch(View v, MotionEvent event) {


      switch (event.getAction() & MotionEvent.ACTION_MASK) {
      case MotionEvent.ACTION_DOWN:
         savedMatrix.set(matrix);
         start.set(event.getX(), event.getY());
         Log.d(TAG, "mode=DRAG");
         mode = DRAG;
         break;
      case MotionEvent.ACTION_POINTER_DOWN:
         oldDist = spacing(event);
         Log.d(TAG, "oldDist=" + oldDist);
         if (oldDist > 10f) {
            savedMatrix.set(matrix);
            midPoint(mid, event);
            mode = ZOOM;
            Log.d(TAG, "mode=ZOOM");
         }
         break;
      case MotionEvent.ACTION_UP:
          // figure out if user clicked
          mode = NONE;
          int xDiff = (int) Math.abs(event.getX() - start.x);
          int yDiff = (int) Math.abs(event.getY() - start.y);
          if (xDiff < CLICK && yDiff < CLICK)
              performClick(); 
          break;
      case MotionEvent.ACTION_POINTER_UP:
         mode = NONE;
         Log.d(TAG, "mode=NONE");
         break;
      case MotionEvent.ACTION_MOVE:
         if (mode == DRAG) {

            matrix.set(savedMatrix);
            matrix.postTranslate(event.getX() - start.x,
                  event.getY() - start.y);
         }
         else if (mode == ZOOM) {
            float newDist = spacing(event);
            Log.d(TAG, "newDist=" + newDist);
            if (newDist > 10f) {
               matrix.set(savedMatrix);
               float scale = newDist / oldDist;
               matrix.postScale(scale, scale, mid.x, mid.y);
            }
         }
         break;
      }


      transformImages(matrix);


      return true;
   }




       private float spacing(MotionEvent event) {
          float x = event.getX(0) - event.getX(1);
          float y = event.getY(0) - event.getY(1);
          return FloatMath.sqrt(x * x + y * y);
       }


       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);
       }





}
0

All Articles