如何在 android 中的特定位置初始化圆形动画计时器
How to initialize circular animated timer at specific position in android
计时器总是从顶部开始,我希望它从某个已绘制部分的初始化位置开始。我参考了 this tutorial.
这是我的 MainActivity:
public class MainActivity extends AppCompatActivity {
private static final int TIMER_LENGTH1= 3600*24;
private static final int TIMER_LENGTH2= 3600;
private static final int TIMER_LENGTH3= 60;
private static int i=0;
private TextView tvDay, tvHour, tvMinute, tvSecond, tvEvent;
private LinearLayout linearLayout1, linearLayout2;
private Handler handler;
private Runnable runnable;
private TimerView mTimerView1,mTimerView2,mTimerView3;
private static long h=0,m=0,s=0,d=0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
initUI();
countDownStart();
mTimerView1 = (TimerView) findViewById(R.id.timer2);
mTimerView2 = (TimerView) findViewById(R.id.timer3);
mTimerView3 = (TimerView) findViewById(R.id.timer4);
}
@SuppressLint("SimpleDateFormat")
private void initUI() {
//linearLayout1 = (LinearLayout) findViewById(R.id.ll1);
linearLayout2 = (LinearLayout) findViewById(R.id.ll2);
tvDay = (TextView) findViewById(R.id.txtTimerDay);
tvHour = (TextView) findViewById(R.id.txtTimerHour);
tvMinute = (TextView) findViewById(R.id.txtTimerMinute);
tvSecond = (TextView) findViewById(R.id.txtTimerSecond);
//tvEvent = (TextView) findViewById(R.id.tvevent);
}
// //////////////COUNT DOWN START/////////////////////////
public void countDownStart() {
handler = new Handler();
runnable = new Runnable() {
@Override
public void run() {
handler.postDelayed(this, 1000);
try {
SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd");
// Here Set your Event Date
Date eventDate = dateFormat.parse("2016-12-29");
Date currentDate = new Date();
if (!currentDate.after(eventDate)) {
long diff = eventDate.getTime()
- currentDate.getTime();
long days = diff / (24 * 60 * 60 * 1000);
diff -= days * (24 * 60 * 60 * 1000);
long hours = diff / (60 * 60 * 1000);
diff -= hours * (60 * 60 * 1000);
long minutes = diff / (60 * 1000);
diff -= minutes * (60 * 1000);
long seconds = diff / 1000;
if(i==0)
{
mTimerView1.start(TIMER_LENGTH1);
mTimerView2.start(TIMER_LENGTH2);
mTimerView3.start(TIMER_LENGTH3);
i++;
d=days;
h=hours;
m=minutes;
s=seconds;
}
if(minutes!=m)
{
mTimerView3.start(TIMER_LENGTH3);
m=minutes;
}
if(hours!=h)
{
mTimerView2.start(TIMER_LENGTH2);
h=hours;
}
if(days!=d)
{
mTimerView1.start(TIMER_LENGTH1);
d=days;
}
tvDay.setText("" + String.format("%02d", days));
tvHour.setText("" + String.format("%02d", hours));
tvMinute.setText("" + String.format("%02d", minutes));
tvSecond.setText("" + String.format("%02d", seconds));
} else {
//linearLayout1.setVisibility(View.VISIBLE);
linearLayout2.setVisibility(View.GONE);
tvEvent.setText("CULFEST IS ON");
handler.removeCallbacks(runnable);
// handler.removeMessages(0);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
handler.postDelayed(runnable, 0);
}
@Override
protected void onPause() {
mTimerView1.stop();
mTimerView2.stop();
mTimerView3.stop();
super.onPause();
}
}
这是我的 TimerView:
public class TimerView extends View {
private static final int ARC_START_ANGLE = 270; // 12 o'clock
private static final float THICKNESS_SCALE = 0.03f;
private Bitmap mBitmap;
private Canvas mCanvas;
private RectF mCircleOuterBounds;
private RectF mCircleInnerBounds;
private Paint mCirclePaint;
private Paint mEraserPaint;
private float mCircleSweepAngle;
private ValueAnimator mTimerAnimator;
public TimerView(Context context) {
this(context, null);
}
public TimerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TimerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
int circleColor = Color.RED;
if (attrs != null) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TimerView);
if (ta != null) {
circleColor = ta.getColor(R.styleable.TimerView_circleColor, circleColor);
ta.recycle();
}
}
mCirclePaint = new Paint();
mCirclePaint.setAntiAlias(true);
mCirclePaint.setColor(circleColor);
mEraserPaint = new Paint();
mEraserPaint.setAntiAlias(true);
mEraserPaint.setColor(Color.TRANSPARENT);
mEraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
@SuppressWarnings("SuspiciousNameCombination")
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec); // Trick to make the view square
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != oldw || h != oldh) {
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mBitmap.eraseColor(Color.TRANSPARENT);
mCanvas = new Canvas(mBitmap);
}
super.onSizeChanged(w, h, oldw, oldh);
updateBounds();
}
@Override
protected void onDraw(Canvas canvas) {
mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
if (mCircleSweepAngle > 0f) {
mCanvas.drawArc(mCircleOuterBounds, ARC_START_ANGLE, mCircleSweepAngle, true, mCirclePaint);
mCanvas.drawOval(mCircleInnerBounds, mEraserPaint);
}
canvas.drawBitmap(mBitmap, 0, 0, null);
}
public void start(int secs) {
stop();
mTimerAnimator = ValueAnimator.ofFloat(0f, 1f);
mTimerAnimator.setDuration(TimeUnit.SECONDS.toMillis(secs));
mTimerAnimator.setInterpolator(new LinearInterpolator());
mTimerAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
drawProgress((float) animation.getAnimatedValue());
}
});
mTimerAnimator.start();
}
public void stop() {
if (mTimerAnimator != null && mTimerAnimator.isRunning()) {
mTimerAnimator.cancel();
mTimerAnimator = null;
drawProgress(0f);
}
}
private void drawProgress(float progress) {
mCircleSweepAngle = 360 * progress;
invalidate();
}
private void updateBounds() {
final float thickness = getWidth() * THICKNESS_SCALE;
mCircleOuterBounds = new RectF(0, 0, getWidth(), getHeight());
mCircleInnerBounds = new RectF(
mCircleOuterBounds.left + thickness,
mCircleOuterBounds.top + thickness,
mCircleOuterBounds.right - thickness,
mCircleOuterBounds.bottom - thickness);
invalidate();
}
}
计时器看起来像这样:
请帮忙。
通过浏览源代码,您似乎可以通过以下方式设置初始值:
1) 创建一个实例变量来存储初始值 setter:
//Value between 0f and 360f
private float mStartValue = 0f;
//...
public void setStartValue(float startValueInDegrees) {
mStartValue = startValueInDegrees;
}
2) 更新 drawProgress()
方法以说明这个新的起始值:
private void drawProgress(float progress) {
mCircleSweepAngle = ((360 - mStartValue) * progress) + mStartValue;
invalidate();
}
计时器总是从顶部开始,我希望它从某个已绘制部分的初始化位置开始。我参考了 this tutorial.
这是我的 MainActivity:
public class MainActivity extends AppCompatActivity {
private static final int TIMER_LENGTH1= 3600*24;
private static final int TIMER_LENGTH2= 3600;
private static final int TIMER_LENGTH3= 60;
private static int i=0;
private TextView tvDay, tvHour, tvMinute, tvSecond, tvEvent;
private LinearLayout linearLayout1, linearLayout2;
private Handler handler;
private Runnable runnable;
private TimerView mTimerView1,mTimerView2,mTimerView3;
private static long h=0,m=0,s=0,d=0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
initUI();
countDownStart();
mTimerView1 = (TimerView) findViewById(R.id.timer2);
mTimerView2 = (TimerView) findViewById(R.id.timer3);
mTimerView3 = (TimerView) findViewById(R.id.timer4);
}
@SuppressLint("SimpleDateFormat")
private void initUI() {
//linearLayout1 = (LinearLayout) findViewById(R.id.ll1);
linearLayout2 = (LinearLayout) findViewById(R.id.ll2);
tvDay = (TextView) findViewById(R.id.txtTimerDay);
tvHour = (TextView) findViewById(R.id.txtTimerHour);
tvMinute = (TextView) findViewById(R.id.txtTimerMinute);
tvSecond = (TextView) findViewById(R.id.txtTimerSecond);
//tvEvent = (TextView) findViewById(R.id.tvevent);
}
// //////////////COUNT DOWN START/////////////////////////
public void countDownStart() {
handler = new Handler();
runnable = new Runnable() {
@Override
public void run() {
handler.postDelayed(this, 1000);
try {
SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd");
// Here Set your Event Date
Date eventDate = dateFormat.parse("2016-12-29");
Date currentDate = new Date();
if (!currentDate.after(eventDate)) {
long diff = eventDate.getTime()
- currentDate.getTime();
long days = diff / (24 * 60 * 60 * 1000);
diff -= days * (24 * 60 * 60 * 1000);
long hours = diff / (60 * 60 * 1000);
diff -= hours * (60 * 60 * 1000);
long minutes = diff / (60 * 1000);
diff -= minutes * (60 * 1000);
long seconds = diff / 1000;
if(i==0)
{
mTimerView1.start(TIMER_LENGTH1);
mTimerView2.start(TIMER_LENGTH2);
mTimerView3.start(TIMER_LENGTH3);
i++;
d=days;
h=hours;
m=minutes;
s=seconds;
}
if(minutes!=m)
{
mTimerView3.start(TIMER_LENGTH3);
m=minutes;
}
if(hours!=h)
{
mTimerView2.start(TIMER_LENGTH2);
h=hours;
}
if(days!=d)
{
mTimerView1.start(TIMER_LENGTH1);
d=days;
}
tvDay.setText("" + String.format("%02d", days));
tvHour.setText("" + String.format("%02d", hours));
tvMinute.setText("" + String.format("%02d", minutes));
tvSecond.setText("" + String.format("%02d", seconds));
} else {
//linearLayout1.setVisibility(View.VISIBLE);
linearLayout2.setVisibility(View.GONE);
tvEvent.setText("CULFEST IS ON");
handler.removeCallbacks(runnable);
// handler.removeMessages(0);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
handler.postDelayed(runnable, 0);
}
@Override
protected void onPause() {
mTimerView1.stop();
mTimerView2.stop();
mTimerView3.stop();
super.onPause();
}
}
这是我的 TimerView:
public class TimerView extends View {
private static final int ARC_START_ANGLE = 270; // 12 o'clock
private static final float THICKNESS_SCALE = 0.03f;
private Bitmap mBitmap;
private Canvas mCanvas;
private RectF mCircleOuterBounds;
private RectF mCircleInnerBounds;
private Paint mCirclePaint;
private Paint mEraserPaint;
private float mCircleSweepAngle;
private ValueAnimator mTimerAnimator;
public TimerView(Context context) {
this(context, null);
}
public TimerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TimerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
int circleColor = Color.RED;
if (attrs != null) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TimerView);
if (ta != null) {
circleColor = ta.getColor(R.styleable.TimerView_circleColor, circleColor);
ta.recycle();
}
}
mCirclePaint = new Paint();
mCirclePaint.setAntiAlias(true);
mCirclePaint.setColor(circleColor);
mEraserPaint = new Paint();
mEraserPaint.setAntiAlias(true);
mEraserPaint.setColor(Color.TRANSPARENT);
mEraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
@SuppressWarnings("SuspiciousNameCombination")
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec); // Trick to make the view square
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != oldw || h != oldh) {
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mBitmap.eraseColor(Color.TRANSPARENT);
mCanvas = new Canvas(mBitmap);
}
super.onSizeChanged(w, h, oldw, oldh);
updateBounds();
}
@Override
protected void onDraw(Canvas canvas) {
mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
if (mCircleSweepAngle > 0f) {
mCanvas.drawArc(mCircleOuterBounds, ARC_START_ANGLE, mCircleSweepAngle, true, mCirclePaint);
mCanvas.drawOval(mCircleInnerBounds, mEraserPaint);
}
canvas.drawBitmap(mBitmap, 0, 0, null);
}
public void start(int secs) {
stop();
mTimerAnimator = ValueAnimator.ofFloat(0f, 1f);
mTimerAnimator.setDuration(TimeUnit.SECONDS.toMillis(secs));
mTimerAnimator.setInterpolator(new LinearInterpolator());
mTimerAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
drawProgress((float) animation.getAnimatedValue());
}
});
mTimerAnimator.start();
}
public void stop() {
if (mTimerAnimator != null && mTimerAnimator.isRunning()) {
mTimerAnimator.cancel();
mTimerAnimator = null;
drawProgress(0f);
}
}
private void drawProgress(float progress) {
mCircleSweepAngle = 360 * progress;
invalidate();
}
private void updateBounds() {
final float thickness = getWidth() * THICKNESS_SCALE;
mCircleOuterBounds = new RectF(0, 0, getWidth(), getHeight());
mCircleInnerBounds = new RectF(
mCircleOuterBounds.left + thickness,
mCircleOuterBounds.top + thickness,
mCircleOuterBounds.right - thickness,
mCircleOuterBounds.bottom - thickness);
invalidate();
}
}
计时器看起来像这样:
请帮忙。
通过浏览源代码,您似乎可以通过以下方式设置初始值:
1) 创建一个实例变量来存储初始值 setter:
//Value between 0f and 360f
private float mStartValue = 0f;
//...
public void setStartValue(float startValueInDegrees) {
mStartValue = startValueInDegrees;
}
2) 更新 drawProgress()
方法以说明这个新的起始值:
private void drawProgress(float progress) {
mCircleSweepAngle = ((360 - mStartValue) * progress) + mStartValue;
invalidate();
}