前一段时间写的,现在分享出来供大家参考。
研究过图库的源码,但着实太复杂,我都看不懂!也参考过网上的一些源码,但很多功能都不全,都不是我想要的! 结合网上的代码以及图库的部分源码自己写了一个类。
未实现的功能——逐级放大,这个矩阵变换把我弄晕了,而且项目中也用不到,所以就没再研究。
该模块主要实现了放大和原大两个级别的缩放。
功能有:
- 以触摸点为中心放大(这个是网上其他的代码没有的)
- 边界控制(这个是网上其他的代码没有的)
- 双击放大或缩小(主要考虑到电阻屏)
- 多点触摸放大和缩小
这个模块已经通过了测试,并且用户也使用有一段时间了,是属于比较稳定的了。
下面贴上代码及使用方法(没有写测试项目,大家见谅):
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 {
- @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#中的委托,我这里使用接口来实现,在放大缩小的切换时要执行的操作都卸载这个方法中。
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的时间也不长,因此有什么纰漏或错误,欢迎大家指出!