【Android】超級手電筒專案總結(上)
阿新 • • 發佈:2019-01-23
一、專案教程連結: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); }
2.FlashLight:
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程式碼的使用,具體如下:
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/flashlight_off"/>
<item android:drawable="@drawable/flashlight_on"/>
</transition>
3.WarningLight: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;
}
}
};
}
3.MorseCodeLight:
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<Character, String> 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;
}
}
}