创建一个放大选点的布局
在工作中,遇到了一个需求,需要选中图片上的某个位置,获得对应图像的坐标,如果直接用手去触摸,误差太大,我们想到的一个方式时,触摸图片后,用另外一个 ImageView 显示触摸点附近的一个范围的局部放大图像,随着手指的移动,局部放大图像也会跟着移动,为了防止局部图像移动太快,设定局部图像移动的距离是手指移动的十分之一,这样可以更好的控制移动距离。其中局部放大的的 ImageView 中心有一个指示 icon,即我们选中的点是恰好在局部放大 ImageView 中心位置的点。
public class ImageDotLayout extends FrameLayout {
//放图片的 imageView
private ImageView ivImage;
/**放大镜作用的 imageView,放局部放大的图片 */
private ImageView ivMagnifier;
// 局部放大的图片
private Bitmap bmMagnifier;
// 瞄准镜指示图片,它的作用是,当移动的图片的位置
// 跟瞄准镜指示图片重合时,表明当前选中的点
private ImageView ivTarget;
// 放大图片用到的 Matrix
private Matrix matrix;
// 布局的宽度和高度
private int mWidth, mHeight;
// 瞄准镜指示图片宽高
private int iconTargetW = 16, iconTargetH = 16;
// 触摸点的坐标
private float mX, mY;
// 偏移坐标
private float mOffsetX, mOffsetY;
// 按下的点的中心坐标
private float centerX, centerY;
// 放大的倍数
private float scaleX = 10f, scaleY = 10f;
// 放大镜显示图片的宽高
private int bmMagnifierW = 50, bmMagnifierH = 50;
// 修改后的图片的宽度,高度
private int resultBitmapWidth, resultBitmapHeight;
// 要显示的图片
private Bitmap bitmap;
void initView(Context context) {
ivImage = new ImageView(context);
LayoutParams layoutParams =
new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
addView(ivImage, layoutParams);
// 获取 ivImage 的宽高,暂时在这里实现
ivImage.post(new Runnable() {
@Override
public void run() {
mWidth = ivImage.getWidth();
mHeight = ivImage.getHeight();
ivMagnifier = new ImageView(mContext);
// 修改宽高,改成正方形
int width;
if (mWidth < mHeight) {
width = mWidth / 2;
} else {
width = mHeight / 2;
}
LayoutParams lpMagnifier =
new LayoutParams(width, width);
addView(ivMagnifier, lpMagnifier);
ivMagnifier.setVisibility(GONE);
ivTarget = new ImageView(mContext);
LayoutParams lpTarget =
new LayoutParams(iconTargetW, iconTargetH);
ivTarget.setImageDrawable(getResources().getDrawable(R.drawable.icon_target));
addView(ivTarget, lpTarget);
ivTarget.setVisibility(GONE);
}
});
matrix = new Matrix();
matrix.postScale(scaleX, scaleY);
ivImage.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (bitmap == null) {
return true;
}
// x y 值做一个处理
// 放大的图加一个中心标准点
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mX = event.getX();
mY = event.getY();
centerX = mX;
centerY = mY;
// 对 x 和 y 进行一个判断,来设置放大的 imageView 在哪里显示
if (mX < mWidth / 2 && mY < mHeight / 2) {
// 调整位置
LayoutParams lpMagnifier = (LayoutParams) ivMagnifier.getLayoutParams();
if (mWidth < mHeight) {
lpMagnifier.leftMargin = mWidth / 2;
lpMagnifier.topMargin = mHeight / 2 + ((mHeight - mWidth) / 4);
} else {
lpMagnifier.leftMargin = mWidth / 2 + ((mWidth - mHeight) / 4);
lpMagnifier.topMargin = mHeight / 2;
}
ivMagnifier.setLayoutParams(lpMagnifier);
LayoutParams lpTarget = (LayoutParams) ivTarget.getLayoutParams();
lpTarget.leftMargin = mWidth / 4 * 3 - iconTargetW / 2;
lpTarget.topMargin = mHeight / 4 * 3 - iconTargetH / 2;
ivTarget.setLayoutParams(lpTarget);
} else if (mX > mWidth / 2 && mY < mHeight / 2) {
LayoutParams lpMagnifier = (LayoutParams) ivMagnifier.getLayoutParams();
if (mWidth < mHeight) {
lpMagnifier.leftMargin = 0;
lpMagnifier.topMargin = mHeight / 2 + ((mHeight - mWidth) / 4);
} else {
lpMagnifier.leftMargin = 0 + ((mWidth - mHeight) / 4);
lpMagnifier.topMargin = mHeight / 2;
}
ivMagnifier.setLayoutParams(lpMagnifier);
LayoutParams lpTarget = (LayoutParams) ivTarget.getLayoutParams();
lpTarget.leftMargin = mWidth / 4 - iconTargetW / 2;
lpTarget.topMargin = mHeight / 4 * 3 - iconTargetH / 2;
ivTarget.setLayoutParams(lpTarget);
} else if (mX < mWidth / 2 && mY > mHeight / 2) {
LayoutParams lpMagnifier = (LayoutParams) ivMagnifier.getLayoutParams();
if (mWidth < mHeight) {
lpMagnifier.leftMargin = mWidth / 2;
lpMagnifier.topMargin = 0 + ((mHeight - mWidth) / 4);
} else {
lpMagnifier.leftMargin = mWidth / 2 + ((mWidth - mHeight) / 4);
lpMagnifier.topMargin = 0;
}
ivMagnifier.setLayoutParams(lpMagnifier);
LayoutParams lpTarget = (LayoutParams) ivTarget.getLayoutParams();
lpTarget.leftMargin = mWidth / 4 * 3 - iconTargetW / 2;
lpTarget.topMargin = mHeight / 4 - iconTargetH / 2;
ivTarget.setLayoutParams(lpTarget);
} else {
LayoutParams lpMagnifier = (LayoutParams) ivMagnifier.getLayoutParams();
if (mWidth < mHeight) {
lpMagnifier.leftMargin = 0;
lpMagnifier.topMargin = 0 + ((mHeight - mWidth) / 4);
} else {
lpMagnifier.leftMargin = 0 + ((mWidth - mHeight) / 4);
lpMagnifier.topMargin = 0;
}
ivMagnifier.setLayoutParams(lpMagnifier);
LayoutParams lpTarget = (LayoutParams) ivTarget.getLayoutParams();
lpTarget.leftMargin = mWidth / 4 - iconTargetW / 2;
lpTarget.topMargin = mHeight / 4 - iconTargetH / 2;
ivTarget.setLayoutParams(lpTarget);
}
// 放大的 imageView 显示
ivMagnifier.setVisibility(VISIBLE);
ivMagnifier.bringToFront();
ivTarget.setVisibility(VISIBLE);
ivTarget.bringToFront();
showMagnifierImage();
break;
case MotionEvent.ACTION_MOVE:
// 将要放大的图片位置显示到 imageView 中
float dx = event.getX() - centerX;
float dy = event.getY() - centerY;
mX = centerX + dx / 10;
mY = centerY + dy / 10;
showMagnifierImage();
break;
case MotionEvent.ACTION_UP:
// 放大的 imageView 隐藏
ivMagnifier.setVisibility(GONE);
ivTarget.setVisibility(GONE);
// 保存当前位置的点
if (bmMagnifier != null) {
Log.e("WillWolf", "mX:" + mX + ", mY" + mY + ", mOffsetX" + mOffsetX + ", mOffsetY" + mOffsetY);
}
break;
}
return true;
}
});
}
private void showMagnifierImage() {
int width = bmMagnifierW;
int height = bmMagnifierH;
// 目标点的坐标
float dst[] = convertPosition(mX, mY);
float dstX = dst[0];
float dstY = dst[1];
// 这里是表示最右边
if (dstX + bmMagnifierW / 2 >= bitmap.getWidth()) {
width = (int) (bmMagnifierW - (dstX + bmMagnifierW / 2 - bitmap.getWidth()));
if (width <= 0) {
width = 1;
}
} else {
width = bmMagnifierW;
}
// 这里是最下边
if (dstY + bmMagnifierH / 2 >= bitmap.getHeight()) {
height = (int) (bmMagnifierH - (dstY + bmMagnifierH / 2 - bitmap.getHeight()));
if (height <= 0) {
height = 1;
}
} else {
height = bmMagnifierH;
}
// 如果从屏幕边缘处下滑,会出现值 < 0 的情况
if (dstX - bmMagnifierW / 2 >= 0 && dstY - bmMagnifierH / 2 >= 0) {
// 做了边界处理,有可能会导致左边的位置 + 显示的宽度 > 图片的宽度而崩溃。
int x = (int) dstX - bmMagnifierW / 2;
if (x + width >= bitmap.getWidth()) {
x = bitmap.getWidth() - width;
}
int y = (int) dstY - bmMagnifierH / 2;
if (y + height >= bitmap.getHeight()) {
y = bitmap.getHeight() - height;
}
bmMagnifier = Bitmap.createBitmap(bitmap, x, y, width, height, matrix, true);
ivMagnifier.setImageBitmap(bmMagnifier);
} else {
// 如果 dstX 大于 0, 表示是在最左边
if (dstX > 0 && dstY - bmMagnifierH / 2 >= 0) {
bmMagnifier = Bitmap.createBitmap(bitmap, (int) dstX, (int) dstY - bmMagnifierH / 2, (int) (dstX * 2), height, matrix, true);
ivMagnifier.setImageBitmap(bmMagnifier);
} else if (dstX - bmMagnifierW / 2 >= 0 && dstY > 0) {
// 如果 dstY 大于 0,表示是在最上边
bmMagnifier = Bitmap.createBitmap(bitmap, (int) dstX - bmMagnifierW / 2, (int) dstY, width, (int) (dstY * 2), matrix, true);
ivMagnifier.setImageBitmap(bmMagnifier);
} else {
}
}
}
/**
* 设置图像并显示
*
*/
public void setBitmap(int direction, Bitmap bitmap) {
// 为了解决边界问题,对 bitmap 进行一个扩大操作
resultBitmapWidth = bitmap.getWidth() + 50;
resultBitmapHeight = bitmap.getHeight() + 50;
Bitmap result = Bitmap.createBitmap(resultBitmapWidth,
resultBitmapHeight, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(result);
canvas.drawBitmap(bitmap, 25, 25, null);
this.bitmap = result;
ivImage.setImageBitmap(result);
}
private int mOffsetX;
private int mOffsetY;
public float[] convertPosition(float x, float y) {
// 使用系统提供的 api 进行坐标点的转换,即 ImageView 的坐标点
// 转换成 Bitmap 上的坐标点
// 目标点的坐标,必须要有图片设置到 ImageView 中
float dst[] = new float[2];
// 获取到ImageView的matrix
Matrix imageMatrix = photoView.getImageMatrix();
// 创建一个逆矩阵
Matrix inverseMatrix = new Matrix();
// 求逆,逆矩阵被赋值
imageMatrix.invert(inverseMatrix);
// 通过逆矩阵映射得到目标点 dst 的值
inverseMatrix.mapPoints(dst, new float[]{x, y});
// 进行一个比例换算,获取到 Bitmap 上的坐标点
// 比如 Bitmap 尺寸是 1280 * 720,ImageView
// 的尺寸是 200 * 100,那么 ImageView 的一半位置
// 肯定也对应 Bitmap 一半的位置。
float[] realPoint = new float[2];
realPoint[0] = resultBitmapWidth * x / mWidth;
realPoint[1] = resultBitmapHeight * y / mHeight;
// 上述两种计算方式会有一点偏差。
mOffsetX = (realPoint[0] - dst[0]) * mWidth / resultBitmapWidth;
mOffsetY = (realPoint[1] - dst[1]) * mHeight / resultBitmapHeight;
Log.e("WillWolf", "mOffset-->" + mOffsetX + ", " + mOffsetY);
return realPoint;
}
public void release() {
if (bitmap != null) {
bitmap.recycle();
}
if (bmMagnifier != null) {
bmMagnifier.recycle();
}
}
}