在自定义视图中重复执行具有时间延迟的任务
Repeat a task with a time delay inside a custom view
问题 Repeat a task with a time delay? talks about a repeated task within an activity. The top voted answer looks good for that situation. I am trying to make a blinking cursor inside a completely custom EditText. I tried copying and adapting code from the Android TextView
and Editor
代码,但我没有得到任何眨眼。
这是我一直在尝试使用的一些当前代码:
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 makeBlink() {
if (shouldBlink()) {
mShowCursor = SystemClock.uptimeMillis();
if (mBlink == null) mBlink = new Blink();
this.removeCallbacks(mBlink);
this.postDelayed(mBlink, BLINK);
} else {
if (mBlink != null) this.removeCallbacks(mBlink);
}
}
private class Blink implements Runnable {
private boolean mCancelled;
public void run() {
if (mCancelled) {
return;
}
MongolEditText.this.removeCallbacks(this);
if (shouldBlink()) {
if (mLayout != null) {
MongolEditText.this.invalidateCursorPath();
}
MongolEditText.this.postDelayed(this, BLINK);
}
}
void cancel() {
if (!mCancelled) {
MongolEditText.this.removeCallbacks(this);
mCancelled = true;
}
}
void uncancel() {
mCancelled = false;
}
}
private void invalidateCursorPath() {
int start = getSelectionStart();
if (start < 0) return;
Rect cursorPath = getCursorPath(start);
invalidate(cursorPath.left, cursorPath.top, cursorPath.right, cursorPath.bottom);
}
private void suspendBlink() {
if (mBlink != null) {
mBlink.cancel();
}
}
private void resumeBlink() {
if (mBlink != null) {
mBlink.uncancel();
makeBlink();
}
}
@Override
public void onScreenStateChanged(int screenState) {
switch (screenState) {
case View.SCREEN_STATE_ON:
resumeBlink();
break;
case View.SCREEN_STATE_OFF:
suspendBlink();
break;
}
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
resumeBlink();
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
suspendBlink();
}
我决定我需要退后一步,用一个更简单的例子来解决问题,所以我正在创建一个 MCVE。我的答案(假设我能做到)如下。我的目标如下:
- 单击时视图开始重复任务。
- 处理程序的可运行代码应在销毁视图时取消。
我的基本问题是如何让视图开始自己的重复任务来改变它的外观? (如闪烁)
以下示例显示如何在自定义视图上设置重复任务。该任务通过使用每秒运行一些代码的处理程序来工作。触摸视图启动和停止任务。
public class MyCustomView extends View {
private static final int DELAY = 1000; // 1 second
private Handler mHandler;
// keep track of the current color and whether the task is running
private boolean isBlue = true;
private boolean isRunning = false;
// constructors
public MyCustomView(Context context) {
this(context, null, 0);
}
public MyCustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mHandler = new Handler();
}
// start or stop the blinking when the view is touched
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
if (isRunning) {
stopRepeatingTask();
} else {
startRepeatingTask();
}
isRunning = !isRunning;
}
return true;
}
// alternate the view's background color
Runnable mRunnableCode = new Runnable() {
@Override
public void run() {
if (isBlue) {
MyCustomView.this.setBackgroundColor(Color.RED);
}else {
MyCustomView.this.setBackgroundColor(Color.BLUE);
}
isBlue = !isBlue;
// repost the code to run again after a delay
mHandler.postDelayed(mRunnableCode, DELAY);
}
};
// start the task
void startRepeatingTask() {
mRunnableCode.run();
}
// stop running the task, cancel any current code that is waiting to run
void stopRepeatingTask() {
mHandler.removeCallbacks(mRunnableCode);
}
// make sure that the handler cancels any tasks left when the view is destroyed
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
stopRepeatingTask();
}
}
这是点击后的视图。
感谢 this answer 的想法。
问题 Repeat a task with a time delay? talks about a repeated task within an activity. The top voted answer looks good for that situation. I am trying to make a blinking cursor inside a completely custom EditText. I tried copying and adapting code from the Android TextView
and Editor
代码,但我没有得到任何眨眼。
这是我一直在尝试使用的一些当前代码:
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 makeBlink() {
if (shouldBlink()) {
mShowCursor = SystemClock.uptimeMillis();
if (mBlink == null) mBlink = new Blink();
this.removeCallbacks(mBlink);
this.postDelayed(mBlink, BLINK);
} else {
if (mBlink != null) this.removeCallbacks(mBlink);
}
}
private class Blink implements Runnable {
private boolean mCancelled;
public void run() {
if (mCancelled) {
return;
}
MongolEditText.this.removeCallbacks(this);
if (shouldBlink()) {
if (mLayout != null) {
MongolEditText.this.invalidateCursorPath();
}
MongolEditText.this.postDelayed(this, BLINK);
}
}
void cancel() {
if (!mCancelled) {
MongolEditText.this.removeCallbacks(this);
mCancelled = true;
}
}
void uncancel() {
mCancelled = false;
}
}
private void invalidateCursorPath() {
int start = getSelectionStart();
if (start < 0) return;
Rect cursorPath = getCursorPath(start);
invalidate(cursorPath.left, cursorPath.top, cursorPath.right, cursorPath.bottom);
}
private void suspendBlink() {
if (mBlink != null) {
mBlink.cancel();
}
}
private void resumeBlink() {
if (mBlink != null) {
mBlink.uncancel();
makeBlink();
}
}
@Override
public void onScreenStateChanged(int screenState) {
switch (screenState) {
case View.SCREEN_STATE_ON:
resumeBlink();
break;
case View.SCREEN_STATE_OFF:
suspendBlink();
break;
}
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
resumeBlink();
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
suspendBlink();
}
我决定我需要退后一步,用一个更简单的例子来解决问题,所以我正在创建一个 MCVE。我的答案(假设我能做到)如下。我的目标如下:
- 单击时视图开始重复任务。
- 处理程序的可运行代码应在销毁视图时取消。
我的基本问题是如何让视图开始自己的重复任务来改变它的外观? (如闪烁)
以下示例显示如何在自定义视图上设置重复任务。该任务通过使用每秒运行一些代码的处理程序来工作。触摸视图启动和停止任务。
public class MyCustomView extends View {
private static final int DELAY = 1000; // 1 second
private Handler mHandler;
// keep track of the current color and whether the task is running
private boolean isBlue = true;
private boolean isRunning = false;
// constructors
public MyCustomView(Context context) {
this(context, null, 0);
}
public MyCustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mHandler = new Handler();
}
// start or stop the blinking when the view is touched
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
if (isRunning) {
stopRepeatingTask();
} else {
startRepeatingTask();
}
isRunning = !isRunning;
}
return true;
}
// alternate the view's background color
Runnable mRunnableCode = new Runnable() {
@Override
public void run() {
if (isBlue) {
MyCustomView.this.setBackgroundColor(Color.RED);
}else {
MyCustomView.this.setBackgroundColor(Color.BLUE);
}
isBlue = !isBlue;
// repost the code to run again after a delay
mHandler.postDelayed(mRunnableCode, DELAY);
}
};
// start the task
void startRepeatingTask() {
mRunnableCode.run();
}
// stop running the task, cancel any current code that is waiting to run
void stopRepeatingTask() {
mHandler.removeCallbacks(mRunnableCode);
}
// make sure that the handler cancels any tasks left when the view is destroyed
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
stopRepeatingTask();
}
}
这是点击后的视图。
感谢 this answer 的想法。