Android相机开发实战,2024年最新Android开发基础面试题

本文详细介绍了Android相机开发中的关键步骤,包括按照分辨率排序预览分辨率、筛选合适的屏幕比例,以及实现自动对焦等功能。通过示例代码展示了如何找到最佳的预览和图片分辨率,确保与屏幕比例匹配。此外,还包含了手动和自动对焦的实现方法,适用于面试和技术提升。
摘要由CSDN通过智能技术生成

// 按照分辨率从大到小排序

List<Camera.Size> supportedPreviewResolutions = new ArrayList<Camera.Size>(rawSupportedSizes);

Collections.sort(supportedPreviewResolutions, new Comparator() {

@Override

public int compare(Camera.Size a, Camera.Size b) {

int aPixels = a.height * a.width;

int bPixels = b.height * b.width;

if (bPixels < aPixels) {

return -1;

}

if (bPixels > aPixels) {

return 1;

}

return 0;

}

});

StringBuilder previewResolutionSb = new StringBuilder();

for (Camera.Size supportedPreviewResolution : supportedPreviewResolutions) {

previewResolutionSb.append(supportedPreviewResolution.width).append(‘x’).append(supportedPreviewResolution.height)

.append(’ ');

}

// 移除不符合条件的分辨率

double screenAspectRatio = (double) screenWidth

/ screenHeight;

Iterator it = supportedPreviewResolutions.iterator();

while (it.hasNext()) {

Camera.Size supportedPreviewResolution = it.next();

int width = supportedPreviewResolution.width;

int height = supportedPreviewResolution.height;

// 移除低于下限的分辨率,尽可能取高分辨率

if (width * height < MIN_PREVIEW_PIXELS) {

it.remove();

continue;

}

// 在camera分辨率与屏幕分辨率宽高比不相等的情况下,找出差距最小的一组分辨率

// 由于camera的分辨率是width>height,我们设置的portrait模式中,width<height

// 因此这里要先交换然preview宽高比后在比较

boolean isCandidatePortrait = width > height;

int maybeFlippedWidth = isCandidatePortrait ? height : width;

int maybeFlippedHeight = isCandidatePortrait ? width : height;

double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;

double distortion = Math.abs(aspectRatio - screenAspectRatio);

if (distortion > MAX_ASPECT_DISTORTION) {

it.remove();

continue;

}

// 找到与屏幕分辨率完全匹配的预览界面分辨率直接返回

if (maybeFlippedWidth == screenWidth

&& maybeFlippedHeight == screenHeight) {

return supportedPreviewResolution;

}

}

// 如果没有找到合适的,并且还有候选的像素,则设置其中最大比例的,对于配置比较低的机器不太合适

if (!supportedPreviewResolutions.isEmpty()) {

Camera.Size largestPreview = supportedPreviewResolutions.get(0);

return largestPreview;

}

// 没有找到合适的,就返回默认的

return defaultPreviewResolution;

}

