写在前面的话:每一个实例的代码都会附上相应的代码片或者图片,保证代码完整展示。*重要的是保证例程的完整性!!!方便自己也方便他人~欢迎大家交流讨论~

在相机预览时增加取景蒙板/浮层的思路是自定义View,用framelayout把自定义view放在surfaceview上面,在oncreat方法中计算坐标位置,调用自定义view中的set…方法设置坐标,根据坐标绘图。
接下来把上篇的自定义相机和增加蒙板那篇的代码结合起来,为相机增加取景蒙板/浮层,上代码!

新建一个Android项目
取名为Cameratwo,具体文件的命名如下

%title插图%num
values文件夹
strings.xml
<resources>
<string name=”app_name”>Cameratwo</string>
<string name=”button_name”>开始拍照</string>
</resources>

layout文件夹
activity_first.xml
<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout 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”
android:orientation=”vertical”
tools:context=”.FirstActivity”>

<Button
android:id=”@+id/button”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:onClick=”customCamera”
android:text=”@string/button_name” />

<ImageView
android:id=”@+id/iv”
android:layout_width=”match_parent”
android:layout_height=”match_parent” />

</LinearLayout>

custom.xml
<?xml version=”1.0″ encoding=”utf-8″?>
<RelativeLayout
xmlns:android=”http://schemas.android.com/apk/res/android” android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”>

<FrameLayout
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:layout_alignParentTop=”true”>

<SurfaceView
android:id=”@+id/preview”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:visibility=”visible” />

<com.example.administrator.cameratwo.TranslucencyView
android:id=”@+id/transView”
android:layout_width=”match_parent”
android:layout_height=”match_parent” />
</FrameLayout>

<ImageButton
android:id=”@+id/button3″
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignParentBottom=”true”
android:layout_centerHorizontal=”true”
android:background=”@drawable/button3″
android:onClick=”capture” />
</RelativeLayout>

result.xml
<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android” android:layout_width=”match_parent”
android:layout_height=”match_parent”>

<ImageView
android:id=”@+id/pic”
android:layout_width=”match_parent”
android:layout_height=”match_parent” />

</LinearLayout>

manifests
AndroidManifest.xml
在该文件中增加以下两项

<uses-permission android:name=”android.permission.CAMERA”/>
<uses-feature android:name=”android.hardware.Camera”/>
<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”/>

<activity android:name=”.Customcamera”/>
<activity android:name=”.ResultActivity”/>

Java文件夹
FirstActivity
package com.example.administrator.cameratwo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.content.Intent;
import android.view.View;

public class FirstActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
}
public void customCamera(View view){
startActivity(new Intent(this,Customcamera.class));
}
}

 

Customcamera
这里完全没有用到Butter Knife 框架,因为我用的不熟,就删掉了orz…

package com.example.administrator.cameratwo;

import android.content.Intent;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class Customcamera extends AppCompatActivity implements SurfaceHolder.Callback{
private Camera mCamera;
private SurfaceView mPreview;
private SurfaceHolder mHolder;
private int cameraId=1;
private int mHeight;

private Camera.PictureCallback mpictureCallback=new Camera.PictureCallback(){
@Override
public void onPictureTaken(byte[] data,Camera camera){
File tempfile=new File(“/sdcard/emp.png”);
try{ FileOutputStream fos =new FileOutputStream(tempfile);
fos.write(data);
fos.close();
Intent intent=new Intent(Customcamera.this,ResultActivity.class);
intent.putExtra(“picpath”,tempfile.getAbsolutePath());
startActivity(intent);
Customcamera.this.finish();
}
catch (IOException e){e.printStackTrace();}
}
};
@Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.custom);
mPreview=findViewById(R.id.preview);
mPreview.setZOrderOnTop(false);
mHolder=mPreview.getHolder();
mHolder.setFormat(PixelFormat.TRANSPARENT);
mHolder.addCallback(this);
mPreview.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCamera.autoFocus(null);
}
});
//获取imgButton的坐标,以便蒙板在imgButton处掏空,并获取页面参数
ImageButton imgButton=findViewById(R.id.button3);
TranslucencyView translucencyView=findViewById(R.id.transView);
imgButton.postDelayed(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
ImageButton imgButton=findViewById(R.id.button3);
mHeight = getSupportActionBar().getHeight();
int left = imgButton.getLeft();
int right = imgButton.getRight();
int top = imgButton.getTop();
int bottom = imgButton.getBottom();
int mCoodinate[] = {left, top, right, bottom};
TranslucencyView translucencyView=findViewById(R.id.transView);
translucencyView.setCircleLocation(mCoodinate);
}
});
}
},1200);
FrameLayout.LayoutParams layoutParams=(FrameLayout.LayoutParams) translucencyView.getLayoutParams();
translucencyView.setLayoutParams(layoutParams);
translucencyView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mCamera.autoFocus(null);
return false;
}
});
mHolder.lockCanvas();
}

public void capture(View view){
Camera.Parameters parameters=mCamera.getParameters();
parameters.setPictureFormat(ImageFormat.JPEG);
parameters.setPreviewSize(800,400);
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
mCamera.autoFocus(new Camera.AutoFocusCallback(){
@Override
public void onAutoFocus(boolean success, Camera camera) {
if(success){mCamera.takePicture(null,null, mpictureCallback);}
}
});

}

