Android 相機開發 Camera-附帶掃碼遮罩介面實現原理(自動聚焦)
阿新 • • 發佈:2019-01-23
相機遮罩實現原理,FrameLayout中如果控制元件一樣大,同一時間只能見到最上面的。
1.自定義ViewFindView,相機遮罩介面
public class ViewFinderView extends View { //相機遮罩框外面的線,陰影區域,滾動線 private Paint border, area, line; //相機遮罩框中間透明區域 private Rect center; //螢幕大小 private int screenHeight, screenWidth; //滾動線的起始點 private int startX, startY, endX, endY; //滾動線向下滾動標識 private boolean isDown = true; //滾動線速度 private static final int SPEED = 2; //中間區域寬高(dp), public static final int centerHeight = 300; public static final int centerWidth = 300; public ViewFinder(Context context) { super(context); } public ViewFinder(Context context, @Nullable AttributeSet attrs) { super(context, attrs); //setAlpha一定要在setStyle後面,否則不起作用 border = new Paint(Paint.ANTI_ALIAS_FLAG); border.setColor(Color.BLUE); border.setStyle(Paint.Style.STROKE); border.setStrokeWidth(5f); border.setAlpha(10); area = new Paint(Paint.ANTI_ALIAS_FLAG); area.setStyle(Paint.Style.FILL); area.setColor(Color.GRAY); area.setAlpha(180); screenHeight = DimenUtil.getScreenSize(context).heightPixels; screenWidth = DimenUtil.getScreenSize(context).widthPixels; center = getCenterRect(context, 300, 300); line = new Paint(Paint.ANTI_ALIAS_FLAG); line.setStyle(Paint.Style.STROKE); line.setColor(Color.GREEN); //設定滾動線的起始點 startX = center.left; startY = center.top; endX = center.right; endY = center.top; } public ViewFinder(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /** * 根據尺寸獲取中間區大小 * * @param context * @param height * @param width * @return */ private Rect getCenterRect(Context context, int height, int width) { height = DimenUtil.px2dip(context, height); width = DimenUtil.px2dip(context, width); Rect rect = new Rect(); int left = (this.screenWidth - width) / 2; int top = (this.screenHeight - height) / 2; rect.set(left, top, left + width, top + height); return rect; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //繪製四周陰影區域(上下左右),注意+1和-1,不設定不顯示邊框 canvas.drawRect(0, 0, screenWidth, center.top - 1, area); canvas.drawRect(0, center.bottom + 1, screenWidth, screenHeight, area); canvas.drawRect(0, center.top - 1, center.left - 1, center.bottom + 1, area); canvas.drawRect(center.right + 1, center.top - 1, screenWidth, center.bottom + 1, area); canvas.drawRect(center, border); //滾動線 if (isDown) { startY = endY += SPEED; if (startY >= center.bottom) isDown = false; } else { startY = endY -= SPEED; if (startY <= center.top) isDown = true; } canvas.drawLine(startX, startY, endX, endY, line); postInvalidate(); } }
2.DimenUtil
public class DimenUtil { //dp轉px public static int dip2px(Context context, int dp) { float density = context.getResources().getDisplayMetrics().density; return (int) (dp * density + 0.5f); } //px轉dp public static int px2dip(Context context, int px) { float density = context.getResources().getDisplayMetrics().density; return (int) (px / density + 0.5f); } //獲取螢幕大小(px) public static DisplayMetrics getScreenSize(Context context) { return context.getResources().getDisplayMetrics(); } }
3.主介面 MainActivity
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback2, Camera.AutoFocusCallback, Camera.PreviewCallback, SensorEventListener { private Camera camera; private SurfaceHolder holder; private SurfaceView surfaceView; //感測器 private SensorManager manager; //上次加速時間戳 private long lastTime; //聚焦標識 private static boolean isFocusing; //Android重力加速度感測器資料去噪 private float[] grivity = new float[3]; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); surfaceView = findViewById(R.id.surfaceview); //相機個數 int count = Camera.getNumberOfCameras(); //預設設定後置攝像頭 if (count <= 0) { Toast.makeText(this, "您的手機不支援相機", Toast.LENGTH_SHORT).show(); return; } else if (count == 1) { camera = Camera.open(); } else { camera = Camera.open(0); } //設定旋轉90度 camera.setDisplayOrientation(90); Camera.Parameters parameters = camera.getParameters(); //設定自動聚焦模式 parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); camera.setParameters(parameters); camera.setPreviewCallback(this); camera.autoFocus(this); holder = surfaceView.getHolder(); holder.addCallback(this); manager = (SensorManager) getSystemService(SENSOR_SERVICE); } @Override public void surfaceRedrawNeeded(SurfaceHolder surfaceHolder) { } @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { if (surfaceHolder.getSurface() == null) return; try { camera.setPreviewDisplay(holder); camera.startPreview(); //註冊感測器 manager.registerListener(this, manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_UI); } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { //取消感測器 manager.unregisterListener(this); camera.stopPreview(); //這個必須加,否則報錯Camera is being used after Camera.release() was called camera.setPreviewCallback(null); camera.release(); camera = null; } @Override public void onAutoFocus(boolean b, Camera camera) { if (b) { isFocusing = false; //連續聚焦必須加這段,每次聚焦成功之後取消自動聚焦 camera.cancelAutoFocus(); Toast.makeText(this, "聚焦成功", Toast.LENGTH_SHORT).show(); } } @Override public void onPreviewFrame(byte[] bytes, Camera camera) { //此處用byte生成YuvImage,然後旋轉90度,按照ViewFinderView中的centerHeight和centerWidth計算出中間框 //的位置,裁剪之後處理中間圖片的資料(二維碼,圖片....) } @Override public void onSensorChanged(SensorEvent sensorEvent) { switch (sensorEvent.sensor.getType()) { case Sensor.TYPE_ACCELEROMETER: final float alpha = 0.8f;//為啥0.8我也不知道 grivity[0] = alpha * grivity[0] + (1 - alpha) * sensorEvent.values[0]; grivity[1] = alpha * grivity[1] + (1 - alpha) * sensorEvent.values[1]; grivity[2] = alpha * grivity[2] + (1 - alpha) * sensorEvent.values[2]; //取絕對值 float x = Math.abs(sensorEvent.values[0] - grivity[0]); float y = Math.abs(sensorEvent.values[1] - grivity[1]); float z = Math.abs(sensorEvent.values[2] - grivity[2]); //獲取當前時間戳 long current = System.currentTimeMillis(); if ((x + y + z) / 3 < 0.3 && !isFocusing && current - lastTime > 500 && current - lastTime < 1500) { //相對靜止並且不在聚焦,加速完成後500-1500ms之間進行聚焦 isFocusing = true; camera.autoFocus(this); } else if ((x + y + z) / 3 > 0.3) { //相對正在加速 isFocusing = false; lastTime = current; } break; } } @Override public void onAccuracyChanged(Sensor sensor, int i) { //精度發生改變呼叫 } }
4.activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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=".MainActivity">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/surfaceview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.example.administrator.myapplication.view.ViewFinder
android:id="@+id/viewfinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</RelativeLayout>
註釋寫的挺詳細的了,功能基本實現
福利
大牛的CSDN,參考他的也行