private Camera.Size findBestPictureResolution() {

Camera.Parameters cameraParameters = mCamera.getParameters();

List<Camera.Size> supportedPicResolutions = cameraParameters.getSupportedPictureSizes(); // 至少会返回一个值

StringBuilder picResolutionSb = new StringBuilder();

for (Camera.Size supportedPicResolution : supportedPicResolutions) {

picResolutionSb.append(supportedPicResolution.width).append(‘x’)

.append(supportedPicResolution.height).append(" ");

}

Camera.Size defaultPictureResolution = cameraParameters.getPictureSize();

// 排序

List<Camera.Size> sortedSupportedPicResolutions = new ArrayList<Camera.Size>(

supportedPicResolutions);

Collections.sort(sortedSupportedPicResolutions, new Comparator<Camera.Size>() {

@Override

public int compare(Camera.Size a, Camera.Size b) {

int aPixels = a.height * a.width;

int bPixels = b.height * b.width;

if (bPixels < aPixels) {

return -1;

}

if (bPixels > aPixels) {

return 1;

}

return 0;

}

});

// 移除不符合条件的分辨率

double screenAspectRatio = screenWidth

/ (double) screenHeight;

Iterator<Camera.Size> it = sortedSupportedPicResolutions.iterator();

while (it.hasNext()) {

Camera.Size supportedPreviewResolution = it.next();

int width = supportedPreviewResolution.width;

int height = supportedPreviewResolution.height;

// 在camera分辨率与屏幕分辨率宽高比不相等的情况下,找出差距最小的一组分辨率

// 由于camera的分辨率是width>height,我们设置的portrait模式中,width<height

// 因此这里要先交换然后在比较宽高比

boolean isCandidatePortrait = width > height;

int maybeFlippedWidth = isCandidatePortrait ? height : width;

int maybeFlippedHeight = isCandidatePortrait ? width : height;

double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;

double distortion = Math.abs(aspectRatio - screenAspectRatio);

if (distortion > MAX_ASPECT_DISTORTION) {

it.remove();

continue;

}

}

// 如果没有找到合适的,并且还有候选的像素,对于照片,则取其中最大比例的,而不是选择与屏幕分辨率相同的

if (!sortedSupportedPicResolutions.isEmpty()) {

return sortedSupportedPicResolutions.get(0);

}

// 没有找到合适的,就返回默认的

return defaultPictureResolution;

}

private Size getOptimalPreviewSize(List sizes, int w, int h) {

final double ASPECT_TOLERANCE = 0.1;

double targetRatio = (double) w / h;

if (sizes == null)

return null;

Size optimalSize = null;

double minDiff = Double.MAX_VALUE;

int targetHeight = h;

// Try to find an size match aspect ratio and size

for (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 (Size size : sizes) {

if (Math.abs(size.height - targetHeight) < minDiff) {

optimalSize = size;

minDiff = Math.abs(size.height - targetHeight);

}

}

}

return optimalSize;

}

public void reAutoFocus() {

if (isSupportAutoFocus) {

mCamera.autoFocus(new Camera.AutoFocusCallback() {

@Override

public void onAutoFocus(boolean success, Camera camera) {

}

});

}

}

public List getResolutionList() {

return mCamera.getParameters().getSupportedPreviewSizes();

}

public Camera.Size getResolution() {

Camera.Parameters params = mCamera.getParameters();

Camera.Size s = params.getPreviewSize();

return s;

}

/*public void setCurrentCameraId(int current) {

mCurrentCameraId = current;

}*/

//定点对焦的代码

public void pointFocus(MotionEvent event) {

mCamera.cancelAutoFocus();

parameters = mCamera.getParameters();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {

//showPoint(x, y);

focusOnTouch(event);

}

mCamera.setParameters(parameters);

autoFocus();

}

//实现自动对焦

public void autoFocus() {

new Thread() {

@Override

public void run() {

try {

sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

if (mCamera == null) {

return;

}

mCamera.autoFocus(new Camera.AutoFocusCallback() {

@Override

public void onAutoFocus(boolean success, Camera camera) {

if (success) {

initCamera();//实现相机的参数初始化

}

}

});

}

};

}

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)

private void showPoint(int x, int y) {

if (parameters.getMaxNumMeteringAreas() > 0) {

List<Camera.Area> areas = new ArrayList<Camera.Area>();

WindowManager wm = (WindowManager) getContext()

.getSystemService(Context.WINDOW_SERVICE);

//xy变换了

int rectY = -x * 2000 / wm.getDefaultDisplay().getWidth() + 1000;

int rectX = y * 2000 / wm.getDefaultDisplay().getHeight() - 1000;

int left = rectX < -900 ? -1000 : rectX - 100;

int top = rectY < -900 ? -1000 : rectY - 100;

int right = rectX > 900 ? 1000 : rectX + 100;

int bottom = rectY > 900 ? 1000 : rectY + 100;

Rect area1 = new Rect(left, top, right, bottom);

areas.add(new Camera.Area(area1, 800));

parameters.setMeteringAreas(areas);

}

parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);

}

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)

