版权声明:本博客主要记录学习笔记和遇到的一些问题解决方案,转载请注明出处! https://blog.csdn.net/u010982507/article/details/81211920
自定义view图片缩放控件
ImageView控件只能展示图片,不能对图片进行手势缩放,此篇文章主要实现对图片的展示和缩放,技能点主要有以下几个方面:
- 继承View,实现自定义View功能
- 绘制bitmap,显示图片
- 实现对图片拖动功能
- 实现对图片的放大缩小功能
- 实现图片的裁剪功能
演示效果图
直接看代码会枯燥,这里先看下演示效果图
代码实现
1、自定义view类CropView,实现图片显示和缩放功能
public class CropView extends View {
public CropView(Context context) {
super(context);
init();
}
public CropView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public CropView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
// 初始化手势识别类
private ScaleGestureDetector scaleGestureDetector;
private GestureDetector gestureDetector;
private Matrix matrix;
private Bitmap bitmap;
private float[] matrixArray;
private float setMinimumScale = 0.2f;
private float maximumScale = 4.0f;
private Rect restrictBound;
private Rect getRestrictedBound() {
return restrictBound;
}
private void init() {
matrix = new Matrix();
matrixArray = new float[9];
// 初始化缩放手势识别类,用于图片的缩放
scaleGestureDetector = new ScaleGestureDetector(getContext(), onScaleGestureListener);
// 初始化手势识别类,用于图片的拖动
gestureDetector = new GestureDetector(getContext(), onGestureListener);
}
// 缩放手势监听
private ScaleGestureDetector.OnScaleGestureListener onScaleGestureListener = new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
// 处理图片缩放
scale(scaleGestureDetector);
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
// 设为true,才能执行接下来的操作,onScale --> onScaleEnd
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
//获取本次缩放事件的缩放因子(缩放事件以onScale()返回值为基准,一旦该方法返回true,代表本次事件结束,要开启下次事件)
float scale = scaleGestureDetector.getScaleFactor();
matrix.postScale(scale, scale);
invalidate();
}
};
// 设置缩放
private void scale(ScaleGestureDetector detector) {
// detector.getScaleFactor() 获取缩放因子,每次都是从1开始,缩小会小于1,放大会大于1
float scale = detector.getScaleFactor();
Log.e("scale: ", scale + "");
// 当前图片矩阵的缩放,放大会增加,缩小会减少
float currentScale = getScale();
Log.e("current scale: ", currentScale + "");
// 设置最小缩放
if (currentScale * scale < setMinimumScale) {
scale = setMinimumScale / currentScale;
}
// 设置最大缩放
if (currentScale * scale > maximumScale) {
scale = maximumScale / currentScale;
}
// 图片以焦点为中心,缩放,缩放比例为scale
matrix.postScale(scale, scale, detector.getFocusX(), detector.getFocusY());
// 更新view
invalidate();
}
// Matrix.MSCALE_X x轴缩放比,Matrix.MSKEW_X x轴旋转角度
private float getScale() {
// 将矩阵赋值给matrixArray
matrix.getValues(matrixArray);
// 获取X轴缩放比
float scale = matrixArray[Matrix.MSCALE_X];
if (Math.abs(scale) <= 0.1) { // 处理最低缩放
scale = matrixArray[Matrix.MSKEW_X];
}
return Math.abs(scale);
}
// 手势监听
private GestureDetector.OnGestureListener onGestureListener = new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent motionEvent) {
return false;
}
@Override
public void onShowPress(MotionEvent motionEvent) {
}
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
return false;
}
@Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float distanceX, float distanceY) {
// 移动
translate(distanceX, distanceY);
return false;
}
@Override
public void onLongPress(MotionEvent motionEvent) {
}
@Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return false;
}
};
// 处理图片拖动功能
private void translate(float distanceX, float distanceY) {
matrix.getValues(matrixArray);
float left = matrixArray[Matrix.MTRANS_X];// X轴坐标
float top = matrixArray[Matrix.MTRANS_Y]; // Y轴坐标
Rect bound = getRestrictedBound();
if (bound != null) {
float scale = getScale();
float right = left + (int) (bitmap.getWidth() / scale);
float bottom = top + (int) (bitmap.getHeight() / scale);
if (left - distanceX > bound.left) {
distanceX = left - bound.left;
}
if (top - distanceY > bound.top) {
distanceY = top - bound.top;
}
if (distanceX > 0) {
if (right - distanceX < bound.right) {
distanceX = right - bound.right;
}
}
if (distanceY > 0) {
if (bottom - distanceY < bound.bottom) {
distanceY = bottom - bound.bottom;
}
}
}
matrix.postTranslate(-distanceX, -distanceY);
invalidate();
}
// 设置显示的图片路径
public void setFilePath(String path) {
Bitmap originalBitmap = BitmapFactory.decodeFile(path);
setBitmap(originalBitmap);
}
// 设置bitmap
private void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
matrix.reset();
centerImage(getWidth(), getHeight());
invalidate();
}
/**
* 令图片居中
*
* @param width 当前控件的宽度
* @param height 当前控件的高度
*/
private void centerImage(int width, int height) {
if (width <= 0 || height <= 0 || bitmap == null) {
return;
}
float widthRatio = 1.0f * height / this.bitmap.getHeight();
float heightRatio = 1.0f * width / this.bitmap.getWidth();
float ratio = Math.min(widthRatio, heightRatio);
float dx = (width - this.bitmap.getWidth()) / 2;
float dy = (height - this.bitmap.getHeight()) / 2;
matrix.setTranslate(0, 0); // 图片的移动
// 设置图片的缩放,ratio是缩放的比例; px,py是缩放的轴点。下面是以以图片中心为轴点的等比例缩放
matrix.setScale(ratio, ratio, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
//以set开头的会把之前的矩阵置为单位矩阵,然后在set,如果想要上面的几种方式一起操作,则需要使用pre或post开头的方法
matrix.postTranslate(dx, dy);
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = scaleGestureDetector.onTouchEvent(event);
result = gestureDetector.onTouchEvent(event) || result;
return result || super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 绘制时,将bitmap进行绘制在控件上
if (bitmap != null) {
canvas.drawBitmap(bitmap, matrix, null);
}
}
// 裁剪图片
public Bitmap crop(Rect frame) {
float scale = getScale();
float[] src = new float[]{frame.left, frame.top};
float[] desc = new float[]{0, 0};
Matrix invertedMatrix = new Matrix();
this.matrix.invert(invertedMatrix);
invertedMatrix.mapPoints(desc, src);
Matrix matrix = new Matrix();
int width = (int) (frame.width() / scale);
int height = (int) (frame.height() / scale);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
Bitmap originalBitmap = this.bitmap;
matrix.postTranslate(-desc[0], -desc[1]);
canvas.drawBitmap(originalBitmap, matrix, null);
return bitmap;
}
}
2、XML布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3">
<com.rzr.capture.view.CropView
android:id="@+id/cropView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!--
<com.rzr.capture.view.FrameOverlayView
android:id="@+id/overlayView"
android:layout_width="match_parent"
android:layout_height="match_parent" />-->
</FrameLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:orientation="vertical">
<Button
android:id="@+id/btn_crop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="裁剪" />
<ImageView
android:id="@+id/iv_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp" />
</LinearLayout>
</LinearLayout>
3、 在MainActivity中调用
public class MainActivity extends AppCompatActivity {
private CropView cropView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cropView = findViewById(R.id.cropView);
String imagePath = Environment.getExternalStorageDirectory() + "/download/crop.jpg";
cropView.setFilePath(imagePath);
}
}