2021SC@SDUSC
前言
上几篇文章已经分析完了飞花令的项目,从这一篇开始分析camera的项目。开发人员的设计中,以手机相机为依托的camera项目不仅需要有基础的拍照功能,并且还需要对拍摄的照?进??系列处理,包括但不仅限于图?抗扭曲,曝光度,聚焦等。具体可以整理为以下功能: 1. 相机预览功能 2. 拍照的偏好设置,如闪光灯,聚焦,曝光补偿 3. 相机可随设备旋转,拍摄横屏和竖屏的照? 4. 拍照后保存在?机的pictures?件夹 5. 可以预览拍摄的照? 6. 图?抗扭曲处理 这篇将继续分析相机的预览功能。
一、项目环境
android studio版本 4.1.2
sdk版本 Compile SDK version:30
Build Tools Version 30.0.3
gradle版本 6.8.3
二、代码分析
1.配置surface类
重写surfaceCreated方法,首先先用getCameraInstance()方法获得camera实例,setPreviewDisplay()方法告知mCamera实例应该在哪里绘制预览;
public void surfaceCreated(SurfaceHolder holder) {
mCamera = getCameraInstance();
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
?
关于surfaceDestroyed()方法,在帮助文档中是:“ This is called immediately before a surface is being destroyed. After returning from this call, you should no longer try to access this surface. If you have a rendering thread that directly accesses the surface, you must ensure that thread is no longer touching the Surface before returning from this function.”这段话表明在surface在关闭之前立即调用,他与surfaceCreated()的关系可以概括为:当view处于可见状态时执行surfaceCreated(),不可见时执行surfaceDestoryed(),类似于c++中的构造函数和析构函数 在surfaceDestroyed()之中,removeCallback()删除了添加的关于surface变化的回调,二者解绑;而camera实例的setPrevireCallback()的作用是在视频预览过程中,每一帧的图像数据均会通过这个callback返回,此时设为null,才能够正确退出,这是必要的一步
public void surfaceDestroyed(SurfaceHolder holder) {
mHolder.removeCallback(this);
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();//释放camera实例空间
mCamera = null;
}
?
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
}
}
并且在xml文件里需要加上:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
获取到相机使用权限
2.相机功能设置
要实现相机的三个功能:聚焦,曝光补偿和闪光灯 在preference.xml里设计界面利用了ListPreference框架 ListPreference框架: 该框架为用户提供UI 来查看排序选项列表。该列表将包含每个选项的单选按钮,而且默认(或当前)选项应该被预先选中。要使用Android首选项框架解决此问题。首先,创建首选项XML文件来描述首选项,然后使用预先构建的活动类。
<ListPreference
android:defaultValue="auto"
android:key="flash_mode"
android:title="闪光灯" />
<ListPreference
android:key="focus_mode"
android:title="对焦模式" />
<ListPreference
android:defaultValue="0"
android:key="exposure_compensation"
android:title="曝光补偿" />
当选择其中某一个功能键时,会有两个视图。底视图称为首选项屏幕,中间的UI是一个列表首选项。当用户选择闪光灯或者对焦或者曝光补偿时时, UI视图将以模态对话框的形式出现,其中包含每个选项的单选按钮。用户选择一个选项之后,将立即该选项并关闭视图。当用户返回选项屏幕时,视图将反映前面保存的选择。
3.编写SettingsFragment类
开发人员使用Android推荐的PreferenceFragment作为相机偏好设置的菜单。PreferenceFragment继承自Fragment。有关于曝光补偿、闪光灯和聚焦的功能的实现,我们主要就是通过PreferenceFragment来实现的:Android专门为有多个参数的选项提供了便捷的基类PreferenceFragment,类内部封装了Preference,会帮我们自动读写设置,方便开发者便捷完成这类功能。 主要的步骤可以概括为: 在res中创建xml文件夹,文件夹中创建preference.xml文件(即2中的xml文件),创建PreferenceFragment的子类,在onCreate方法中调用addPreferencesFromResource方法加载xml目录下的资源即可 我们可以来看一下开发人员对此的编写:
1)定义SettringsFragment类
定义了SettringsFragment类,继承了PreferenceFragment父类,并且使用了SharedPreferences.OnSharedPreferenceChangeListener接口,用于监听首选项的变化
public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener{
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);//添加首选项xml文件
loadSupportedFlashMode();//加载闪光灯
loadSupportedFocusMode();//加载聚焦
loadSupportedExposeCompensation();//加载曝光补偿
}
}
2)获取相机
我们使用CameraPreview中的相机
public static Camera getCameraInstance() {
if (mCamera == null) {
try {
mCamera = Camera.open();
} catch (Exception e) {
Log.d(TAG, "camera is not available");
}
}
return mCamera;
}
然后我们将这个相机传入到SettingFragment中,用到的是passCamera()方法:mCamera用于存储传递过来的camera实例,mParameters用于存储camera实例的参数,passCamera()用来将相机传输给SettingsFragment。
static Camera mCamera;
static Camera.Parameters mParameters;
public static void passCamera(Camera camera) {
mCamera = camera;
mParameters = camera.getParameters();
}
然后在MainActivity中按钮监听之前添加
SettingsFragment.passCamera(cameraPreview.getCameraInstance());
向SettingsFragment传递来自camaraPreview的相机。
这样就做到了绑定相机的功能
3)设置曝光补偿
这是一个用来动态加载曝光补偿的方法,即和preference.xml中的曝光补偿相结合 代码如下:
private void stringListToListPreference(List<String> list, String key) {
final CharSequence[] charSeq = list.toArray(new CharSequence[list.size()]);
ListPreference listPref = (ListPreference) getPreferenceScreen().findPreference(key);
listPref.setEntries(charSeq);
listPref.setEntryValues(charSeq);
}
CharSequence是一个描述字符串结构的接口,在这个接口里面一般发现有三种常用的子类 :String类 :StringBuffer类 :StringBuilder类 在该接口中有如下方法 :public char charAt?(int index);//获取指定索引的字符 :public int length?();//获取字符串长度 :public CharSequence subSequence?(int start, int end);//截取部分字符串
有关PreferenceScreen的使用可以参考文章: https://blog.csdn.net/ZoeyZY/article/details/85128821 而方法getPreferenceScreen().findPreference()的作用则是获取由key指定的菜单条目,将其存储在listPref里面 再由setEntries()和setEntryValues向这个菜单条目指定用户可见的所有value和代码可见的所有value,就完成了对这个key条目的列表内容的动态加载。
静态成员变量KEY_PREF_PREV_SIZE存储预览分辨率的key(在preferences.xml定义)。 对于stringListToListPreference()的方法,其首先将List转换为CharSequence[];由getPreferenceScreen().findPreference()获取由key指定的菜单条目;由setEntries()和setEntryValues向这个菜单条目指定用户可见的所有value和代码可见的所有value,就完成了对这个key条目的列表内容的动态加载。
public static final String KEY_PREF_EXPOS_COMP = "exposure_compensation";
private void loadSupportedExposeCompensation() {
int minExposComp = mParameters.getMinExposureCompensation();//得到相机参数返回来的相机所具有的最小曝光补偿参数
int maxExposComp = mParameters.getMaxExposureCompensation();得到相机参数返回来的相机所具有的最大曝光补偿参数
List<String> exposComp = new ArrayList<>();//创建一个String类型的List,用来存储所有曝光补偿的最小值和最大值的所有中间的值,由最低到最高形成一个List<String>
for (int value = minExposComp; value <= maxExposComp; value++) {
exposComp.add(Integer.toString(value));
}
stringListToListPreference(exposComp, KEY_PREF_EXPOS_COMP);
}
由mParameters.getMinExposureCompensation()获取相机支持的最低曝光补偿,mParameters.getMaxExposureCompensation()获取相机支持的最高曝光补偿,由最低到最高形成一个List,指定key后交给stringListToListPreference()就好了。
三、总结
开始处理预览和参数设置的问题了,初步了解了ListPreference框架和PreferenceFragment类二者结合去处理首选项参数的问题,看到一篇有关于相机参数的文章,结合开发人员的注释,感觉学到了很多东西。
|