public void focusOnTouch(MotionEvent event) {

Rect focusRect = calculateTapArea(event.getRawX(), event.getRawY(), 1f);

Rect meteringRect = calculateTapArea(event.getRawX(), event.getRawY(), 1.5f);

Camera.Parameters parameters = mCamera.getParameters();

parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);

if (parameters.getMaxNumFocusAreas() > 0) {

List<Camera.Area> focusAreas = new ArrayList<Camera.Area>();

focusAreas.add(new Camera.Area(focusRect, 1000));

parameters.setFocusAreas(focusAreas);

}

if (parameters.getMaxNumMeteringAreas() > 0) {

List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();

meteringAreas.add(new Camera.Area(meteringRect, 1000));

parameters.setMeteringAreas(meteringAreas);

}

mCamera.setParameters(parameters);

mCamera.autoFocus(this);

}

/**

  • Convert touch position x:y to {@link Camera.Area} position -1000:-1000 to 1000:1000.

*/

private Rect calculateTapArea(float x, float y, float coefficient) {

float focusAreaSize = 300;

int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue();

int centerX = (int) (x / getResolution().width * 2000 - 1000);

int centerY = (int) (y / getResolution().height * 2000 - 1000);

int left = clamp(centerX - areaSize / 2, -1000, 1000);

int right = clamp(left + areaSize, -1000, 1000);

int top = clamp(centerY - areaSize / 2, -1000, 1000);

int bottom = clamp(top + areaSize, -1000, 1000);

return new Rect(left, top, right, bottom);

}

private int clamp(int x, int min, int max) {

if (x > max) {

return max;

}

if (x < min) {

return min;

}

return x;

}

@Override

public void onAutoFocus(boolean success, Camera camera) {

}

public void setNull() {

adapterSize = null;

mPreviewSize = null;

}

}

以下是CameraActivity类:

public class CameraActivity extends Activity implements View.OnTouchListener,OnClickListener {

public static final String CAMERA_PATH_VALUE1 = “PHOTO_PATH”;

public static final String CAMERA_PATH_VALUE2 = “PATH”;

public static final String CAMERA_TYPE = “CAMERA_TYPE”;

public static final String CAMERA_RETURN_PATH = “return_path”;

private int PHOTO_SIZE_W = 2000;

private int PHOTO_SIZE_H = 2000;

public static final int CAMERA_TYPE_1 = 1;

public static final int CAMERA_TYPE_2 = 2;

private final int PROCESS = 1;

private CameraPreview preview;

private Camera camera;

private Context mContext;

private View focusIndex;

private ImageView flashBtn;

private int mCurrentCameraId = 0; // 1是前置 0是后置

private SurfaceView mSurfaceView;

private CameraGrid mCameraGrid;

private int type = 1; //引用的矩形框

private Button mBtnSearch;

private Button mBtnTakePhoto;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

mContext = this;

//requestWindowFeature(Window.FEATURE_NO_TITLE);

//getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏

//getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//拍照过程屏幕一直处于高亮

setContentView(R.layout.camera_home);

type = getIntent().getIntExtra(CAMERA_TYPE, CAMERA_TYPE_2);

initView();

InitData();

}

private void initView() {

focusIndex = (View) findViewById(R.id.focus_index);

flashBtn = (ImageView) findViewById(R.id.flash_view);

mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);

mCameraGrid = (CameraGrid) findViewById(R.id.camera_grid);

mBtnSearch = (Button) findViewById(R.id.search);

mBtnTakePhoto = (Button) findViewById(R.id.takephoto);

}

private void InitData() {

preview = new CameraPreview(this, mSurfaceView);

preview.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,

LayoutParams.MATCH_PARENT));

