Android Studio | 直接调用系统摄像头拍照并回显、在SurfaceView中实现嵌入拍照并回显
1.直接调用系统摄像头拍照
功能实现:点击按钮,调用系统摄像头拍照之后,回显在imageviewl里面。
public class register extends AppCompatActivity
{
private ImageView shotview ;//shotview定义在这里
//【warning】如果这里写成:private ImageView shotview = findViewById(R.id.imageView);会报错
//因为需要在onCreate()将类实例化之后,才可以进行初始化。
private File currentImageFile = null;
@Override
protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.regiester);
shotview = findViewById(R.id.imageView); //shotview的初始化在这里
Button takePhoto = findViewById(R.id.getcamera);
takePhoto.setOnClickListener(new View.OnClickListener()
{
//直接调用系统摄像头
@Override
public void onClick(View v)
{
//dir指的是directory,目录。storage directory 存储目录。
//如果你想在外存储上放公共文件你可以使用getExternalStoragePublicDirectory()
//but!如果你的api 版本低于8,那么不能使用getExternalStoragePublicDirectory(),
//而是使用Environment.getExternalStorageDirectory(),不带参数,不能自己创建一个目录,只是返回外部存储的根路径。
File dir = new File(Environment.getExternalStorageDirectory(),”pictures”);
//函数原型:File newFile=new File(directory, filename)
//创建了一个文件夹,名字是dir,路径是外部存储的根路径,名字是”pictures”。
if(dir.exists())
{
dir.mkdirs();//在根路径下建子目录,子目录名是”pictures”
}
//命名临时图片的文件名
currentImageFile = new File(dir,System.currentTimeMillis() + “.jpg”);
if(!currentImageFile.exists())
{
try
{
currentImageFile.createNewFile();
}
catch (IOException e)
{
e.printStackTrace();
}
}
Intent it = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//通过intent调用照相机照相
it.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(currentImageFile));
//存到外部存储的临时文件currentImageFile的路径下
startActivityForResult(it, Activity.DEFAULT_KEYS_DIALER);
//如果想在Activity中得到新打开Activity 关闭后返回的数据,
// 需要使用系统提供的startActivityForResult(Intent intent, int requestCode)方法
//打开新的Activity,新的Activity 关闭后会向前面的Activity传回数据,为了得到传回的数据,
//必须在前面的Activity中重写onActivityResult(int requestCode, int resultCode, Intent data)方法。
}
});
}
//重写onActivityResult(int requestCode, int resultCode, Intent data)方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (requestCode == Activity.DEFAULT_KEYS_DIALER)
{
shotview.setImageURI(Uri.fromFile(currentImageFile));
}
}
}
关于内部存储与外部存储:
1、写一个相册程序,图片肯定是放在外部存储中;而如果要保存一个应用的一些设置数据,是放在内部存储的data目录下。因此其实在安卓文件管理中,我们都是在操作*对路径。
2、如今的设备,很多中高端机器都将自己的机身存储扩展到了8G以上,他们将存储在概念上分成了”内部internal” 和”外部external” 两部分,但其实都在手机内部。所以不管安卓手机是否有可移动的sdcard,他们总是有外部存储和内部存储。
3、你把手机连接电脑,能被电脑识别的部分就一定是外部存储。
4、公共文件Public files:文件是可以被自由访问,且文件的数据对其他应用或者用户来说都是由意义的,当应用被卸载之后,其卸载前创建的文件仍然保留。
私有文件Private files:外部存储上,应用私有文件的价值在于卸载之后,这些文件也会被删除。
5、如果你想在外存储上放公共文件你可以使用getExternalStoragePublicDirectory()。but!如果你的api 版本低于8,那么不能使用getExternalStoragePublicDirectory(),而是使用Environment.getExternalStorageDirectory(),它不带参数,不能自己创建一个目录,只是返回外部存储的根路径。
【引用总结自:个人认为的一个关于内部存储和外部存储的很好的总结】
2.将拍照界面通过SurfaceView嵌入自己写的界面,存储拍下的照片,照片回调显示
其中关于surfaceView的设置:
<SurfaceView
android:id=”@+id/sfv_preview”
android:layout_width=”350dp”
android:layout_height=”393dp”
android:text=”@string/photo_preview”
app:layout_constraintBottom_toBottomOf=”parent”
app:layout_constraintEnd_toEndOf=”parent”
app:layout_constraintHorizontal_bias=”0.529″
app:layout_constraintLeft_toLeftOf=”parent”
app:layout_constraintRight_toRightOf=”parent”
app:layout_constraintStart_toStartOf=”parent”
app:layout_constraintTop_toTopOf=”parent”
app:layout_constraintVertical_bias=”0.135″ />
register.java中的代码如下:
public class register extends AppCompatActivity
{
private SurfaceView sfv_preview;
private Button btn_take_photo;
private Camera camera = null;
//surfaceHolder是surface的监听器,提供访问和控制SurfaceView背后的Surface 相关的方法
private SurfaceHolder.Callback cpHolderCallback = new SurfaceHolder.Callback()
{
@Override
public void surfaceCreated(SurfaceHolder holder)
//当surface对象创建后,该方法就会被立即调用。
{
startPreview();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
//当surface发生任何结构性的变化时(格式或者大小),该方法就会被立即调用
{
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
//当surface对象在将要销毁前,该方法会被立即调用。
{
stopPreview();
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.regiester);
bindViews();
}
private void bindViews()
{
sfv_preview = (SurfaceView) findViewById(R.id.sfv_preview);
btn_take_photo = (Button) findViewById(R.id.getcamera);
sfv_preview.getHolder().addCallback(cpHolderCallback);
//获得sfv_preview的surfaceview,
//再由 addCallback为SurfaceHolder添加一个SurfaceHolder.Callback回调接口。
btn_take_photo.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)//按下“开始拍照”按钮
{
camera.takePicture(null, null, new Camera.PictureCallback()
//拍照函数
{
@Override
public void onPictureTaken(byte[] data, Camera camera)
{
String path = “”;//定义路径
if ((path = saveFile(data)) != null)//如果路径存在
{
Intent it = new Intent(register.this, show_registered_photo.class);
it.putExtra(“path”, path);
startActivity(it);
//用intent实现从当前界面跳转到预览照片的界面
}
else
{
Toast.makeText(register.this, “保存照片失败”, Toast.LENGTH_SHORT).show();
}
}
});
}
});
}
//保存临时文件的方法
private String saveFile(byte[] bytes){
try {
File file = File.createTempFile(“img”,””);
FileOutputStream fos = new FileOutputStream(file);
fos.write(bytes);
fos.flush();
fos.close();
return file.getAbsolutePath();
}
catch (IOException e)
{
e.printStackTrace();
}
return “”;
}
//开始预览
private void startPreview()
{
camera = Camera.open();
try
{
camera.setPreviewDisplay(sfv_preview.getHolder());
camera.setDisplayOrientation(90); //让相机旋转90度
camera.startPreview();
}
catch (IOException e)
{
e.printStackTrace();
}
}
//停止预览
private void stopPreview() {
camera.stopPreview();
camera.release();
camera = null;
}
}
3.1遇到的小插曲(1):
代码没有错的情况下,和camera有关的方法都报错。如下图所示:
后来发现是因为import了错误的包,关于Camera有两个不同的包,在快捷import的时候有两个选项,如下图(画×的是我之前调用的包,画√的是导入了之后就不再报错的包),导入的时候要多注意:
3.2 遇到的小插曲(2)
拍照界面跳出来之后,按下btn_take_photo按钮后闪退回登录界面。
报错原因:是因为show_registered_photo类没有注册,Alt+Enter快捷键,讲其写入 AndroidManifest.xml 即可。