Android 图片拖拽、放大缩小的自定义控件

2023-10-28

需求:像相册中的图片跟随手指拖动,双指的放大和缩小,相册中拖出范围之后有弹回的动画,感觉上很圆润,很舒服,我写的例子中并没有加动画

思路:1.自定义DragImageView.java

            2.自定义中先画图片,图片大于屏幕就把图片缩小后显示,图片小于屏幕就直接显示,显示之前要计算显示图片的Rect,Rect是其实就是四个坐标,用来控制显示图片的范                  围,这个Rect是根据图片的长宽比例计算而来,显示在屏幕中间。

            3.跟随手指移动:在touchEvent事件中处理好单双指的各种事件之后,跟随手指就是不断的改变Rect的坐标,然后不断的invalidate()(该方法是重新强制绘制View);

            4.双指缩放:双指缩放的时候从图片Rect的中心缩放的,没有实现相册那种从手指中心缩放的那种。

            5.单指和双指的事件在touc事件中已近处理好了

                   event.getPointerCount()==2的情况就是双指部分

                   float    X0,Y0,X1,Y1双指的四个坐标

                   在双指移动的时候计算一个双指的距离出来m1,在MOVE执行的时候重新计算一个新的距离m2,去判断m1和m2大小来判断双指是张开还是捏合,并且执行相应事件,判断之后把m2复制给m1,这时的MOVE事件会有新的m2出来,所以就一直判断就ok了。放大和缩小就是按照一定的比例去改变Rect的值。


DragImageView.java

public class DragImageView extends View {

	private Paint mPaint;
	private Drawable mDrawable;
	private Rect mDrawableRect = new Rect();
	// private Rect mDrawableOffsetRect = new Rect();
	private Context mContext;
	private float mRation_WH = 0;
	private float mOldX = 0;
	private float mOldY = 0;
	private float mOldX0, mOldY0, mOldX1, mOldY1, mOldK, mOldB, mOldHandsX,
			mOldHandsY;
	private double mD1;
	private boolean isFirst = true;
	private int SINGALDOWN = 1;// 单点按下
	private int MUTILDOWM = 2;// 双点按下
	private int MUTILMOVE = 3;// 双点拖拽
	private int mStatus = 0;

	enum STATUS {
		SINGAL, MUTILDOWN, MUTILMOVE;
	}

	public DragImageView(Context context) {
		super(context);
		this.mContext = context;
		mPaint = new Paint();
		mPaint.setAntiAlias(true);
		mPaint.setColor(Color.BLACK);
		mPaint.setStyle(Style.FILL);
		mPaint.setTextSize(35.0f);
	}

