Android仿微博加載餅形圈

完整代碼
自定義屬性
Java代碼
public class CircleAnnulusProgressBar extends View {//-------------------- 默認(rèn)值 -------------------/*** 默認(rèn)的當(dāng)前進度,默認(rèn)為0*/private static final int DEFAULT_PROGRESS = 0;/*** 默認(rèn)的最大值,默認(rèn)為100*/private static final int DEFAULT_MAX = 100;/*** 默認(rèn)的外圓輪廓寬度,默認(rèn)是1dp*/private static int DEFAULT_OUTER_CIRCLE_BORDER_WIDTH = 3;/*** 默認(rèn)的外圓的顏色*/private final int DEFAULT_OUTER_CIRCLE_COLOR = Color.parseColor("#FFFFFF");/*** 默認(rèn)的內(nèi)餅形的顏色*/private final int DEFAULT_PIE_CIRCLE = Color.parseColor("#FFFFFF");//-------------------- 自定義屬性 -------------------/*** 默認(rèn)的外圓輪廓寬度,默認(rèn)是1dp*/private float mOuterCircleBorderWidth;/*** 外圓的顏色,默認(rèn)白色*/private int mOuterCircleColor;/*** 內(nèi)餅圖填充的顏色,默認(rèn)是白色*/private int mPieColor;//-------------------- 繪制相關(guān)對象 -------------------/*** 外圓的畫筆*/private Paint mOuterCirclePaint;/*** 內(nèi)部的餅形的畫筆*/private Paint mPiePaint;/*** View繪制區(qū)域,去除了padding*/private RectF mRect;//-------------------- View寬高等參數(shù) -------------------/*** View的寬,包括padding*/private int mWidth;/*** View的高,包括padding*/private int mHeight;/*** 設(shè)置的上下左右padding值*/private int mPaddingTop;private int mPaddingBottom;private int mPaddingLeft;private int mPaddingRight;/*** 外圓的半徑,已經(jīng)處理了padding*/private float mRadius;/*** 當(dāng)前進度,默認(rèn)為最大值*/private int mProgress;/*** 設(shè)置的最大進度,默認(rèn)為100*/private int mMax;//-------------------- 對外使用的對象 -------------------/*** 監(jiān)聽器集合*/private ArrayListmListeners; public CircleAnnulusProgressBar(Context context) {super(context);init(null);}public CircleAnnulusProgressBar(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init(attrs);}public CircleAnnulusProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(attrs);}private void init(AttributeSet attrs) {//由于dp轉(zhuǎn)換操作必須在初始化后context才不為空,所以在這里初始化默認(rèn)的外圓輪廓寬度DEFAULT_OUTER_CIRCLE_BORDER_WIDTH = dip2px(getContext(), 1f);//取出Xml設(shè)置的自定義屬性,當(dāng)前進度,最大進度if (attrs != null) {TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.CircleAnnulusProgressBar);//Xml設(shè)置的進度mProgress = array.getInt(R.styleable.CircleAnnulusProgressBar_progress, DEFAULT_PROGRESS);//Xml設(shè)置的最大值mMax = array.getInt(R.styleable.CircleAnnulusProgressBar_max, DEFAULT_MAX);//Xml設(shè)置的外圓顏色,先讀取直接寫#FFFFFF等樣式的,如果沒有,則讀取使用引用方式的,就是@color/white這樣的int resultOuterCircleColor = array.getColor(R.styleable.CircleAnnulusProgressBar_outer_circle_color, DEFAULT_OUTER_CIRCLE_COLOR);if (resultOuterCircleColor != DEFAULT_OUTER_CIRCLE_COLOR) {mOuterCircleColor = resultOuterCircleColor;} else {int outerCircleResId = array.getResourceId(R.styleable.CircleAnnulusProgressBar_outer_circle_color, android.R.color.white);mOuterCircleColor = getContext().getResources().getColor(outerCircleResId);}//Xml設(shè)置的內(nèi)餅圖顏色,同上,先讀取直接寫顏色值的,沒有再讀取使用引用方式的int resultPieCircleColor = array.getColor(R.styleable.CircleAnnulusProgressBar_pie_color, DEFAULT_PIE_CIRCLE);if (resultPieCircleColor != DEFAULT_PIE_CIRCLE) {mPieColor = resultPieCircleColor;} else {int pieColorResId = array.getResourceId(R.styleable.CircleAnnulusProgressBar_pie_color, android.R.color.white);mPieColor = getContext().getResources().getColor(pieColorResId);}//讀取設(shè)置的外圓輪廓寬度,讀取dimensionmOuterCircleBorderWidth = array.getDimensionPixelSize(R.styleable.CircleAnnulusProgressBar_outer_circle_border_width, DEFAULT_OUTER_CIRCLE_BORDER_WIDTH);//記得回收資源array.recycle();} else {//沒有在Xml中設(shè)置屬性,使用默認(rèn)屬性//當(dāng)前進度mProgress = DEFAULT_PROGRESS;//最大值mMax = DEFAULT_MAX;//外圓的顏色mOuterCircleColor = DEFAULT_OUTER_CIRCLE_COLOR;//內(nèi)餅形的顏色mPieColor = DEFAULT_PIE_CIRCLE;//外圓的寬度mOuterCircleBorderWidth = DEFAULT_OUTER_CIRCLE_BORDER_WIDTH;}//外層圓的畫筆mOuterCirclePaint = new Paint();mOuterCirclePaint.setColor(mOuterCircleColor);mOuterCirclePaint.setStyle(Paint.Style.STROKE);mOuterCirclePaint.setStrokeWidth(mOuterCircleBorderWidth);mOuterCirclePaint.setAntiAlias(true);//中間進度餅形畫筆mPiePaint = new Paint();mPiePaint.setColor(mPieColor);mPiePaint.setStyle(Paint.Style.FILL);mPiePaint.setAntiAlias(true);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);//取出總寬高mWidth = w;mHeight = h;//取出設(shè)置的padding值mPaddingTop = getPaddingTop();mPaddingBottom = getPaddingBottom();mPaddingLeft = getPaddingLeft();mPaddingRight = getPaddingRight();//計算外圓直徑,取寬高中最小的為圓的直徑,這里要處理添加padding的情況。float diameter = (Math.min(mWidth, mHeight)) - mPaddingLeft - mPaddingRight;//直徑除以2算出半徑mRadius = (float) ((diameter / 2) * 0.98);//建立一個Rect保存View的范圍,后面畫餅形也需要用到mRect = new RectF(mPaddingLeft, mPaddingTop, mWidth - mPaddingRight, mHeight - mPaddingBottom);}@Overrideprotected void onFinishInflate() {super.onFinishInflate();setBackgroundColor(getContext().getResources().getColor(android.R.color.transparent));}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//取出寬的模式和大小int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);//取出高的模式和大小int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);//設(shè)置的寬高不相等時,將寬高都進行校正,取最小的為標(biāo)準(zhǔn)if (widthSize != heightSize) {int finalSize = Math.min(widthSize, heightSize);widthSize = finalSize;heightSize = finalSize;}//默認(rèn)寬高值int defaultWidth = dip2px(getContext(), 55);int defaultHeight = dip2px(getContext(), 55);if (widthMode != MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY) {//當(dāng)寬高都設(shè)置wrapContent時設(shè)置我們的默認(rèn)值if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(defaultWidth, defaultHeight);} else if (widthMode == MeasureSpec.AT_MOST) {//寬、高任意一個為wrapContent都設(shè)置我們默認(rèn)值setMeasuredDimension(defaultWidth, heightSize);} else if (heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(widthSize, defaultHeight);}} else {setMeasuredDimension(widthSize, heightSize);}}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//獲取當(dāng)前的進度int curProgress = getProgress();//越界處理if (curProgress < 0) {curProgress = 0;}if (curProgress > mMax) {curProgress = mMax;}//畫外圓,這里使用寬和高都可以,因為我們限定寬和高都是相等的//這里圓心坐標(biāo)一直都在View的寬高的中間,就算有padding都是不會變的,變的只是半徑,半徑初始化前已經(jīng)去處理了padding,這里要注意canvas.drawCircle(mWidth / 2, mWidth / 2, mRadius, mOuterCirclePaint);//要進行畫布縮放操作,先保存圖層,因為縮放、平移等操作是疊加的,所以使用完必須恢復(fù),否則下次的onDraw就會累加縮放canvas.save();//用縮放畫布,進行縮放中心的餅圖,設(shè)置縮放中心是控件的中心canvas.scale(0.90f, 0.90f, mWidth / 2, mHeight / 2);//計算當(dāng)前進度對應(yīng)的角度float angle = 360 * (curProgress * 1.0f / getMax());//畫餅圖,-90度就是12點方向開始canvas.drawArc(mRect, -90, angle, true, mPiePaint);//還原畫布圖層canvas.restore();//回調(diào)進度給外面的監(jiān)聽器for (OnProgressUpdateListener listener : mListeners) {listener.onProgressUpdate(curProgress);}}/*** 設(shè)置進度** @param progress 要設(shè)置的進度*/public synchronized void setProgress(int progress) {if (progress < 0) {progress = 0;}this.mProgress = progress;//設(shè)置進度可能是子線程,所以將重繪調(diào)用交給主線程postInvalidate();}/*** 獲取當(dāng)前進度** @return 當(dāng)前的進度*/public int getProgress() {return mProgress;}/*** 設(shè)置最大值** @param max 要設(shè)置的最大值*/public synchronized void setMax(int max) {if (max < 0) {max = 0;}this.mMax = max;//設(shè)置進度可能是子線程,所以將重繪調(diào)用交給主線程postInvalidate();}/*** 進度更新的回調(diào)監(jiān)聽*/public interface OnProgressUpdateListener {//當(dāng)進度更新時回調(diào)void onProgressUpdate(int progress);}/*** 設(shè)置更新回調(diào)** @param listener 監(jiān)聽器實例*/public void addOnProgressUpdateListener(OnProgressUpdateListener listener) {if (mListeners == null) {mListeners = new ArrayList(); }this.mListeners.add(listener);}/*** 獲取設(shè)置的最大值** @return 設(shè)置的最大值*/public int getMax() {return mMax;}//------------------ 一些尺寸轉(zhuǎn)換方法 ------------------public static int dip2px(Context context, float dipValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (dipValue * scale + 0.5f);}public static int px2dp(Context context, float pxValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (pxValue / scale + 0.5f);}private int sp2px(Context context, float spVal) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,spVal, context.getResources().getDisplayMetrics());}}
示例代碼
Xml布局
xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#66000000">android:id="@+id/tipText"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="15dp"android:text="當(dāng)前進度:"android:textColor="#FFFFFF"android:textSize="14sp" />android:id="@+id/progressBar"android:layout_width="55dp"android:layout_height="55dp"android:layout_centerInParent="true"app:max="100"app:outer_circle_border_width="1dp"app:outer_circle_color="#FFFFFF"app:pie_color="@android:color/white"app:progress="30" />
Java代碼
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//餅圖控件final CircleAnnulusProgressBar progressBar = findViewById(R.id.progressBar);//提示問題final TextView tipText = findViewById(R.id.tipText);//設(shè)置進度最大值progressBar.setMax(100);//設(shè)置進度更新監(jiān)聽,每次更新時重新設(shè)置提示文字progressBar.addOnProgressUpdateListener(new CircleAnnulusProgressBar.OnProgressUpdateListener() {@Overridepublic void onProgressUpdate(int progress) {tipText.setText("當(dāng)前進度: ".concat(String.valueOf(progress)));}});//用值動畫不斷改變進度,測試進度ValueAnimator animator = ValueAnimator.ofInt(0, 100);animator.setRepeatMode(ValueAnimator.RESTART);animator.setRepeatCount(ValueAnimator.INFINITE);animator.setInterpolator(new LinearInterpolator());animator.setDuration(3000);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {Integer cValue = (Integer) animation.getAnimatedValue();progressBar.setProgress(cValue);}});animator.start();}}
評論
圖片
表情
