實現Android監控任意控制元件或按鍵雙擊事件方法

明月春秋發表於2017-11-21

為了實現對Android任意控制元件和按鍵進行雙擊事件的監聽,所以寫了一個可以在程式碼中任意移植的解決方法。

一、解決思路

為了實現雙擊事件的監聽,首先需要一個執行緒來執行兩次點選事件的判斷,決定是否屬於一次雙擊事件,為了可以實現多次監聽,所以線上程中是一個死迴圈,避免開無數個執行緒;然後將判斷結果通過handler傳遞出去,由handler對message中攜帶的boolean資料進行判斷,決定回撥雙擊事件監聽器中的雙擊或單擊方法,監聽器中的事件由使用者覆寫;最後在自己想要新增雙擊事件監聽的控制元件或按鍵的點選事件監聽器中建立一個雙加事件管理器並啟動就可以了。

二、準備工作

1.建立雙擊事件監聽器介面

/**
 * Created by 明月春秋 on 2017/11/18.
 * 負責對是否雙擊事件結果的回撥監聽器
 * 方法:
 *      1.onSinglePress:用於處理單擊事件
 *      2.onDoublePress:用於處理雙擊事件
 */

public interface OnDoubleClickListener {

    /**
     * 用於處理單擊事件
     */
    public void onSinglePress();

    /**
     * 用於處理雙擊事件
     */
    public void onDoublePress();
}複製程式碼

2.建立雙擊事件處理handler,用來決定是執行監聽器中的雙擊事件還是單擊事件

/**
 * Created by 明月春秋 on 2017/11/18.
 * 負責處理雙擊事件的handler
 * 方法:
 *      1.setDoubleRunnable:設定handler處理所在的執行緒執行體
 */

public class DoubleHandler extends Handler {

    private OnDoubleClickListener mListener;//對是否雙擊結果的回撥監聽器
    private DoubleRunnable mDoubleRunnable;//負責雙擊事件的執行緒執行體

    /**
     * 雙擊事件處理器的建構函式
     * @param listener
     *          對是否雙擊結果的回撥監聽器
     */
    public DoubleHandler(OnDoubleClickListener listener){
        super(Looper.getMainLooper());
        this.mListener = listener;
    }

    @Override
    public void handleMessage(Message msg) {
        if ((boolean)msg.obj){
            mListener.onDoublePress();
        }
        else {
            mListener.onSinglePress();
        }
        //通知雙擊事件執行緒執行體,事件已處理完
        mDoubleRunnable.setIsStarted(false);
    }

    /**
     * 設定handler處理所在的執行緒執行體
     * @param doubleRunnable
     *          傳入的當前執行緒執行體
     */
    public void setDoubleRunnable(DoubleRunnable doubleRunnable){
        this.mDoubleRunnable = doubleRunnable;
    }
}複製程式碼

3.建立執行雙擊事件判斷的執行緒執行體,在其中進行死迴圈,一直等待使用者的點選,並對相關執行狀態進行判斷,將雙擊事件的判斷結果傳送給前面的DoubleHandler 進行處理

/**
 * Created by 明月春秋 on 2017/11/18.
 * 負責雙擊事件的執行緒執行體
 * 方法:
 *      1.setPreparedState:設定當前雙擊事件的執行狀態
 *      2.setIsStarted:設定執行緒是否處於一次雙擊事件判斷的阻塞之中
 *      3.setDoubleSpan:設定用於判斷雙擊的間隔時間
 */

public class DoubleRunnable implements Runnable {

    private DoubleHandler mHandler;//處理點選事件的handler
    private boolean isStarted = false;//表示執行緒是否正處於阻塞中
    private boolean isPrepared = false;//表示是否已準備好啟動執行緒
    private boolean isDoubleClicked = false;//表示是否被雙擊
    private int doubleSpan;//用於判斷雙擊的間隔時間

    /**
     * 雙擊事件執行緒執行體的建構函式
     * @param handler
     *          用於處理是否雙擊事件的handler
     */
    public DoubleRunnable(DoubleHandler handler) {
        handler.setDoubleRunnable(this);
        this.mHandler = handler;
    }

