我正在开发一个app&刚建立其逻辑部分。现在我想设计这个应用程序,就像在着名的计时器apps.for例如:
我想要的是外圈,它充满了某些事件的每个触发器或者数字的增量。我实际上不知道它是动画部分(比如用闪存或什么构建)或者只是通过使用其内置属性和功能在android本身编码。 所以任何人如果告诉我解释我使用了什么工具或任何可以从底部解释事情的参考教程。我真的不知道任何设计。这个的任何代码??
答案 0 :(得分:6)
这会吗?
更新:现在也正确处理现实世界时间。
示例屏幕截图:
<强>代码:强>
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextPaint;
import android.view.View;
import android.graphics.*;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new CircularCountdown(this));
}
private static class CircularCountdown extends View {
private final Paint backgroundPaint;
private final Paint progressPaint;
private final Paint textPaint;
private long startTime;
private long currentTime;
private long maxTime;
private long progressMillisecond;
private double progress;
private RectF circleBounds;
private float radius;
private float handleRadius;
private float textHeight;
private float textOffset;
private final Handler viewHandler;
private final Runnable updateView;
public CircularCountdown(Context context) {
super(context);
// used to fit the circle into
circleBounds = new RectF();
// size of circle and handle
radius = 200;
handleRadius = 10;
// limit the counter to go up to maxTime ms
maxTime = 5000;
// start and current time
startTime = System.currentTimeMillis();
currentTime = startTime;
// the style of the background
backgroundPaint = new Paint();
backgroundPaint.setStyle(Paint.Style.STROKE);
backgroundPaint.setAntiAlias(true);
backgroundPaint.setStrokeWidth(10);
backgroundPaint.setStrokeCap(Paint.Cap.SQUARE);
backgroundPaint.setColor(Color.parseColor("#4D4D4D")); // dark gray
// the style of the 'progress'
progressPaint = new Paint();
progressPaint.setStyle(Paint.Style.STROKE);
progressPaint.setAntiAlias(true);
progressPaint.setStrokeWidth(10);
progressPaint.setStrokeCap(Paint.Cap.SQUARE);
progressPaint.setColor(Color.parseColor("#00A9FF")); // light blue
// the style for the text in the middle
textPaint = new TextPaint();
textPaint.setTextSize(radius / 2);
textPaint.setColor(Color.BLACK);
textPaint.setTextAlign(Paint.Align.CENTER);
// text attributes
textHeight = textPaint.descent() - textPaint.ascent();
textOffset = (textHeight / 2) - textPaint.descent();
// This will ensure the animation will run periodically
viewHandler = new Handler();
updateView = new Runnable(){
@Override
public void run(){
// update current time
currentTime = System.currentTimeMillis();
// get elapsed time in milliseconds and clamp between <0, maxTime>
progressMillisecond = (currentTime - startTime) % maxTime;
// get current progress on a range <0, 1>
progress = (double) progressMillisecond / maxTime;
CircularCountdown.this.invalidate();
viewHandler.postDelayed(updateView, 1000/60);
}
};
viewHandler.post(updateView);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// get the center of the view
float centerWidth = canvas.getWidth() / 2;
float centerHeight = canvas.getHeight() / 2;
// set bound of our circle in the middle of the view
circleBounds.set(centerWidth - radius,
centerHeight - radius,
centerWidth + radius,
centerHeight + radius);
// draw background circle
canvas.drawCircle(centerWidth, centerHeight, radius, backgroundPaint);
// we want to start at -90°, 0° is pointing to the right
canvas.drawArc(circleBounds, -90, (float)(progress*360), false, progressPaint);
// display text inside the circle
canvas.drawText((double)(progressMillisecond/100)/10 + "s",
centerWidth,
centerHeight + textOffset,
textPaint);
// draw handle or the circle
canvas.drawCircle((float)(centerWidth + (Math.sin(progress * 2 * Math.PI) * radius)),
(float)(centerHeight - (Math.cos(progress * 2 * Math.PI) * radius)),
handleRadius,
progressPaint);
}
}
}
答案 1 :(得分:0)
Antimonit
的解决方案存在两个重要问题:
circular clock view
破坏活动/碎片并再次显示时钟时,发生内存泄漏。基于Antimonit
代码(谢谢!),我创建了更多可重用且内存安全的解决方案。现在,几乎所有参数都可以从XML
文件中进行设置。在活动/片段类的最后,我们需要调用startCount
方法。当活动/片段将被破坏以避免内存泄漏时,我强烈建议调用removeCallbacks
方法。
KakaCircularCounter.java类:
public class KakaCircularCounter extends View {
public static final int DEF_VALUE_RADIUS = 250;
public static final int DEF_VALUE_EDGE_WIDTH = 15;
public static final int DEF_VALUE_TEXT_SIZE = 18;
private Paint backgroundPaint;
private Paint progressPaint;
private Paint textPaint;
private RectF circleBounds;
private long startTime;
private long currentTime;
private long maxTime;
private long progressMillisecond;
private double progress;
private float radius;
private float edgeHeadRadius;
private float textInsideOffset;
private KakaDirectionCount countDirection;
private Handler viewHandler;
private Runnable updateView;
public KakaCircularCounter(Context context) {
super(context);
init(null);
}
public KakaCircularCounter(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public KakaCircularCounter(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
public KakaCircularCounter(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
private void init(AttributeSet attrSet) {
if (attrSet == null) {
return;
}
TypedArray typedArray = getContext().obtainStyledAttributes(attrSet, R.styleable.KakaCircularCounter);
circleBounds = new RectF();
backgroundPaint = setupBackground(typedArray);
progressPaint = setupProgress(typedArray);
textPaint = setupText(typedArray);
textInsideOffset = (textPaint.descent() - textPaint.ascent() / 2) - textPaint.descent();
radius = typedArray.getDimensionPixelSize(R.styleable.KakaCircularCounter_clockRadius, DEF_VALUE_RADIUS);
edgeHeadRadius = typedArray.getDimensionPixelSize(R.styleable.KakaCircularCounter_edgeHeadRadius, DEF_VALUE_EDGE_WIDTH);
countDirection = KakaDirectionCount.values()[typedArray.getInt(R.styleable.KakaCircularCounter_countFrom,
KakaDirectionCount.MAXIMUM.ordinal())];
typedArray.recycle();
}
private Paint setupText(TypedArray typedArray) {
Paint t = new Paint();
t.setTextSize(typedArray.getDimensionPixelSize(R.styleable.KakaCircularCounter_textInsideSize, DEF_VALUE_TEXT_SIZE));
t.setColor(typedArray.getColor(R.styleable.KakaCircularCounter_textInsideColor, Color.BLACK));
t.setTextAlign(Paint.Align.CENTER);
return t;
}
private Paint setupProgress(TypedArray typedArray) {
Paint p = new Paint();
p.setStyle(Paint.Style.STROKE);
p.setAntiAlias(true);
p.setStrokeCap(Paint.Cap.SQUARE);
p.setStrokeWidth(typedArray.getDimensionPixelSize(R.styleable.KakaCircularCounter_clockWidth, DEF_VALUE_EDGE_WIDTH));
p.setColor(typedArray.getColor(R.styleable.KakaCircularCounter_edgeBackground, Color.parseColor("#4D4D4D")));
return p;
}
private Paint setupBackground(TypedArray ta) {
Paint b = new Paint();
b.setStyle(Paint.Style.STROKE);
b.setStrokeWidth(ta.getDimensionPixelSize(R.styleable.KakaCircularCounter_clockWidth, DEF_VALUE_EDGE_WIDTH));
b.setColor(ta.getColor(R.styleable.KakaCircularCounter_clockBackground, Color.parseColor("#4D4D4D")));
b.setAntiAlias(true);
b.setStrokeCap(Paint.Cap.SQUARE);
return b;
}
public void startCount(long maxTimeInMs) {
startTime = System.currentTimeMillis();
this.maxTime = maxTimeInMs;
viewHandler = new Handler();
updateView = () -> {
currentTime = System.currentTimeMillis();
progressMillisecond = (currentTime - startTime) % maxTime;
progress = (double) progressMillisecond / maxTime;
KakaCircularCounter.this.invalidate();
viewHandler.postDelayed(updateView, 1000 / 60);
};
viewHandler.post(updateView);
}
public void removeCallbacks() {
viewHandler.removeCallbacks(updateView);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float centerWidth = getWidth() / 2f;
float centerHeight = getHeight() / 2f;
circleBounds.set(centerWidth - radius,
centerHeight - radius,
centerWidth + radius,
centerHeight + radius);
canvas.drawCircle(centerWidth, centerHeight, radius, backgroundPaint);
canvas.drawArc(circleBounds, -90, (float) (progress * 360), false, progressPaint);
canvas.drawText(getTextToDraw(),
centerWidth,
centerHeight + textInsideOffset,
textPaint);
canvas.drawCircle((float) (centerWidth + (Math.sin(progress * 2 * Math.PI) * radius)),
(float) (centerHeight - (Math.cos(progress * 2 * Math.PI) * radius)),
edgeHeadRadius,
progressPaint);
}
@NonNull
private String getTextToDraw() {
if (countDirection.equals(KakaDirectionCount.ZERO)) {
return String.valueOf(progressMillisecond / 1000);
} else {
return String.valueOf((maxTime - progressMillisecond) / 1000);
}
}
}
KakaDirectionCount枚举:
public enum KakaDirectionCount {
ZERO, MAXIMUM
}
在值目录(kaka_circular_counter.xml)中分配文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="KakaCircularCounter">
<attr name="clockRadius" format="dimension"/>
<attr name="clockBackground" format="color"/>
<attr name="clockWidth" format="dimension"/>
<attr name="edgeBackground" format="color"/>
<attr name="edgeWidth" format="dimension"/>
<attr name="edgeHeadRadius" format="dimension"/>
<attr name="textInsideSize" format="dimension"/>
<attr name="textInsideColor" format="color"/>
<attr name="countFrom" format="enum">
<enum name="ZERO" value="0"/>
<enum name="MAXIMUM" value="1"/>
</attr>
</declare-styleable>
</resources>
在xml文件中使用的示例:
<pl.kaka.KakaCircularCounter
android:id="@+id/circular_counter"
android:layout_width="180dp"
android:layout_height="180dp"
app:layout_constraintBottom_toBottomOf="@id/backgroundTriangle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/backgroundTriangle"
app:clockRadius="85dp"
app:clockBackground="@color/colorTransparent"
app:clockWidth="3dp"
app:edgeBackground="@color/colorAccentSecondary"
app:edgeWidth="5dp"
app:edgeHeadRadius="1dp"
app:textInsideSize="60sp"
app:textInsideColor="@color/colorWhite"
app:countFrom="MAXIMUM"/>
在活动或片段中使用的示例:
//the number in parameter is the value of the counted time
binding.circularCounter.startCount(12000);
警告:请记住,销毁活动/片段时要删除回调,因为会发生内存泄漏。例如:
@Override
public void onDestroyView() {
super.onDestroyView();
binding.circularCounter.removeCallbacks();
}