可拖動的圓形進度條

效果圖如下:



1,通過繼承View,重寫onDraw(Canvas canvas)方法,實現界面進度顯示

2,重寫onTouchEvent(MotionEvent event)方法,監聽拖動事件

3,在監聽位置改變時,計算角度,重新調用onDraw()方法進行繪製


關鍵代碼如下:

<span style="font-size:18px;">public class MusicProgressBar extends View {
	/**
	 * 畫筆對象的引用
	 */
	private Paint paint;

	/**
	 * 圓環的顏色
	 */
	private int roundColor;

	/**
	 * 圓環進度的顏色
	 */
	private int roundProgressColor;

	/**
	 * 圓環的寬度
	 */
	private float roundWidth;

	/**
	 * 最大進度
	 */
	private int max;

	/**
	 * 當前進度
	 */
	private int progress;

	/**
	 * 中間進度百分比的字符串的顏色
	 */
	private int textColor;

	/**
	 * 中間進度百分比的字符串的字體
	 */
	private float textSize;

	/**
	 * 點的半徑
	 */
	private float pointRadius;
	
	/**
	 * 空心點的寬度
	 */
	private float pointWidth;
	
    private Drawable mThumb, mThumbPress;

	public MusicProgressBar(Context context) {
		this(context, null);
	}

	public MusicProgressBar(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public MusicProgressBar(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

		paint = new Paint();

		TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressBar);

		//獲取自定義屬性和默認值
		roundColor = mTypedArray.getColor(R.styleable.RoundProgressBar_roundColor, Color.RED);
		roundProgressColor = mTypedArray.getColor(R.styleable.RoundProgressBar_roundProgressColor, Color.GREEN);
		roundWidth = mTypedArray.getDimension(R.styleable.RoundProgressBar_roundWidth, 3);
		textColor = mTypedArray.getColor(R.styleable.RoundProgressBar_textColor, Color.GREEN);  
        textSize = mTypedArray.getDimension(R.styleable.RoundProgressBar_textSize, 15);  
		max = mTypedArray.getInteger(R.styleable.RoundProgressBar_imageMax, 100);
		
		pointRadius = mTypedArray.getDimension(R.styleable.RoundProgressBar_pointRadius, 3);  
		pointWidth = mTypedArray.getDimension(R.styleable.RoundProgressBar_pointWidth, 2);  

		mTypedArray.recycle();
		
		// 加載拖動圖標
        mThumb = getResources().getDrawable(R.drawable.btn_lamp_plate_nor);// 圓點圖片
        int thumbHalfheight = mThumb.getIntrinsicHeight() / 2;
        int thumbHalfWidth = mThumb.getIntrinsicWidth() / 2;
        mThumb.setBounds(-thumbHalfWidth, -thumbHalfheight, thumbHalfWidth, thumbHalfheight);
        
        mThumbPress = getResources().getDrawable(R.drawable.btn_lamp_plate_down);// 圓點圖片
        thumbHalfheight = mThumbPress.getIntrinsicHeight() / 2;
        thumbHalfWidth = mThumbPress.getIntrinsicWidth() / 2;
        mThumbPress.setBounds(-thumbHalfWidth, -thumbHalfheight, thumbHalfWidth, thumbHalfheight);
        paddingOuterThumb = thumbHalfheight;
	}

	@Override
	public void onDraw(Canvas canvas) {
		/**
		 * 畫最外層的大圓環
		 */
		paint.setColor(roundColor); //設置圓環的顏色
		paint.setStyle(Paint.Style.STROKE); //設置空心
		paint.setStrokeWidth(roundWidth); //設置圓環的寬度
		paint.setAntiAlias(true);  //消除鋸齒 
		canvas.drawCircle(centerX, centerY, radius, paint); //畫出圓環

		/**
		 * 畫文字
		 */
		paint.setStrokeWidth(0);   
        paint.setColor(textColor);  
        paint.setTextSize(textSize);  
        // paint.setTypeface(Typeface.DEFAULT); //設置字體  
        String textTime = getTimeText(progress);
        float textWidth = paint.measureText(textTime);   //測量字體寬度,我們需要根據字體的寬度設置在圓環中間  
          
        canvas.drawText(textTime, centerX - textWidth / 2, centerY + textSize/2, paint);

		/**
		 * 畫圓弧 ,畫圓環的進度
		 */
		paint.setStrokeWidth(roundWidth); //設置圓環的寬度
		paint.setColor(roundProgressColor);  //設置進度的顏色
		RectF oval = new RectF(centerX - radius, centerY - radius, centerX + radius, centerY + radius);  //用於定義的圓弧的形狀和大小的界限

		paint.setStyle(Paint.Style.STROKE);
		canvas.drawArc(oval, 270, 360 * progress / max, false, paint);  //根據進度畫圓弧
		
		// 畫圓上的兩個點
		paint.setStrokeWidth(pointWidth);
		PointF startPoint = ChartUtil.calcArcEndPointXY(centerX, centerY, radius, 0, 270); 
		canvas.drawCircle(startPoint.x, startPoint.y, pointRadius, paint);
		
		//paint.setStyle(Paint.Style.FILL_AND_STROKE);
		PointF progressPoint = ChartUtil.calcArcEndPointXY(centerX, centerY, radius, 360 * progress / max, 270); 
		//canvas.drawCircle(progressPoint.x, progressPoint.y, pointRadius, paint);
		// 畫Thumb
        canvas.save();
        canvas.translate(progressPoint.x, progressPoint.y);
        if (downOnArc) {
			mThumbPress.draw(canvas);
		} else {
			mThumb.draw(canvas);
		}
        canvas.restore();
	} 

	private boolean downOnArc = false;
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		int action = event.getAction();
		int x = (int) event.getX();
		int y = (int) event.getY();
		switch (action) {
		case MotionEvent.ACTION_DOWN:
			if (isTouchArc(x, y)) {
				downOnArc = true;
				updateArc(x, y);
				return true;
			}
			break;
		case MotionEvent.ACTION_MOVE:
			if (downOnArc) {
				updateArc(x, y);
				return true;
			}
			break;
		case MotionEvent.ACTION_UP:
			downOnArc = false;
			invalidate();
			if (changeListener != null) {
				changeListener.onProgressChangeEnd(max, progress);
			}
			break;
		}
		return super.onTouchEvent(event);
	}
	
	 private int centerX, centerY;
	 private int radius;
	 private int paddingOuterThumb;
	
	@Override
	protected void onSizeChanged(int width, int height, int oldw, int oldh) {
		centerX = width / 2;
        centerY = height / 2;
        int minCenter = Math.min(centerX, centerY);
        
		radius = (int) (minCenter - roundWidth/2 - paddingOuterThumb); //圓環的半徑
		minValidateTouchArcRadius = (int) (radius - paddingOuterThumb*1.5f);
		maxValidateTouchArcRadius = (int) (radius + paddingOuterThumb*1.5f);
		super.onSizeChanged(width, height, oldw, oldh);
	}

	// 根據點的位置,更新進度
	private void updateArc(int x, int y) {
		int cx = x - getWidth() / 2;
	    int cy = y - getHeight() / 2;
	    // 計算角度,得出(-1->1)之間的數據,等同於(-180°->180°)
	    double angle = Math.atan2(cy, cx)/Math.PI;
	    // 將角度轉換成(0->2)之間的值,然後加上90°的偏移量
	    angle = ((2 + angle)%2 + (90/180f))%2;
	    // 用(0->2)之間的角度值乘以總進度,等於當前進度 
	    progress = (int) (angle * max/2);
	    if (changeListener != null) {
			changeListener.onProgressChange(max, progress);
		}
		invalidate();
	}

	private int minValidateTouchArcRadius; // 最小有效點擊半徑
	private int maxValidateTouchArcRadius; // 最大有效點擊半徑
	// 判斷是否按在圓邊上
	private boolean isTouchArc(int x, int y) {
		double d = getTouchRadius(x, y);
		if (d >= minValidateTouchArcRadius && d <= maxValidateTouchArcRadius) {
			return true;
		}
		return false;
	}
	
	// 計算某點到圓點的距離
	private double getTouchRadius(int x, int y) {
		int cx = x - getWidth() / 2;
		int cy = y - getHeight() / 2;
		return Math.hypot(cx, cy);
	}

	private String getTimeText(int progress) {
		int minute = progress / 60;
		int second = progress % 60;
		String result = (minute < 10 ? "0" : "") + minute + ":" + (second < 10 ? "0" : "") + second;
		return result;
	}

	public synchronized int getMax() {
		return max;
	}

	/**
	 * 設置進度的最大值
	 * @param max
	 */
	public synchronized void setMax(int max) {
		if(max < 0){
			throw new IllegalArgumentException("max not less than 0");
		}
		this.max = max;
	}

	/**
	 * 獲取進度.需要同步
	 * @return
	 */
	public synchronized int getProgress() {
		return progress;
	}

	/**
	 * 設置進度,此爲線程安全控件,由於考慮多線的問題,需要同步
	 * 刷新界面調用postInvalidate()能在非UI線程刷新
	 * @param progress
	 */
	public synchronized void setProgress(int progress) {
		if(progress < 0){
			throw new IllegalArgumentException("progress not less than 0");
		}
		if(progress > max){
			progress = max;
		}
		if(progress <= max){
			this.progress = progress;
			postInvalidate();
		}

	}

	public int getCricleColor() {
		return roundColor;
	}

	public void setCricleColor(int cricleColor) {
		this.roundColor = cricleColor;
	}

	public int getCricleProgressColor() {
		return roundProgressColor;
	}

	public void setCricleProgressColor(int cricleProgressColor) {
		this.roundProgressColor = cricleProgressColor;
	}

	public float getRoundWidth() {
		return roundWidth;
	}

	public void setRoundWidth(float roundWidth) {
		this.roundWidth = roundWidth;
	}
	
	public static class ChartUtil {
	    
	    /**
	     * 依圓心座標,半徑,扇形角度,計算出扇形終射線與圓弧交叉點的xy座標  
	     * @param cirX
	     * @param cirY
	     * @param radius
	     * @param cirAngle
	     * @return
	     */
	    public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, float cirAngle){  
	    	float posX = 0.0f;  
	        float posY = 0.0f;  
	        //將角度轉換爲弧度        
	        float arcAngle = (float) (Math.PI * cirAngle / 180.0);  
	        if (cirAngle < 90)  
	        {  
	            posX = cirX + (float)(Math.cos(arcAngle)) * radius;  
	            posY = cirY + (float)(Math.sin(arcAngle)) * radius;  
	        }  
	        else if (cirAngle == 90)  
	        {  
	            posX = cirX;  
	            posY = cirY + radius;  
	        }  
	        else if (cirAngle > 90 && cirAngle < 180)  
	        {  
	            arcAngle = (float) (Math.PI * (180 - cirAngle) / 180.0);  
	            posX = cirX - (float)(Math.cos(arcAngle)) * radius;  
	            posY = cirY + (float)(Math.sin(arcAngle)) * radius;  
	        }  
	        else if (cirAngle == 180)  
	        {  
	            posX = cirX - radius;  
	            posY = cirY;  
	        }  
	        else if (cirAngle > 180 && cirAngle < 270)  
	        {  
	            arcAngle = (float) (Math.PI * (cirAngle - 180) / 180.0);  
	            posX = cirX - (float)(Math.cos(arcAngle)) * radius;  
	            posY = cirY - (float)(Math.sin(arcAngle)) * radius;  
	        }  
	        else if (cirAngle == 270)  
	        {  
	            posX = cirX;  
	            posY = cirY - radius;  
	        }  
	        else 
	        {  
	            arcAngle = (float) (Math.PI * (360 - cirAngle) / 180.0);  
	            posX = cirX + (float)(Math.cos(arcAngle)) * radius;  
	            posY = cirY - (float)(Math.sin(arcAngle)) * radius;  
	        }  
	        return new PointF(posX, posY);        
	    }  
	    
	    public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, float cirAngle, float orginAngle){
	    	cirAngle = (orginAngle + cirAngle) % 360;
	    	return calcArcEndPointXY(cirX, cirY, radius, cirAngle);
	    }
	}
	
	private OnProgressChangeListener changeListener;

	public void setChangeListener(OnProgressChangeListener changeListener) {
		this.changeListener = changeListener;
	}

	public interface OnProgressChangeListener {
		void onProgressChange(int duration, int progress);

		void onProgressChangeEnd(int duration, int progress);
	}
}</span>


下載地址:http://download.csdn.net/detail/u010134293/9196313


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章