1. 程式人生 > >【Android】超級手電筒專案總結(上)

【Android】超級手電筒專案總結(上)

一、專案教程連結: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;
        }
    }
}