自定义裁剪框

本文介绍了一种自定义裁剪视图的方法,通过触摸事件调整裁剪区域的位置,实现屏幕宽度等宽的裁剪功能。适用于身份证等固定宽度图片的截取。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前段时间做身份证上传,需求是当截取框展现出来的时候,截取框的宽要和屏幕一样宽。于是乎我就用系统的截取框设置宽高,结果嘞。设置的宽度太宽根本就截取不了。这是系统自带的诟病。所以只能用自定义的裁剪框。

我的功能比较简单就3点要求:

1.裁剪框的宽度和屏幕一样宽

2.截框上下滑动

3.能够截取正确

下面是示意图:


现在来说下思路:

1.首先要Canvas一个Bitmap,然后得到这个自定义View的宽高

2.根据宽高在bitmap上Canvas两个宽和屏幕一样的矩形,也就是图中的1和3两个矩形,把颜色置为black

3.根据onTouchEvent(MotionEvent event),计算我们的手指在屏幕上滑动的距离和方向来改变1和3两个矩形的高。

4.最后画上右图,上下两条白色线作为截取框的边(也随着手指上下滑动)

  先说说代码中需要准备的前奏:

1.定义一个继承View的类  

 2.重写 onSizeChanged(int w, int h, int oldw, int oldh) 方法,这个方法是用来获取控件的实际高度的。

 3.重写onDraw(Canvas canvas)方法, 画所需要的图形效果

 4.onTouchEvent(MotionEvent event)  用来测量手指的滑动距离的

然后嘞就聊聊具体怎么实现吧

首先我要在ondraw中画个Bitmap吧:

    canvas.drawBitmap(bitmap, left, top, paint);

bitmap The bitmap to be drawn
left The position of the left side of the bitmap being drawn
top The position of the top side of the bitmap being drawn
paint The paint used to draw the bitmap (may be null)
bitmap = 被画的图片

大家可以理解为left top 及时图片左上角的(x,y)坐标(left , top)

其次就是我要知道这个矩形是怎么画的,

以上面的一号矩形举个例子的话

代码就是这样的:

 int w = 屏幕宽;int h = 屏幕的高

  canvas.drawRect(left, top, right, bottom, paint);

Parameters:

left The left side of the rectangle to be drawn

top The top side of the rectangle to be drawn

right The right side of the rectangle to be drawn

bottom The bottom side of the rectangle to be drawn

paint The paint used to draw the rect

你可以这样理解:

paint 就是定义矩形样式的-就比如你要画黑 的长方形当然不会去拿蓝色画笔的来画。你得找个黑色的吧。所以来定义画笔的样式:

    Paint paint = new Paint(); // 上下两个矩形的画笔
    paint.setColor(Color.BLACK);
     paint.setAlpha(100); //当然不能画纯黑的,设置透明度

left和right 就是矩形在X轴上的起始X坐标和结束的X坐标

top和bottom 及时在Y轴上起始Y坐标和结束的Y坐标

举个例子:

   int w = 整个View的宽;

   int h = 整个View的高;

  canvas.drawRect(0, 0, w, h/2-300, paint); 如下图:

  上代码:

下来我们就讲讲这个具体的计算:

我下边的代码是以中间的截取区域为600计算的:

在onDraw中画好Bitmap后,1号黑色半透明的矩形是不是就是  canvas.drawRect(0, 0, w, h/2-300, paint);那3号黑色长方形就是canvas.drawRect(0, h/2+300, w, h, paint);

因为截取框的的宽是View的整个View的控件宽,所以这考虑上下滑动中间的“截取区域”。我们使用OnTouchEvent() 来记下手指落下和最终抬起时的event.getY()。计算移动的距离。和 移动的方向。并同时不停的调用onDraw()来刷新View;我们知道了移动的方向后,就能很好的来 Canvas.drawRect() 重新画1号和3号的区域。我们只需要改动1号的bottom和3号的top就行。具体的请参考代码。(还有需要考虑的就是,不能让截取框滑出View的范围吧。。。详情看代码)

