android自定义控件 多点触控(实现图片自由缩放)

本文介绍了一种实现多点触控缩放图片的方法。通过自定义ImageView类MultiTouch,利用OnGlobalLayoutListener监听布局加载完成,并实现OnScaleGestureListener接口处理多点触控事件。文中详细解释了如何根据图片尺寸和屏幕尺寸调整图片位置和缩放比例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于需要真机测试, 所以没有弄效果图..


1, 建立一个类MultiTouch  继承 ImageView, 并在xml使用, 获取要缩放图片. 如果图片的宽/高大于屏幕宽/高, 那么我们进行缩放,并移动到屏幕中心点.

    那么问题来了. 如果我们在构造器中直接获取宽度 那么可能会获取不到. 我们需要知道布局什么时候加载完成. 这时候就需要用到一个接口 OnGlobalLayoutListener (全球布局监听)   实现 onGlobalLayout() 方法. 并且需要在view 从window出现时 监听它, 从window时 移除.

 于是对图片初始化的操作 都会在 onGlobalLayout()内执行.

2,我们还需要实现  OnScaleGestureListener  接口 来对 多点触控进行操作...    很方便

3. 把touch 事件 交给 OnScaleGestureListener .  


下面贴代码

public class MultiTouch extends ImageView implements OnGlobalLayoutListener,
		OnScaleGestureListener, OnTouchListener {

	private Context context;

	private boolean mOnce = false; // 初始化一次

	private float scale = 1.0f; // 默认缩放比

	private float mInitScale;// 初始化缩放比, 也就是最小缩放
	private float mMidScale; // 双击时的 缩放比
	private float mMaxScale; // 极限 最大 的缩放比

	private ScaleGestureDetector mScaleGestureDetector;

	private Matrix mMatrix;

	private Drawable drawable;

	public MultiTouch(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public MultiTouch(Context context) {
		this(context, null);
	}

	public MultiTouch(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		this.context = context;
		// 初始化操作
		// 是一个 3*3的矩阵.
		// xScale xskwe xtrans
		// yskwe yscale ytrans
		// 0 0 0
		mMatrix = new Matrix();

		setOnTouchListener(this);
	}

	/**
	 * 当 自定义空间 显示在window上
	 */
	@Override
	protected void onAttachedToWindow() {
		super.onAttachedToWindow();
		// 返回一个布局层级视图
		getViewTreeObserver().addOnGlobalLayoutListener(this);// 监听布局加载完成事件
	}

	/**
	 * 当view 从window消失
	 */
	@Override
	protected void onDetachedFromWindow() {
		super.onDetachedFromWindow();
		// 取消监听
		getViewTreeObserver().removeGlobalOnLayoutListener(this);
	}

	// 当图片加载完成后, 图片大的话我们需要进行 缩小, 图片小的话我们需要进行放大.
	// 怎么知道图片加载完成 ? 需要实现 接口 OnGlobalLayoutListener 全球布局监听
	// 布局都加载完的方法. 需要注册

	@Override
	public void onGlobalLayout() {
		// 初始化只需要一次. need boolean 变量
		if (!mOnce) {
			int width = getWidth(); // 控件宽
			int height = getHeight(); // 控件高
			drawable = getDrawable();
			if (drawable != null) {
				// 返回的是根据像素密度 变化后的值.. dip.
				// ldip * 0.75 mdip *1 hdip *1.5 xhdip*2 xxhdip *3
				int dw = drawable.getIntrinsicWidth();
				int dh = drawable.getIntrinsicHeight();
				// 图片宽大于 view宽, 高小于view 高
				if (dw > width && dh < height) {
					scale = width * 1.0f / dw;
				}
				// 图片宽小于view宽,高大于view高
				if (dw < width && dh > height) {
					scale = height * 1.0f / dh;
				}
				// 图片宽高 都大于 view宽高 || 图片宽高 都小于 view宽高
				if ((dw > width && dh > height) || (dw < width && dh < height)) {
					scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);
				}

				mInitScale = scale; // 初始缩放比
				mMidScale = mInitScale * 2; // 双击时缩放比
				mMaxScale = mInitScale * 4; // 最大缩放比

				int dx = getWidth() / 2 - dw / 2;
				int dy = getHeight() / 2 - dh / 2;

				mMatrix.postTranslate(dx, dy);
				mMatrix.postScale(mInitScale, mInitScale);
				setImageMatrix(mMatrix);
				// 多点触控 的api
				mScaleGestureDetector = new ScaleGestureDetector(context, this);
			}
			mOnce = true;
		}
	}

	public float getScale() {
		float[] values = new float[9];
		mMatrix.getValues(values);
		return values[Matrix.MSCALE_X];
	}

	/**
	 * 缩放进行
	 */
	@Override
	public boolean onScale(ScaleGestureDetector detector) {

		float scale = getScale(); // 当前的缩放比
		float currScale = detector.getScaleFactor(); // 准备要缩放的值
		if (drawable == null)
			return true;

		if ((scale < mMaxScale && currScale > 1.0f)
				|| (scale > mInitScale && currScale < 1.0f)) {
			// 进行放大 || 缩小

			if (scale * currScale < mInitScale) { //当缩放比 小于最小点时 直接返回
				return true;
			}
			if (scale * currScale > mMaxScale) { // 当缩放比 大于最大点时,直接返回
				return true;
			}
		}
		mMatrix.postScale(currScale, currScale, detector.getFocusX(),
				detector.getFocusY());
		checkBorderAndCenterWhenScale();
		setImageMatrix(mMatrix);

		return true;
	}

	/**
	 * 返回缩放时候图片的信息
	 * 
	 * @return
	 */
	private RectF getMatrixRectF() {
		Matrix matrix = mMatrix;
		RectF rect = new RectF();

		Drawable d = getDrawable();

		if (d != null) {
			rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
			matrix.mapRect(rect); // 这样就能得到图片放大缩小以后的大小,位置
		}
		return rect;
	}

	/**
	 * 检查边界和中心在缩放时
	 */
	private void checkBorderAndCenterWhenScale() {
		// 获取到缩放后的图片大小
		RectF rect = getMatrixRectF();

		float deltaX = 0f;
		float deltaY = 0f;

		int width = getWidth();
		int height = getHeight();
//		Log.v("info", "rect.left--->" + rect.left);
//		Log.v("info", "rect.top--->" + rect.top);
//		Log.v("info", "rect.right--->" + rect.right);
//		Log.v("info", "rect.bottom--->" + rect.bottom);
//		Log.v("info", "rect.width--->" + rect.width());
//		Log.v("info", "rect.height--->" + rect.height());
		if (rect.width() >= width) {
			if (rect.left > 0) {
				deltaX = -rect.left;
			}
			if (rect.right < width) {
				deltaX = width - rect.right;
			}
		}
		if (rect.height() >= height) {
			if (rect.top > 0) {
				deltaY = -rect.top;
			}
			if (rect.bottom < height) {
				deltaY = height - rect.bottom;
			}
		}

		// 如果宽度或高度 小于控件的宽和高 让齐居中
		if (rect.width() < width) {
			deltaX = width / 2f - rect.right + rect.width() / 2f;
		}

		if (rect.height() < height) {
			deltaY = height / 2f - rect.bottom + rect.height() / 2f;
		}
		mMatrix.postTranslate(deltaX, deltaY);
	}

	/**
	 * 缩放开始
	 */
	@Override
	public boolean onScaleBegin(ScaleGestureDetector detector) {
		// 必须返回true
		return true;
	}
	/**
	 * 缩放结束
	 */
	@Override
	public void onScaleEnd(ScaleGestureDetector detector) {
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		mScaleGestureDetector.onTouchEvent(event);
		return true;
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值