	@SuppressLint("DrawAllocation")
	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		if (mDrawable == null || mDrawable.getIntrinsicHeight() == 0
				|| mDrawable.getIntrinsicWidth() == 0) {
			return;
		}
		setBounds();
		mDrawable.draw(canvas);
		// Log.i("draw", "draw+++++++++++++++++++++++++++++++++++++++");

	}

	@SuppressLint("ClickableViewAccessibility")
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		switch (event.getPointerCount()) {
		case 1:

			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				mStatus = SINGALDOWN;
				mOldX = event.getX();
				mOldY = event.getY();
				// Log.i("x_y_down", event.getX() + "__" + event.getY());
				break;
			case MotionEvent.ACTION_UP:
				checkBounds();
				// Log.i("x_y_up", event.getX() + "__" + event.getY());
				break;
			case MotionEvent.ACTION_MOVE:
				// Log.i("x_y_move", event.getX() + "__" + event.getY());
				if (mStatus == SINGALDOWN) {
					int offsetWidth = (int) (event.getX() - mOldX);
					int offsetHeight = (int) (event.getY() - mOldY);
					// Log.i("x_y_offset", offsetWidth + "__" + offsetHeight);
					mOldX = event.getX();
					mOldY = event.getY();
					mDrawableRect.offset(offsetWidth, offsetHeight);
					invalidate();
				}

				break;
			default:
				break;
			}
			break;
		default:
			/*
			 * mStatus = MUTILDOWM; if (mStatus == MUTILDOWM) { mOldX0 =
			 * event.getX(0); mOldY0 = event.getY(0); mOldX1 = event.getX(1);
			 * mOldY1 = event.getY(1); mOldK = (mOldY1 - mOldY0) / (mOldX1 -
			 * mOldX0); mOldB = (mOldY0 * mOldX1 - mOldY1 * mOldX0) / (mOldX1 -
			 * mOldX0); mOldHandsX = (mOldX0 + mOldX1) / 2; mOldHandsY =
			 * mOldHandsX * mOldK + mOldB; mD1 = Math.sqrt(Math.pow(mOldX0 -
			 * mOldX1, 2) + Math.pow(mOldY0 - mOldY1, 2)); Log.i("mD1", mD1 +
			 * "________________"); }
			 */
			switch (event.getAction()) {
			case MotionEvent.ACTION_POINTER_DOWN:
				Log.i("DOUBLETOWDOWN", "true");
				break;
			case MotionEvent.ACTION_MOVE:
				// Log.i("x_y_move", event.getX(0) + "__" + event.getY(0) +
				// "___"
				// + event.getX(1) + "__" + event.getY(1));
				mStatus = MUTILMOVE;
				float X0 = event.getX(0);
				float Y0 = event.getY(0);
				float X1 = event.getX(1);
				float Y1 = event.getY(1);
				float k = (Y1 - Y0) / (X1 - X0);
				float b = (Y0 * X1 - Y1 * X0) / (X1 - X0);
				int RectCenterX = mDrawableRect.centerX();
				int RectCenterY = mDrawableRect.centerY();
				float mHandsX = (X0 + X1) / 2;
				float mHandsY = mHandsX * k + b;
				double mD2 = Math.sqrt(Math.pow(X0 - X1, 2)
						+ Math.pow(Y0 - Y1, 2));

				Log.i("GCM", mD2 + "________________X:" + mHandsX + "___Y:"
						+ mHandsY);
				if (mD1 < mD2) {

					// double mMultiple = mD2 / mD1;
					// int newWidth = (int) (mDrawableRect.width() * mMultiple);
					// int newHeight = (int) (newWidth / mRation_WH);
					//
					// int newleft = mDrawableRect.left / 2;
					// int newtop = mDrawableRect.top / 2;
					// int newright = mDrawableRect.right * (3 / 2);
					// int newbotto = mDrawableRect.bottom * (3 / 2);
					// // mDrawableRect.set(newleft, newtop, newright,
					// newbotto);
					//
					// mDrawableRect.set(RectCenterX - newWidth / 2, RectCenterY
					// - newHeight / 2, RectCenterX + newWidth / 2,
					// RectCenterY + newHeight / 2);
					// invalidate();
					if (mDrawableRect.width() < mContext.getResources()
							.getDisplayMetrics().widthPixels * 2) {
						int offsetwidth = 10;
						int offsettop = (int) (offsetwidth / mRation_WH);
						mDrawableRect.set(mDrawableRect.left - offsetwidth,
								mDrawableRect.top - offsettop,
								mDrawableRect.right + offsetwidth,
								mDrawableRect.bottom + offsettop);
						Log.i("GCM", "aaaaaaaaaaaaaaa");

						invalidate();
					}
					// mDrawableRect.offset((int) mHandsX, (int) mHandsY);

				} else {
					if (mDrawableRect.width() > mContext.getResources()
							.getDisplayMetrics().widthPixels / 3) {
						int offsetwidth = 10;
						int offsettop = (int) (offsetwidth / mRation_WH);
						mDrawableRect.set(mDrawableRect.left + offsetwidth,
								mDrawableRect.top + offsettop,
								mDrawableRect.right - offsetwidth,
								mDrawableRect.bottom - offsettop);
						invalidate();
						Log.i("GCM", "bbbbbbbbbbbbbbb");
					}
				}
				mD1 = mD2;
				if (mHandsX < RectCenterX) {
					if (mHandsY < RectCenterY) {
						Log.i("PPPPPPP", "1");

					} else {
						Log.i("PPPPPPP", "3");
					}
				} else {
					if (mHandsY < RectCenterY) {
						Log.i("PPPPPPP", "2");
					} else {
						Log.i("PPPPPPP", "4");
					}
				}

				//

				break;
			case MotionEvent.ACTION_UP:
				Log.i("mStatus", "mutildouble_up");
				mStatus = 0;
				break;
			default:
				break;
			}
			break;
		}

		return true;
	}

	public void setBounds() {
		if (isFirst) {
			mRation_WH = (float) mDrawable.getIntrinsicWidth()
					/ (float) mDrawable.getIntrinsicHeight();
			int px_w = Math.min(getWidth(),
					dip2px(mContext, mDrawable.getIntrinsicWidth()));
			int px_h = (int) (px_w / mRation_WH);
			int left = (getWidth() - px_w) / 2;
			int top = (getHeight() - px_h) / 2;
			int right = px_w + left;
			int bottom = px_h + top;
			mDrawableRect.set(left, top, right, bottom);
			// mDrawableOffsetRect.set(mDrawableRect);
			isFirst = false;
			Log.i("rect1______", mDrawableRect.left + "," + mDrawableRect.top
					+ "," + mDrawableRect.right + "," + mDrawableRect.bottom);
		}
		mDrawable.setBounds(mDrawableRect);
		Log.i("rect2______", mDrawableRect.left + "," + mDrawableRect.top + ","
				+ mDrawableRect.right + "," + mDrawableRect.bottom);
		Log.i("center_______",
				mDrawableRect.centerX() + "," + mDrawableRect.centerY());

	}

	public void checkBounds() {
		int newLeft = mDrawableRect.left;
		int newTop = mDrawableRect.top;
		boolean isChange = false;
		if (newLeft < -mDrawableRect.width()) {
			newLeft = -mDrawableRect.width();
			isChange = true;
		}
		if (newTop < -mDrawableRect.height()) {
			newTop = -mDrawableRect.height();
			isChange = true;
		}
		if (newLeft > getWidth()) {
			newLeft = getWidth();
			isChange = true;
		}
		if (newTop > getHeight()) {
			newTop = getHeight();
			isChange = true;
		}
		if (isChange) {
			mDrawableRect.offsetTo(newLeft, newTop);
			invalidate();
		}
	}

	public Drawable getmDrawable() {
		return mDrawable;
	}

	public void setmDrawable(Drawable mDrawable) {
		this.mDrawable = mDrawable;
	}

	public int dip2px(Context context, int value) {
		final float scale = context.getResources().getDisplayMetrics().density;
		return (int) (value * scale + 0.5f);
	}

}
在界面中使用