((FrameLayout) findViewById(R.id.layout)).addView(preview);

preview.setKeepScreenOn(true);

mSurfaceView.setOnTouchListener(this);

mCameraGrid.setType(type);

}

private Handler handler = new Handler();

private void takePhoto() {

try {

camera.takePicture(shutterCallback, rawCallback, jpegCallback);

} catch (Throwable t) {

t.printStackTrace();

Toast.makeText(getApplication(), “拍照失败,请重试!”, Toast.LENGTH_LONG)

.show();

try {

camera.startPreview();

} catch (Throwable e) {

}

}

}

@Override

protected void onResume() {

super.onResume();

int numCams = Camera.getNumberOfCameras();

if (numCams > 0) {

try {

mCurrentCameraId = 0;

camera = Camera.open(mCurrentCameraId);

camera.startPreview();

preview.setCamera(camera);

preview.reAutoFocus();

} catch (RuntimeException ex) {

Toast.makeText(mContext, “未发现相机”, Toast.LENGTH_LONG).show();

}

}

}

@Override

protected void onPause() {

if (camera != null) {

camera.stopPreview();

preview.setCamera(null);

camera.release();

camera = null;

preview.setNull();

}

super.onPause();

}

private void resetCam() {

camera.startPreview();

preview.setCamera(camera);

}

ShutterCallback shutterCallback = new ShutterCallback() {

public void onShutter() {

}

};

PictureCallback rawCallback = new PictureCallback() {

public void onPictureTaken(byte[] data, Camera camera) {

}

};

PictureCallback jpegCallback = new PictureCallback() {

public void onPictureTaken(byte[] data, Camera camera) {

new SaveImageTask(data).execute();

resetCam();

}

};

@Override

public boolean onTouch(View v, MotionEvent event) {

try {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {

preview.pointFocus(event);

}

} catch (Exception e) {

e.printStackTrace();

}

RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(

focusIndex.getLayoutParams());

layout.setMargins((int) event.getX() - 60, (int) event.getY() - 60, 0,0);

focusIndex.setLayoutParams(layout);

focusIndex.setVisibility(View.VISIBLE);

ScaleAnimation sa = new ScaleAnimation(3f, 1f, 3f, 1f,

ScaleAnimation.RELATIVE_TO_SELF, 0.5f,

ScaleAnimation.RELATIVE_TO_SELF, 0.5f);

sa.setDuration(800);

focusIndex.startAnimation(sa);

handler.postAtTime(new Runnable() {

@Override

public void run() {

focusIndex.setVisibility(View.INVISIBLE);

}

}, 800);

return false;

}

@Override

public void onClick(View v) {

switch (v.getId()) {

/*case R.id.camera_back:

setResult(0);

finish();

break;*/

case R.id.camera_flip_view:

switchCamera();

break;

case R.id.flash_view:

turnLight(camera);

break;

case R.id.action_button:

takePhoto();

break;

case R.id.search: //处理选中状态

mBtnSearch.setSelected(true);

mBtnTakePhoto.setSelected(false);

break;

case R.id.takephoto: //处理选中状态

mBtnTakePhoto.setSelected(true);

mBtnSearch.setSelected(false);

break;

}

}