public class CropPhotoView extends View {
	private static String TAG = "CropPhotoView.......";
	private Bitmap mBitmap = null;
	private Bitmap resultBitmap = null;// 最终截取的bitmap
	private int mViewWidth, mViewHeight;// 控件实际宽高
	private float downY, upY; // 手指落下的和抬起在Y轴上的纵坐标
	private float moveY = 0; // 最新的移动距离
	private float oldmoveY; // 原来的移动距离

	private int flag = 0; // 0-初始化View 1-手指向上滑 2-手指向下滑
	private int halfOfCropBox = 300; // 截取框的长度的一半
	public CropPhotoView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		// TODO Auto-generated method stub
		/**
		 * 获取控件宽高
		 */
		mViewWidth = w;
		mViewHeight = h;
		halfOfCropBox = (h/2)/2;
		// 缩放位图与控件一致
		mBitmap = Bitmap.createScaledBitmap(mBitmap, mViewWidth, mViewHeight,
				true);
		super.onSizeChanged(w, h, oldw, oldh);
	}

	/**
	 * @-halfOfCropBox  1号
	 * --------------------------------------------
	 * @+halfOfCropBox  3号
	 * 
	 * **/

	@SuppressLint("ClickableViewAccessibility")
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			downY = event.getY();
			break;
		case MotionEvent.ACTION_MOVE:
		case MotionEvent.ACTION_UP:
			moveY = Math.abs(upY - downY);
			upY = event.getY();
			if (upY > downY) { // 手指向下移动
				flag = 2;
			} else { // 手指向上移动
				flag = 1;
			}
			invalidate(); //刷新画布,调用onDraw();
			break;
		default:
			break;
		}
		return true;
		// return super.onTouchEvent(event);
	}

	@SuppressLint("DrawAllocation")
	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub

		// 首先锁定画布,canvas一个Bitmap
