Reputation: 511896
The question 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
code, but I wasn't getting anything to blink.
Here is some of the current code I have been trying to get to work:
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();
}
I decided I needed to step back and solve the problem with an easier example, so I am creating an MCVE. My answer (assuming I can do it) is below. My goals are as follows:
My basic question is how to make the view start its own repeating task which changes its appearance? (like blinking on and off)
Upvotes: 1
Views: 1041
Reputation: 511896
The following example shows how to set a repeating task on a custom view. The task works by using a handler that runs some code every second. Touching the view starts and stops the task.
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();
}
}
Here is what the view looks like after being clicked.
Thanks to this answer for ideas.
Upvotes: 2