巧家县住房和城乡建设局网站,接网站建设的单子,免费制作网站net域名,快速微信网站建设一、前言
很久没有更新Android 原生技术内容了#xff0c;前些年一直在做跨端方向开发#xff0c;最近换工作用重新回到原生技术#xff0c;又回到了熟悉但有些生疏的环境#xff0c;真是感慨万分。 近期也是因为准备做地图交互相关的需求#xff0c;功能非常复杂#x…一、前言
很久没有更新Android 原生技术内容了前些年一直在做跨端方向开发最近换工作用重新回到原生技术又回到了熟悉但有些生疏的环境真是感慨万分。 近期也是因为准备做地图交互相关的需求功能非常复杂尤其是交互部分不过再复杂的交互只要一点点将它拆解分而治之问题还是可以解决就比如接下来要做的放大镜功能。
二、功能设计
该功能的场景是在操作地图时对于边缘的精细化操作像素级别的需要在放大镜里显示正在操作的地图区域。比如
如上图我在操作地图里的内容时可以在左上角看到我手指操作的内容。 这里需要支持如下几点
支持设置放大镜的放大倍数支持实时更新放大镜里的内容手指操作地图时放大镜要里的内容要一直显示刷新手指松开时放大镜里的内容要清空。放大镜是个圆形。
三、功能实现
分下下来放大镜的功能实现拆解下来可以分成两部分来实现。
绘制圆形容器。绘制手指操作的区域内容。 接下来我们逐个实现
3.1 绘制圆形容器
关于绘制圆形容器这里会涉及到Path 路径知识因为正常的图形都是方形因此需要图形裁切才行这里会涉及到canvas.clipPathAPI。 具体代码如下 private Paint paint;// 用于裁剪成圆形private Path clipPath;private void init() {paint new Paint(Paint.ANTI_ALIAS_FLAG);clipPath new Path();}Overrideprotected void onDraw(NonNull Canvas canvas) {super.onDraw(canvas);// 获取 MagnifierView 的宽高int viewWidth getWidth();int viewHeight getHeight();// 计算圆形半径float radius Math.min(viewWidth, viewHeight) / 2f;clipPath.reset();// 绘制圆形路径clipPath.addCircle(viewHeight / 2f, (float) viewHeight /2,radius,Path.Direction.CW);// 裁切圆形画布canvas.clipPath(clipPath);}3.2 绘制手指操作区域
放大镜本质就是放大图片而图片在Android 里面就是Bitmap。这里有个问题。
3.2.1 如何获取当前手指操作View 的Bitmap呢
答案是用getDrawingCache。 具体实现如下
setDrawingCacheEnabled(true);
Bitmap bitmap Bitmap.createBitmap(getDrawingCache());
setDrawingCacheEnabled(false);当然这里里的Bitmap是一整个View的我们仅想放大手指操作比如点击、滑动区域的Bitmap这就需要手指所在的坐标区域。 我们可以自定义一个View然后重写它的onTouchEvent方法通过event.getX(), event.getY()获取手指实时操作的坐标然后传给放大镜View比如我们这次自定义的放大镜MagnifierView里面连同前面的Bitmap一起传过去。这里可以搞一个回调接口。 public interface MagnifierListener {// 传递放大镜内容void onMagnify(Bitmap bitmap, float x, float y);// 隐藏放大镜void onHideMagnifier();}3.2.2 绘制操作区域
得到了要绘制的内容Bitmap接下来就是绘制出来。 绘制Bitmap很简单就是调用下面的API
canvas.drawBitmap(bitmap, srcRect, dstRect, paint);复杂的是计算放大内容的源区域也就是srcRect。 这里先解释下dstRect它是定义目标矩形也就是放大镜自身大小实现如下
RectF dstRect new RectF(0, 0, viewWidth, viewHeight);接下来我们开始设置srcRect。写到这里我们好像漏了一个关键参数“放大倍数”。
// 放大倍数
private static final float SCALE_FACTOR 2.0f;因为我们要显示的区域和放大倍数有直接关联。srcRect 是一个Rect 对象它里面有四个参数左上右下相当于当前显示区域范围计算公式如下
// 计算放大内容的源区域
float srcLeft focusX - (viewWidth / SCALE_FACTOR) / 2;
float srcTop focusY - (viewHeight / SCALE_FACTOR) / 2;
float srcRight focusX (viewWidth / SCALE_FACTOR) / 2;
float srcBottom focusY (viewHeight / SCALE_FACTOR) / 2;先来解释下上面的公式意义focusX对应的就是手指操作的x坐标即event.getX()focusY 同理则是event.getY()。 SCALE_FACTOR 是放大倍数。 因为我们想要显示的是“手指为中心显示区域大小是当前放大镜的面积”因此需要(viewWidth / SCALE_FACTOR) / 2这里用focusX是确定左边界的位置。后面参数计算和前面差不多这里我不重复解释。不过写到这里还不算完成。为了防止越界这里还需要做一次矫正防护
// 防止越界
srcLeft Math.max(0, srcLeft);
srcTop Math.max(0, srcTop);
srcRight Math.min(bitmap.getWidth(), srcRight);
srcBottom Math.min(bitmap.getHeight(), srcBottom);最后的放大区域代码如下 // 计算放大内容的源区域float srcLeft focusX - (viewWidth / SCALE_FACTOR) / 2;float srcTop focusY - (viewHeight / SCALE_FACTOR) / 2;float srcRight focusX (viewWidth / SCALE_FACTOR) / 2;float srcBottom focusY (viewHeight / SCALE_FACTOR) / 2;// 防止越界srcLeft Math.max(0, srcLeft);srcTop Math.max(0, srcTop);srcRight Math.min(bitmap.getWidth(), srcRight);srcBottom Math.min(bitmap.getHeight(), srcBottom);// 定义源矩形放大的内容区域Rect srcRect new Rect((int) srcLeft,(int) srcTop,(int) srcRight,(int) srcBottom);3.3 小结
这里附上这个功能的完整代码
/*** author : coffer* date : 2025/1/11* description : 放大镜视图*/
public class MagnifierView extends View {private float focusX 0f; // 放大内容的中心点Xprivate float focusY 0f; // 放大内容的中心点Y// 要放大的内容private Bitmap bitmap;private Paint paint;// 用于裁剪成圆形private Path clipPath;// 放大倍数private static final float SCALE_FACTOR 2.0f;// 是否可以显示private boolean isVisible false;public MagnifierView(Context context) {super(context);init();}public MagnifierView(Context context, Nullable AttributeSet attrs) {super(context, attrs);init();}public MagnifierView(Context context, Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {paint new Paint(Paint.ANTI_ALIAS_FLAG);clipPath new Path();}/*** 设置放大镜的内容和位置*/public void setFocus(Bitmap bitmap, float x, float y) {this.bitmap bitmap;this.focusX x;this.focusY y;this.isVisible true;invalidate(); // 触发重绘}/*** 隐藏放大镜*/public void hide() {this.isVisible false;invalidate();}Overrideprotected void onDraw(NonNull Canvas canvas) {super.onDraw(canvas);if (!isVisible || bitmap null) {return;}// 获取 MagnifierView 的宽高int viewWidth getWidth();int viewHeight getHeight();// 计算圆形半径float radius Math.min(viewWidth, viewHeight) / 2f;clipPath.reset();clipPath.addCircle(viewHeight / 2f, (float) viewHeight /2,radius,Path.Direction.CCW);canvas.clipPath(clipPath);// 计算放大内容的源区域float srcLeft focusX - (viewWidth / SCALE_FACTOR) / 2;float srcTop focusY - (viewHeight / SCALE_FACTOR) / 2;float srcRight focusX (viewWidth / SCALE_FACTOR) / 2;float srcBottom focusY (viewHeight / SCALE_FACTOR) / 2;// 防止越界srcLeft Math.max(0, srcLeft);srcTop Math.max(0, srcTop);srcRight Math.min(bitmap.getWidth(), srcRight);srcBottom Math.min(bitmap.getHeight(), srcBottom);// 定义源矩形放大的内容区域Rect srcRect new Rect((int) srcLeft,(int) srcTop,(int) srcRight,(int) srcBottom);// 定义目标矩形放大镜自身大小RectF dstRect new RectF(0, 0, viewWidth, viewHeight);// 绘制放大内容canvas.drawBitmap(bitmap, srcRect, dstRect, paint);}
}?xml version1.0 encodingutf-8?
shape xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:shapeovalstroke android:color#abf4dbandroid:width1dp//shape上面的这个drawable 可有可无这里这是方便测试用。
四、总结
一开始接到这个需求的时候我是真的有些懵因为以前从来没有写过。不过后来在仔细拆分问题后觉的还是可以实现的。这里我要着重感谢ChatGPT它给了我很大的帮助就比如这个功能的实现它就给了我思路就像老师一样能从它身上学到很多技能。 但是请注意不能完全依赖它即使AI 帮你做了你也必须要深入搞懂背后的原理要把知识吸收到自己大脑思维中否则未来你将会被AI取代