<Android 应用 之路> 简易手电筒

来源:互联网 发布:photoshop2017 mac 编辑:程序博客网 时间:2024/05/31 19:05

前言

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

这个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();                }            }        }    }}

实际效果图

手电筒关闭状态

这里写图片描述

手电筒开启状态

这里写图片描述

手电筒开启状态提示信息

这里写图片描述


备注

由于开发时间比较短,测试可能不充分,有问题欢迎留言讨论~

2 0
原创粉丝点击