Android 手电筒制作
一、SystemUI中手电筒的控制
通过手机下拉状态栏,可以找到手电筒图标。也就是说SystemUI中存在控制手电筒的代码。
SystemUI源码路径:
frameworks\base\packages\SystemUI
找到SystemUI中关于对手电筒控制相应类的位置:
\frameworks\base\packages\SystemUI\src\com\android\systemui\qs\tiles\FlashlightTile.java
/** Quick settings tile: Control flashlight **/
public class FlashlightTile extends QSTileImpl<BooleanState> implements
FlashlightController.FlashlightListener {
@Override
protected void handleClick() {
if (ActivityManager.isUserAMonkey()) {
return;
}
boolean newState = !mState.value;
//调用QSTileImpl的refreshState方法,更新下拉状态栏状态
refreshState(newState);
//通过FlashlightController设置手电筒开或关
mFlashlightController.setFlashlight(newState);
}
}
FlashlightController 源码位于:
frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\policy\FlashlightController.java
public interface FlashlightController extends CallbackController<FlashlightListener>, Dumpable
这是一个接口类,具体实现在FlashlightControllerImpl中:
public class FlashlightControllerImpl implements FlashlightController{
private final CameraManager mCameraManager;
public FlashlightControllerImpl(Context context) {
mContext = context;
mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
tryInitCamera();
}
private void tryInitCamera() {
try {
mCameraId = getCameraId();
} catch (Throwable e) {
Log.e(TAG, "Couldn't initialize.", e);
return;
}
if (mCameraId != null) {
ensureHandler();
mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
}
}
}
初始化了CameraManager,通过相机控制操作手电筒开关。
在setFlashlight方法中控制手电筒开关。
public void setFlashlight(boolean enabled) {
boolean pendingError = false;
synchronized (this) {
if (mCameraId == null) return;
if (mFlashlightEnabled != enabled) {
mFlashlightEnabled = enabled;
try {
//使用setTorchMode方法控制
mCameraManager.setTorchMode(mCameraId, enabled);
} catch (CameraAccessException e) {
Log.e(TAG, "Couldn't set torch mode", e);
mFlashlightEnabled = false;
pendingError = true;
}
}
}
dispatchModeChanged(mFlashlightEnabled);
if (pendingError) {
dispatchError();
}
}
二、制作简单手电筒
1、布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/open_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/openbutton"
/>
</LinearLayout>
直接在主方法布局文件中布局,放置一张按钮图片。
2、声明权限
android.permission.FLASHLIGHT
允许访问闪光灯
(不声明权限,好像也行,当然加上最好)
//用于5之前,已过时
<uses-permission android:name="android.hardware.camera" />
3、主要代码
oncreate
private CameraManager mCameraManager = null;
private boolean FlashlightEnable = true;
private static ImageView imageButton;
private boolean imageStatues = true;
private String mCameraId = "0";
private static int count = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化
initView();
//获得mCameraManager对象
this.mCameraManager = (CameraManager) getApplication().getSystemService(Context.CAMERA_SERVICE);
//获得CameraID,注册手电筒回调
tryInitCamera();
//默认开启程序,打开手电筒
openFlashlight();
imageButton.setImageResource(R.drawable.opening);
}
private void initView() {
imageButton = findViewById(R.id.open_button);
imageButton.setOnClickListener(this);
tryInitCamera();
}
//获得CameraID,注册手电筒回调
private void tryInitCamera() {
try {
mCameraId = getCameraId();
} catch (Throwable e) {
Log.e(TAG, "Couldn't initialize.", e);
return;
}
if (mCameraId != null) {
mCameraManager.registerTorchCallback(mTorchCallback, null);
}
}
private String getCameraId() throws CameraAccessException {
String[] ids = mCameraManager.getCameraIdList();
for (String id : ids) {
CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
if (flashAvailable != null && flashAvailable
&& lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
return id;
}
}
return null;
}
@Override
public void onClick(View v) {
if (imageStatues) {
closeFlashlight();
imageButton.setImageResource(R.drawable.openbutton);
} else {
openFlashlight();
imageButton.setImageResource(R.drawable.opening);
}
imageStatues = !imageStatues;
}
此方法中
- 给按钮图片赋值,并设置点击事件监听,每次点击切换图片,并打开或关闭手电筒。
- 通过
getApplication().getSystemService(Context.CAMERA_SERVICE);
获得CameraManager对象。 - tryInitCamera()方法中,获得CameraID与registerTorchCallback。
- 默认点开应用即打开手电筒。(可自定义)
CameraManager对象用于后面对手电筒的开关。
CameraId在此事实上并不需要去获得,直接给一个值即可。
例:private String mCameraId = "0";
TorchCallback:
private final CameraManager.TorchCallback mTorchCallback =
new CameraManager.TorchCallback() {
@Override
public void onTorchModeUnavailable(String cameraId) {
FlashlightEnable = false;
if (imageStatues) {
imageStatues = false;
change();
}
}
@Override
public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) {
super.onTorchModeChanged(cameraId, enabled);
if (count++==1){
return;
}
if (FlashlightEnable != enabled) {
FlashlightEnable = enabled;
}
if (imageStatues != enabled) {
imageStatues = enabled;
change();
}
}
};
通过TorchCallback监听手电筒是否可用与状态变化。
onTorchModeChanged方法在启动程序时会调用两次,
第一次cameraId是我们的id(获得到为0),enabled是当前手机手电筒状态
第二次cameraId为2,暂未知代表何意,enabled为false,与当前手电筒状态不同步,与cameraId为0的enable无关。
由此,可以设置判断,只有当监听到的是自己的id时,去同步参数。
通过自设change()方法去同步改变应用图片。
private void change() {
if (imageStatues) {
imageButton.setImageResource(R.drawable.opening);
} else {
imageButton.setImageResource(R.drawable.openbutton);
}
}
open&close:
private void openFlashlight() {
try {
if (mCameraManager != null) {
mCameraManager.setTorchMode(mCameraId, true);
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void closeFlashlight() {
try {
if (mCameraManager != null) {
mCameraManager.setTorchMode(mCameraId, false);
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
主要通过mCameraManager的setTorchMode()方法去控制手电筒状态。
Android 5之前可使用:
camera = Camera.open();
Camera.Parameters parameters = camera.getParameters();
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
camera.setParameters(parameters);
camera.startPreview();
// camera.stopPreview();
// camera.release();
开启或关闭手电筒。
后记:
如果在打开应用之后,不做任何操作,直接点Recent(显示最近使用应用)页面,在此关掉的话。onDestory()偶尔并不调用。(本机打log查看,大部分时候无onDestory方法log日志)。
这种情况,如果未开启应用前在SystemUI状态栏中打开手电筒,在打开应用不进行任何操作,之后直接在Recent页面,关掉应用,导致手电筒不关闭。
设置应用打开后的默认状态(即开启默认打开或者关闭一次手电筒)后,onDestory()方法正常被调用,上述现象不在出现。
找到另一篇关于onDestory的介绍:
扩展:
Android使用Camera2 替代过时的Camera API
在Android 5(API 21,Lollipop)以前,获得相机权限只需要在 AndroidManifest.xml文件里面添加权限就可以用了 。5之后的版本, 允许用户单独管理应用的某个权限,打开或者关闭 。使用相机需要判断是否有权限,没有就需要申请权限。
相机的闪光灯,在相机打开后,在外部会变成不可用状态。也就是说,想要使用闪光灯(手电筒用的就是闪光灯),就需要相机是关闭状态。此处不需要去申请权限。
gitee:https://gitee.com/TongYing1652/torch-test