@Override
protected void onResume() {
super.onResume();
if (mCamera==null){
mCamera=getCamera();
if(mHolder!=null){
setStartPreview(mCamera,mHolder);}
}
}

@Override
protected void onPause() {
super.onPause();
releaseCamera(); }

private Camera getCamera(){
Camera camera;
try{
camera=Camera.open(cameraId);
}
catch (Exception e){
camera=null;
e.printStackTrace(); }
return camera;
}

private void setStartPreview(Camera camera,SurfaceHolder holder){
try{
camera.setPreviewDisplay(holder);
camera.setDisplayOrientation(90);
camera.startPreview();
}
catch (Exception e){
e.printStackTrace(); }
}
private void releaseCamera(){
if(mCamera!=null){
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera=null;
}
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
setStartPreview(mCamera,mHolder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mCamera.stopPreview();
setStartPreview(mCamera,mHolder);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
releaseCamera();
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus); }

}

TranslucencyView
package com.example.administrator.cameratwo;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.ImageView;

public class TranslucencyView extends ImageView{
private final Context mContext;
private int[] mCircleLocation;//声明存放坐标的数组

public TranslucencyView(Context context){this(context,null);}
public TranslucencyView(Context context, AttributeSet attributeSet){this(context,attributeSet,0);}
public TranslucencyView(Context context,AttributeSet attributeSet,int defStyleAttr){
super(context,attributeSet,defStyleAttr);
this.mContext=context;
initView();
}

private void initView(){setBackgroundColor(Color.parseColor(“#7f000000”));}//设置半透明底色
public void setCircleLocation(int[] location){
this.mCircleLocation=location;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mCircleLocation!=null){
//掏空一个圆形
Paint paintarc=new Paint(Paint.ANTI_ALIAS_FLAG);//创建一个画笔实例
PorterDuffXfermode porterDuffXfermode=new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
paintarc.setXfermode(porterDuffXfermode);
paintarc.setAntiAlias(true);
RectF rectF=new RectF(mCircleLocation[0],mCircleLocation[1],mCircleLocation[2],mCircleLocation[3]);
canvas.drawArc(rectF,0,360,true,paintarc);
//画虚线
Paint paintdashed=new Paint(Paint.ANTI_ALIAS_FLAG);
paintdashed.setStyle(Paint.Style.STROKE);
paintdashed.setColor(Color.WHITE);
paintdashed.setStrokeWidth(5);
PathEffect pathEffect=new DashPathEffect(new float[]{10,10},0);
paintdashed.setPathEffect(pathEffect);
canvas.drawArc(rectF,0,360,true,paintdashed);
//画矩形框
/** Paint paintrect=new Paint(Paint.ANTI_ALIAS_FLAG);
PorterDuffXfermode porterDuffXfermode1=new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
paintrect.setXfermode(porterDuffXfermode1);
paintrect.setAntiAlias(true);
//paintrect.setStrokeWidth(5);
canvas.drawRect(200, 400, 900, 1300, paintrect);*/
//画椭圆
Paint paintoval=new Paint(Paint.ANTI_ALIAS_FLAG);
PorterDuffXfermode porterDuffXfermode2=new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
paintoval.setXfermode(porterDuffXfermode2);
paintoval.setAntiAlias(true);
//paintoval.setStrokeWidth(10);
canvas.drawOval(100,200,1000,1200,paintoval);
}
}
}

这里画了个椭圆,注意用的都是*对坐标,我估计在不同的手机中可能会出现适配问题,不过还没想好相对的怎么画啦

ResultActivity
这个的代码和之前的3.3ResultActivity.java代码一模一样,大家直接看
Android Studio:使用Camera拍照(二)自定义相机

小结
上完了代码,我要小结一下给相机加取景蒙版和之前一篇的直接添加(文章后面有附上)有什么差别,我*开始的时候确实是很简单的两处代码合在一起,然后执行时发现两个问题:
1.整个显示出来的是深蓝色的
2.在蒙版还没稳定出现前,surfaceview尚在预览,蒙板稳定出现后,surfaceview瞬间卡在了那时的页面
解决问题1:我又回去看了自定义相机的执行(那个项目叫startcamera),发现surfaceview从开始到预览确实有个从暗变深蓝再变浅的过程,于是我猜是 showMask() 方法(这个方法中的内容后来被单独拿出来放到了onCreate中)中设置的时间太短,该时间用于测量view是够了(因为蒙板能顺利显示,如果时间不够,整个页面其实会闪退),那就是通过intent传给TranslucencyActivity.java的时间太短,surfaceview还没构造完,intent就把坐标信息给TranslucencyActivity了,于是我把时间从500ms改到1200ms
解决问题2:其实从解决问题1的*后几句话已经能看出问题2的端倪了,就是原本的showMask() *后有几句代码:

Intent intent = new Intent(SecondActivity.this, TranslucencyActivity.class);
intent.putExtra(“Location”, mCoodinate);
startActivity(intent);

这就意味着信使不仅将坐标传给TranslucencyActivity,还打开了TranslucencyActivity!!那这个界面就把surfaceview定住了,因为TranslucencyActivity没有任何写预览的方法,它只是把自定义的view和自己的页面绑在一起,所以要弄取景框蒙板没有TranslucencyActivity任何事情,我就把它删掉了,其他所做的更改上面代码已详细给出。

执行效果:

%title插图%num