編輯:關於Android編程
之前幫別人寫了一個不是那麼優美的圓形進度條,效果圖大家也看過了。但是後某人不滿意,說原應用是倒計時時間最後5s,才開始顯示數字的,同時轉完一圈需要的時間只能是30s左右。然後我掐時間看了一下雖然總時間設置的是30s,但是總共轉完一圈卻耗費了50多秒的樣子。
問題出來了:
1. 轉圈總時間30s不正確
2. 數字顯示時間不正確
3. 數字1的動畫沒原應用的好(2144手機令牌)
花了一個小時搞了一下,忍不住終於射出來了什麼東西,解決了上面的3個問題(請看1從有到無);國際慣例效果圖先行,先看下改善之後的效果圖(不好請不要吐我口水)
當對於一個問題無從下手的話,有時候反編譯看下別人家的源代碼也是不錯的選擇。反編譯工具用的是Android逆向助手。
看了下它的代碼,發現他原來是用定時器區去完成這個工作的
public class MyProgressCount extends CountDownTimer { public MyProgressCount() { super(30000L, 25L); } public void onFinish() { start(); } public void onTick(long paramLong) { MainFragment.this.count = (MainFragment.this.max - (float)paramLong); MainFragment.this.progress_bar.setProgress(MainFragment.this.count); MainFragment.this.circleRadius = (MainFragment.this.progressBar_width / 2); MainFragment.this.circleX = (MainFragment.this.window_width / 2 - MainFragment.this.circleRadius); MainFragment.this.circleY = (MainFragment.this.circleY_init + MainFragment.this.circleRadius); if (MainFragment.this.count >= MainFragment.this.max - 200.0F) { MainFragment.this.round_text.setVisibility(8); MainFragment.this.password_time = (DateUtils.getStringToDate(DateUtils.getCurrentDate()) + MainFragment.this.d_value); MainFragment.this.count = 0.0F; MainFragment.this.password = Util.DynamicPassword(MainFragment.this.uid, MainFragment.this.password_time - (MainFragment.this.password_time % 30L), MainFragment.this.token); DbManage.getInstance(MainFragment.this.activity).saveActionStr(MainFragment.this.password, MainFragment.this.password_time - (MainFragment.this.password_time % 30L)); MainFragment.this.stringArr = MainFragment.this.password.toCharArray(); MainFragment.this.dynamic_password.setText(MainFragment.this.stringArr[0] + " " + MainFragment.this.stringArr[1] + " " + MainFragment.this.stringArr[2] + " " + MainFragment.this.stringArr[3] + " " + MainFragment.this.stringArr[4] + " " + MainFragment.this.stringArr[5]); } if (MainFragment.this.count > MainFragment.this.max * 5.0F / 6.0F) { float f = MainFragment.this.max * 5.0F / 6.0F; f = (float)(MainFragment.this.count / 1000.0D); double d = 3.141592653589793D * 12.0F * f / 180.0D; int i = (int)(Math.sin(d) * MainFragment.this.progress_bar.getWidth() / 2.0D); int j = (int)(Math.cos(d) * MainFragment.this.progress_bar.getWidth() / 2.0D); Util.setLayout(MainFragment.this.round_text, MainFragment.this.circleX + MainFragment.this.circleRadius - Math.abs(i), MainFragment.this.circleY - Math.abs(j)); i = ((Integer)ColorUtils.evaluate((float)((MainFragment.this.count / MainFragment.this.max - 0.75D) * 4.0D), Integer.valueOf(-1853686), Integer.valueOf(-3407872))).intValue(); MainFragment.this.round_text.setBackgroundDrawable(Util.getRoundBg(i, 100)); MainFragment.this.round_text.setTextColor(-1); if ((f >= 25.0F) && (f <= 30.0F)) { MainFragment.this.round_text.setVisibility(0); MainFragment.this.round_text.setText((int)(31.0F - f)); if ((f >= 29.0F) && (f < 30.0F) && (!(MainFragment.this.scaleAnimation.hasStarted()))) { MainFragment.this.round_text.setAnimation(MainFragment.this.scaleAnimation); MainFragment.this.scaleAnimation.setAnimationListener(new Animation.AnimationListener() { public void onAnimationEnd(Animation paramAnimation) { MainFragment.this.scaleAnimation = new ScaleAnimation(1.0F, 0.0F, 1.0F, 0.0F, 1, 0.5F, 1, 0.5F); MainFragment.this.scaleAnimation.setDuration(MainFragment.this.duration); } public void onAnimationRepeat(Animation paramAnimation) { } public void onAnimationStart(Animation paramAnimation) { } }); MainFragment.this.scaleAnimation.start(); } } while (true) { MainFragment.this.round_text.invalidate(); return; MainFragment.this.round_text.setVisibility(8); } } MainFragment.this.round_text.setVisibility(8); } }
至於裡面數字的顯示用的是一個TextView動態改變他的位置以及內容和背景顏色,然後配合上
CircleProgressBar就行了(當然裡面涉及到有顏色值改變的計算),詳情看下文給出的源代碼。
當然看了反編譯代碼思路說起來很輕松,估計是個稍微會一點安卓的人都知道,那下面我們改進入正題了(talk is cheap,show me the fucking code).
裡面牽涉到4個類,工具類:
Util和
ColorUtils;界面類:
TestActivity01和
CircleProgressBar。
先看下CircleProgressBar裡面有些什麼狗屎東西:
package com.example.tangxb.myapplication;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
public class CircleProgressBar extends View {
private static final int DEFAULT_BACKGROUND_COLOR = Color.parseColor("#ffffff");
private static final int DEFAULT_MAX = 100;
private static final int DEFAULT_PROGRESS_COLOR = Color.parseColor("#54bfad");
private static final int DEFAULT_PROGRESS_END_COLOR = 0;
private static final int DEFAULT_SIZE = 80;
private static final boolean DEFAULT_TRANSITION_ENABLE = false;
private static final int END_ANGLE = 360;
private static final float MAX_SWEEP_ANGLE = 360.0F;
private static final String TAG = "ArcProgressBar";
private float DEFAULT_STROKE_WIDTH;
int c;
Context context;
SizeChangeListener l;
private float mMax;
private Path mPath;
private float mProgress;
private int mProgressBackgroundColor;
private Paint mProgressBgPaint;
private RectF mProgressBgRectF;
private int mProgressColor;
private int mProgressEndColor;
private int mProgressEndColor2;
private Paint mProgressPaint;
private Paint mProgressPaintEnd;
private RectF mProgressRectF;
private float mStrokeWith;
private float unitAngle;
public CircleProgressBar(Context paramContext) {
this(paramContext, null);
}
public CircleProgressBar(Context paramContext, AttributeSet paramAttributeSet) {
this(paramContext, paramAttributeSet, 0);
}
public CircleProgressBar(Context paramContext, AttributeSet paramAttributeSet, int paramInt) {
super(paramContext, paramAttributeSet, paramInt);
this.DEFAULT_STROKE_WIDTH = 6.0F;
this.mProgressRectF = new RectF(0.0F, 0.0F, 0.0F, 0.0F);
this.mProgressBgRectF = new RectF(0.0F, 0.0F, 0.0F, 0.0F);
this.mStrokeWith = 0.0F;
this.unitAngle = 0.0F;
this.context = paramContext;
loadStyledAttr(paramContext, paramAttributeSet, paramInt);
initPaint();
}
private void drawProgress(Canvas paramCanvas) {
int i = getWidth() / 2;
int j = i - Util.dip2px(this.context, 13.0F);
paramCanvas.drawArc(new RectF(i - j, i - j, i + j, i + j), getStartAngle(), getSweepAngel(), getUseCenter(), this.mProgressPaint);
}
private void drawProgressBg(Canvas paramCanvas) {
int i = getWidth() / 2;
int j = i - Util.dip2px(this.context, 20.0F);
paramCanvas.drawArc(new RectF(i - j, i - j, i + j, i + j), getStartAngle(), 360.0F, true, this.mProgressBgPaint);
}
private void drawProgressNum(Canvas paramCanvas) {
this.mProgressBgRectF.left = 30.0F;
this.mProgressBgRectF.top = 30.0F;
this.mProgressBgRectF.right = 105.0F;
this.mProgressBgRectF.bottom = 105.0F;
Paint localPaint = new Paint(1);
localPaint.setColor(Color.rgb(216, 76, 75));
localPaint.setStyle(Paint.Style.STROKE);
paramCanvas.drawArc(this.mProgressBgRectF, getStartAngle(), 360.0F, getUseCenter(), localPaint);
}
private int getStartAngle() {
return -90;
}
private float getSweepAngel() {
return (this.unitAngle * this.mProgress);
}
private boolean getUseCenter() {
return false;
}
private void initPaint() {
this.mPath = new Path();
this.mProgressPaint = new Paint(1);
this.mProgressPaint.setColor(this.mProgressColor);
this.mProgressBgPaint = new Paint(1);
this.mProgressBgPaint.setColor(this.mProgressBackgroundColor);
this.mProgressBgPaint.setAntiAlias(true);
this.mProgressPaint.setStyle(Paint.Style.STROKE);
this.mProgressPaint.setStrokeJoin(Paint.Join.ROUND);
this.mProgressPaint.setStrokeWidth(Util.dip2px(this.context, 10.0F));
this.mProgressBgPaint.setStyle(Paint.Style.FILL);
this.mProgressBgPaint.setStrokeWidth(this.mStrokeWith);
}
private void loadStyledAttr(Context paramContext, AttributeSet paramAttributeSet, int paramInt) {
// 顏色設置
this.mProgressBackgroundColor = DEFAULT_BACKGROUND_COLOR;
this.mProgressColor = Color.parseColor("#ff48d502");
this.mProgressEndColor = Color.parseColor("#ffe3b70a");
this.mProgressEndColor2 = Color.parseColor("#ffcc0000");
this.mMax = 100;
this.mProgress = 0;
this.DEFAULT_STROKE_WIDTH = Util.dip2px(getContext(), this.DEFAULT_STROKE_WIDTH);
this.mStrokeWith = Util.dip2px(getContext(), this.DEFAULT_STROKE_WIDTH);
}
private int measure(int paramInt) {
int i = View.MeasureSpec.getMode(paramInt);
paramInt = View.MeasureSpec.getSize(paramInt);
if (i == 1073741824)
return paramInt;
return Util.dip2px(getContext(), 80.0F);
}
private void onProgressChanged() {
this.c = ((Integer) ColorUtils.evaluate((float) (this.mProgress / this.mMax * 1.5D), Integer.valueOf(this.mProgressColor), Integer.valueOf(this.mProgressEndColor))).intValue();
this.mProgressPaint.setColor(this.c);
}
private void onProgressChangedEnd() {
this.c = ((Integer) ColorUtils.evaluate((float) ((float) (this.mProgress / this.mMax - 0.75D) * 4.0D), Integer.valueOf(this.mProgressEndColor), Integer.valueOf(this.mProgressEndColor2))).intValue();
this.mProgressPaint.setColor(this.c);
}
private void setUnitProgress() {
this.unitAngle = (360.0F / this.mMax);
}
public float getMax() {
return this.mMax;
}
public double getPrgoress() {
return this.mProgress;
}
protected void onDraw(Canvas paramCanvas) {
drawProgressBg(paramCanvas);
drawProgress(paramCanvas);
}
protected void onMeasure(int paramInt1, int paramInt2) {
setMeasuredDimension(measure(paramInt1), measure(paramInt2));
setUnitProgress();
}
protected void onSizeChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) {
this.l.sizeChanged(paramInt1, paramInt2, paramInt3, paramInt4);
super.onSizeChanged(paramInt1, paramInt2, paramInt3, paramInt4);
}
public void setMax(float paramFloat) {
this.mMax = paramFloat;
setUnitProgress();
invalidate();
}
public void setProgress(float paramFloat) {
this.mProgress = paramFloat;
if (paramFloat <= this.mMax * 0.75D)
onProgressChanged();
if (paramFloat > this.mMax * 0.75D)
onProgressChangedEnd();
invalidate();
}
public void setProgressWithAnim(int paramInt1, int paramInt2, long paramLong) {
ValueAnimator localValueAnimator = ValueAnimator.ofInt(new int[]{paramInt1, paramInt2});
localValueAnimator.setDuration(paramLong);
localValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator paramValueAnimator) {
}
});
localValueAnimator.setInterpolator(new LinearInterpolator());
localValueAnimator.start();
}
public void setProgressWithAnim(int paramInt, long paramLong) {
setProgressWithAnim(0, paramInt, paramLong);
}
/**
* 這裡可用getViewTreeObserver()去做監聽,消除耦合
*
* @param paramSizeChangeListener
*/
public void setSizeChangeListener(SizeChangeListener paramSizeChangeListener) {
this.l = paramSizeChangeListener;
}
public interface SizeChangeListener {
void sizeChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4);
}
}
源碼有點長,但是裡面畫的東西卻很少
protected void onDraw(Canvas paramCanvas) {
drawProgressBg(paramCanvas);
drawProgress(paramCanvas);
}
然後看下
TestActivity01:
package com.example.tangxb.myapplication;
import android.graphics.Color;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.TextView;
/**
* Created by Tangxb on 2016/4/19.
*/
public class TestActivity01 extends AppCompatActivity {
private int circleRadius;
private int circleX;
private int circleY;
private int circleY_init;
private int duration = 1200;
private float count = 0.0F;
private float max;
private MyProgressCount progressCount;
private CircleProgressBar progress_bar;
/**
* 寬高為20dp的TextView
*/
private TextView round_text;
// 第一個效果表現沒有第二個好
Animation scaleAnimation1 = new ScaleAnimation(1.0F, 0.4F, 1.0F, 0.4F, Animation.RELATIVE_TO_SELF, 0.3F, Animation.RELATIVE_TO_SELF, 0.2F);
// 使用第二個替代了第一個
Animation scaleAnimation = new ScaleAnimation(1.0F, 0.0F, 1.0F, 0.0F, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F);
private int window_width;
private int progressBar_width;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.acttivity_test);
init();
}
private void init() {
scaleAnimation.setDuration(this.duration);
max = 30000.0F;
circleY_init = Util.dip2px(this, 85.0F);
window_width = getWindowManager().getDefaultDisplay().getWidth();
progress_bar = ((CircleProgressBar) findViewById(R.id.cpb));
round_text = ((TextView) findViewById(R.id.tv));
progress_bar.setMax(max);
progress_bar.setSizeChangeListener(new CircleProgressBar.SizeChangeListener() {
public void sizeChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) {
progressBar_width = progress_bar.getWidth();
if (progressCount == null) {
progressCount = new MyProgressCount();
}
progressCount.start();
}
});
}
@Override
protected void onStart() {
super.onStart();
if (progressBar_width != 0) {
if (progressCount == null) {
progressCount = new MyProgressCount();
}
progressCount.start();
}
}
@Override
protected void onStop() {
super.onStop();
if (progressCount != null) {
progressCount.cancel();
progressCount = null;
}
}
class MyProgressCount extends CountDownTimer {
public MyProgressCount() {
super(30000L, 25L);
}
public void onFinish() {
start();
}
public void onTick(long paramLong) {
count = (max - (float) paramLong);
// 設置進度
progress_bar.setProgress(count);
circleRadius = (progressBar_width / 2);
circleX = (window_width / 2 - circleRadius);
circleY = (circleY_init + circleRadius);
if (count >= max - 200.0F) {
}
if (count > max * 5.0F / 6.0F) {
float f = (float) (count / 1000.0D);
// 計算弧度值(sin需要的是弧度值不是角度值),至於是怎麼得到的,數學渣渣表示懵逼
double d = Math.PI * 12.0F * f / 180.0D;
int i = (int) (Math.sin(d) * progress_bar.getWidth() / 2.0D);
int j = (int) (Math.cos(d) * progress_bar.getWidth() / 2.0D);
// 這裡請注意裡面使用的FrameLayout.LayoutParams,所以容器請用FrameLayout
Util.setLayout(round_text, circleX + circleRadius - Math.abs(i), circleY - Math.abs(j));
i = ((Integer) ColorUtils.evaluate((float) ((count / max - 0.75D) * 4.0D), Integer.valueOf(-1853686), Integer.valueOf(-3407872))).intValue();
round_text.setBackgroundDrawable(Util.getRoundBg(i, 100));
round_text.setTextColor(Color.WHITE);
if ((f >= 25.0F) && (f <= 30.0F)) {
round_text.setVisibility(View.VISIBLE);
round_text.setText((int) (31.0F - f) + "");
if ((f >= 29.0F) && (f < 30.0F) && (!(scaleAnimation.hasStarted()))) {
round_text.setAnimation(scaleAnimation);
scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
public void onAnimationEnd(Animation paramAnimation) {
scaleAnimation = new ScaleAnimation(1.0F, 0.0F, 1.0F, 0.0F, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F);
scaleAnimation.setDuration(duration);
// 設置數字的text為GONE狀態
round_text.setVisibility(View.GONE);
}
public void onAnimationRepeat(Animation paramAnimation) {
}
public void onAnimationStart(Animation paramAnimation) {
// 設置數字的text為VISIBLE狀態
round_text.setVisibility(View.VISIBLE);
}
});
scaleAnimation.start();
}
}
}
}
}
}
附上
ColorUtils和
Util以及
xml文件
package com.example.tangxb.myapplication;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
/**
* Created by Tangxb on 2016/7/25.
*/
public class Util {
public static int dip2px(Context paramContext, float paramFloat) {
return (int) (paramFloat * paramContext.getResources().getDisplayMetrics().density + 0.5F);
}
public static void setLayout(View paramView, int paramInt1, int paramInt2) {
ViewGroup.MarginLayoutParams localMarginLayoutParams = new ViewGroup.MarginLayoutParams(paramView.getLayoutParams());
localMarginLayoutParams.setMargins(paramInt1 - 14, paramInt2 + 10, localMarginLayoutParams.width + paramInt1, localMarginLayoutParams.height + paramInt2);
paramView.setLayoutParams(new FrameLayout.LayoutParams(localMarginLayoutParams));
}
public static Drawable getRoundBg(int paramInt1, int paramInt2) {
ShapeDrawable localShapeDrawable = new ShapeDrawable(new RoundRectShape(new float[]{paramInt2, paramInt2, paramInt2, paramInt2, paramInt2, paramInt2, paramInt2, paramInt2}, null, null));
paramInt2 = paramInt1;
if (paramInt1 == -1)
paramInt2 = -1;
localShapeDrawable.getPaint().setColor(paramInt2);
return localShapeDrawable;
}
}
package com.example.tangxb.myapplication;
/**
* Created by Tangxb on 2016/7/25.
*/
public class ColorUtils {
public static Object evaluate(float paramFloat, Object paramObject1, Object paramObject2)
{
int l = ((Integer)paramObject1).intValue();
int i = l >> 24 & 0xFF;
int j = l >> 16 & 0xFF;
int k = l >> 8 & 0xFF;
l &= 255;
int i1 = ((Integer)paramObject2).intValue();
return Integer.valueOf((int)(((i1 >> 24 & 0xFF) - i) * paramFloat) + i << 24 | (int)(((i1 >> 16 & 0xFF) - j) * paramFloat) + j << 16 | (int)(((i1 >> 8 & 0xFF) - k) * paramFloat) + k << 8 | (int)(((i1 & 0xFF) - l) * paramFloat) + l);
}
public static Object evaluate2(float paramFloat, Object paramObject1, Object paramObject2, Object paramObject3)
{
int l = ((Integer)paramObject1).intValue();
int i = l >> 24 & 0xFF;
int j = l >> 16 & 0xFF;
int k = l >> 8 & 0xFF;
l &= 255;
int i1 = ((Integer)paramObject2).intValue();
int i2 = ((Integer)paramObject3).intValue();
return Integer.valueOf((int)(((i1 >> 24 & 0xFF) - i - (i2 >> 24 & 0xFF)) * paramFloat) + i << 24 | (int)(((i1 >> 16 & 0xFF) - j - (i2 >> 16 & 0xFF)) * paramFloat) + j << 16 | (int)(((i1 >> 8 & 0xFF) - k - (i2 >> 8 & 0xFF)) * paramFloat) + k << 8 | (int)(((i1 & 0xFF) - l - (i2 & 0xFF)) * paramFloat) + l);
}
}
<framelayout android:layout_height="385.0dp" android:layout_width="match_parent">
</framelayout>
我把裡面能夠自己還原的部分還原了,然而這裡為了偷懶,就沒有再用自定義屬性去設置顏色值了。
一、說起進度條,必須說說條形進度條,經常都會使用到嘛,特別是下載文件進度等等,還有像騰訊QQ安裝進度條一樣,有個進度總給人良好的用戶體驗。先來找圖看看,做這
這個是基於最新v4包實現的一個下拉刷新的東東~~~先給大家透露一下,整體很簡單,畢竟不是自定義,還請大家放寬心對待!!!廢話不多說,直接貼代碼 package
Android studio詳解我們古人又雲:工欲善其事,必先利其器。1、android studio的背景Android Studio 是一個Android開發環境,基
前言:上篇介紹了自定義控件的基本要求以及繪制的基本原理,本篇文章主要介紹如何給自定義控件自定義一些屬性。本篇文章將繼續以上篇文章自定義圓形百分比為例進行講解。有關原理知識