public class MainActivity extends Activity {

	Context mContext;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		this.mContext = getApplicationContext();
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		DragImageView mView = new DragImageView(mContext);
		mView.setmDrawable(mContext.getResources().getDrawable(R.drawable.bbb));
		setContentView(mView);
		
	}
}

效果



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 图片拖拽、放大缩小的自定义控件 的相关文章

随机推荐

  • SpringCloud Config简介

    简介 Spring Cloud Config为分布式系统的外部配置提供服务端 server 和客户端 client 的支持 Config服务端提供了一个集中的地方来管理所有环境下各个应用的配置 Config客户端即普通的Spring应用 但
  • history命令查看操作时间

    export HISTTIMEFORMAT F T whoami 给history加上时间戳 展示 1013 2016 08 08 12 15 40 root y 1014 2016 08 08 12 15 41 root df 1015
  • LDO系列--LDO并联扩容

    1 不能简单并联 无法电流均衡 两个LDO的内部的带隙基准源 参考电压 FET的特性 以及误差放大器的噪声不同 如失调电压 实际LDO输出的目标电压依旧是有差异的 这就导致了 LDO High的目标输出电压高一些 5V 相反LDO Low的
  • define #使用

    include
  • 定制音库成本骤降98%,PaddleSpeech小样本语音合成方案重磅来袭!

    随着以语音为交互渠道的产业不断升级 企业对语音合成有着越来越多的需求 比如智能语音助手 手机地图导航 有声书播报等场景都需要用到语音合成技术 通过语音合成技术想要得到一个新的音色 需要定制音库 但是定制音库所耗费的人力成本和时间成本巨大 成
  • (字符串)寻找字符串连续最多出现的字符以及出现的次数

    function maxLength str var arr str split var max item count 1 var count 1 for var i 1 i
  • [工业互联-12]:主流的工业以太网技术简介(PROFINET、POWERLINK、ETHERNET/IP、ETHERCAT、SERCOSIII、MODBUS TCP、CC-LINK IE)

    目录 前言 1 工业通信要求 1 1 工业通信网络分层模型 1 2 工业控制的实时性要求 2 以太网技术 2 1 协议分层 2 2 实时应用 3 常见工业以太网技术 3 1 PROFINET通信 3 2 POWERLINK通信 3 3 Et
  • MySQL安装及初始密码设置

    运行mysql help grep my cnf查看my cnf配置位置的读取顺序 etc my cnf不存在 则我们需要在etc下创建my cnf配置文件 mysql会优先度读取 ps ef grep mysqld mysql不要随意修改
  • 类中的静态常量

    关于类中静态常量的声明和定义 class Test public static const int const value 1 static const int const value 1为类中的声明式 而非定义式 定义与声明 定义即为一个
  • 多CPU与单CPU的spin_lock使用上的区别

    这样 考虑这四个方面的因素 通过判断我们要互斥的数据会被这四个因素中 的哪几个来存取 就可以决定具体使用哪种形式的spinlock 如果只要和其他CPU 互斥 就要用spin lock spin unlock 如果要和irq及其他CPU互斥
  • 从性能考虑web的应用设计

    Author skate Time 2010 5 21 从性能考虑web的应用设计 最近看了一些性能优化的资料 根据我们自身系统的特点谈谈想法 希望对大家有帮助 1 适当的放弃一致性说 这里的一致性不仅指数据的一致性 还广泛指集中的紧耦合的
  • Linux rpm 命令 【转】

    文章来源 Linux下面使用rpm命令 RPM是RedHat Package Manager RedHat软件包管理工具 类似Windows里面的 添加 删除程序 rpm 执行安装包 二进制包 Binary 以及源代码包 Source 两种
  • visual studio 2017 报错 无法下载安装文件。请检查Internet连接,然后重试

    vs下载完离线安装包在无网络环境下点击安装时出现无法下载安装文件 请检查Internet连接 然后重试 原因是证书没有导 解决方法 打开刚刚存放离线文件的路径 比如D vs2017offline 然后找到certificates文件夹并打开
  • SpringBoot对接支付宝当面付和手机网站支付

    一 前期准备 1 注册商家账号 支付宝 2 登录进入 在产品中心选择自己所需要对接的功能 3 进入对接功能 选择立即开通 4 填写商户信息 5 开通成功之后 产品中心 gt gt 开发设置 gt gt 创建应用并关联 进行应用创建 6 应用
  • Kubernetes 弹性伸缩全场景解析(三) - HPA 实践手册

    在上一篇文章中 给大家介绍和剖析了 HPA 的实现原理以及演进的思路与历程 本文我们将会为大家讲解如何使用 HPA 以及一些需要注意的细节 autoscaling v1 实践 v1 的模板可能是大家平时见到最多的也是最简单的 v1 版本的
  • 【Audio音频兴趣拓展】Pop音的几种原因及解决方案

    1 硬件贴片错误可能会导致pop音 比如两个输入电容的容值相差过大 一个33nf 一个1uf 这时在播放音乐的开始阶段可能会发生POP音 2 时序控制不合理也会导致POP音 正确的时序应该为 开启时 先开启BB音源 delay 20 30m
  • idea之热部署插件jrebel的使用

    背景 一个java web项目 在写的过程中我们需要不断调试 如果没有热部署 则我们每修改一次项目要重启一次 验证问题有没有得到解决 如果项目很小 启动只要几秒或十几秒 可能感觉影响不是很大 但当项目变大了 重启一次需要几十秒 几分钟 甚至
  • 利用python进行数据分析,学习笔记1(numpy) ndarray的创建与修改

    我是通过学习mooc上嵩天老师的数据分析与展示和阅读 利用python进行数据分析 做出的笔记 import numpy as np 为了缩小代码量 公认约定使用np作为numpy from numpy import 往往实不可取的 因为它
  • Ubuntu时间显示不准确的解决方案

    参考 解决ubuntu里面时间不正确的办法 作者 三速何时sub20 发布时间 2020 12 08 16 24 27 网址 https blog csdn net weixin 44234294 article details 11087
  • Android 图片拖拽、放大缩小的自定义控件

    需求 像相册中的图片跟随手指拖动 双指的放大和缩小 相册中拖出范围之后有弹回的动画 感觉上很圆润 很舒服 我写的例子中并没有加动画 思路 1 自定义DragImageView java 2 自定义中先画图片 图片大于屏幕就把图片缩小后显示