Android 13.0 MTK Camera2 设置默认拍照尺寸功能实现
文章目录
- 需求:
- 参考资料
- 架构图了解
- Camera相关专栏
- 零散知识了解
- 部分相机源码参考,学习API使用,梳理流程,偏应用层
- Camera2 系统相关
- 修改文件-修改方案
- 修改文件:
- 修改内容:
- PictureSize.java onValueInitialized 方法
- PictureSizeHelper.java getCustomDefault 方法
- 源码分析
- 设置拍照尺寸的核心类
- 源码逐步分析
- 关联的照片大小搜索
- PictureSizeSelector
- 进入界面选中的逻辑
- 点击选中图片大小逻辑
- PictureSizeSelectorPreference
- PictureSizeSettingView
- onItemClick
- onPreferenceClick
- PictureSize
- onValueInitialized
- 总结
需求:
默认相机拍照尺寸
需求原因
1)客户自身喜好
2)部分客户自己的摄像头不兼容,比如拍照尺寸太大会卡顿,拉丝等现象
参考资料
Android 13.0 MTK Camera2 设置默认拍照尺寸功能实现
Android 11.0 MTK Camera2 设置默认拍照尺寸功能实现
这两篇文章介绍的蛮好的,可以看看,对于类似需求同理也可以按照改一改? 为什么有些会默认最高分辨率拍照,可能原因在于手机端产品为了最好的拍摄效果,尺寸会默认到最大。
对于Camera2 开发,遇到的困难点就是源码代码量太多,对于很多同事来说就是一脸懵逼,建议多积累一定的代码量,掌握基本的知识。
架构图了解
MTKCamera2相机架构
Camera2架构
Android Camera架构简析
Camera相关专栏
Camera Framework 专栏
小驰私房菜系列
小驰私房菜MTK系列
小驰Camera 开发系列
Camera 相机开发
展讯平台 Camera
官方文档:谷歌官方 API 描述
零散知识了解
MTK 相机UI介绍
Camera2 相机认知
Camera2学习笔记
camera2关于拍照预览方向旋转90度和拍照图片镜像功能实现
Camera2 预览集成、简单拍照:熟悉预览步骤流程比较有用
Camera镜像上下左右颠倒问题的解决办法
MTK相机成像质量差
Camera应用分析
部分相机源码参考,学习API使用,梳理流程,偏应用层
极客相机 Camera2 API
Camera2 API详解
极客相机源码
Camera2 相机Demo
Camera2 专业相机Demo
拍照、预览、录像Demo
使用Camera2 拍照
Camera2 系统相关
Camera2 Service 启动
修改文件-修改方案
修改文件:
\vendor\mediatek\proprietary\packages\apps\Camera2\feature\setting\picturesize\src\com\mediatek\camera\feature\setting\picturesize\PictureSize.java\vendor\mediatek\proprietary\packages\apps\Camera2\feature\setting\picturesize\src\com\mediatek\camera\feature\setting\picturesize\PictureSizeHelper.java
修改内容:
PictureSize.java onValueInitialized 方法
在 onValueInitialized 方法中,默认valueInStore 中调用PictureSizeHelper 类中自定义的 getCustomDefault 方法
if (valueInStore == null) {// Default picture size is the max full-ratio size.
- List<String> entryValues = getEntryValues();
+ LogHelper.d(TAG, "valueInStore == null Default picture size is the max full-ratio size:");
+ /*List<String> entryValues = getEntryValues();for (String value : entryValues) {if (PictureSizeHelper.getStandardAspectRatio(value) == fullRatio) {valueInStore = value;
+ LogHelper.d(TAG, "valueInStore == null:valueInStore:"+valueInStore);break;}
- }
+ }*/
+ valueInStore =PictureSizeHelper.getCustomDefault(getEntryValues());
+ }
PictureSizeHelper.java getCustomDefault 方法
自定义 getCustomDefault 方法,设置默认分辨率。 这里直接写死,或者 根据客需要求设置为最大、最小 等。
public static String getCustomDefault(List<String> supportedEntryValues){for (int i=0;i< supportedEntryValues.size();i++) { /*Size size = valueToSize(supportedEntryValues.get(i));temp = size.width * size.height; if (temp > maxSize) {maxSize = temp;maxIndex = i;}*/LogHelper.d(TAG, "getCustomDefault:" +supportedEntryValues.get(i));}return "1920x1088";}
源码分析
设置拍照尺寸的核心类
\vendor\mediatek\proprietary\packages\apps\Camera2\feature\setting\picturesize\src\com\mediatek\camera\feature\setting\picturesize\PictureSize.java
\vendor\mediatek\proprietary\packages\apps\Camera2\feature\setting\picturesize\src\com\mediatek\camera\feature\setting\picturesize\PictureSizeHelper.java
\vendor\mediatek\proprietary\packages\apps\Camera2\feature\setting\picturesize\src\com\mediatek\camera\feature\setting\picturesize\PictureSizeSettingView.java
源码逐步分析
关联的照片大小搜索
grep -rn 照片大小
找到如下路径相关关键字:照片大小
feature/mode/vsdof/res/values-zh-rCN/strings.xml
<string name="sdof_picture_size_title">"照片大小"</string>feature/setting/picturesize/res/values-zh-rCN/strings.xml<string name="pref_camera_picturesize_title">"照片大小"</string>
PictureSizeSelector
上面已经找到了两个关键字,这里先根据第一个关键字找一下源码:
路径:
/vendor/mediatek/proprietary/packages/apps/Camera2/feature/setting/picturesize/src/com/mediatek/camera/feature/setting/picturesize/PictureSizeSelector.java
R.string.pref_camera_picturesize_title
我们看类定义:
/*** Picture size selector.*/public class PictureSizeSelector extends PreferenceFragment {
就是图片大小选择器
对应的是如下界面:
既然是这个界面,那就看一下点击选中方法和进入这个界面的设置方法
进入界面选中的逻辑
这里关联的有四个方法,对应的进入界面选中逻辑:
@Overridepublic void onCreate(Bundle savedInstanceState) {LogHelper.d(TAG, "[onCreate]");super.onCreate(savedInstanceState);prepareValuesOnShown();Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);if (toolbar != null) {toolbar.setTitle(getActivity().getResources().getString(R.string.pref_camera_picturesize_title));}addPreferencesFromResource(R.xml.picturesize_selector_preference);PreferenceScreen screen = getPreferenceScreen();for (int i = 0 ; i < mEntryValues.size(); i++) {RadioPreference preference = new RadioPreference(getActivity());if (mEntryValues.get(i).equals(mSelectedValue)) {preference.setChecked(true);}LogHelper.d(TAG," onCreate ->mEntryValues.get(i):"+mEntryValues.get(i));preference.setTitle(mTitleList.get(i));preference.setSummary(mSummaryList.get(i));preference.setOnPreferenceClickListener(mOnPreferenceClickListener);screen.addPreference(preference);}}/*** Set the default selected value.** @param value The default selected value.*/public void setValue(String value) {LogHelper.d(TAG," setValue: value->"+value);mSelectedValue = value;}/*** Set the picture sizes supported.** @param entryValues The picture sizes supported.*/public void setEntryValues(List<String> entryValues) {mEntryValues.clear();mEntryValues.addAll(entryValues);}private void prepareValuesOnShown() {List<String> tempValues = new ArrayList<>(mEntryValues);mEntryValues.clear();mTitleList.clear();mSummaryList.clear();for (int i = 0; i < tempValues.size(); i++) {String value = tempValues.get(i);String title = PictureSizeHelper.getPixelsAndRatio(value);LogHelper.d(TAG," prepareValuesOnShown: value->"+value+" title:"+title);if (title != null) {mTitleList.add(title);mEntryValues.add(value);mSummaryList.add(value);}else{LogHelper.d(TAG, "[prepareValuesOnShown] value :"+value+" will not shown.");}}}
方法 | 作用 |
---|---|
setEntryValues | 设置支持的图片大小 分辨率 |
prepareValuesOnShown | 准备显示的数据 在onCreate 方法里面调用的 |
setValue | 设置默认选择的值 |
onCreate | 通过setEntryValue 设置的集合,遍历创建PreferenceScreen ,对于传递进来的value 作为选中效果 |
点击选中图片大小逻辑
对于点击逻辑更为简单,回调 返回上一层,代码如下:
private class MyOnPreferenceClickListener implements Preference.OnPreferenceClickListener {@Overridepublic boolean onPreferenceClick(Preference preference) {String summary = (String) preference.getSummary();int index = mSummaryList.indexOf(summary);String value = mEntryValues.get(index);mListener.onItemClick(value);LogHelper.d(TAG," MyOnPreferenceClickListener: onPreferenceClick value->"+value);mSelectedValue = value;getActivity().getFragmentManager().popBackStack();return true;}}
PictureSizeSelectorPreference
同 PictureSizeSelector 上面已经找到了两个关键字,这里先根据第二个关键字找一下源码:
路径:
./vendor/mediatek/proprietary/packages/apps/Camera2/feature/mode/aicombo/src/com/mediatek/camera/feature/mode/aicombo/photo/view/PictureSizeSelectorPreference.java
./vendor/mediatek/proprietary/packages/apps/Camera2/feature/mode/vsdof/src/com/mediatek/camera/feature/mode/vsdof/photo/view/PictureSizeSelectorPreference.java看源码发现和 PictureSizeSelector 代码基本一致 ,但是仔细看一个方法的说明如下:/*** Set the video quality supported.* @param entryValues The video quality supported.*/public void setEntryValues(List<String> entryValues) {mEntryValues.clear();mEntryValues.addAll(entryValues);}这些事设置Vieo 的 并不是设置picture 的。 所以我们需求设置图片质量的其实应该是上面找到的 PictureSizeSelector
PictureSizeSettingView
路径:
/vendor/mediatek/proprietary/packages/apps/Camera2/feature/setting/picturesize/src/com/mediatek/camera/feature/setting/picturesize/PictureSizeSettingView.java
找到这个类的方法有两种
- grep 查找关键字 “照片大小”
- 根据上面定位到的源码 PictureSizeSelector
就是进入到 PictureSizeSelector 界面之前的页面,如下:
准确的来说, PictureSizeSettingView 对应的其实是这个界面中 图片大小的这个item .
源码代码量不多,我们赋上来看看:
package com.mediatek.camera.feature.setting.picturesize;import android.app.Activity;
import android.app.FragmentTransaction;
import android.preference.PreferenceFragment;import com.mediatek.camera.R;
import com.mediatek.camera.common.debug.LogHelper;
import com.mediatek.camera.common.debug.LogUtil;
import com.mediatek.camera.common.preference.Preference;
import com.mediatek.camera.common.setting.ICameraSettingView;import java.util.ArrayList;
import java.util.List;/*** Picture size setting view.*/
public class PictureSizeSettingView implements ICameraSettingView,PictureSizeSelector.OnItemClickListener {private static final LogUtil.Tag TAG =new LogUtil.Tag(PictureSizeSettingView.class.getSimpleName());private Activity mActivity;private Preference mPref;private OnValueChangeListener mListener;private String mKey;private String mSelectedValue;private List<String> mEntryValues = new ArrayList<>();private String mSummary;private PictureSizeSelector mSizeSelector;private boolean mEnabled;/*** Listener to listen picture size value changed.*/public interface OnValueChangeListener {/*** Callback when picture size value changed.** @param value The changed picture size, such as "1920x1080".*/void onValueChanged(String value);}/*** Picture size setting view constructor.** @param key The key of picture size.*/public PictureSizeSettingView(String key) {mKey = key;}@Overridepublic void loadView(PreferenceFragment fragment) {LogHelper.d(TAG, "[loadView]");mActivity = fragment.getActivity();if (mSizeSelector == null) {mSizeSelector = new PictureSizeSelector();mSizeSelector.setOnItemClickListener(this);}fragment.addPreferencesFromResource(R.xml.picturesize_preference);mPref = (Preference) fragment.findPreference(mKey);mPref.setRootPreference(fragment.getPreferenceScreen());mPref.setId(R.id.picture_size_setting);mPref.setContentDescription(mActivity.getResources().getString(R.string.picture_size_content_description));mPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {@Overridepublic boolean onPreferenceClick(android.preference.Preference preference) {mSizeSelector.setValue(mSelectedValue);mSizeSelector.setEntryValues(mEntryValues);FragmentTransaction transaction = mActivity.getFragmentManager().beginTransaction();transaction.addToBackStack(null);transaction.replace(R.id.setting_container,mSizeSelector, "picture_size_selector").commit();return true;}});mPref.setEnabled(mEnabled);if (mSelectedValue != null) {mSummary = PictureSizeHelper.getPixelsAndRatio(mSelectedValue);}}@Overridepublic void refreshView() {if (mPref != null) {LogHelper.d(TAG, "[refreshView]");mPref.setSummary(mSummary);mPref.setEnabled(mEnabled);}}@Overridepublic void unloadView() {LogHelper.d(TAG, "[unloadView]");}@Overridepublic void setEnabled(boolean enabled) {mEnabled = enabled;}@Overridepublic boolean isEnabled() {return mEnabled;}/*** Set listener to listen the changed picture size value.** @param listener The instance of {@link OnValueChangeListener}.*/public void setOnValueChangeListener(OnValueChangeListener listener) {mListener = listener;}/*** Set the default selected value.** @param value The default selected value.*/public void setValue(String value) {mSelectedValue = value;}/*** Set the picture sizes supported.** @param entryValues The picture sizes supported.*/public void setEntryValues(List<String> entryValues) {mEntryValues = entryValues;}@Overridepublic void onItemClick(String value) {mSelectedValue = value;mSummary = PictureSizeHelper.getPixelsAndRatio(value);if (mListener != null) {mListener.onValueChanged(value);}}
}
onItemClick
这里关注下这个 onItemClick 点击方法,我们看类声明如下:
PictureSizeSettingView implements ICameraSettingView,PictureSizeSelector.OnItemClickListener
所以 这个 onItemClick 方法其实是PictureSizeSelector 类的点击方法的回调。
onPreferenceClick
先看源码,如下:
mPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {@Overridepublic boolean onPreferenceClick(android.preference.Preference preference) {mSizeSelector.setValue(mSelectedValue);mSizeSelector.setEntryValues(mEntryValues);FragmentTransaction transaction = mActivity.getFragmentManager().beginTransaction();transaction.addToBackStack(null);transaction.replace(R.id.setting_container,mSizeSelector, "picture_size_selector").commit();return true;}});
这个方法其实就做了三件事情:
- setValue 设置默认图片大小
- setEntryValues 设置支持的所有图片大小集合
- transaction.replace 操作,跳转到PictureSizeSelector 界面
这里其实就和PictureSizeSelector 完全关联起来了。
接下来就要关注 两个事情:
- mListener.onValueChanged(value); 在哪里设置回调的
- setValue 这个类里面也有设置默认图片大小,到底在哪里设置的
PictureSize
源码路径:
/vendor/mediatek/proprietary/packages/apps/Camera2/feature/setting/picturesize/src/com/mediatek/camera/feature/setting/picturesize/PictureSize.java
如何关联到这个类?
上面已经分析到了 PictureSizeSettingView 的 onValueChanged,其实就是要找 PictureSizeSettingView 中 接口 OnValueChangeListener 是在哪里实现的,这样来找到具体的实现和调用地方。
先看类说明:
这里我们看下部分代码片段截图:
上面截图就可以基本确认了,它对应的界面才是 照片设置图,如下:
onValueInitialized
源代码如下:
/*** Invoked after setting's all values are initialized.** @param supportedPictureSize Picture sizes which is supported in current platform.*/public void onValueInitialized(List<String> supportedPictureSize) {LogHelper.d(TAG, "[onValueInitialized], supportedPictureSize:" + supportedPictureSize);double fullRatio = PictureSizeHelper.findFullScreenRatio(mActivity);List<Double> desiredAspectRatios = new ArrayList<>();desiredAspectRatios.add(fullRatio);desiredAspectRatios.add(PictureSizeHelper.RATIO_4_3);PictureSizeHelper.setDesiredAspectRatios(desiredAspectRatios);PictureSizeHelper.setFilterParameters(DEGRESSIVE_RATIO, MAX_COUNT);if (sFilterPictureSize) {supportedPictureSize = PictureSizeHelper.filterSizes(supportedPictureSize);LogHelper.d(TAG, "[onValueInitialized], after filter, supportedPictureSize = "+ supportedPictureSize);}if (FILTER_MODE.equals(mModeKey)|| VFB_MODE.equals(mModeKey)|| FB_MODE.equals(mModeKey)) {//for low romif ((VFB_MODE.equals(mModeKey)||FILTER_MODE.equals(mModeKey))&& isLowRam()) {List<String> supportedPictureSizeAfterCheck = new ArrayList<String>();for (String pictureSize : supportedPictureSize) {String[] size = pictureSize.split("x");int width = Integer.parseInt(size[0]);int height = Integer.parseInt(size[1]);if (width < PICTURE_SIZE_9M_WIDTH&& height < PICTURE_SIZE_9M_HEIGHT) {supportedPictureSizeAfterCheck.add(pictureSize);}}supportedPictureSize = supportedPictureSizeAfterCheck;LogHelper.d(TAG, "[onValueInitialized], low ram, after check, " +"supportedPictureSize:"+ supportedPictureSize);} else {List<String> supportedPictureSizeAfterCheck = new ArrayList<String>();for (String pictureSize : supportedPictureSize) {String[] size = pictureSize.split("x");int width = Integer.parseInt(size[0]);int height = Integer.parseInt(size[1]);if (width <= PictureSizeHelper.getMaxTexureSize()&& height <= PictureSizeHelper.getMaxTexureSize()) {supportedPictureSizeAfterCheck.add(pictureSize);}}supportedPictureSize = supportedPictureSizeAfterCheck;LogHelper.d(TAG, "[onValueInitialized], GPU Mode, after check, " +"supportedPictureSize:"+ supportedPictureSize);}}if (HDR_MODE.equals(mModeKey)) {List<String> supportedPictureSizeAfterCheck = new ArrayList<String>();for (String pictureSize : supportedPictureSize) {for (String yuvSize:mYUVsupportedSize){if(pictureSize.equals(yuvSize)){supportedPictureSizeAfterCheck.add(pictureSize);}}}supportedPictureSize=supportedPictureSizeAfterCheck;LogHelper.d(TAG, "[onValueInitialized], PostAlgo Mode, after check, supportedPictureSize:"+ supportedPictureSize);}if (AIBEAUTYPHOTO_MODE.equals(mModeKey)|| AIBOKEHPHOTO_MODE.equals(mModeKey)|| AICOLORPHOTO_MODE.equals(mModeKey)|| AILEGGYPHOTO_MODE.equals(mModeKey)|| AISLIMMINGPHOTO_MODE.equals(mModeKey)) {List<String> supportedPictureSizeAfterCheck = new ArrayList<String>();for (String pictureSize : supportedPictureSize) {String[] size = pictureSize.split("x");int width = Integer.parseInt(size[0]);int height = Integer.parseInt(size[1]);if (width <= PictureSizeHelper.getMaxTexureSize()&& height <= PictureSizeHelper.getMaxTexureSize()) {supportedPictureSizeAfterCheck.add(pictureSize);}}supportedPictureSize = supportedPictureSizeAfterCheck;LogHelper.d(TAG, "[onValueInitialized], mModeKey:" + mModeKey + ",after check, supportedPictureSize:"+ supportedPictureSize);}setSupportedPlatformValues(supportedPictureSize);setSupportedEntryValues(supportedPictureSize);setEntryValues(supportedPictureSize);refreshViewEntry();String valueInStore = mDataStore.getValue(getKey(), null, getStoreScope());if (valueInStore != null&& !supportedPictureSize.contains(valueInStore)) {LogHelper.d(TAG, "[onValueInitialized], value:" + valueInStore+ " isn't supported in current platform");valueInStore = null;mDataStore.setValue(getKey(), null, getStoreScope(), false);}if (valueInStore == null) {// Default picture size is the max full-ratio size.LogHelper.d(TAG, "valueInStore == null Default picture size is the max full-ratio size:");/*List<String> entryValues = getEntryValues();for (String value : entryValues) {if (PictureSizeHelper.getStandardAspectRatio(value) == fullRatio) {valueInStore = value;LogHelper.d(TAG, "valueInStore == null:valueInStore:"+valueInStore);break;}}*/valueInStore =PictureSizeHelper.getCustomDefault(getEntryValues());}// If there is no full screen ratio picture size, use the first value in// entry values as the default value.if (valueInStore == null) {valueInStore = getEntryValues().get(0);LogHelper.d(TAG, "valueInStore:valueInStore:"+valueInStore);}setValue(valueInStore);}
看方法注释就一目了然了,根据当前平台设置-初始化图片大小
/*** Invoked after setting's all values are initialized.** @param supportedPictureSize Picture sizes which is supported in current platform.*/
对于 valueInStore 的具体设置内容和关联逻辑,这里暂不分析。
总结
- 实现了MTK Android13 平台下默认图片大小的逻辑
- 分析了解决问题的思路,源码分析流程
- 建议对Camera2 相关知识需要一定的了解最好
- 在查看源码的过程中,难免找不到关键字,不知道修改哪里。 可以借助IDE- AS VS 操作,方便定位源码位置。