目录
介绍
使用代码
- 下载源代码 - 1 MB
基本思路非常简单:
- 显示相机预览
- 添加矩形视图到相机预览(新裁剪图像的边界)
- 裁剪图像并将结果保存到文件中
让我们开始吧!在Android studio中创建一个新项目(我使用的是3.2.1版),或者您可以下载源文件并选择:File-New-Import项目。添加到build.gradle应用程序级别:
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
我使用Butterknife库,它非常有用。此外,我们需要相机和写入权限,因此将其添加到AndroidManifest.xml。
我们需要2个片段,一个用于相机预览,另一个用于显示裁剪图像。创建新片段——ImageFragment(文件-新建-片段-片段(空白)——添加TextView和Imageview到layout-xml文件:
将一些代码添加到ImageFragment类中,它只显示image和textview信息:
public class ImageFragment extends Fragment {
private Bitmap bitmap;
@BindView(R.id.res_photo)
ImageView resPhoto;
@BindView(R.id.res_photo_size)
TextView resPhotoSize;
public void imageSetupFragment(Bitmap bitmap) {
if (bitmap != null) {
this.bitmap = bitmap;
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setRetainInstance(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_image, container, false);
ButterKnife.bind(this, view);
//check if bitmap exist, set to ImageView
if (bitmap != null) {
resPhoto.setImageBitmap(bitmap);
String info = "image with:" + bitmap.getWidth() + "\n" +
"image height:" + bitmap.getHeight();
resPhotoSize.setText(info);
}
return view;
}
}
创建第二个片段—— PhotoFragment(文件-新建- Fragment- Fragment(空白)——向layout-xml文件添加一些组件。主要是SurfaceView(用于摄像机预览)和View(用于裁剪的边框)。
我使用了一个自定义按钮(绿色按钮),这是简单的代码,但很棒的视图!为此,您需要在Res-drawable中创建一个新的XML文件,如photo_button.xml,根据需要设置形状,颜色或使用一些Web资源,如下所示:http://angrytools.com/android/button/。
然后将背景按钮设置为:
android:background="@drawable/photo_button"
此外,我们需要裁剪边框——它将是一个简单的矩形,为此,您需要在Res-drawable中创建一个新的XML文件,如border.xml:
并设置view-background为:
android:background="@drawable/border" />
下一步,PhotoFragment类。我们不能使用标准意图来制作照片,我们需要自定义函数,所以我们可以使用Camera类——它已被弃用,但仍然很好用,所以让我们使用它。该Camera类用来设置图像捕获设置,启动/停止预览,抓拍图片,并检索编码的视频帧。此类是Camera服务的客户端,它管理实际的相机硬件。
要控制预览,我们需要使用SurfaceHolder.Callback。这abstract interface是为了保持显示界面。允许您控制曲面大小和格式,编辑曲面中的像素以及监视曲面的更改。
public class PhotoFragment extends Fragment implements SurfaceHolder.Callback
{}
还实现一些方法:
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
像这样:
@Override
public void surfaceCreated(SurfaceHolder holder) {
camera = Camera.open();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (previewing) {
camera.stopPreview();
previewing = false;
}
if (camera != null) {
try {
Camera.Parameters parameters = camera.getParameters();
//get preview sizes
List previewSizes = parameters.getSupportedPreviewSizes();
//find optimal - it very important
previewSizeOptimal = getOptimalPreviewSize(previewSizes, parameters.getPictureSize().width,
parameters.getPictureSize().height);
//set parameters
if (previewSizeOptimal != null) {
parameters.setPreviewSize(previewSizeOptimal.width, previewSizeOptimal.height);
}
if (camera.getParameters().getFocusMode().contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}
if (camera.getParameters().getFlashMode().contains(Camera.Parameters.FLASH_MODE_AUTO)) {
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
}
camera.setParameters(parameters);
//rotate screen, because camera sensor usually in landscape mode
Display display = ((WindowManager) context.getSystemService
(Context.WINDOW_SERVICE)).getDefaultDisplay();
if (display.getRotation() == Surface.ROTATION_0) {
camera.setDisplayOrientation(90);
} else if (display.getRotation() == Surface.ROTATION_270) {
camera.setDisplayOrientation(180);
}
//write some info
int x1 = previewLayout.getWidth();
int y1 = previewLayout.getHeight();
int x2 = borderCamera.getWidth();
int y2 = borderCamera.getHeight();
String info = "Preview width:" + String.valueOf(x1) + "\n" +
"Preview height:" + String.valueOf(y1) + "\n" +
"Border width:" + String.valueOf(x2) +
"\n" + "Border height:" + String.valueOf(y2);
resBorderSizeTV.setText(info);
camera.setPreviewDisplay(surfaceHolder);
camera.startPreview();
previewing = true;
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
camera.stopPreview();
camera.release();
camera = null;
previewing = false;
}
设置正确的相机预览尺寸非常重要,如果图像的纵横比不适合预览相机尺寸——裁剪的图像尺寸不正确。
public Camera.Size getOptimalPreviewSize(List sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
定义一些功能来拍照:
@OnClick(R.id.make_photo_button)
void makePhoto() {
if (camera != null) {
camera.takePicture(myShutterCallback,
myPictureCallback_RAW, myPictureCallback_JPG);
}
}
和一些回调:
Camera.ShutterCallback myShutterCallback = new Camera.ShutterCallback() {
@Override
public void onShutter() {
}
};
//leave it empty
Camera.PictureCallback myPictureCallback_RAW = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
}
};
//we need only JPG
Camera.PictureCallback myPictureCallback_JPG = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
Bitmap bitmapPicture
= BitmapFactory.decodeByteArray(data, 0, data.length);
Bitmap croppedBitmap = null;
Display display = ((WindowManager) context.getSystemService
(Context.WINDOW_SERVICE)).getDefaultDisplay();
if (display.getRotation() == Surface.ROTATION_0) {
//rotate bitmap, because camera sensor usually in landscape mode
Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmapPicture, 0, 0,
bitmapPicture.getWidth(), bitmapPicture.getHeight(), matrix, true);
//save file
createImageFile(rotatedBitmap);
//calculate aspect ratio
float koefX = (float) rotatedBitmap.getWidth() / (float) previewLayout.getWidth();
float koefY = (float) rotatedBitmap.getHeight() / previewLayout.getHeight();
//get viewfinder border size and position on the screen
int x1 = borderCamera.getLeft();
int y1 = borderCamera.getTop();
int x2 = borderCamera.getWidth();
int y2 = borderCamera.getHeight();
//calculate position and size for cropping
int cropStartX = Math.round(x1 * koefX);
int cropStartY = Math.round(y1 * koefY);
int cropWidthX = Math.round(x2 * koefX);
int cropHeightY = Math.round(y2 * koefY);
//check limits and make crop
if (cropStartX + cropWidthX
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?