<Android 应用 之路> 简易手电筒
前言
快一个月没有写自己的博客了,由于最近换了工作,换了居住地,所以有一些杂事需要处理,从今天开始恢复正常,不赘述了。进入今天的主题 —– 简易的手电筒。
这个Demo中使用的是比较新的API,M版本之后添加的针对于手电筒的接口。这里使用的是Camera的API2接口,主要使用CameraManager中针对于闪光灯的一些方法,对于Camera API2的接口,后面在涉及相机应用的时候,API1和API2应该都会梳理一下,到时候再仔细的研究一下。
思路
实现一个简单的手电筒,考虑到M版本上新增的接口,可以直接通过setTorchMode来改变闪光灯的状态,实现开关,然后根据当前的闪光灯状态,有回调函数,若其他的应用打开了闪光灯或者是关闭了闪光灯,该应用要作出对应的调整,同时,开启和关闭的过程,需要有明显的用户感知和提示,这就要结合NotificationManager和CameraManager的接口一起实现了。
接口介绍
CameraManager.java(frameworks/base/core/java/android/hardware/camera2)
| 方法 | 含义 |
|---|---|
| TorchCallback | 针对闪光灯的回调 |
| AvailabilityCallback | 针对相机是否可用的回调 |
| CameraManager() | 构造函数 |
| getCameraIdList() | 获取相机的Id |
| registerAvailabilityCallback() | 注册相机是否可用的回调 |
| unregisterAvailabilityCallback() | 解除注册 |
| registerTorchCallback() | 注册针对闪光灯状态的回调 |
| unregisterTorchCallback() | 解除注册 |
| getCameraCharacteristics() | 传入参数为相机的id,获取相机的一些参数信息,如支持的预览大小,支持的滤镜等等 |
| openCamera() | 传入的参数为相机的id和状态的回调StateCallback,这个是在CameraDevice中定义的,打开相机操作 |
| setTorchMode() | 设置闪光灯的状态 |
实战代码
1.布局文件
由于是手电筒,布局文件很简单,主布局中只有一个button
activity_custom_button.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/flash_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
tools:context=".FlashActivity">
<Button
android:id="@+id/bt_flash"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:background="@drawable/flash_open" />
</FrameLayout>
显示当前闪光灯被占用的自定义Toast布局
busy_toast.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/busy_snackbar_bg"
android:gravity="center"
android:padding="@dimen/activity_horizontal_margin"
android:text="FlashLight is Busy , Sorry!"
android:textStyle="bold" />
</LinearLayout>
2.代码文件
主要就是两个类,一个是主Activity,一个就是用来执行notification的pendingintent的广播接收器
FlashActivity.java
package mraz.com.custombutton;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraManager;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.NotificationCompat;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Toast;
@TargetApi(Build.VERSION_CODES.M)
public class FlashActivity extends AppCompatActivity {
public static final String CLOSE_FLASH_ACTION = "android.intent.action.close_flash";
private static final int NOTIFICATIONID = 0;
private Button btFlash;
private boolean mIsFlashOn = false;
private CameraManager cameraManager = null;
private String[] mCameraIds;
private Notification mFlashOnNotification = null;
private NotificationManager notificationManager = null;
private boolean isFlashAvailbale = true;
private FrameLayout mContentPanel = null;
//闪光灯状态变化的回调
private CameraManager.TorchCallback torchCallback = new CameraManager.TorchCallback() {
@Override
public void onTorchModeUnavailable(String cameraId) {
super.onTorchModeUnavailable(cameraId);
//onTorchModeUnavailable 当前闪光灯不可用,如果当前闪光处于打开状态,则关闭它,并且对应的标志位
if (cameraId.equals(mCameraIds[0]) && mIsFlashOn) {
reverseFlashState();
}
isFlashAvailbale = false;
System.out.println("cameraId = " + cameraId + " onTorchModeUnavailable");
}
@Override
public void onTorchModeChanged(String cameraId, boolean enabled) {
super.onTorchModeChanged(cameraId, enabled);
//onTorchModeChanged 闪光灯状态变化回调 enabled=false 闪光灯关闭
//enabled=true 闪光灯已经开启
//通过这个回调设置标志位,如果当前闪光灯开着但是收到了闪光灯已经被关闭的回调,则改变对应的状态
isFlashAvailbale = true;
System.out.println("cameraid = " + cameraId + " enabled = " + enabled + " misFlashOn = " + mIsFlashOn);
if (cameraId.equals(mCameraIds[0]) && enabled == false && mIsFlashOn) {
reverseFlashState();
}
System.out.println("cameraId = " + cameraId + " onTorchModeChanged enabled = " + enabled);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_button);
//闪光灯开关按钮
btFlash = (Button) findViewById(R.id.bt_flash);
//整个布局
mContentPanel = (FrameLayout) findViewById(R.id.flash_content);
btFlash.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
reverseFlashState();
}
});
//根据当前闪光灯装填设置一下UI界面的颜色
changeFlashUi(mIsFlashOn);
}
//flash状态翻转
private void reverseFlashState() {
//如果当前Flash 处于unavailable状态,说明当前闪光灯被占用,无法使用
if (!isFlashAvailbale) {
//显示当前闪光灯被占用的提示
showFlashBusy();
return;
}
changeFlashState(mIsFlashOn);//开->关 关->开
mIsFlashOn = !mIsFlashOn;//标志位装换
changeFlashUi(mIsFlashOn);//界面UI切换,这里主要就是为了突出闪光灯开关的状态不同
applyNotification(mIsFlashOn);//闪光灯开启的提示显示和消除
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void applyNotification(boolean isFlashOn) {
if (!isFlashOn) {
dismissNotification();
return;
}
if (mFlashOnNotification != null && notificationManager != null) {
notificationManager.notify(NOTIFICATIONID, mFlashOnNotification);
}
}
@Override
protected void onResume() {
super.onResume();
cameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
cameraManager.registerTorchCallback(torchCallback, null);//注册回调
getCameraList();//获取当前手机的摄像头个数
generateNotify();//生成需要显示的提示,方便后面显示
}
@Override
protected void onDestroy() {
super.onDestroy();
cameraManager.unregisterTorchCallback(torchCallback);//ondestory的时候解除回调
}
@TargetApi(Build.VERSION_CODES.M) //只有M版本的手机可以使用这个方法
private void changeFlashState(boolean isFlashOn) {
if (cameraManager != null && mCameraIds != null) {
try {
cameraManager.setTorchMode(mCameraIds[0], !isFlashOn);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP) //只有L版本的收集可以使用这个方法
private void getCameraList() {
if (cameraManager != null) {
try {
mCameraIds = cameraManager.getCameraIdList();
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}
//按钮的背景图切换
private void changeFlashUi(boolean isFlashOn) {
if (isFlashOn) {
btFlash.setBackgroundResource(R.drawable.flash_open);
} else {
btFlash.setBackgroundResource(R.drawable.flash_close);
}
}
//生成notification的大图标
private Bitmap createNotificationLargeIcon(Context c) {
Resources res = c.getResources();
int width = (int) res.getDimension(android.R.dimen.notification_large_icon_width);
int height = (int) res.getDimension(android.R.dimen.notification_large_icon_height);
Bitmap result = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(res, R.mipmap.ic_flash_on_normal), width, height, false);
return result;
}
//生成notification
private void generateNotify() {
if (mFlashOnNotification != null) return;
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setLargeIcon(createNotificationLargeIcon(this))
.setContentTitle("手电筒已开启")
.setContentText("点击可关闭手电筒")
.setSmallIcon(R.mipmap.ic_flash_off_normal, 3)
.setContentIntent(createCloseFlashPendingIntent());
mFlashOnNotification = builder.build();
}
//消除notification
private void dismissNotification() {
if (notificationManager != null && mFlashOnNotification != null) {
notificationManager.cancel(NOTIFICATIONID);
}
}
//创建点击notification对应的PendingIntent
private PendingIntent createCloseFlashPendingIntent() {
Intent intent = new Intent();
intent.setClass(this, FlashCloseReceiver.class);
intent.setAction(CLOSE_FLASH_ACTION);
return PendingIntent.getBroadcast(this, 0, intent, 0);
}
//显示一个手电筒忙碌的提示
private void showFlashBusy() {
View toastContent = getLayoutInflater().inflate(R.layout.busy_toast, null, false);
Toast toast = new Toast(this);
toast.setView(toastContent);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.setDuration(Toast.LENGTH_LONG);
toast.show();
}
}
FlashCloseReceiver.java
package mraz.com.custombutton;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraManager;
import android.os.Build;
public class FlashCloseReceiver extends BroadcastReceiver {
CameraManager mCameraManager = null;
String[] mCameraIds = null;
public FlashCloseReceiver() {
}
@TargetApi(Build.VERSION_CODES.M)
@Override
public void onReceive(Context context, Intent intent) {
System.out.println("onReceiver");
mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
if (mCameraManager != null) {
try {
mCameraIds = mCameraManager.getCameraIdList();
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
String action = intent.getAction();
if (action.equals(FlashActivity.CLOSE_FLASH_ACTION)) {
if (mCameraManager != null && mCameraIds != null && mCameraIds.length != 0) {
try {
System.out.println("setTorchMode");
mCameraManager.setTorchMode(mCameraIds[0], false);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}
}
}
实际效果图
手电筒关闭状态
手电筒开启状态
手电筒开启状态提示信息
备注
由于开发时间比较短,测试可能不充分,有问题欢迎留言讨论~
<Android 应用 之路> 简易手电筒的更多相关文章
- Android学习之路——简易版微信为例(一)
这是“Android学习之路”系列文章的开篇,可能会让大家有些失望——这篇文章中我们不介绍简易版微信的实现(不过不是标题党哦,我会在后续博文中一步步实现这个应用程序的).这里主要是和广大园友们聊聊一个 ...
- Android学习之路——简易版微信为例(三)
最近好久没有更新博文,一则是因为公司最近比较忙,另外自己在Android学习过程和简易版微信的开发过程中碰到了一些绊脚石,所以最近一直在学习充电中.下面来列举一下自己所走过的弯路: (1)本来打算前端 ...
- Android学习之路——简易版微信为例(二)
1 概述 从这篇博文开始,正式进入简易版微信的开发.深入学习前,想谈谈个人对Android程序开发一些理解,不一定正确,只是自己的一点想法.Android程序开发不像我们在大学时候写C控制台程序那样, ...
- <Android 应用 之路> 简易贪吃蛇
最简单的贪吃蛇 最近想着忙里偷闲写点简单的Android应用,增加一些生活乐趣,由于平时工作主要精力并不是集中在书写apk上,更多的是解决代码问题和维护模块稳定,但是写代码本身是一件比较有趣的事情,因 ...
- Android高薪之路-Android程序员面试宝典
Android高薪之路-Android程序员面试宝典
- 小猪的Android入门之路 Day 3 - part 3
小猪的Android入门之路 Day 3 - part 3 各种UI组件的学习 Part 3 本节引言: 在前面两个部分中我们对Android中一些比較经常使用的基本组件进行了一个了解, part 1 ...
- 小猪的Android入门之路 Day 7 part 2
小猪的Android入门之路 Day 7 part 2 Android的数据存储与訪问之--SharedPreferences(保存偏好參数) ---转载请注明出处:coder-pig 本节引言: 在 ...
- 小猪的Android入门之路 day 1
小猪的Android入门之路 Day 1 Android相关背景与开发环境的搭建 ------转载请注明出处:coder-pig 本节引言: 随着社会经济的发展,移动互联网的越来越热,手机APP开发显 ...
- 小猪的Android入门之路 Day 4 - part 1
小猪的Android入门之路 Day 4 - part 1 Android事件处理机制之--基于监听的事件处理机制 本节引言: 在開始本个章节前,我们先回想下,如今我们已经知道了android的一些相 ...
随机推荐
- php代码书写习惯优化小结
(1)使用 static 静态方法比普通方法快4倍(2)echo输出快于print(3)连接字符使用 , 代替 .(4)循环之前先取出最大值,而不是在循环里面取值 正确的方法 $max ...
- oracle知识总结
Oracle 分页案例: SELECT * FROM(SELECT A.*, ROWNUM RN FROM (SELECT * FROM Table_name) AWHERE ROWNUM <= ...
- AngularJS 1.x系列:AngularJS服务-Service
1. AngularJS服务 AngularJS可注入类型包括:Service.Factory.Provider.Value及Constant. 2. Service AngularJS Servic ...
- Cboard 实现看板里面图表仪表盘之间的联动
首先要了解Cboard的概念, 1.看板 2.图表 看Cborad的菜单知道了,首先先设计图表,可以展现未柱状图或者其它图片形式或者数据表格式.然后看板可以由多个图表组合而成. 需求如下: 看板包含两 ...
- linux中tr的功能多多
功能 通过使用 tr,您可以非常容易地实现 sed 的许多最基本功能. 您可以将 tr 看作为 sed 的(极其)简化的变体:它可以用一个字符来替换另一个字符,或者可以完全除去一些字符.您也可以用它来 ...
- 小Q系列故事——最佳裁判
Time Limit:200MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Description 过去的2012年 ...
- linux驱动之设备模型
linux 设备驱动模型 inux2.6提供了新的设备模型:总线.驱动.设备.基本关系简要的概括如下: 驱动核心可以注册多种类型的总线. 每种总线下面可以挂载许多设备.(通过kset devices) ...
- vim(二) 代码查看
ctags,cscope 查看代码 生成cscope脚本文件 #!/bin/bash if [ -f "*.cscope" ]; then rm -fr *.cscope fi i ...
- mysql允许数据库远程连接
2018-11-06 进入数据库 mysql -uroou(用户) -p123456(密码) 授权某个user可远程访问 grant all privileges on *.* to ' with g ...
- rabbitmq 事务消息
事务消息主要用在发送方 在connection上加上事务属性, 发送方感知到本地事务执行失败, 需要通知broker将先前已经接收到的消息rollback,不要发给后面的消费者, 满足强一致性的要求 ...