最近手机界开始流行双摄像头,大光圈功能也应用而生。所谓大光圈功能就是能够对照片进行后期重新对焦,其实现的原理主要是对拍照期间获取的深度图片与对焦无穷远的图像通过算法来实现重新对焦的效果。
在某双摄手机的大光圈操作界面有个光圈的操作图标,能够模拟光圈调节时的真实效果,感觉还不错,于是想着实现该效果。现在把我的实现方法贡献给大家,万一你们公司也要做双摄手机呢?( ̄┰ ̄*)
首先,百度一下光圈图片,观察观察,就可以发现其关键在于计算不同的光圈值时各个光圈叶片的位置。为了计算简便,我以六个直边叶片的光圈效果为例来实现(其他形式,比如七个叶片,也就是位置计算稍微没那么方便;而一些圆弧的叶片,只要满足叶片两边的圆弧半径是一样的就行。为什么要圆弧半径一样呢?仔细观察就可以发现,相邻两叶片之间要相互滑动,而且要保持一样的契合距离,根据我曾今小学几何科打满分的经验可以判断出,等径的圆弧是不错滴,其他高级曲线能不能实现该效果,请问数学家( ̄┰ ̄*)!其他部分原理都是一样的)。
制作效果图:
先说明一下本自定义view的主要内容:
- 本效果的实现就是在光圈内六边形六个角上分别绘制六个光圈叶片
- 根据不同的光圈值计算出内六边形的大小,从而计算每个六边形的顶点的位置
- 设计叶片。也可以让美工MM提供,本方案是自己用代码画的。注意预留叶片之间的间隔距离以及每个叶片的角度为60°
- 定义颜色、间隔等自定义属性
- 上下滑动可以调节光圈大小
- 提供光圈值变动的监听接口
代码
可以在GitHub上下载:https://github.com/willhua/CameraAperture.git
1 package com.example.cameraaperture;
2
3 import android.content.Context;
4 import android.content.res.TypedArray;
5 import android.graphics.Bitmap;
6 import android.graphics.Bitmap.Config;
7 import android.graphics.Canvas;
8 import android.graphics.Paint;
9 import android.graphics.Path;
10 import android.graphics.PointF;
11 import android.util.AttributeSet;
12 import android.util.Log;
13 import android.view.MotionEvent;
14 import android.view.View;
15
16 /**
17 * 上下滑动可以调节光圈大小;
18 * 调用setApertureChangedListener设置光圈值变动监听接口;
19 * 绘制的光圈最大直径将填满整个view
20 * @author willhua http://www.cnblogs.com/willhua/
21 *
22 */
23 public class ApertureView extends View {
24
25 public interface ApertureChanged {
26 public void onApertureChanged(float newapert);
27 }
28
29 private static final float ROTATE_ANGLE = 30;
30 private static final String TAG = "ApertureView";
31 private static final float COS_30 = 0.866025f;
32 private static final int WIDTH = 100; // 当设置为wrap_content时测量大小
33 private static final int HEIGHT = 100;
34 private int mCircleRadius;
35 private int mBladeColor;
36 private int mBackgroundColor;
37 private int mSpace;
38 private float mMaxApert = 1;
39 private float mMinApert = 0.2f;
40 private float mCurrentApert = 0.5f;
41
42 //利用PointF而不是Point可以减少计算误差,以免叶片之间间隔由于计算误差而不均衡
43 private PointF[] mPoints = new PointF[6];
44 private Bitmap mBlade;
45 private Paint mPaint;
46 private Path mPath;
47 private ApertureChanged mApertureChanged;
48
49 private float mPrevX;
50 private float mPrevY;
51
52 public ApertureView(Context context, AttributeSet attrs) {
53 super(context, attrs);
54 init(context, attrs);
55 }
56
57 private void init(Context context, AttributeSet attrs) {
58 //读取自定义布局属性
59 TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ApertureView);
60 mSpace = (int)array.getDimension(R.styleable.ApertureView_blade_space, 5);
61 mBladeColor = array.getColor(R.styleable.ApertureView_blade_color, 0xFF000000);
62 mBackgroundColor = array.getColor(R.styleable.ApertureView_background_color, 0xFFFFFFFF);
63 array.recycle();
64 mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
65 mPaint.setAntiAlias(true);
66 for (int i = 0; i < 6; i++) {
67 mPoints[i] = new PointF();
68 }
69 }
70
71 @Override
72 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
73 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
74 int widthSpecMode = MeasureSpec.g