一、项目教程链接:http://study.163.com/course/courseMain.htm?courseId=897003
与教程源码略有不同。
二、涉及功能点:
1.控制闪光灯实现手电筒的功能;
2.实现屏幕上灯光图的交替闪烁;
3.使用闪光灯的亮与灭发送莫尔斯电码;
4.使用高亮度纯色背景充当另一种手电功能。
三、涉及的技术:
1.控制闪光灯的亮与灭;
2.获取和设置屏幕亮度;
3.UI线程和非UI线程通讯;
4.自定义控件——颜色选择对话框
四、项目结构:
目前采用继承链的结构,即BaseActivity→ColorLight→WarningLight→
MorseCodeLight→ColorLight→Setting→MainActivity。其中BaseActivity获取通用控件并处理公共方法,Setting设置闪烁频率和管理快捷方式,MainActivity进行功能的选择以及相应Layout的加载隐藏,其余均为相关功能点。
五、功能点部分代码:
1.BaseActivity:
protected enum UIType {
//使用枚举类型记录每个Layout,方便在加载和隐藏时使用
UI_TYPE_MAIN,
UI_TYPE_FLASHLIGHT,
UI_TYPE_WARNINGLIGHT,
UI_TYPE_MORSECODELIGHT,
UI_TYPE_COLORLIGHT,
UI_TYPE_SETTING
}
//设置起始Layout为FlashLight的Layout
protected UIType currentUIType = UIType.UI_TYPE_FLASHLIGHT;
protected UIType lastUIType = UIType.UI_TYPE_FLASHLIGHT;
//返回键点击次数,用于“再按一次退出”功能
protected int finishCount;
protected void hideAllUI() {
//隐藏所有Layout,再根据功能点加载相应的Layout
main_ui.setVisibility(View.GONE);
flashnight_ui.setVisibility(View.GONE);
warninglight_ui.setVisibility(View.GONE);
morsecodelight_ui.setVisibility(View.GONE);
colorlight_ui.setVisibility(View.GONE);
setting_ui.setVisibility(View.GONE);
}
protected void setScreenBrightness(float value) {
//设置屏幕亮度,value取值0-1,0表最暗,1表最亮
WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
layoutParams.screenBrightness = value;
getWindow().setAttributes(layoutParams);
}
protected float getDefaultScreenBrightness() {
//获取当前屏幕亮度
int value = 0;
try {
//获取系统参数:亮度,范围0-255,int型
value = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
}
return value/255f;
}
@Override
public void finish() {
//结合重写的dispatchTouchEvent方法完成“再按一次退出”功能
finishCount++;
if (finishCount == 1) {
Toast.makeText(this, "再按一次退出",Toast.LENGTH_SHORT).show();
} else if (finishCount == 2) {
super.finish();
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//当点击屏幕时(未连续两次点击返回),返回点击次数清零
finishCount = 0;
return super.dispatchTouchEvent(ev);
}
public class FlashLight extends BaseActivity {
private ImageView imageView;
private Camera camera;
private Camera.Parameters parameters;
//通过安卓drawable资源文件的使用来减少Java代码
private TransitionDrawable transitionDrawable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
imageView = (ImageView) findViewById(R.id.image_flashnight);
transitionDrawable = (TransitionDrawable) imageView.getDrawable();
imageView.setTag(false);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/*if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {
Toast.makeText(FlashLight.this, "设备没有闪光灯", Toast.LENGTH_SHORT).show();
} else {
//因模拟器没有闪光灯,故此处逻辑注释掉
}*/
if ((Boolean) imageView.getTag()) {
closeLight();
} else {
openLight();
}
}
});
}
//打开闪光灯
protected void openLight() {
//正向动画转换,转换时长200毫秒
transitionDrawable.startTransition(200);
imageView.setTag(true);
camera = Camera.open();
try {
camera.setPreviewTexture(new SurfaceTexture(0));
camera.startPreview();
parameters = camera.getParameters();
parameters.setFlashMode(parameters.FLASH_MODE_TORCH);
camera.setParameters(parameters);
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭闪光灯
protected void closeLight() {
if ((Boolean) imageView.getTag()) {
//反向动画转化
transitionDrawable.reverseTransition(200);
imageView.setTag(false);
if (camera != null) {
parameters = camera.getParameters();
parameters.setFlashMode(parameters.FLASH_MODE_OFF);
camera.setParameters(parameters);
camera.stopPreview();
camera.release();
camera = null;
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
closeLight();
}
}
其中,ImageView使用的flashlight.xml,可减少Java代码的使用,具体如下:
public class WarningLight extends FlashLight {
private ImageView warningImage_1;
private ImageView warningImage_2;
// 此处之所以设置为protected,因为在MainActivity中会用到此变量
protected boolean flicker = true; //true:闪烁,false:停止闪烁。
private boolean state = true; //true:on→off, false:off→on
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
warningImage_1 = (ImageView) findViewById(R.id.warningImage_1);
warningImage_2 = (ImageView) findViewById(R.id.warningImage_2);
}
class WarningLightThread extends Thread {
@Override
public void run() {
super.run();
while (flicker) {
try {
Thread.sleep(warninglight_interval);
warningHanlder.sendEmptyMessage(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private Handler warningHanlder = new Handler(){
@Override
public void handleMessage(Message msg) {
if (state) {
warningImage_1.setImageResource(R.drawable.flashlight_off);
warningImage_2.setImageResource(R.drawable.flashlight_on);
state = false;
} else {
warningImage_1.setImageResource(R.drawable.flashlight_on);
warningImage_2.setImageResource(R.drawable.flashlight_off);
state = true;
}
}
};
}
public class MorseCodeLight extends WarningLight {
//闪光灯在“点”保持亮度时长,单位毫秒
private static final int DOT_TIME = 200;
//“划”时长
private static final int LINE_TIME = DOT_TIME * 3;
//点滑间隔时长
private static final int DOT_LINE_TIME = DOT_TIME;
//字符间隔
private static final int CHAR_CHAR_TIME = LINE_TIME;
//单词间隔
private static final int WORD_WORD_TIME = DOT_TIME * 7;
private EditText morsecodelight_edittext;
private String morseCode;
//使用HashMap存储莫尔斯电码表
private Map morseCodeMap = new HashMap<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
morsecodelight_edittext = (EditText) findViewById(R.id.morsecodelight_edittext);
morseCodeMap.put('a', ".-");
morseCodeMap.put('b', "-...");
morseCodeMap.put('c', "-.-.");
morseCodeMap.put('d', "-..");
morseCodeMap.put('e', ".");
morseCodeMap.put('f', "..-.");
morseCodeMap.put('g', "--.");
morseCodeMap.put('h', "....");
morseCodeMap.put('i', "..");
morseCodeMap.put('j', ".---");
morseCodeMap.put('k', "-.-");
morseCodeMap.put('l', ".-..");
morseCodeMap.put('m', "--");
morseCodeMap.put('n', "-.");
morseCodeMap.put('o', "---");
morseCodeMap.put('p', ".--.");
morseCodeMap.put('q', "--.-");
morseCodeMap.put('r', ".-.");
morseCodeMap.put('s', "...");
morseCodeMap.put('t', "-");
morseCodeMap.put('u', "..-");
morseCodeMap.put('v', "...-");
morseCodeMap.put('w', ".--");
morseCodeMap.put('x', "-..-");
morseCodeMap.put('y', "-.--");
morseCodeMap.put('z', "--..");
morseCodeMap.put('0', "-----");
morseCodeMap.put('1', ".----");
morseCodeMap.put('2', "..---");
morseCodeMap.put('3', "...--");
morseCodeMap.put('4', "....-");
morseCodeMap.put('5', ".....");
morseCodeMap.put('6', "-....");
morseCodeMap.put('7', "--...");
morseCodeMap.put('8', "---..");
morseCodeMap.put('9', "----.");
morseCodeMap.put('.', ".-.-.-");
morseCodeMap.put(',', "--..--");
morseCodeMap.put('?', "..--..");
findViewById(R.id.morsecodelight_checkbutton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
morseCode = morsecodelight_edittext.getText().toString().toLowerCase();
if (verifyMorseCode()) {
sendSentence(morseCode);
}
}
});
final FadedTextView fadedTextView = (FadedTextView) findViewById(R.id.fadedtextview);
fadedTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
fadedTextView.hideText();
}
});
}
private void sleep(long t) {
try {
Thread.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//发送点
private void sendDot() {
openLight();
sleep(DOT_TIME);
closeLight();
}
//发送线
private void sendLine() {
openLight();
sleep(LINE_TIME);
closeLight();
}
//发送字符
private void sendChar(char c) {
String morseCode = morseCodeMap.get(c);
if (!morseCode.isEmpty()) {
char single_char;
for (int i = 0; i < morseCode.length(); i++) {
single_char = morseCode.charAt(i);
switch (single_char) {
case '.':
sendDot();
break;
case '-':
sendLine();
break;
}
if (i < morseCode.length() - 1) {
sleep(DOT_LINE_TIME);
}
}
}
}
//发送单词
private void sendWord(String s) {
for (int i = 0; i < s.length(); i++) {
sendChar(s.charAt(i));
if (i < s.length() - 1) {
sleep(CHAR_CHAR_TIME);
}
}
}
//发送句子
private void sendSentence(String s) {
//使用正则表达式划分句子为单词数组
String[] words = s.split(" +");
for (int i = 0; i < words.length; i++) {
sendWord(words[i]);
if (i < words.length - 1) {
sleep(WORD_WORD_TIME);
}
}
Toast.makeText(this, "莫尔斯电码发送完成", Toast.LENGTH_SHORT).show();
}
//判断输入合法性
private boolean verifyMorseCode() {
if ("".equals(morseCode)) {
Toast.makeText(this, "请输入", Toast.LENGTH_SHORT).show();
return false;
} else {
for (int i = 0; i < morseCode.length(); i++) {
char c = morseCode.charAt(i);
if (!(c >= 'a' && c <= 'z') &&
!(c >= '0' && c <= '9') &&
c != ' ' &&
c != ',' &&
c != '.' &&
c != '?') {
Toast.makeText(this, "只能包含数字字母和,.?", Toast.LENGTH_SHORT).show();
return false;
}
}
return true;
}
}
}