    @Override
    public void run() {

        while (true){
            //如果沒有被點選,則繼續下次迴圈
            if (!isPrepared) {
                continue;
            }
            isPrepared = false;
            //如果執行緒處於一次雙擊事件的阻塞中,則繼續下次迴圈,直至事件結束
            if (isStarted){
                continue;
            }
            isStarted = true;
            try {
                Thread.sleep(doubleSpan);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Message msg = mHandler.obtainMessage();
            msg.obj = isDoubleClicked;
            mHandler.sendMessage(msg);
        }

    }

    /**
     * 設定當前雙擊事件的執行狀態
     * @param isPrepared
     *          是否已被點選(可以執行執行緒中程式碼)
     * @param isDoubleClicked
     *          是否已被雙擊
     */
    public void setPreparedState(boolean isPrepared, boolean isDoubleClicked) {
        this.isPrepared = isPrepared;
        this.isDoubleClicked = isDoubleClicked;
    }

    /**
     * 設定執行緒是否處於一次雙擊事件判斷的阻塞之中
     * @param isStarted
     *          傳入是否阻塞開啟
     */
    public void setIsStarted(boolean isStarted){
        this.isStarted = isStarted;
    }

    /**
     * 設定用於判斷雙擊的間隔時間
     * @param doubleSpan
     *          傳入的時間間隔
     */
    public void setDoubleSpan(int doubleSpan){
        this.doubleSpan = doubleSpan;
    }
}複製程式碼

4.最後建立一個雙擊事件管理器,進行執行緒的建立管理,並用於啟動執行緒和更新使用者的點選時間,之所以在管理器中更新時間並進行判斷,是因為可以隨時根據使用者的點選去更新DoubleRunnable中的雙擊判斷結果,如果在DoubleRunnable中進行時間判斷,則會因為判斷只會在下一次阻塞迴圈結束之後才能進行,導致一直是單擊事件執行。

/**
 * Created by 明月春秋 on 2017/11/18.
 * 雙擊事件的管理器
 * 方法:
 *      1.start:開啟雙擊事件的執行緒執行
 */

public class DoubleManager {

    private DoubleRunnable mDoubleRunnable = null;//負責雙擊事件的執行緒執行體
    private Thread mThread = null;//負責雙擊事件的執行緒
    private long mClickTime = 0;//記錄點選時間

    private static final int DOUBLE_SPAN = 300;//用於判斷雙擊的間隔時間

    /**
     * 雙擊事件管理器的建構函式
     * @param doubleRunnable
     *          傳入負責雙擊事件的執行緒執行體
     */
    public DoubleManager(DoubleRunnable doubleRunnable){
        if (doubleRunnable == null){
            return;
        }
        if (mDoubleRunnable == null){
            doubleRunnable.setDoubleSpan(DOUBLE_SPAN);
            this.mDoubleRunnable = doubleRunnable;
        }
    }

    /**
     * 開啟雙擊事件的執行緒執行
     */
    public void start(){
        if (mDoubleRunnable == null){
            return;
        }
        boolean isDoubleClicked = false;
        if ((System.currentTimeMillis() - mClickTime) < DOUBLE_SPAN) {
            isDoubleClicked = true;
        }
        else {
            isDoubleClicked = false;
            mClickTime = System.currentTimeMillis();
        }
        mDoubleRunnable.setPreparedState(true, isDoubleClicked);
        if (mThread == null){
            mThread = new Thread(mDoubleRunnable);
            mThread.start();
        }
    }
}複製程式碼

5.接下來就可以在任意控制元件的單擊事件監聽器中或按鍵事件中,進行雙擊事件的判斷了。

public class DoubleClick extends AppCompatActivity {

    private DoubleManager doubleManager = null;
    private DoubleManager keyDoubleManager = null;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.button_double_click);

    }

    public void doubleClick(View view){
        if (doubleManager == null){
            doubleManager = new DoubleManager(new DoubleRunnable(new DoubleHandler(new OnDoubleClickListener() {
                @Override
                public void onSinglePress() {
                    Toast.makeText(DoubleClick.this, "單擊", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onDoublePress() {
                    Toast.makeText(DoubleClick.this, "雙擊", Toast.LENGTH_SHORT).show();
                }
            })));
        }
        doubleManager.start();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_ENTER){
            if (keyDoubleManager == null){
                keyDoubleManager = new DoubleManager(new DoubleRunnable(new DoubleHandler(new OnDoubleClickListener() {
                    @Override
                    public void onSinglePress() {
                        Toast.makeText(DoubleClick.this, "按鍵單擊", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onDoublePress() {
                        Toast.makeText(DoubleClick.this, "按鍵雙擊", Toast.LENGTH_SHORT).show();
                    }
                })));
            }
            keyDoubleManager.start();
        }
        return super.onKeyDown(keyCode, event);
    }
}複製程式碼

執行例子中按鍵事件時,記得將Button在佈局中設為android:visibility="gone",防止按鍵事件直接點選到按鈕上,執行按鈕的雙擊事件監聽了。

三、執行效果圖

1.執行按鈕單擊

這裡寫圖片描述
這裡寫圖片描述

2.執行按鈕雙擊

這裡寫圖片描述
這裡寫圖片描述

3.執行按鍵單擊

這裡寫圖片描述
這裡寫圖片描述

4.執行按鍵雙擊

這裡寫圖片描述
這裡寫圖片描述

四、總結
到此,Android任意控制元件或按鍵的雙擊事件監聽就已經全部實現了。只要前面將準備工作做好,就可以隨意在任何程式碼處新增雙擊事件管理器並執行了,不會對原有程式碼產生任何影響,如果對本文有什麼意見和問題歡迎指正,如果覺得本文可以,請幫忙點個贊,謝謝觀看。

相關文章