//		canvas.save(Canvas.CLIP_SAVE_FLAG);
//		canvas.restore(); 
		canvas.scale(1.0F, 1.0F);
	
		canvas.drawBitmap(mBitmap, 0, 0, null);
		Paint mpaint = new Paint(); // 截取框的边的画笔
		mpaint.setStyle(Style.STROKE);
		mpaint.setAntiAlias(true);  
		mpaint.setStrokeWidth(2); // 画笔的粗细
		mpaint.setColor(Color.WHITE); // 在这里可以改变边框的颜色

		Paint paint = new Paint(); // 上下两个矩形的画笔
		paint.setColor(Color.BLACK);
		paint.setAlpha(100);
		int startY = 0; //截取Bitmap的开始纵坐标
		
		if (flag == 0) // 初始化
		{ 
					startY = mViewHeight / 2 - halfOfCropBox;
					canvas.drawRect(0, 0, mViewWidth, mViewHeight / 2 - halfOfCropBox,paint);// 
					canvas.drawRect(0, mViewHeight / 2 + halfOfCropBox, mViewWidth,mViewHeight, paint);
					// 线
					canvas.drawLine(0, mViewHeight / 2 - halfOfCropBox, mViewWidth,
							mViewHeight / 2 - halfOfCropBox, mpaint);// 画线
					canvas.drawLine(0, mViewHeight / 2 + halfOfCropBox, mViewWidth,
							mViewHeight / 2 + halfOfCropBox, mpaint);// 斜线
		} 
		else if (flag == 1)// 向上滑
		{ 
				if (mViewHeight / 2 - halfOfCropBox - moveY >= 0) {// 向上滑到底
					startY = (int) (mViewHeight / 2 - halfOfCropBox - moveY);
					canvas.drawRect(0, 0, mViewWidth,mViewHeight / 2 - halfOfCropBox - moveY, paint);// 
					canvas.drawRect(0, mViewHeight / 2 + halfOfCropBox - moveY,
							mViewWidth, mViewHeight, paint);
					// 线
					canvas.drawLine(0, mViewHeight / 2 - halfOfCropBox - moveY,mViewWidth, mViewHeight / 2 - halfOfCropBox - moveY, mpaint);// 画线
					canvas.drawLine(0, mViewHeight / 2 + halfOfCropBox - moveY,	mViewWidth, mViewHeight / 2 + halfOfCropBox - moveY, mpaint);
				} else {
					startY = (int) (mViewHeight / 2 - halfOfCropBox - oldmoveY);
					canvas.drawRect(0, 0, mViewWidth, mViewHeight / 2
							- halfOfCropBox - oldmoveY, paint);// -50
					canvas.drawRect(0, mViewHeight / 2 + halfOfCropBox - oldmoveY,
							mViewWidth, mViewHeight, paint);
					// 线
					canvas.drawLine(0, mViewHeight / 2 - halfOfCropBox - oldmoveY,mViewWidth, mViewHeight / 2 - halfOfCropBox - oldmoveY,mpaint);// 画线
					canvas.drawLine(0, mViewHeight / 2 + halfOfCropBox - oldmoveY,mViewWidth, mViewHeight / 2 + halfOfCropBox - oldmoveY,mpaint);
					return;
				}
		}
		else if (flag == 2)// 向下滑动
		{
		
				if (mViewHeight / 2 + halfOfCropBox + moveY < mViewHeight) { // 向下滑到底
					startY = (int) (mViewHeight / 2 - halfOfCropBox + moveY);
					canvas.drawRect(0, 0, mViewWidth, mViewHeight / 2- halfOfCropBox + moveY, paint);// 
					canvas.drawRect(0, mViewHeight / 2 + halfOfCropBox + moveY,mViewWidth, mViewHeight, paint);
					// //线
					canvas.drawLine(0, mViewHeight / 2 - halfOfCropBox + moveY,mViewWidth, mViewHeight / 2 - halfOfCropBox + moveY,mpaint);// 画线
					canvas.drawLine(0, mViewHeight / 2 + halfOfCropBox + moveY,mViewWidth, mViewHeight / 2 + halfOfCropBox + moveY,mpaint);
				} else {
					startY = (int) (mViewHeight / 2 - halfOfCropBox + oldmoveY);
					canvas.drawRect(0, 0, mViewWidth, mViewHeight / 2- halfOfCropBox + oldmoveY, paint);// 
					canvas.drawRect(0, mViewHeight / 2 + halfOfCropBox + oldmoveY,mViewWidth, mViewHeight, paint);
					// 线
					canvas.drawLine(0, mViewHeight / 2 - halfOfCropBox + oldmoveY,mViewWidth, mViewHeight / 2 - halfOfCropBox + oldmoveY,mpaint);
					canvas.drawLine(0, mViewHeight / 2 + halfOfCropBox + oldmoveY,mViewWidth, mViewHeight / 2 + halfOfCropBox + oldmoveY,mpaint);
					return;
				}
		}
		oldmoveY = moveY;// 当超过有效范围的时候就会重新加载上次的有效位置
		resultBitmap = Bitmap.createBitmap(mBitmap, 0, startY, mViewWidth, halfOfCropBox*2,null, true);
		super.onDraw(canvas);
	}

	/**
	 * 返回截取好的图片Bitmap
	 * */
	public Bitmap getCropBitmap() {
		if (resultBitmap != null) {

			return resultBitmap;
		}
		Log.e(TAG, "图片是空的.........");
		return null;
	}
  public void  setPhotoBitmap(Bitmap photo){
	 this.mBitmap = photo;
  }
}


在Activity中代码如何调用
CropPhotoView cropimg = (CropPhotoView) findViewById(R.id.img);  

Bitmap bitmap = cropimg .getBitmap(); //得到截取后的图片

在布局中的调用:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.ccc.ImageActivity" >
    <Button
        android:id="@+id/btn_save"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
         android:textColor="#00000F"
         android:background="@null"
         android:textSize="16sp"
         android:text="保存"
        />
    <yangyangyang.MyImage
        android:layout_below="@+id/btn_save"
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
      />
</RelativeLayout>






评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值