5

我正在尝试制作一个支持平移和缩放其内容的 ViewGroup。我在网上只能找到在 ImageView 中执行此操作的想法和实现,但绝不是容器。我想显示一张地图,在它上面我想显示多个标记,这些标记是 ImageButtons,因此用户可以点击它们以获取更多信息。这是通过 UIScrollView 在 iOS 上实现的,但我在 Android 上找不到替代方案。

我决定使用 FrameView,所以我可以设置一个 ImageView 与图像作为背景,并在它的顶部添加一个 RelativeLayout,我可以在其上添加 ImageButtons 并使用边距定位它们。

我在这里借用了 TouchImageView 的部分实现,但遇到了麻烦。我已经开始平移,我部分成功了,它平移了容器,但是平移效果很糟糕,抖动很多。这是我的代码:

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
            }

        });
    }

任何想法都非常感谢。

编辑:抖动似乎是原因,因为移动时,deltaX 和 deltaY 在正数和负数之间交替,检查 LogCat ......仍然不知道为什么。这是由 curr 变量引起的,该变量每次都给出不同的值,但不是一致的,它们看起来好像手指会前后移动而不是向前移动。例如,curr.x 不是 0,1,2,3,4 等,而是 0,1,0.5,2,1.5 等。也不知道为什么。

4

1 回答 1

0

我有一个适用于一些先决条件的解决方案:

  • 容器中的项目需要是 ImageView 类型
  • 所有图像都需要具有相同的大小才能同等缩放

容器中的项目可以缩放和平移(但不能同时)。该代码还能够确定用户是否单击而不是移动或缩放。ImageView 存储在 FrameLayout 中的 ArrayList 中,并一起缩放和移动。此代码的某些部分摘自 Ed Burnette 在 ZDNet 上的一篇非常好的文章(链接),该文章摘自一本非常好的 Android 书籍“ Hello, Android ”。

查看此代码。您可以在任何活动中使用此类作为布局。您甚至应该能够在 XML 中使用它。现在有一个在构造函数中调用的方法 initializeViews(),您可以在其中硬编码应该在创建布局时加载的图像视图。您应该在此方法中在“ArrayList sampleBitmaps = new ArrayList();”行之后添加一些位图 对于实际使用,最好实现一个方法 addImageView(ImageView item) ,您可以在其中动态添加视图。

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





}
于 2012-05-31T16:19:24.987 回答