前言

快一个月没有写自己的博客了,由于最近换了工作,换了居住地,所以有一些杂事需要处理,从今天开始恢复正常,不赘述了。进入今天的主题 ----- 简易的手电筒。

这个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"><Buttonandroid: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"><TextViewandroid: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() {@Overridepublic void onTorchModeUnavailable(String cameraId) {super.onTorchModeUnavailable(cameraId);//onTorchModeUnavailable 当前闪光灯不可用,如果当前闪光处于打开状态,则关闭它,并且对应的标志位if (cameraId.equals(mCameraIds[0]) && mIsFlashOn) {reverseFlashState();}isFlashAvailbale = false;System.out.println("cameraId = " + cameraId + " onTorchModeUnavailable");}@Overridepublic 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);}};@Overrideprotected 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() {@Overridepublic 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);}}@Overrideprotected void onResume() {super.onResume();cameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);cameraManager.registerTorchCallback(torchCallback, null);//注册回调getCameraList();//获取当前手机的摄像头个数generateNotify();//生成需要显示的提示,方便后面显示}@Overrideprotected 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;}//生成notificationprivate 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();}//消除notificationprivate void dismissNotification() {if (notificationManager != null && mFlashOnNotification != null) {notificationManager.cancel(NOTIFICATIONID);}}//创建点击notification对应的PendingIntentprivate 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)@Overridepublic 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 应用 之路》 简易手电筒相关推荐

  1. Android 小应用之一个activity实现简易手电筒(内附免费源码)

    Android小应用之一个activity实现简易手电筒(内附免费源码) 1.activity代码 2.xml代码 3.资源文件代码 4.源码压缩包(免费) 今天爷爷说他手机更新之后找不到手电筒了,我 ...

  2. Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现​-[Android取经之路]

    摘要:本节主要来讲解Android10.0 selinux.kernel日志在logd中的实现,包括LogAudit.LogKlog的源码分析 阅读本文大约需要花费15分钟. 文章首发微信公众号:In ...

  3. Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析-[Android取经之路]

    摘要:本节主要来讲解Android10.0 logd.logcat读写日志源码内容 阅读本文大约需要花费20分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Andro ...

  4. Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化-[Android取经之路]

    摘要:本节主要来讲解Android10.0 日志系统的架构分析,以及logd.logcat的初始化操作 阅读本文大约需要花费15分钟. 文章首发微信公众号:IngresGe 专注于Android系统级 ...

  5. Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性-[Android取经之路]

    摘要:本节主要来讲解Android10.0 日志系统的logd.logcat相关指令说明.日志分类和常用日志属性 阅读本文大约需要花费15分钟. 文章首发微信公众号:IngresGe 专注于Andro ...

  6. Android 10.0 PackageManagerService(三)APK扫描-[Android取经之路]

    摘要:上一节讲解了PKMS的 权限扫描,扫描/system/etc/permissions中的xml,存入相应的结构体中,供之后权限管理使用. 这一节主要来讲讲APK的扫描. 阅读本文大约需要花费15 ...

  7. Android 10.0 PackageManagerService(二)权限扫描-[Android取经之路]

    摘要:PackageManagerService在systemReady()后,进行了/system/etc/permissions中的各种xml进行扫描,进行相应的权限存储,供以后使用 阅读本文大约 ...

  8. Android 10.0 PackageManagerService(一)工作原理及启动流程-[Android取经之路]

    摘要:PackageManagerService是Android系统核心服务之一,在Android中的非常重要,主要负责APK.jar包等的管理. 阅读本文大约需要花费50分钟. 文章的内容主要还是从 ...

  9. Android10.0应用进程创建过程以及Zygote的fork流程-[Android取经之路]

    摘要:点击手机桌面图标,例如微信,它是如何启动的呢,让我们从系统源码级来一起分析. 阅读本文大约需要花费1小时. 文章的内容主要还是从源码进行分析,虽然又臭又长,但是如果想要学习Android系统源码 ...

  10. Android10.0系统启动之Launcher(桌面)启动流程-[Android取经之路]

    摘要:上一节我们讲完了Android10.0的ActivityManagerService的启动流程,在AMS的最后启动了Launcher进程,今天我们就来看看Launcher的真正启动流程. 阅读本 ...

最新文章

  1. RabbitMQ 入门系列(1)— Ubuntu 安装 RabbitMQ 及配置
  2. 3软件测试原理与软件缺陷
  3. 真惨!连各大编程语言都摆起地摊了!
  4. Codeforces Round #352 (Div. 2) A. Summer Camp 水题
  5. [Winform]DataGridView列自适应宽度
  6. 喜报!神策数据入选“中国科创企业百强榜”前 3 名
  7. 总结PHP中DateTime的常用方法
  8. python的类和对象例题_Python类、类对象和实例对象、组合、绑定详细讲解
  9. 解决:VB.NET程序中 datagridview 多次绑定出现 的“不能在数据绑定的 DataGridView 控件上设置 ColumnCount...
  10. ubuntu 14.04 gitlab 的搭建
  11. excel单元格设置了数值类型,但是代码里读的时候还是文本类型
  12. PPT如何导出高质量图片
  13. 吴恩达Course1《神经网络与深度学习》week2:神经网络基础 测验及作业
  14. 细侃那些悬而未决的数学趣味谜题
  15. Värde任命新合伙人和高级董事总经理
  16. 云服务器修改dns 114,为什么改成114dns就行?
  17. ES5 ES6相关内容 day15
  18. 【无标题】解决“该项目不在请确认该项目位置,然后重试” 文件无法删除问题
  19. java 创建word文件_Java 创建Word
  20. (4.2.49)微信APM:Matrix源码浅析

热门文章

  1. 亚马逊爆锤之下,中国跨境电商出路何在?
  2. 【基于pyAudioKits的Python音频信号处理(七)】端点检测和语音识别
  3. 美国软件是如何最终装备在中国攻击直升机上的(一)
  4. 索引图像读取与分析理解
  5. 华为OD机试 - 射击比赛(Python)
  6. 5g上行速率怎么提升_5G手机到底牛逼在哪里?(SRS轮发)
  7. baostock证券数据集下使用LSTM模型预测A股走势
  8. Storm-Like Projects
  9. 题解-百度2017春招笔试真题编程题集合
  10. 一文带你搞定JDBC