当前位置:   article > 正文

Android自定义View实现图片裁剪功能(本地选择图片进行裁剪)_android图片局部选取

android图片局部选取

使用安卓自带的裁剪工具,发现有版本兼容问题,而且图片模糊问题也不好解决,于是自己动手绘制一个裁剪工具。先看效果!

最终效果

自定义截图

实现思路
  1. 打开本地相册,获得图片Uri,Uri转为Bitmap。
  2. 用自定义View绘制可拖动选框,获得用户的裁剪意图。
  3. 用Bitmap.createBitmap(bitmap,0,0,0,0,null,false);进行裁剪。
一、打开本地相册,获得图片Uri,Uri转为Bitmap
  1. 首先是打开相册
Intent intent = new Intent();
        intent.setAction(Intent.ACTION_PICK);
        intent.setType("image/*");
        intentActivityResultLauncher.launch(intent);
  • 1
  • 2
  • 3
  • 4
  1. 然后监听回调结果,onActivityResult被摒弃的事情不多说了,这里用registerForActivityResult。
private ActivityResultLauncher<Intent> intentActivityResultLauncher;
    private void initActivityResult() {
        intentActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
            if (result.getResultCode() == RESULT_OK) {
                Uri imageUri = Objects.requireNonNull(result.getData()).getData();
                InputStream image_stream = null;
                try {
                    image_stream = getContentResolver().openInputStream(imageUri);
                    Bitmap bitmap= BitmapFactory.decodeStream(image_stream );
                    //这里的bitmap就是我们要进行操作的位图,实现了第一步
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        });
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

需要注意的是,这个东西一定要记得注销监听,不然会有内存泄露风险。

@Override
protected void onDestroy() {
   super.onDestroy();
   intentActivityResultLauncher.unregister();
}
  • 1
  • 2
  • 3
  • 4
  • 5
2. 自定义View实现位图裁剪(直接copy去用,不需要考虑兼容性问题)
package com.example.cavasdemo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import androidx.annotation.Nullable;

/**
 * 自定义View: Bitmap自定义裁剪工具
 * @Author 绝命三郎
 */
public class BitmapClippingView extends View {

    public BitmapClippingView(Context context) {
        this(context,null);
    }

    public BitmapClippingView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public BitmapClippingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private float widthSize;//布局宽度
    private float heightSize;//布局高度

    private Bitmap bitmap;//要裁剪的位图
    private float bitmapWidth;//位图原始宽度
    private float bimapHeight;//位图原始高度
    private float proportionWidth;//比例:宽  如裁图比例3:4,此处传3
    private float proportionHeight;//比例:高  如裁图比例3:4,此处传4

    private Paint bitmapPaint;//图片画笔
    private Paint shadowPaint;//阴影画笔
    private Paint linePaint;//线条画笔

    float scaleStep;//缩放比例

    private boolean initTag=true;//用于判断是不是首次绘制
    private float leftLine=-1;//选区左线
    private float topLine=-1;//选区上线
    private float rightLine=-1;//选区右线
    private float bottomLine=-1;//选区下线

    private String focus="NONE";//事件焦点
    private final String LEFT_TOP="LEFT_TOP";//LEFT_TOP:拖动左上角
    private final String BODY="BODY";//BODY:拖动整体
    private final String RIGHT_BOTTOM="RIGHT_BOTTOM";//RIGHT_BOTTOM:拖动右下角
    private final String NONE="NONE";//NONE:释放焦点

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        widthSize=MeasureSpec.getSize(widthMeasureSpec);
        heightSize=MeasureSpec.getSize(heightMeasureSpec);

        bitmapPaint=new Paint();
        bitmapPaint.setStrokeWidth(0);

        shadowPaint=new Paint();
        shadowPaint.setColor(Color.parseColor("#57FF9800"));
        shadowPaint.setStrokeWidth(4);
        shadowPaint.setStyle(Paint.Style.FILL_AND_STROKE);

        linePaint=new Paint();
        linePaint.setColor(Color.parseColor("#FF9800"));
        linePaint.setStrokeWidth(4);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (bitmap==null)return;

        //绘制参考背景背景
        scaleStep=widthSize/bitmapWidth;
        float backgroundImgHeight=bimapHeight*scaleStep;
        heightSize=backgroundImgHeight;//把有效图像高度设置作为布局高度计算
        Rect rect=new Rect(0,0,(int)bitmapWidth,(int)bimapHeight);//裁剪图片中的部分(此处:全图)
        RectF rectF=new RectF(0,0,widthSize,backgroundImgHeight);//显示在屏幕中的什么位置
        canvas.drawBitmap(bitmap,rect,rectF,bitmapPaint);
        canvas.save();

        if (initTag){
            //绘制初始状态的选框(最大选框)
            if (bitmapWidth>bimapHeight){
                //宽大于高,取高
                float checkboxHeight=backgroundImgHeight;//选框的高
                float checkboxWidth=((checkboxHeight/proportionHeight)*proportionWidth);//选框的宽
                leftLine=(widthSize/2f)-(checkboxWidth/2f);
                topLine=(heightSize/2f)-(checkboxHeight/2f);
                rightLine=(widthSize/2f)+(checkboxWidth/2f);
                bottomLine=(heightSize/2f)+(checkboxHeight/2f);
            }else {
                //高大于宽 取宽
                float checkboxWidth=widthSize;//选框的宽
                float checkboxHeight=(widthSize/proportionWidth)*proportionHeight;//选框的高
                leftLine=(widthSize/2f)-(checkboxWidth/2f);
                topLine=(heightSize/2f)-(checkboxHeight/2f);
                rightLine=(widthSize/2f)+(checkboxWidth/2f);
                bottomLine=(heightSize/2f)+(checkboxHeight/2f);
            }
            initTag=false;
        }

        //绘制选择的区域
        //绘制周边阴影部分(分四个方块)
        linePaint.setColor(Color.parseColor("#FF9800"));
        linePaint.setStrokeWidth(4);
        canvas.drawRect(0,0,leftLine,heightSize,shadowPaint);//左
        canvas.drawRect(leftLine+4,0,rightLine-4,topLine,shadowPaint);//上
        canvas.drawRect(rightLine,0,widthSize,heightSize,shadowPaint);//右
        canvas.drawRect(leftLine+4,bottomLine,rightLine-4,heightSize,shadowPaint);//下

        //绘制选区边缘线
        canvas.drawLine(leftLine,topLine,rightLine,topLine,linePaint);
        canvas.drawLine(rightLine,topLine,rightLine,bottomLine,linePaint);
        canvas.drawLine(rightLine,bottomLine,leftLine,bottomLine,linePaint);
        canvas.drawLine(leftLine,bottomLine,leftLine,topLine,linePaint);

        //绘制左上和右下调节点
        linePaint.setColor(Color.RED);
        linePaint.setStrokeWidth(6);
        canvas.drawLine(rightLine-4,bottomLine-4,rightLine-4,bottomLine-40-4,linePaint);
        canvas.drawLine(rightLine-4,bottomLine-4,rightLine-40-4,bottomLine-4,linePaint);
        canvas.drawLine(leftLine+4,topLine+4,leftLine+40+4,topLine+4,linePaint);
        canvas.drawLine(leftLine+4,topLine+4,leftLine+4,topLine+40+4,linePaint);

        //绘制焦点圆
        linePaint.setStrokeWidth(2);
        linePaint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(rightLine-4,bottomLine-4,80,linePaint);
        canvas.drawCircle(leftLine+4,topLine+4,80,linePaint);

        //绘制扇形
        linePaint.setColor(Color.parseColor("#57FF0000"));
        linePaint.setStyle(Paint.Style.FILL_AND_STROKE);
        RectF mRectF = new RectF(rightLine-4-40, bottomLine-4-40, rightLine-4+40, bottomLine-4+40);
        canvas.drawArc(mRectF, 270, 270, true, linePaint);

        linePaint.setColor(Color.parseColor("#57FF0000"));
        linePaint.setStyle(Paint.Style.FILL_AND_STROKE);
        RectF mRectF2 = new RectF(leftLine+4-40, topLine+4-40, leftLine+4+40, topLine+4+40);
        canvas.drawArc(mRectF2, 90, 270, true, linePaint);

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if (leftLine==-1)return false;
        if (topLine==-1)return false;
        if (rightLine==-1)return false;
        if (bottomLine==-1)return false;
        if (bitmap==null)return false;

        float touchX=event.getX();
        float touchY=event.getY();

        if (event.getAction()==MotionEvent.ACTION_DOWN){
            return actionDown(touchX,touchY);
        }

        if (event.getAction()==MotionEvent.ACTION_MOVE){
            return actionMove(touchX,touchY);
        }

        if (event.getAction()==MotionEvent.ACTION_UP){
            return actionUp(touchX,touchY);
        }

        return true;
    }

    //抬起
    private boolean actionUp(float touchX, float touchY) {
        Log.d("fxHou","抬起X="+touchX+"   touchY="+touchY);
        Log.d("fxHou","释放焦点");
        focus=NONE;//释放焦点
        return true;
    }

    //移动
    private boolean actionMove(float touchX, float touchY) {
        Log.d("fxHou","滑动X="+touchX+"   touchY="+touchY);
        if (focus.equals(LEFT_TOP)){
            //移动边线
            leftLine=touchX;
            topLine=bottomLine-(((rightLine-leftLine)/proportionWidth)*proportionHeight);//约束比例

            //限制最小矩形 宽
            if (rightLine-leftLine<100){
                leftLine=rightLine-100;
                topLine=bottomLine-(((rightLine-leftLine)/proportionWidth)*proportionHeight);
                //重绘
                postInvalidate();
                return true;
            }

            //限制最小矩形 高
            if (bottomLine-topLine<100){
                topLine=bottomLine-100;
                leftLine=rightLine-((bottomLine-topLine)/proportionHeight)*proportionWidth;
                //重绘
                postInvalidate();
                return true;
            }

            //防止超出边界
            if (leftLine<0){
                leftLine=0;
                topLine=bottomLine-(((rightLine-leftLine)/proportionWidth)*proportionHeight);
            }

            //防止超出边界
            if (topLine<0){
                topLine=0;
                leftLine=rightLine-((bottomLine-topLine)/proportionHeight)*proportionWidth;
            }

            //重绘
            postInvalidate();
            return true;
        }else if (focus.equals(RIGHT_BOTTOM)){
            //移动边线
            rightLine=touchX;
            bottomLine=topLine+(((rightLine-leftLine)/proportionWidth)*proportionHeight);//约束比例

            //限制最小矩形 宽
            if (rightLine-leftLine<100){
                rightLine=leftLine+100;
                bottomLine=topLine+(((rightLine-leftLine)/proportionWidth)*proportionHeight);
                //重绘
                postInvalidate();
                return true;
            }

            //限制最小矩形 高
            if (bottomLine-topLine<100){
                bottomLine=topLine+100;
                rightLine=leftLine+(((bottomLine-topLine)/proportionHeight)*proportionWidth);
                //重绘
                postInvalidate();
                return true;
            }

            //防止超出边界
            if (rightLine>widthSize){
                rightLine=widthSize;
                bottomLine=topLine+(((rightLine-leftLine)/proportionWidth)*proportionHeight);
            }

            //防止超出边界
            if (bottomLine>heightSize){
                bottomLine=heightSize;
                rightLine=leftLine+(((bottomLine-topLine)/proportionHeight)*proportionWidth);
            }
            //重绘
            postInvalidate();
            return true;
        }else if (focus.equals(BODY)){
            float moveX=touchX-downX;
            float moveY=touchY-downY;
            leftLine=downLeftLine+moveX;
            rightLine=downRightLine+moveX;
            topLine=downTopLine+moveY;
            bottomLine=downBottomLine+moveY;

            if (leftLine<0){
                rightLine=(rightLine-leftLine);
                leftLine=0;

                if (topLine<0){
                    bottomLine=bottomLine-topLine;
                    topLine=0;
                    //重绘
                    postInvalidate();
                    return true;
                }

                if (bottomLine>heightSize){
                    topLine=heightSize-(bottomLine-topLine);
                    bottomLine=heightSize;
                    //重绘
                    postInvalidate();
                    return true;
                }

                //重绘
                postInvalidate();
                return true;
            }

            if (rightLine>widthSize){
                leftLine=widthSize-(rightLine-leftLine);
                rightLine=widthSize;

                if (topLine<0){
                    bottomLine=bottomLine-topLine;
                    topLine=0;
                    //重绘
                    postInvalidate();
                    return true;
                }

                if (bottomLine>heightSize){
                    topLine=heightSize-(bottomLine-topLine);
                    bottomLine=heightSize;
                    //重绘
                    postInvalidate();
                    return true;
                }

                //重绘
                postInvalidate();
                return true;
            }

            if (topLine<0){
                bottomLine=bottomLine-topLine;
                topLine=0;
                //重绘
                postInvalidate();
                return true;
            }

            if (bottomLine>heightSize){
                topLine=heightSize-(bottomLine-topLine);
                bottomLine=heightSize;
                //重绘
                postInvalidate();
                return true;
            }
            //重绘
            postInvalidate();
            return true;
        }
        return true;
    }

    //按下
    private float downX,downY,downLeftLine,downTopLine,downRightLine,downBottomLine;
    private boolean actionDown(float touchX, float touchY) {
        downX=touchX;
        downY=touchY;
        downLeftLine=leftLine;
        downTopLine=topLine;
        downRightLine=rightLine;
        downBottomLine=bottomLine;
        Log.d("fxHou","按下X="+touchX+"   touchY="+touchY);
        boolean condition1=touchX>leftLine-40 && touchX<leftLine+40;
        boolean condition2=touchY>topLine-40 && touchY<topLine+40;
        if (condition1 && condition2){
            Log.d("fxHou","左上获得焦点");
            focus=LEFT_TOP;//左上获得焦点
            return true;
        }

        boolean condition3=touchX>rightLine-40 && touchX<rightLine+40;
        boolean condition4=touchY>bottomLine-40 && touchY<bottomLine+40;
        if (condition3 && condition4){
            Log.d("fxHou","右下获得焦点");
            focus=RIGHT_BOTTOM;//右下获得焦点
            return true;
        }

        boolean condition5=touchX>leftLine && touchX<rightLine;
        boolean condition6=touchY>topLine && touchY<bottomLine;
        if (condition5 && condition6){
            Log.d("fxHou","整体获得焦点");
            focus=BODY;//整体获得焦点
            return true;
        }

        return true;
    }

    /**
     * 设置要裁剪的位图
     * @param bitmap 要裁剪的位图
     * @param proportionWidth  比例:宽  如裁图比例3:4,此处传3
     * @param proportionHeight 比例:高  如裁图比例3:4,此处传4
     */
    public void setBitmap(Bitmap bitmap,int proportionWidth,int proportionHeight){
        this.bitmap=bitmap;
        bitmapWidth=bitmap.getWidth();
        bimapHeight=bitmap.getHeight();
        this.proportionWidth=proportionWidth;
        this.proportionHeight=proportionHeight;
        initTag=true;
        postInvalidate();
    }

    /**
     * 获取裁剪后的位图
     * @param context
     * @param minPixelWidth 限制最小宽度(像素)
     * @param minPixelHeight 限制最小高度(像素)
     * @return 裁切后的位图
     */
    public Bitmap getBitmap(Context context,int minPixelWidth,int minPixelHeight){
        if (bitmap==null)return null;
        int startX= (int) (leftLine/scaleStep);
        int startY= (int) (topLine/scaleStep);
        int cutWidth=(int) ((rightLine/scaleStep)-(leftLine/scaleStep));
        int cutHeight=(int) (bottomLine/scaleStep-topLine/scaleStep);

        Bitmap newBitmap=Bitmap.createBitmap(bitmap,startX,startY,cutWidth,cutHeight,null,false);

        if (newBitmap.getWidth()<minPixelWidth || newBitmap.getHeight()<minPixelHeight){
            Toast.makeText(context, "图片太模糊了", Toast.LENGTH_SHORT).show();
            return null;
        }

        return newBitmap;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430

使用方法

<com.example.cavasdemo.BitmapClippingView
            android:id="@+id/my_cavas"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
  • 1
  • 2
  • 3
  • 4
BitmapClippingView my_cavas=(BitmapClippingView)findViewById(R.id.my_cavas);
//把刚刚本地相册拿到的bitmap传进去,用户进行自定义裁剪
my_cavas.setBitmap(bitmap,3,4);
//获取裁剪后的结果
Bitmap resulBitmap=my_cavas.getBitmap(context,600,800);
  • 1
  • 2
  • 3
  • 4
  • 5
最后:拿到裁剪后的resulBitmap,做你想做的事。
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号