前言
最近遇到拍照时带取景框的需求,于是自己写了个简单的取景框配合相机预览界面实现拍照功能。本文涉及自定义view的内容,还不太了解同学的可以移步https://hencoder.com/大牛系统介绍了Android自定义View相关知识结构,可以让你对自定义view有个清晰的认识,感谢大牛的分享。首先看下完成后的效果:
正文
首先创建BackgroundView继承自View,通常实现三个构造函数。
public BackgroundView(Context context) {
this(context, null);
}
public BackgroundView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BackgroundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
mPaint = new Paint();
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.BackgroundView);
if (ta != null) {
mRectLeft = ta.getDimension(R.styleable.BackgroundView_rectLeft, defaultRectLeft);
mRectTop = ta.getDimension(R.styleable.BackgroundView_rectTop, defaultRectTop);
mRectRight = ta.getDimension(R.styleable.BackgroundView_rectRight, defaultRectRight);
mRectBottom = ta.getDimension(R.styleable.BackgroundView_rectBottom, defaultRectBottom);
mRectCornerRadius = ta.getDimension(R.styleable.BackgroundView_rectCornerRadius, defaultRectCornerRadius);
mRectOutColor = ta.getColor(R.styleable.BackgroundView_rectOutColor, defaultRectOutColor);
}
}
单个参数的构造方法是在代码中new对象用的,带AttributeSet参数的构造是在xml中用的:
<com.example.customcamera.BackgroundView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:rectLeft="30dp"
app:rectTop="100dp"
app:rectRight="30dp"
app:rectBottom="300dp"
app:rectCornerRadius="10dp"
app:rectOutColor="#99ff0000" />
如果要自定义属性需要在/res/values/attrs.xml中创建
<resources>
<declare-styleable name="BackgroundView">
<attr name="rectLeft" format="dimension" /><!--取景框左边缘距离屏幕左边的距离-->
<attr name="rectTop" format="dimension" /><!--取景框上边缘距离屏幕上边的距离-->
<attr name="rectRight" format="dimension" /><!--取景框右边缘距离屏幕右边的距离-->
<attr name="rectBottom" format="dimension" /><!--取景框下边缘距离屏幕下边的距离-->
<attr name="rectCornerRadius" format="dimension" /><!--取景框圆角半径-->
<attr name="rectOutColor" format="color" /><!--取景框外部颜色,通常为半透明-->
</declare-styleable>
</resources>
name即属性名,format即你要声明的属性是什么类型如尺寸为dimension、字符串为string,Android Studio中有提示。attrs.xml中声明的属性在构造方法中初始化:
public BackgroundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
mPaint = new Paint();
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.BackgroundView);
if (ta != null) {
mRectLeft = ta.getDimension(R.styleable.BackgroundView_rectLeft, defaultRectLeft);
mRectTop = ta.getDimension(R.styleable.BackgroundView_rectTop, defaultRectTop);
mRectRight = ta.getDimension(R.styleable.BackgroundView_rectRight, defaultRectRight);
mRectBottom = ta.getDimension(R.styleable.BackgroundView_rectBottom, defaultRectBottom);
mRectCornerRadius = ta.getDimension(R.styleable.BackgroundView_rectCornerRadius, defaultRectCornerRadius);
mRectOutColor = ta.getColor(R.styleable.BackgroundView_rectOutColor, defaultRectOutColor);
}
}
接下来开始画取景框,重写View的onDraw方法super.onDraw是空实现可以直接删掉,如果是继承自其它已经实现了onDraw方法的View如TextView则不能删除super.onDraw,至于你的代码写在之前还是之后要根据实际需求,写在父方法之前则自定义的画面在父控件绘制之前绘制,父控件的绘制会覆盖你自己的绘制,否则相反。Android的绘制是通过Canvas和Paint实现的Canvas映射到实际生活中好比是块画布,Paint好比是作画的画笔,画笔可以控制颜色、粗细、形状等,google已经为我们提供好了一系列api方便我们快速画出想要的画面。
@Override
protected void onDraw(Canvas canvas) {
mPaint.setAntiAlias(true);//抗锯齿
mPaint.setColor(mRectOutColor);
// 取景框和全屏
RectF rect = new RectF(mRectLeft, mRectTop, screenWidth - mRectRight, screenHeight - mRectBottom);
Path path = new Path();
path.addRoundRect(rect, mRectCornerRadius, mRectCornerRadius, Path.Direction.CW);
Region region = new Region();
region.setPath(path, new Region(0, 0, screenWidth, screenHeight));
Region region1 = new Region();
Path path1 = new Path();
path1.addRect(0, 0, screenWidth, screenHeight, Path.Direction.CW);
region1.setPath(path1, new Region(0, 0, screenWidth, screenHeight));
region.op(region1, Region.Op.XOR);// 范围取异并集
RegionIterator iterator = new RegionIterator(region);
Rect rec = new Rect();
while (iterator.next(rec)) {
canvas.drawRect(rec, mPaint);
}
}
Paint在构造方法中已经初始化,mPaint.setAntiAlias(true);设置抗锯齿可以使不规则线条看起来更加平滑,原理是给线条边缘像素点一个颜色过渡由深到浅。由于需要中间取景框全透明,取景框以外的区域画上一层半透明的阴影,所以我用一个中间圆角矩形和一个屏幕大小的区域取异并集得到的就是取景框以外的区域范围然后用Canvas直接根据Paint设置的颜色、透明度等参数直接画出取景框以外的背景即可。
总结
本文虽然实现比较简单但是要先有自定义View的基础知识,在这里再次感谢HenCoder朱凯老师的分享。文章中如有错误欢迎指出。