当活动暂停时,在自定义视图中取消runnable / handler

时间:2017-07-26 09:03:36

标签: android runnable android-handler

我有一个闪烁光标的自定义视图。我使用Handler制作闪烁光标,并在500毫秒延迟后向其发布Runnable

当视图所在的活动时,我想通过删除处理程序上的回调来停止闪烁。但是,我注意到当我切换到另一个应用程序时,处理程序/ runnable继续运行,即日志显示它仍在闪烁。

如果我控制了视图,我会做this

之类的操作
@Override
protected void onPause() {
     handler.removeCallbacks(runnable);
     super.onPause();
}

但我的自定义视图将成为图书馆的一部分,因此我无法控制其他开发者使用我的自定义视图的活动。

我尝试了onFocusChangedonScreenStateChangedonDetachedFromWindow,但当用户切换到其他应用时,这些都不起作用。

这是我的代码。我通过删除与问题无关的任何内容来简化它。

public class MyCustomView extends View {

    static final int BLINK = 500;
    private Handler mBlinkHandler;

    private void init() {
        // ...
        mBlinkHandler = new Handler();

        mTextStorage.setOnChangeListener(new MongolTextStorage.OnChangeListener() {
            @Override
            public void onTextChanged(/*...*/) {
                // ...
                startBlinking();
            }
        });
    }

    Runnable mBlink = new Runnable() {
        @Override
        public void run() {
            mBlinkHandler.removeCallbacks(mBlink);
            if (shouldBlink()) {
                // ...
                Log.i("TAG", "Still blinking...");
                mBlinkHandler.postDelayed(mBlink, BLINK);
            }
        }
    };

    private boolean shouldBlink() {
        if (!mCursorVisible || !isFocused()) return false;
        final int start = getSelectionStart();
        if (start < 0) return false;
        final int end = getSelectionEnd();
        if (end < 0) return false;
        return start == end;
    }

    void startBlinking() {
        mBlink.run();
    }

    void stopBlinking() {
        mBlinkHandler.removeCallbacks(mBlink);
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        if (focused) {
            startBlinking();
        } else {
            stopBlinking();
        }
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
    }

    @Override
    public void onScreenStateChanged(int screenState) {
        switch (screenState) {
            case View.SCREEN_STATE_ON:
                startBlinking();
                break;
            case View.SCREEN_STATE_OFF:
                stopBlinking();
                break;
        }
    }

    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        startBlinking();
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stopBlinking();
    }
}

2 个答案:

答案 0 :(得分:0)

我猜你是使用thread.run()分别启动线程,而只是创建一个方法并递归调用它像这样:

public void blink(){
     mBlinkHandler.postDelayed(mBlink, BLINK);
} 

在runnable中:

Runnable mBlink = new Runnable() {
    @Override
    public void run() {
        mBlinkHandler.removeCallbacks(mBlink);
        if (shouldBlink()) {
            // ...
            Log.i("TAG", "Still blinking...");
           blink();
        }
    }
};

当您使用run方法直接启动线程时。所以它不会因删除回调而停止。

希望这有帮助。

答案 1 :(得分:0)

我在评论中遵循@ pskink的建议解决了问题,并调整了Android 1.6的代码。这可能是Android的旧版本,但闪烁的光标部分适用于我的目的。覆盖onWindowFocusChanged是关键。

我的full code在GitHub上。以下是相关部分:

public class MyCustomView extends View {

    private boolean mCursorVisible = true;
    private Blink mBlink;
    private long mShowCursor; // cursor blink timing based on system clock
    static final int BLINK = 500;

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        mShowCursor = SystemClock.uptimeMillis();
        if (focused) {
            makeBlink();
        }
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        int start = getSelectionStart();
        int end = getSelectionEnd();

        // draw the blinking cursor on top
        if (start == end && blinkShouldBeOn()) {
            canvas.drawRect(getCursorPath(start), mCursorPaint);
        }
    }

    private boolean blinkShouldBeOn() {
        if (!mCursorVisible || !isFocused()) return false;
        return (SystemClock.uptimeMillis() - mShowCursor) % (2 * BLINK) < BLINK;
    }
    private void makeBlink() {
        if (!mCursorVisible) {
            if (mBlink != null) {
                mBlink.removeCallbacks(mBlink);
            }

            return;
        }

        if (mBlink == null)
            mBlink = new Blink(this);

        mBlink.removeCallbacks(mBlink);
        mBlink.postAtTime(mBlink, mShowCursor + BLINK);
    }

    public void setCursorVisible(boolean visible) {
        mCursorVisible = visible;
        invalidateCursorPath();

        if (visible) {
            makeBlink();
        } else if (mBlink != null) {
            mBlink.removeCallbacks(mBlink);
        }
    }

    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);

        if (hasWindowFocus) {
            if (mBlink != null) {
                mBlink.uncancel();

                if (isFocused()) {
                    mShowCursor = SystemClock.uptimeMillis();
                    makeBlink();
                }
            }
        } else {
            if (mBlink != null) {
                mBlink.cancel();
            }
            hideSystemKeyboard();
        }
    }

    private static class Blink extends Handler implements Runnable {
        private WeakReference<MongolEditText> mView;
        private boolean mCancelled;

        Blink(MongolEditText v) {
            mView = new WeakReference<>(v);
        }

        public void run() {
            if (mCancelled) {
                return;
            }

            removeCallbacks(Blink.this);

            MongolEditText met = mView.get();

            if (met != null && met.isFocused()) {
                int st = met.getSelectionStart();
                int en = met.getSelectionEnd();

                if (st == en && st >= 0 && en >= 0) {
                    if (met.mLayout != null) {
                        met.invalidateCursorPath();
                    }

                    postAtTime(this, SystemClock.uptimeMillis() + BLINK);
                }
            }
        }

        void cancel() {
            if (!mCancelled) {
                removeCallbacks(Blink.this);
                mCancelled = true;
            }
        }

        void uncancel() {
            mCancelled = false;
        }
    }
}