紫苏水釉吧 关注:4贴子:143
  • 6回复贴,共1

【转】Android图片查看支持双击放大缩小、多点触摸

只看楼主收藏回复

前一段时间写的,现在分享出来供大家参考。研究过图库的源码,但着实太复杂,我都看不懂!也参考过网上的一些源码,但很多功能都不全,都不是我想要的! 结合网上的代码以及图库的部分源码自己写了一个类。未实现的功能——逐级放大,这个矩阵变换把我弄晕了,而且项目中也用不到,所以就没再研究。该模块主要实现了放大和原大两个级别的缩放。功能有:
以触摸点为中心放大(这个是网上其他的代码没有的)
边界控制(这个是网上其他的代码没有的)
双击放大或缩小(主要考虑到电阻屏)
多点触摸放大和缩小这个模块已经通过了测试,并且用户也使用有一段时间了,是属于比较稳定的了。下面贴上代码及使用方法(没有写测试项目,大家见谅): ImageControl.cs 类似一个用户自定义的ImageView控件。用法将在下面的代码中贴出。


IP属地:江苏1楼2013-01-18 20:18回复

    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Matrix;
    import android.util.AttributeSet;
    import android.util.FloatMath;
    import android.view.MotionEvent;
    import android.widget.ImageView;
    public class ImageControl extends ImageView {
    public ImageControl(Context context) {
    super(context);
    // TODO Auto-generated constructor stub
    }
    public ImageControl(Context context, AttributeSet attrs) {
    super(context, attrs);
    // TODO Auto-generated constructor stub
    }
    public ImageControl(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    // TODO Auto-generated constructor stub
    }
    // ImageView img;
    Matrix imgMatrix = null; // 定义图片的变换矩阵
    static final int DOUBLE_CLICK_TIME_SPACE = 300; // 双击时间间隔
    static final int DOUBLE_POINT_DISTANCE = 10; // 两点放大两点间最小间距
    static final int NONE = 0;
    static final int DRAG = 1; // 拖动操作
    static final int ZOOM = 2; // 放大缩小操作
    private int mode = NONE; // 当前模式
    float bigScale = 3f; // 默认放大倍数
    Boolean isBig = false; // 是否是放大状态
    long lastClickTime = 0; // 单击时间
    float startDistance; // 多点触摸两点距离
    float endDistance; // 多点触摸两点距离
    float topHeight; // 状态栏高度和标题栏高度
    Bitmap primaryBitmap = null;
    float contentW; // 屏幕内容区宽度
    float contentH; // 屏幕内容区高度
    float primaryW; // 原图宽度
    float primaryH; // 原图高度
    float scale; // 适合屏幕缩放倍数
    Boolean isMoveX = true; // 是否允许在X轴拖动
    Boolean isMoveY = true; // 是否允许在Y轴拖动
    float startX;
    float startY;
    float endX;
    float endY;
    float subX;
    float subY;
    float limitX1;
    float limitX2;
    float limitY1;
    float limitY2;
    ICustomMethod mCustomMethod = null;
    /**
    * 初始化图片
    *
    * @param bitmap
    * 要显示的图片
    * @param contentW
    * 内容区域宽度
    * @param contentH
    * 内容区域高度
    * @param topHeight
    * 状态栏高度和标题栏高度之和
    */
    public void imageInit(Bitmap bitmap, int contentW, int contentH,
    int topHeight, ICustomMethod iCustomMethod) {


    IP属地:江苏2楼2013-01-18 20:19
    回复
      this.primaryBitmap = bitmap;
      this.contentW = contentW;
      this.contentH = contentH;
      this.topHeight = topHeight;
      mCustomMethod = iCustomMethod;
      primaryW = primaryBitmap.getWidth();
      primaryH = primaryBitmap.getHeight();
      float scaleX = (float) contentW / primaryW;
      float scaleY = (float) contentH / primaryH;
      scale = scaleX < scaleY ? scaleX : scaleY;
      if (scale < 1 && 1 / scale < bigScale) {
      bigScale = (float) (1 / scale + 0.5);
      }
      imgMatrix = new Matrix();
      subX = (contentW - primaryW * scale) / 2;
      subY = (contentH - primaryH * scale) / 2;
      this.setImageBitmap(primaryBitmap);
      this.setScaleType(ScaleType.MATRIX);
      imgMatrix.postScale(scale, scale);
      imgMatrix.postTranslate(subX, subY);
      this.setImageMatrix(imgMatrix);
      }
      /**
      * 按下操作
      *
      * @param event
      */
      public void mouseDown(MotionEvent event) {
      mode = NONE;
      startX = event.getRawX();
      startY = event.getRawY();
      if (event.getPointerCount() == 1) {
      // 如果两次点击时间间隔小于一定值,则默认为双击事件
      if (event.getEventTime() - lastClickTime < DOUBLE_CLICK_TIME_SPACE) {
      changeSize(startX, startY);
      } else if (isBig) {
      mode = DRAG;
      }
      }
      lastClickTime = event.getEventTime();
      }
      /**
      * 非第一个点按下操作
      *
      * @param event
      */
      public void mousePointDown(MotionEvent event) {
      startDistance = getDistance(event);
      if (startDistance > DOUBLE_POINT_DISTANCE) {
      mode = ZOOM;
      } else {
      mode = NONE;
      }
      }
      /**
      * 移动操作
      *
      * @param event
      */
      public void mouseMove(MotionEvent event) {
      if ((mode == DRAG) && (isMoveX || isMoveY)) {
      float[] XY = getTranslateXY(imgMatrix);
      float transX = 0;
      float transY = 0;
      if (isMoveX) {
      endX = event.getRawX();
      transX = endX - startX;
      if ((XY[0] + transX) <= limitX1) {
      transX = limitX1 - XY[0];
      }
      if ((XY[0] + transX) >= limitX2) {
      transX = limitX2 - XY[0];
      }
      }
      if (isMoveY) {
      endY = event.getRawY();
      transY = endY - startY;
      if ((XY[1] + transY) <= limitY1) {
      transY = limitY1 - XY[1];
      }
      if ((XY[1] + transY) >= limitY2) {
      transY = limitY2 - XY[1];
      }
      }
      imgMatrix.postTranslate(transX, transY);
      startX = endX;
      startY = endY;
      this.setImageMatrix(imgMatrix);
      } else if (mode == ZOOM && event.getPointerCount() > 1) {
      endDistance = getDistance(event);
      float dif = endDistance - startDistance;
      if (Math.abs(endDistance - startDistance) > DOUBLE_POINT_DISTANCE) {
      if (isBig) {
      if (dif < 0) {
      changeSize(0, 0);
      mode = NONE;
      }
      } else if (dif > 0) {
      float x = event.getX(0) / 2 + event.getX(1) / 2;
      float y = event.getY(0) / 2 + event.getY(1) / 2;
      changeSize(x, y);
      mode = NONE;
      }
      }
      }
      }
      /**
      * 鼠标抬起事件
      */
      public void mouseUp() {
      mode = NONE;
      }
      /**
      * 图片放大缩小
      *
      * @param x


      IP属地:江苏3楼2013-01-18 20:19
      回复
        * 点击点X坐标
        * @param y
        * 点击点Y坐标
        */
        private void changeSize(float x, float y) {
        if (isBig) {
        // 如果处于最大状态,则还原
        imgMatrix.reset();
        imgMatrix.postScale(scale, scale);
        imgMatrix.postTranslate(subX, subY);
        isBig = false;
        } else {
        imgMatrix.postScale(bigScale, bigScale); // 在原有矩阵后乘放大倍数
        float transX = -((bigScale - 1) * x);
        float transY = -((bigScale - 1) * (y - topHeight)); // (bigScale-1)(y-statusBarHeight-subY)+2*subY;
        float currentWidth = primaryW * scale * bigScale; // 放大后图片大小
        float currentHeight = primaryH * scale * bigScale;
        // 如果图片放大后超出屏幕范围处理
        if (currentHeight > contentH) {
        limitY1 = -(currentHeight - contentH); // 平移限制
        limitY2 = 0;
        isMoveY = true; // 允许在Y轴上拖动
        float currentSubY = bigScale * subY; // 当前平移距离
        // 平移后,内容区域上部有空白处理办法
        if (-transY < currentSubY) {
        transY = -currentSubY;
        }
        // 平移后,内容区域下部有空白处理办法
        if (currentSubY + transY < limitY1) {
        transY = -(currentHeight + currentSubY - contentH);
        }
        } else {
        // 如果图片放大后没有超出屏幕范围处理,则不允许拖动
        isMoveY = false;
        }
        if (currentWidth > contentW) {
        limitX1 = -(currentWidth - contentW);
        limitX2 = 0;
        isMoveX = true;
        float currentSubX = bigScale * subX;
        if (-transX < currentSubX) {
        transX = -currentSubX;
        }
        if (currentSubX + transX < limitX1) {
        transX = -(currentWidth + currentSubX - contentW);
        }
        } else {
        isMoveX = false;
        }
        imgMatrix.postTranslate(transX, transY);
        isBig = true;
        }
        this.setImageMatrix(imgMatrix);
        if (mCustomMethod != null) {
        mCustomMethod.customMethod(isBig);
        }
        }
        /**
        * 获取变换矩阵中X轴偏移量和Y轴偏移量
        *
        * @param matrix
        * 变换矩阵
        * @return
        */
        private float[] getTranslateXY(Matrix matrix) {
        float[] values = new float[9];
        matrix.getValues(values);
        float[] floats = new float[2];
        floats[0] = values[Matrix.MTRANS_X];
        floats[1] = values[Matrix.MTRANS_Y];
        return floats;
        }
        /**
        * 获取两点间的距离
        *
        * @param event
        * @return
        */
        private float getDistance(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);
        }
        /**
        * @author Administrator 用户自定义方法
        */
        public interface ICustomMethod {
        public void customMethod(Boolean currentStatus);
        }
        }


        IP属地:江苏4楼2013-01-18 20:19
        回复
          ImageVewActivity.cs 这个用于测试的Activity


          IP属地:江苏5楼2013-01-18 20:19
          回复

            import android.app.Activity;
            import android.graphics.Bitmap;
            import android.graphics.Rect;
            import android.graphics.drawable.BitmapDrawable;
            import android.os.Bundle;
            import android.view.MotionEvent;
            import android.view.View;
            import android.widget.LinearLayout;
            import android.widget.TextView;
            import android.widget.Toast;
            import ejiang.boiler.ImageControl.ICustomMethod;
            import ejiang.boiler.R.id;
            public class ImageViewActivity extends Activity {
            @Override
            protected void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            setContentView(R.layout.common_image_view);
            findView();
            }
            public void onWindowFocusChanged(boolean hasFocus) {
            super.onWindowFocusChanged(hasFocus);
            init();
            }
            ImageControl imgControl;
            LinearLayout llTitle;
            TextView tvTitle;
            private void findView() {
            imgControl = (ImageControl) findViewById(id.common_imageview_imageControl1);
            llTitle = (LinearLayout) findViewById(id.common_imageview_llTitle);
            tvTitle = (TextView) findViewById(id.common_imageview_title);
            }
            private void init() {
            tvTitle.setText("图片测试");
            // 这里可以为imgcontrol的图片路径动态赋值
            // ............
            Bitmap bmp;
            if (imgControl.getDrawingCache() != null) {
            bmp = Bitmap.createBitmap(imgControl.getDrawingCache());
            } else {
            bmp = ((BitmapDrawable) imgControl.getDrawable()).getBitmap();
            }
            Rect frame = new Rect();
            getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
            int statusBarHeight = frame.top;
            int screenW = this.getWindowManager().getDefaultDisplay().getWidth();
            int screenH = this.getWindowManager().getDefaultDisplay().getHeight()
            - statusBarHeight;
            if (bmp != null) {
            imgControl.imageInit(bmp, screenW, screenH, statusBarHeight,
            new ICustomMethod() {
            @Override
            public void customMethod(Boolean currentStatus) {
            // 当图片处于放大或缩小状态时,控制标题是否显示
            if (currentStatus) {
            llTitle.setVisibility(View.GONE);
            } else {
            llTitle.setVisibility(View.VISIBLE);
            }
            }
            });
            }
            else
            {
            Toast.makeText(ImageViewActivity.this, "图片加载失败,请稍候再试!", Toast.LENGTH_SHORT)
            .show();
            }
            }
            @Override
            public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
            imgControl.mouseDown(event);
            break;
            /**
            * 非第一个点按下
            */
            case MotionEvent.ACTION_POINTER_DOWN:
            imgControl.mousePointDown(event);
            break;
            case MotionEvent.ACTION_MOVE:
            imgControl.mouseMove(event);
            break;
            case MotionEvent.ACTION_UP:
            imgControl.mouseUp();
            break;
            }
            return super.onTouchEvent(event);
            }
            }
            在上面的代码中,需要注意两点。一Activity中要重写onTouchEvent方法,将触摸事件传递到ImageControl,这点类似于WPF中的路由事件机制。二初始化imgControl即imgControl.imageInit,注意其中的参数。最后一个参数类似于C#中的委托,我这里使用接口来实现,在放大缩小的切换时要执行的操作都卸载这个方法中。


            IP属地:江苏6楼2013-01-18 20:20
            回复
              原文链接
              http://blog.csdn.net/wuwo333/article/details/8057294


              IP属地:江苏8楼2013-01-18 20:21
              回复