private static String getCameraPath() {

Calendar calendar = Calendar.getInstance();

StringBuilder sb = new StringBuilder();

sb.append(“IMG”);

sb.append(calendar.get(Calendar.YEAR));

int month = calendar.get(Calendar.MONTH) + 1; // 0~11

sb.append(month < 10 ? “0” + month : month);

int day = calendar.get(Calendar.DATE);

sb.append(day < 10 ? “0” + day : day);

int hour = calendar.get(Calendar.HOUR_OF_DAY);

sb.append(hour < 10 ? “0” + hour : hour);

int minute = calendar.get(Calendar.MINUTE);

sb.append(minute < 10 ? “0” + minute : minute);

int second = calendar.get(Calendar.SECOND);

sb.append(second < 10 ? “0” + second : second);

if (!new File(sb.toString() + “.jpg”).exists()) {

return sb.toString() + “.jpg”;

}

StringBuilder tmpSb = new StringBuilder(sb);

int indexStart = sb.length();

for (int i = 1; i < Integer.MAX_VALUE; i++) {

tmpSb.append(‘(’);

tmpSb.append(i);

tmpSb.append(‘)’);

tmpSb.append(“.jpg”);

if (!new File(tmpSb.toString()).exists()) {

break;

}

tmpSb.delete(indexStart, tmpSb.length());

}

return tmpSb.toString();

}

//处理拍摄的照片

private class SaveImageTask extends AsyncTask<Void, Void, String> {

private byte[] data;

SaveImageTask(byte[] data) {

this.data = data;

}

@Override

protected String doInBackground(Void… params) {

// Write to SD Card

String path = “”;

try {

showProgressDialog(“处理中”);

path = saveToSDCard(data);

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} finally {

}

return path;

}

@Override

protected void onPostExecute(String path) {

super.onPostExecute(path);

if (!TextUtils.isEmpty(path)) {

Log.d(“DemoLog”, “path=” + path);

dismissProgressDialog();

Intent intent = new Intent();

intent.setClass(CameraActivity.this, PhotoProcessActivity.class);

intent.putExtra(CAMERA_PATH_VALUE1, path);

startActivityForResult(intent, PROCESS);

} else {

Toast.makeText(getApplication(), “拍照失败,请稍后重试!”,

Toast.LENGTH_LONG).show();

}

}

}

private AlertDialog mAlertDialog;

private void dismissProgressDialog() {

this.runOnUiThread(new Runnable() {

@Override

public void run() {

if (mAlertDialog != null && mAlertDialog.isShowing()

&& !CameraActivity.this.isFinishing()) {

mAlertDialog.dismiss();

mAlertDialog = null;

}

}

});

}

private void showProgressDialog(final String msg) {

this.runOnUiThread(new Runnable() {

@Override

public void run() {

if (mAlertDialog == null) {

mAlertDialog = new GenericProgressDialog(

CameraActivity.this);

}

mAlertDialog.setMessage(msg);

((GenericProgressDialog) mAlertDialog)

.setProgressVisiable(true);

mAlertDialog.setCancelable(false);

mAlertDialog.setOnCancelListener(null);

mAlertDialog.show();

mAlertDialog.setCanceledOnTouchOutside(false);

}

});

}

/**

  • 将拍下来的照片存放在SD卡中

*/

public String saveToSDCard(byte[] data) throws IOException {

Bitmap croppedImage;

// 获得图片大小

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

BitmapFactory.decodeByteArray(data, 0, data.length, options);

// PHOTO_SIZE = options.outHeight > options.outWidth ? options.outWidth

// : options.outHeight;

PHOTO_SIZE_W = options.outWidth;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

写在最后

对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。

![
[]


文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img
roid工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-7UQzxvrP-1712620186521)]
[外链图片转存中…(img-nQCBYEsr-1712620186522)]
[外链图片转存中…(img-gn96rT7O-1712620186522)]
[外链图片转存中…(img-otU3kaBR-1712620186523)]
[外链图片转存中…(img-8lFHvIuW-1712620186523)]
[外链图片转存中…(img-EmRU1VoF-1712620186523)]
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-NVskXhKl-1712620186524)]

写在最后

对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。

[外链图片转存中…(img-hRQyTdpQ-1712620186524)]

[外链图片转存中…(img-ySTJ5bQc-1712620186524)]
[]

[外链图片转存中…(img-3xXe8vTG-1712620186525)]
[外链图片转存中…(img-sDkhMi6Y-1712620186525)]

文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-cYbfp4Dd-1712620186525)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值