Android实现自定义相机系列(2)—自定义view图片缩放控件

版权声明:本博客主要记录学习笔记和遇到的一些问题解决方案,转载请注明出处! 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);
    }
}

猜你喜欢

转载自blog.csdn.net/u010982507/article/details/81211920
今日推荐