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

来源:互联网 发布:java restaurant chef 编辑:程序博客网 时间:2024/05/21 19:47

        前一段时间写的,现在分享出来供大家参考。

        研究过图库的源码,但着实太复杂,我都看不懂!也参考过网上的一些源码,但很多功能都不全,都不是我想要的! 结合网上的代码以及图库的部分源码自己写了一个类。

        未实现的功能——逐级放大,这个矩阵变换把我弄晕了,而且项目中也用不到,所以就没再研究。

        该模块主要实现了放大和原大两个级别的缩放。

 功能有:

  1. 以触摸点为中心放大(这个是网上其他的代码没有的)
  2. 边界控制(这个是网上其他的代码没有的)
  3. 双击放大或缩小(主要考虑到电阻屏)
  4. 多点触摸放大和缩小

        这个模块已经通过了测试,并且用户也使用有一段时间了,是属于比较稳定的了。

下面贴上代码及使用方法(没有写测试项目,大家见谅):

 ImageControl.cs   类似一个用户自定义的ImageView控件。用法将在下面的代码中贴出。

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) {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 *            点击点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);}}

 

ImageVewActivity.cs   这个用于测试的Activity

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 {@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.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() {                      @Overridepublic void customMethod(Boolean currentStatus) {// 当图片处于放大或缩小状态时,控制标题是否显示if (currentStatus) {llTitle.setVisibility(View.GONE);} else {llTitle.setVisibility(View.VISIBLE);}}});}else{Toast.makeText(ImageViewActivity.this, "图片加载失败,请稍候再试!", Toast.LENGTH_SHORT).show();}}@Overridepublic 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#中的委托,我这里使用接口来实现,在放大缩小的切换时要执行的操作都卸载这个方法中。


common_image_view.xml  布局文件

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/rl"    android:layout_width="fill_parent"    android:layout_height="fill_parent" >    <ejiang.boiler.ImageControl        android:id="@+id/common_imageview_imageControl1"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:src="@drawable/ic_launcher" />    <LinearLayout        android:id="@+id/common_imageview_llTitle"        style="@style/reportTitle1"        android:layout_alignParentLeft="true"        android:layout_alignParentTop="true" >        <TextView            android:id="@+id/common_imageview_title"            style="@style/title2"            android:layout_width="fill_parent"            android:layout_height="wrap_content"            android:layout_weight="1"            android:text="报告" />    </LinearLayout></RelativeLayout>


        我学习android的时间也不长,因此有什么纰漏或错误,欢迎大家指出!

原创粉丝点击