一 .功能介绍(UI布局)
打开camera app
首先会进入拍照模式的预览界面:
总共就四个控件
1.整体的预览控件TextureView
2.左下角的缩略图ImageView
3.中间的拍照按钮ImageButton
4.切换摄像头的ImageButton
主要是两个Fragment 通过ViewPage 左右滑动切换拍照模式跟录像模式具体实现在后面,在我的Demo中向左滑动切换到录像模式
进入录像的预览模式:
总共就五个控件
1.整体的预览控件TextureView
2.左下角的缩略图ImageView
3.中间的拍照按钮ImageButton
4.切换摄像头的ImageButton
5.顶部的计时器Chronometer
二 .UI的代码部分
1.首先是 activity_main.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">
<androidx.viewpager.widget.ViewPager
android:layout_gravity="center"
android:id="@+id/change_page"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!--标题 <androidx.viewpager.widget.PagerTitleStrip
android:layout_gravity="top"
android:id="@+id/page_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>-->
</androidx.viewpager.widget.ViewPager>
</RelativeLayout>
主界面主要是注册了一个ViewPager 注释的部分是之前准备的标题栏,是想通过标题提示是录像界面还是拍照界面,但是自己没有找到把标题栏背景变成透明的方法于是就抛弃了,我方在这里如果后续找到的话及时更新.
2.其次是 Mainactivity.java中
package com.example.camera;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ViewPager change_page; //拍照录像切换
private List<Fragment> layoutList; //布局集合(拍照录像切换)
//private List<String> titleList; //标题集合(拍照录像切换)
private MyPagerAdapter myPagerAdapter;
//初始化视图界面(拍照/摄像)
private void initView(){
change_page = findViewById(R.id.change_page);
layoutList = new ArrayList<>();
layoutList.add(new TakePictureFragment());
layoutList.add(new RecorderVideoFragment());
// titleList = new ArrayList<>();
// titleList.add("拍照");
// titleList.add("录像");
//设置适配器
myPagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), layoutList);
change_page.setAdapter(myPagerAdapter);
}
//活动的创建
@Override
protected void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
initView();
//隐藏通知栏状态栏
if (Build.VERSION.SDK_INT >= 21) {
View decorView=getWindow().getDecorView();//获取当前界面的decorView
int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|View.SYSTEM_UI_FLAG_FULLSCREEN//隐藏状态栏
|View.SYSTEM_UI_FLAG_LAYOUT_STABLE//保持整个View的稳定,使其不会随着SystemUI的变化而变化;
|View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION//让导航栏悬浮在Activity上
// |View.SYSTEM_UI_FLAG_HIDE_NAVIGATION//隐藏导航栏
|View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;//沉浸模式且状态栏和导航栏出现片刻后会自动隐藏
decorView.setSystemUiVisibility(option);
getWindow().setStatusBarColor(Color.TRANSPARENT);//设置透明颜色
getWindow().setNavigationBarColor(Color.TRANSPARENT);
}
ActionBar actionBar=getSupportActionBar();
actionBar.hide();
}
protected void onDestroy() {
super.onDestroy();
TakePictureFragment.closeCamera();
}
}
主活动中:
1.做了初始化视图界面添加两个碎片TakePictureFragment和RecorderVideoFragment
2.因为ViewPage要用到PagerAdapter 将默认的界面设置成了拍照预览界面
3.onCreate中做了个沉浸式通知栏(隐藏状态栏和通知栏)
4.onDestroy中调用了TakePictureFrament中的closeCamera
5.注释掉的部分同上是标题栏
3.MyPagerAdapter.java中
package com.example.camera;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import java.util.List;
public class MyPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> layoutList;
// private List<String> titleList;
public MyPagerAdapter(FragmentManager manager, List<Fragment> layoutList ){
super(manager);
this.layoutList = layoutList;
// this.titleList = titleList;
}
@Override
public int getCount() {
// 页面数
return layoutList.size();
}
@NonNull
@Override
public Fragment getItem(int position) {
return layoutList.get(position);
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
}
}
这个类继承FragmentPageAdapter跟ViewPage联合使用,注:ViewPage跟activity也可以不一定是需要Fragment主要的功能就是通过左右滑动来切换fragment
3.fragment_take_picture.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">
<TextureView
android:id="@+id/textureView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<ImageButton
android:background="@drawable/shape_white_ring"
android:id="@+id/takePicture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/shape_take_photo"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="50dp"
/>
<ImageView
android:id="@+id/image_show"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_marginLeft="50dp"
android:layout_marginBottom="50dp" />
<ImageButton
android:id="@+id/change"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ic_baseline_flip_camera_android_24"
android:layout_alignParentBottom="true"
android:layout_marginRight="50dp"
android:layout_marginBottom="50dp"
android:layout_alignParentRight="true"
/>
</RelativeLayout>
4 TakePpictureFragment.java中
package com.example.camera;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class TakePictureFragment extends Fragment implements View.OnClickListener {
private static final String TAG = "TakePictureFragment";
private static final SparseIntArray ORIENTATION = new SparseIntArray();
static {
//手机ROTATION逆时针旋转
ORIENTATION.append(Surface.ROTATION_0, 90);
ORIENTATION.append(Surface.ROTATION_90, 0);
ORIENTATION.append(Surface.ROTATION_180, 270);
ORIENTATION.append(Surface.ROTATION_270, 180);
}
private TextureView textureView; //预览框控件
private ImageButton takePicture; //拍照按钮
private ImageButton change; //前后摄像头切换按钮
private ImageView mImageView; // 缩略图显示
private String mCameraId; // 摄像头Id
private Size mPreviewSize; //获取分辨率
private ImageReader mImageReader; //图片阅读器
private static CameraDevice mCameraDevice; //摄像头设备
private static CameraCaptureSession mCaptureSession; //获取会话
private CaptureRequest mPreviewRequest; //获取预览请求
private CaptureRequest.Builder mPreviewRequestBuilder; //获取到预览请求的Builder通过它创建预览请求
private Surface mPreviewSurface; //预览显示图
//权限申请
private String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.RECORD_AUDIO};
private List<String> permissionList = new ArrayList();
private ArrayList<String> imageList = new ArrayList<>(); //图片集合
protected boolean isCreated=false; //Fragment是否创建成功
private boolean isVisible; //Fragment是否可见
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreateView: success");
View view = inflater.inflate(R.layout.fragment_take_picture, container, false);
//注册监听控件
initView(view);
textureView.setSurfaceTextureListener(textureListener); //surfaceView回调里面配置相机打开相机
takePicture.setOnClickListener(this); //拍照监听
mImageView.setOnClickListener(this); //缩略图监听
change.setOnClickListener(this); //摄像头切换监听
getPermission(); //申请权限
//显示最后一张图
isCreated = true; //Fragment View 创建成功
return view; //显示当前View
}
// 第一步:获取权限
/**
* 获取拍照和读写权限
*/
private void getPermission() {
Log.d(TAG, "getPermission: success");
//版本判断 当手机系统大于23时,才有必要去判断权限是否获取
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//权限是否已经 授权 GRANTED-授权 DINIED-拒绝
for (String permission : permissions) {
//检查权限是否全部授予
if (ContextCompat.checkSelfPermission(getContext(), permission) != PackageManager.PERMISSION_GRANTED) {
//如果没有就添加到权限集合
permissionList.add(permission);
}
}
//是空返回ture
if (!permissionList.isEmpty()) {
requestPermissions(permissionList.toArray(new String[permissionList.size()]), 1);
} else {
//表示全都授权了
textureView.setSurfaceTextureListener(textureListener);
//显示最后一张图片 最新
setLastImagePath();
}
}
}
//权限回调
@Override
public void onRequestPermis