1. 程式人生 > >打造你自己的圓形或圓環形progressBar——圓形SeekBar演變圓形progressbar

打造你自己的圓形或圓環形progressBar——圓形SeekBar演變圓形progressbar

簡介

上篇部落格給大家分析了圓形SeekBar的案例,不瞭解的朋友請看上篇文章。這裡在圓形SeekBar的基礎上給大家演變出圓形的progressBar和圓環形的ProgressBar。先看效果圖:


 

分析

上篇文章分析了圓環seekBar的實現,那如何將Seekbar變成progressbar呢?直接的思路就是新增一個對外的方法可以直接設定SeekBar的progress,然後定時的跟新progress就可以實現progressbar的效果了,是不是so easy!!!

那如何變成圓形的progressBar呢?只要提供一個方法設定不再繪製圓環中間小圓就可以了,是不是更加的so easy了。

程式碼實現

首先看自定義view的程式碼MCircleSeekBar:

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;
	}

}
大家在看一下MainActivity的程式碼變化:
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的程式碼就不再貼出了!請大家自己下載原始碼!

如果你覺得有用就關注我吧,在這裡將不定期的發表原創文章!如果你覺得有用就留個言,點個贊吧!

下面是我微信公眾號:如果你喜歡就掃掃看吧!


原始碼下載