打造你自己的圓形或圓環形progressBar——圓形SeekBar演變圓形progressbar
阿新 • • 發佈:2019-02-08
簡介
上篇部落格給大家分析了圓形SeekBar的案例,不瞭解的朋友請看上篇文章。這裡在圓形SeekBar的基礎上給大家演變出圓形的progressBar和圓環形的ProgressBar。先看效果圖:
分析
上篇文章分析了圓環seekBar的實現,那如何將Seekbar變成progressbar呢?直接的思路就是新增一個對外的方法可以直接設定SeekBar的progress,然後定時的跟新progress就可以實現progressbar的效果了,是不是so easy!!!
那如何變成圓形的progressBar呢?只要提供一個方法設定不再繪製圓環中間小圓就可以了,是不是更加的so easy了。
程式碼實現
首先看自定義view的程式碼MCircleSeekBar:
大家在看一下MainActivity的程式碼變化:package com.lzg.circleseekbar.widget; /** * @author lzg * 2016/8/15 */ import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import com.lzg.circleseekbar.R; /** * The Class CircularSeekBar. */ public class MCircleSeekBar extends View { /** The context */ private Context mContext; /** seekbar變化監聽 */ private OnSeekChangeListener mListener; /** 畫圓環的paint */ private Paint circleColor; /** 畫內圓的paint */ private Paint innerColor; /** 圓環的Paint,其實是畫弧形 */ private Paint circleRing; /** 移動之後的角度 */ private int angle = 0; /** 12點位置的開始角度 */ private int startAngle = 270; /** 圓環寬度 */ private int barWidth = 30; /** view的寬 */ private int width; /** view的高 */ private int height; /** seekbar總值 */ private int maxProgress = 100; /** 當前的百分值 */ private int progress; /** 百分值 */ private int progressPercent; /** 內圓半徑 */ private float innerRadius; /** 外圓半徑 */ private float outerRadius; /** 圓心X座標 */ private float cx; /** 圓心Y座標 */ private float cy; /** 畫圓圖層左邊邊距 */ private float left; /** 畫圓圖層右邊邊距 */ private float right; /** T畫圓圖層頂端邊距 */ private float top; /** 畫圓圖層底邊邊距 */ private float bottom; /** 背景圖左邊邊距 */ private float bgLeft; /** 背景圖右邊邊距 */ private float bgRight; /** 背景圖頂端邊距 */ private float bgTop; /** 背景圖底邊邊距 */ private float bgBottom; /** progressMark X座標 */ private float dx; /** progressMark Y座標 */ private float dy; /** 12點鐘位置的X座標 */ private float startPointX; /** 12點鐘位置的Y座標 */ private float startPointY; /** * 標記的seekbar當前位置的X座標,預設定值為12點位置 */ private float markPointX; /** * T標記的seekbar當前位置的Y座標,預設定值為12點位置 */ private float markPointY; /** * 進度調整指數 */ private float adjustmentFactor = 100; /** 正常狀態下的進度條點 */ private Bitmap progressMark; /** 手指按下後的進度條點 */ private Bitmap progressMarkPressed; /** * 中間背景圖 */ private Bitmap progressBg; /** 是否手指按下的標誌位 */ private boolean IS_PRESSED = false; /** * 繪製圓中間的背景圖的Paint */ private Paint paint = null; private Canvas canvas = new Canvas(); /** 畫圓圖層. */ private RectF rect = new RectF(); /** 背景圖層 */ RectF rectBg = new RectF(); /** 是否顯示progressBar */ private boolean showProgressBar = true; /** * 是否顯示內圓,不顯示就做成了圓形progressBar,顯示就是圓環形progressBar */ private boolean isRingMode = true; /** * 是否可以move */ private boolean canMove = true; { mListener = new OnSeekChangeListener() { @Override public void onProgressChange(MCircleSeekBar view, int newProgress) { } }; /* * Paint.ANTI_ALIAS_FLAG為抗鋸齒 */ paint = new Paint(Paint.ANTI_ALIAS_FLAG); circleColor = new Paint(); innerColor = new Paint(); circleRing = new Paint(); /* * 設定外圓的顏色為藍色 */ circleColor.setColor(Color.GRAY);// Set default background color to Gray /* * 設定內圓的顏色 */ innerColor.setColor(Color.parseColor("#313131")); /* * 設定圓環的顏色 */ circleRing.setColor(Color.parseColor("#ff33b5e5")); /* * Paint.ANTI_ALIAS_FLAG為抗鋸齒 */ circleColor.setAntiAlias(true); innerColor.setAntiAlias(true); circleRing.setAntiAlias(true); circleColor.setStrokeWidth(5); innerColor.setStrokeWidth(5); circleRing.setStrokeWidth(5); circleRing.setStyle(Paint.Style.FILL); } /** * MCircleSeekBar的構造方法. * * @param context * @param attrs * @param defStyle */ public MCircleSeekBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mContext = context; initDrawable(); } /** * MCircleSeekBar的構造方法. * * @param context * @param attrs */ public MCircleSeekBar(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; initDrawable(); } /** * MCircleSeekBar的構造方法. * * @param context */ public MCircleSeekBar(Context context) { super(context); mContext = context; initDrawable(); } /** * 初始化圖片 */ public void initDrawable() { progressMark = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.seek_bar); progressMarkPressed = BitmapFactory.decodeResource( mContext.getResources(), R.drawable.seek_bar1); progressBg = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.open_perecent_bg); } /* * 重寫view的計算方法 * * @see android.view.View#onMeasure(int, int) */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = getWidth(); // Get View Width height = getHeight();// Get View Height int size = (width > height) ? height : width; // Choose the smaller cx = width / 2; // Center X for circle cy = height / 2; // Center Y for circle outerRadius = size / 2 * 80 / 100; // Radius of the outer circle innerRadius = outerRadius - barWidth; // Radius of the inner circle left = cx - outerRadius; // Calculate left bound of our rect right = cx + outerRadius;// Calculate right bound of our rect top = cy - outerRadius;// Calculate top bound of our rect bottom = cy + outerRadius;// Calculate bottom bound of our rect bgLeft = cx - innerRadius; bgRight = cx + innerRadius; bgTop = cy - innerRadius; bgBottom = cy + innerRadius; startPointX = getXByProgress(progress); startPointY = getYByProgress(progress); markPointX = startPointX;// Initial locatino of the marker X coordinate markPointY = startPointY;// Initial locatino of the marker Y coordinate rect.set(left, top, right, bottom); // assign size to rect rectBg.set(bgLeft, bgTop, bgRight, bgBottom); } /** * */ @Override protected void onDraw(Canvas canvas) { /* * 畫外圓 */ canvas.drawCircle(cx, cy, outerRadius, circleColor); /* * 畫圓弧 */ canvas.drawArc(rect, startAngle, angle, true, circleRing); if (isRingMode) { /* * 畫內圓 */ canvas.drawCircle(cx, cy, innerRadius, innerColor); /* * 畫seek點 */ canvas.drawBitmap(progressBg, null, rectBg, paint); } /* * 判斷是否顯示seekBar圓點。 */ if (showProgressBar) { /* * 獲取seek點X座標 */ dx = getXFromAngle(); /* * 獲取seek點Y座標 */ dy = getYFromAngle(); /* * 繪製seek點的位置 */ drawMarkerAtProgress(canvas); } super.onDraw(canvas); } /** * 畫progressMark(seek)在圓環上的位置. * * @param canvas */ public void drawMarkerAtProgress(Canvas canvas) { if (IS_PRESSED) { canvas.drawBitmap(progressMarkPressed, dx, dy, null); } else { canvas.drawBitmap(progressMark, dx, dy, null); } } /** * 獲取seek點X座標 * * @return */ public float getXFromAngle() { int size1 = progressMark.getWidth(); int size2 = progressMarkPressed.getWidth(); int adjust = (size1 > size2) ? size1 : size2; float x = markPointX - (adjust / 2); return x; } /** * 獲取seek點Y座標 * * @return */ public float getYFromAngle() { int size1 = progressMark.getHeight(); int size2 = progressMarkPressed.getHeight(); int adjust = (size1 > size2) ? size1 : size2; float y = markPointY - (adjust / 2); return y; } /** * 獲取圓弧角度 * * @return the angle */ public int getAngle() { return angle; } /** * 設定圓弧的角度 * * @param angle */ public void setAngle(int angle) { this.angle = angle; float donePercent = (((float) this.angle) / 360) * 100; float progress = (donePercent / 100) * getMaxProgress(); setProgressPercent(Math.round(donePercent)); if (IS_PRESSED) { setProgress(Math.round(progress)); } } /** * 設定seekbar變化的監聽 * * @param listener */ public void setSeekBarChangeListener(OnSeekChangeListener listener) { mListener = listener; } /** * 獲取seekbar變化的監聽 * * @return the seek bar change listener */ public OnSeekChangeListener getSeekBarChangeListener() { return mListener; } /** * 獲得bar寬度 * * @return the bar width */ public int getBarWidth() { return barWidth; } /** * 設定bar寬度 * * @param barWidth */ public void setBarWidth(int barWidth) { this.barWidth = barWidth; } /** * 定義seekbar變化監聽介面 * * @see OnSeekChangeEvent */ public interface OnSeekChangeListener { /** * On progress change. * * @param view * @param newProgress */ public void onProgressChange(MCircleSeekBar view, int newProgress); } /** * Gets the max progress. * * @return the max progress */ public int getMaxProgress() { return maxProgress; } /** * * @param maxProgress * */ public void setMaxProgress(int maxProgress) { this.maxProgress = maxProgress; } /** * * @return the progress */ public int getProgress() { return progress; } /** * * @param progress * */ public void setProgress(int progress) { this.progress = progress; if (!IS_PRESSED) { int newPercent = (this.progress * 100) / this.maxProgress; int newAngle = (newPercent * 360) / 100; this.setAngle(newAngle); this.setProgressPercent(newPercent); this.angle = newAngle; } invalidate(); mListener.onProgressChange(this, this.getProgress()); } /** * * @return */ public int getProgressPercent() { return progressPercent; } /** * * @param progressPercent */ public void setProgressPercent(int progressPercent) { this.progressPercent = progressPercent; } /** * * @param color */ public void setRingBackgroundColor(int color) { circleRing.setColor(color); } /** * * @param color */ public void setBackGroundColor(int color) { innerColor.setColor(color); } /** * * @param color */ public void setProgressColor(int color) { circleRing.setColor(color); } /** * 重寫onTouch方法 */ @Override public boolean onTouchEvent(MotionEvent event) { if (canMove) {// 是否可以手動move float x = event.getX(); float y = event.getY(); boolean up = false; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: moved(x, y, up); break; case MotionEvent.ACTION_MOVE: moved(x, y, up); break; case MotionEvent.ACTION_UP: up = true; moved(x, y, up); break; } } return true; } /** * seek點移動方法 * * @param x * the x * @param y * the y * @param up * the up */ private void moved(float x, float y, boolean up) { float distance = (float) Math.sqrt(Math.pow((x - cx), 2) + Math.pow((y - cy), 2)); if (distance < outerRadius + adjustmentFactor && distance > innerRadius - adjustmentFactor && !up) { IS_PRESSED = true; /* * 根據三角函式整切定理計算得到X.Y的座標 */ markPointX = (float) (cx + outerRadius * Math.cos(Math.atan2(x - cx, cy - y) - (Math.PI / 2))); markPointY = (float) (cy + outerRadius * Math.sin(Math.atan2(x - cx, cy - y) - (Math.PI / 2))); float degrees = (float) ((float) ((Math.toDegrees(Math.atan2( x - cx, cy - y)) + 360.0)) % 360.0); if (degrees < 0) { degrees += 2 * Math.PI; } setAngle(Math.round(degrees)); invalidate(); } else { IS_PRESSED = false; invalidate(); } } public float getXByProgress(int progress) { float x = 0; float angle = (float) (2 * progress * Math.PI / 100); x = (float) (cx + outerRadius * Math.cos(angle - Math.PI / 2)); return x; } public float getYByProgress(int progress) { float y = 0; float angle = (float) (2 * progress * Math.PI / 100); y = (float) (cy + outerRadius * Math.sin(angle - Math.PI / 2)); return y; } public void setMarkPointXY(int progress) { this.progress = progress; } /** * * @return */ public float getAdjustmentFactor() { return adjustmentFactor; } /** * * @param adjustmentFactor */ public void setAdjustmentFactor(float adjustmentFactor) { this.adjustmentFactor = adjustmentFactor; } public boolean isShowProgressBar() { return showProgressBar; } /** * 是否顯示ProgressBar * * @param showProgressBar */ public void setShowProgressBar(boolean showProgressBar) { this.showProgressBar = showProgressBar; } public boolean isRingMode() { return isRingMode; } /** * 設定是否圓環模式或圓模式 * * @param showInCircle */ public void setRingMode(boolean isRingMode) { this.isRingMode = isRingMode; } public boolean isCanMove() { return canMove; } /** * 是否可以通過手指移動 * * @param canMove */ public void setCanMove(boolean canMove) { this.canMove = canMove; } }
package com.lzg.circleseekbar; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.widget.TextView; import com.lzg.circleseekbar.widget.MCircleSeekBar; import com.lzg.circleseekbar.widget.MCircleSeekBar.OnSeekChangeListener; /** * * @author lzg * */ public class MainActivity extends BaseActivity { private TextView tvPerencetValue; private MCircleSeekBar mCircleSeekBar; private int progress; private Handler handler = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvPerencetValue = getViewById(R.id.tv_perencet_set_perencet); mCircleSeekBar = getViewById(R.id.m_circleSeekBar_set_perencet); /* * 下面三行程式碼請大家自己嘗試值不同時的效果,第一行是控制圓環效果和圓效果的切換 */ mCircleSeekBar.setRingMode(true); mCircleSeekBar.setCanMove(false); mCircleSeekBar.setShowProgressBar(false); progress = 0; mCircleSeekBar.setSeekBarChangeListener(new OnSeekChangeListener() { public void onProgressChange(MCircleSeekBar view, int newProgress) { tvPerencetValue.setText(Integer.toString(view.getProgress())); } }); handler = new Handler() { @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); switch (msg.what) { case 1: mCircleSeekBar.setProgress(progress); break; default: break; } } }; new Thread() { public void run() { while (true) { if (progress == 100) progress = 0; Message msg = Message.obtain(); msg.what = 1; handler.sendMessage(msg); progress = progress + 1; try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }.start(); } }
剩下的佈局程式碼和BaseActiviy的程式碼就不再貼出了!請大家自己下載原始碼!
如果你覺得有用就關注我吧,在這裡將不定期的發表原創文章!如果你覺得有用就留個言,點個贊吧!
下面是我微信公眾號:如果你喜歡就掃掃看吧!