自定义View实现环形SeekBar

原文地址: http://blog.csdn.net/ly0904010214/article/details/42805363


由于android系统提供的SeekBar是直线型的。但是有些时候我们需要用到其他形状的SeekBar,那么就需要自定义View来实现。这里只是实现了一个环形的,其他形状的类似。


效果图如下

自定义View实现环形SeekBar_第1张图片



CircleSeekBar文件如下 

[java]  view plain copy print ?
  1. package com.lee.circleseekbar.view;  
  2.   
  3. import com.lee.circleseekbar.R;  
  4.   
  5. import android.content.Context;  
  6. import android.content.res.TypedArray;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.Color;  
  9. import android.graphics.Paint;  
  10. import android.graphics.RectF;  
  11. import android.graphics.drawable.Drawable;  
  12. import android.util.AttributeSet;  
  13. import android.util.Log;  
  14. import android.view.MotionEvent;  
  15. import android.view.View;  
  16.   
  17. /** 
  18.  *  
  19.  * CircleSeekBar 
  20.  *  
  21.  * @author lee 
  22.  *  
  23.  */  
  24.   
  25. public class CircleSeekBar extends View {  
  26.   
  27.     private final boolean DEBUG = true;  
  28.     private final String TAG = "CircleSeekBar";  
  29.       
  30.     private Context mContext = null;  
  31.       
  32.     private Drawable mThumbDrawable = null;  
  33.     private int mThumbHeight = 0;  
  34.     private int mThumbWidth = 0;  
  35.     private int[] mThumbNormal = null;  
  36.     private int[] mThumbPressed = null;  
  37.       
  38.     private int mSeekBarMax = 0;  
  39.     private Paint mSeekBarBackgroundPaint = null;  
  40.     private Paint mSeekbarProgressPaint = null;  
  41.     private RectF mArcRectF = null;  
  42.       
  43.     private boolean mIsShowProgressText = false;  
  44.     private Paint mProgressTextPaint = null;  
  45.     private int mProgressTextSize = 0;  
  46.       
  47.     private int mViewHeight = 0;  
  48.     private int mViewWidth = 0;  
  49.     private int mSeekBarSize = 0;  
  50.     private int mSeekBarRadius = 0;  
  51.     private int mSeekBarCenterX = 0;  
  52.     private int mSeekBarCenterY = 0;  
  53.     private float mThumbLeft = 0;  
  54.     private float mThumbTop = 0;  
  55.       
  56.     private float mSeekBarDegree = 0;  
  57.     private int mCurrentProgress = 0;  
  58.       
  59.     private OnSeekBarChangeListener mOnSeekBarChangeListener = null;  
  60.       
  61.     public interface OnSeekBarChangeListener {  
  62.           
  63.         void onProgressChanged(int progress);  
  64.       
  65.         void onStartTrackingTouch();  
  66.           
  67.         void onStopTrackingTouch();  
  68.     }  
  69.       
  70.     public CircleSeekBar(Context context, AttributeSet attrs, int defStyle) {  
  71.         super(context, attrs, defStyle);  
  72.         mContext = context;  
  73.         initViewAttrs(attrs);  
  74.         mArcRectF = new RectF();  
  75.     }  
  76.       
  77.     public CircleSeekBar(Context context, AttributeSet attrs) {  
  78.         super(context, attrs);  
  79.         mContext = context;  
  80.         initViewAttrs(attrs);  
  81.         mArcRectF = new RectF();  
  82.     }  
  83.       
  84.     public CircleSeekBar(Context context) {  
  85.         super(context);  
  86.         mContext = context;  
  87.         initViewDefault();  
  88.         mArcRectF = new RectF();  
  89.     }  
  90.       
  91.     private void initViewAttrs(AttributeSet attrs){  
  92.         if(DEBUG) Log.d(TAG, "initView");  
  93.         TypedArray localTypedArray = mContext.obtainStyledAttributes(attrs, R.styleable.CircleSeekBar);  
  94.           
  95.         //thumb的属性是使用android:thumb属性进行设置的  
  96.         //返回的Drawable为一个StateListDrawable类型,即可以实现选中效果的drawable list  
  97.         //mThumbNormal和mThumbPressed则是用于设置不同状态的效果,当点击thumb时设置mThumbPressed,否则设置mThumbNormal  
  98.         mThumbDrawable = localTypedArray.getDrawable(R.styleable.CircleSeekBar_android_thumb);  
  99.         mThumbWidth = mThumbDrawable.getIntrinsicWidth();  
  100.         mThumbHeight = mThumbDrawable.getIntrinsicHeight();  
  101.           
  102.         mThumbNormal = new int[]{-android.R.attr.state_focused, -android.R.attr.state_pressed,   
  103.                 -android.R.attr.state_selected, -android.R.attr.state_checked};  
  104.         mThumbPressed = new int[]{android.R.attr.state_focused, android.R.attr.state_pressed,   
  105.                 android.R.attr.state_selected, android.R.attr.state_checked};  
  106.           
  107.         float progressWidth = localTypedArray.getDimension(R.styleable.CircleSeekBar_progress_width, 5);  
  108.         int progressBackgroundColor = localTypedArray.getColor(R.styleable.CircleSeekBar_progress_background, Color.GRAY);  
  109.         int progressFrontColor = localTypedArray.getColor(R.styleable.CircleSeekBar_progress_front, Color.BLUE);  
  110.         mSeekBarMax = localTypedArray.getInteger(R.styleable.CircleSeekBar_progress_max, 100);  
  111.           
  112.         mSeekbarProgressPaint = new Paint();  
  113.         mSeekBarBackgroundPaint = new Paint();  
  114.           
  115.         mSeekbarProgressPaint.setColor(progressFrontColor);  
  116.         mSeekBarBackgroundPaint.setColor(progressBackgroundColor);  
  117.           
  118.         mSeekbarProgressPaint.setAntiAlias(true);  
  119.         mSeekBarBackgroundPaint.setAntiAlias(true);  
  120.           
  121.         mSeekbarProgressPaint.setStyle(Paint.Style.STROKE);  
  122.         mSeekBarBackgroundPaint.setStyle(Paint.Style.STROKE);  
  123.           
  124.         mSeekbarProgressPaint.setStrokeWidth(progressWidth);  
  125.         mSeekBarBackgroundPaint.setStrokeWidth(progressWidth);  
  126.           
  127.         mIsShowProgressText = localTypedArray.getBoolean(R.styleable.CircleSeekBar_show_progress_text, false);  
  128.         int progressTextStroke = (int) localTypedArray.getDimension(R.styleable.CircleSeekBar_progress_text_stroke_width, 5);  
  129.         int progressTextColor = localTypedArray.getColor(R.styleable.CircleSeekBar_progress_text_color, Color.GREEN);  
  130.         mProgressTextSize = (int) localTypedArray.getDimension(R.styleable.CircleSeekBar_progress_text_size, 50);  
  131.           
  132.         mProgressTextPaint = new Paint();  
  133.         mProgressTextPaint.setColor(progressTextColor);  
  134.         mProgressTextPaint.setAntiAlias(true);  
  135.         mProgressTextPaint.setStrokeWidth(progressTextStroke);  
  136.         mProgressTextPaint.setTextSize(mProgressTextSize);  
  137.           
  138.         localTypedArray.recycle();  
  139.     }  
  140.       
  141.     private void initViewDefault(){  
  142.         mThumbDrawable = null;  
  143.         mThumbWidth = 0;  
  144.         mThumbHeight = 0;  
  145.           
  146.         mThumbNormal = new int[]{-android.R.attr.state_focused, -android.R.attr.state_pressed,   
  147.                 -android.R.attr.state_selected, -android.R.attr.state_checked};  
  148.         mThumbPressed = new int[]{android.R.attr.state_focused, android.R.attr.state_pressed,   
  149.                 android.R.attr.state_selected, android.R.attr.state_checked};  
  150.           
  151.         float progressWidth = 5;  
  152.         int progressBackgroundColor = Color.GRAY;  
  153.         int progressFrontColor = Color.BLUE;  
  154.         mSeekBarMax = 100;  
  155.           
  156.         mSeekbarProgressPaint = new Paint();  
  157.         mSeekBarBackgroundPaint = new Paint();  
  158.           
  159.         mSeekbarProgressPaint.setColor(progressFrontColor);  
  160.         mSeekBarBackgroundPaint.setColor(progressBackgroundColor);  
  161.           
  162.         mSeekbarProgressPaint.setAntiAlias(true);  
  163.         mSeekBarBackgroundPaint.setAntiAlias(true);  
  164.           
  165.         mSeekbarProgressPaint.setStyle(Paint.Style.STROKE);  
  166.         mSeekBarBackgroundPaint.setStyle(Paint.Style.STROKE);  
  167.           
  168.         mSeekbarProgressPaint.setStrokeWidth(progressWidth);  
  169.         mSeekBarBackgroundPaint.setStrokeWidth(progressWidth);  
  170.           
  171.         mIsShowProgressText = false;  
  172.         int progressTextStroke = 5;  
  173.         int progressTextColor = Color.GREEN;  
  174.         mProgressTextSize = 50;  
  175.           
  176.         mProgressTextPaint = new Paint();  
  177.         mProgressTextPaint.setColor(progressTextColor);  
  178.         mProgressTextPaint.setAntiAlias(true);  
  179.         mProgressTextPaint.setStrokeWidth(progressTextStroke);  
  180.         mProgressTextPaint.setTextSize(mProgressTextSize);  
  181.     }  
  182.   
  183.     @Override  
  184.     protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  185.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  186.         if(DEBUG) Log.d(TAG, "onMeasure");  
  187.         mViewWidth = getWidth();  
  188.         mViewHeight = getHeight();  
  189.           
  190.         mSeekBarSize = mViewWidth > mViewHeight ? mViewHeight : mViewWidth;  
  191.   
  192.         mSeekBarCenterX = mViewWidth / 2;  
  193.         mSeekBarCenterY = mViewHeight / 2;  
  194.           
  195.         mSeekBarRadius = mSeekBarSize / 2 - mThumbWidth / 2;  
  196.           
  197.         int left = mSeekBarCenterX - mSeekBarRadius;  
  198.         int right = mSeekBarCenterX + mSeekBarRadius;  
  199.         int top = mSeekBarCenterY - mSeekBarRadius;  
  200.         int bottom = mSeekBarCenterY + mSeekBarRadius;  
  201.         mArcRectF.set(left, top, right, bottom);  
  202.           
  203.         // 起始位置,三点钟方向  
  204.         setThumbPosition(Math.toRadians(mSeekBarDegree));  
  205.     }  
  206.       
  207.     @Override  
  208.     protected synchronized void onDraw(Canvas canvas) {  
  209.         canvas.drawCircle(mSeekBarCenterX, mSeekBarCenterY, mSeekBarRadius,  
  210.                 mSeekBarBackgroundPaint);  
  211.         canvas.drawArc(this.mArcRectF, 0.0F, mSeekBarDegree, false, mSeekbarProgressPaint);  
  212.         drawThumbBitmap(canvas);  
  213.         drawProgressText(canvas);  
  214.           
  215.         super.onDraw(canvas);  
  216.     }  
  217.       
  218.     private void drawThumbBitmap(Canvas canvas) {  
  219.         if(null != mThumbDrawable){  
  220.             mThumbDrawable.setBounds((int) mThumbLeft, (int) mThumbTop,  
  221.                     (int) (mThumbLeft + mThumbWidth), (int) (mThumbTop + mThumbHeight));  
  222.             mThumbDrawable.draw(canvas);  
  223.         }  
  224.     }  
  225.       
  226.     private void drawProgressText(Canvas canvas) {  
  227.         if (true == mIsShowProgressText){  
  228.             float textWidth = mProgressTextPaint.measureText("" + mCurrentProgress);  
  229.             canvas.drawText("" + mCurrentProgress, mSeekBarCenterX - textWidth / 2, mSeekBarCenterY  
  230.                     + mProgressTextSize / 2, mProgressTextPaint);  
  231.         }  
  232.     }  
  233.       
  234.     @Override  
  235.     public boolean onTouchEvent(MotionEvent event) {  
  236.         float eventX = event.getX();  
  237.         float eventY = event.getY();  
  238.         switch (event.getAction()) {  
  239.             case MotionEvent.ACTION_DOWN:  
  240.                 if(null != mOnSeekBarChangeListener){  
  241.                     mOnSeekBarChangeListener.onStartTrackingTouch();  
  242.                 }  
  243.                 seekTo(eventX, eventY, false);  
  244.                 break ;  
  245.                   
  246.             case MotionEvent.ACTION_MOVE:  
  247.                 seekTo(eventX, eventY, false);  
  248.                 break ;  
  249.                   
  250.             case MotionEvent.ACTION_UP:  
  251.                 if(null != mOnSeekBarChangeListener){  
  252.                     mOnSeekBarChangeListener.onStopTrackingTouch();  
  253.                 }  
  254.                 seekTo(eventX, eventY, true);  
  255.                 break ;  
  256.         }  
  257.         return true;  
  258.     }  
  259.   
  260.     private void seekTo(float eventX, float eventY, boolean isUp) {  
  261.         if (true == isPointOnThumb(eventX, eventY) && false == isUp) {  
  262.             if(null != mThumbDrawable){  
  263.                 mThumbDrawable.setState(mThumbPressed);  
  264.             }  
  265.             double radian = Math.atan2(eventY - mSeekBarCenterY, eventX - mSeekBarCenterX);  
  266.             /* 
  267.              * 由于atan2返回的值为[-pi,pi] 
  268.              * 因此需要将弧度值转换一下,使得区间为[0,2*pi] 
  269.              */  
  270.             if (radian < 0){  
  271.                 radian = radian + 2*Math.PI;  
  272.             }  
  273.             if(DEBUG) Log.e(TAG, "seekTo radian = " + radian);  
  274.             setThumbPosition(radian);  
  275.               
  276.             mSeekBarDegree = (float) Math.round(Math.toDegrees(radian));  
  277.             mCurrentProgress = (int) (mSeekBarMax * mSeekBarDegree / 360);  
  278.             if(null != mOnSeekBarChangeListener){  
  279.                 mOnSeekBarChangeListener.onProgressChanged(mCurrentProgress);  
  280.             }  
  281.             invalidate();  
  282.         }else{  
  283.             if(null != mThumbDrawable){  
  284.                 mThumbDrawable.setState(mThumbNormal);  
  285.             }  
  286.             invalidate();  
  287.         }  
  288.     }  
  289.   
  290.     private boolean isPointOnThumb(float eventX, float eventY) {  
  291.         boolean result = false;  
  292.         double distance = Math.sqrt(Math.pow(eventX - mSeekBarCenterX, 2)  
  293.                 + Math.pow(eventY - mSeekBarCenterY, 2));  
  294.         if (distance < mSeekBarSize && distance > (mSeekBarSize / 2 - mThumbWidth)){  
  295.             result = true;  
  296.         }  
  297.         return result;  
  298.     }  
  299.       
  300.     private void setThumbPosition(double radian) {  
  301.         if(DEBUG) Log.v(TAG, "setThumbPosition radian = " + radian);  
  302.         double x = mSeekBarCenterX + mSeekBarRadius * Math.cos(radian);  
  303.         double y = mSeekBarCenterY + mSeekBarRadius * Math.sin(radian);  
  304.         mThumbLeft = (float) (x - mThumbWidth / 2);  
  305.         mThumbTop = (float) (y - mThumbHeight / 2);  
  306.     }  
  307.       
  308.     /* 
  309.      * 增加set方法,用于在java代码中调用 
  310.      */  
  311.     public synchronized void setProgress(int progress) {  
  312.         if(DEBUG) Log.v(TAG, "setProgress progress = " + progress);  
  313.         if (progress > mSeekBarMax){  
  314.             progress = mSeekBarMax;  
  315.         }  
  316.         if (progress < 0){  
  317.             progress = 0;  
  318.         }  
  319.         mCurrentProgress = progress;  
  320.         mSeekBarDegree = (progress * 360 / mSeekBarMax);  
  321.         if(DEBUG) Log.d(TAG, "setProgress mSeekBarDegree = " + mSeekBarDegree);  
  322.         setThumbPosition(Math.toRadians(mSeekBarDegree));  
  323.           
  324.         invalidate();  
  325.     }  
  326.       
  327.     public synchronized int getProgress(){  
  328.         return mCurrentProgress;  
  329.     }  
  330.       
  331.     public synchronized void setProgressMax(int max){  
  332.         if(DEBUG) Log.v(TAG, "setProgressMax max = " + max);  
  333.         mSeekBarMax = max;  
  334.     }  
  335.       
  336.     public synchronized int getProgressMax(){  
  337.         return mSeekBarMax;  
  338.     }  
  339.       
  340.     public void setProgressThumb(int thumbId){  
  341.         mThumbDrawable = mContext.getResources().getDrawable(thumbId);  
  342.         mThumbWidth = mThumbDrawable.getIntrinsicWidth();  
  343.         mThumbHeight = mThumbDrawable.getIntrinsicHeight();  
  344.     }  
  345.       
  346.     public void setProgressWidth(int width){  
  347.         if(DEBUG) Log.v(TAG, "setProgressWidth width = " + width);  
  348.         mSeekbarProgressPaint.setStrokeWidth(width);  
  349.         mSeekBarBackgroundPaint.setStrokeWidth(width);  
  350.     }  
  351.       
  352.     public void setProgressBackgroundColor(int color){  
  353.         mSeekBarBackgroundPaint.setColor(color);  
  354.     }  
  355.       
  356.     public void setProgressFrontColor(int color){  
  357.         mSeekbarProgressPaint.setColor(color);  
  358.     }  
  359.       
  360.     public void setProgressTextColor(int color){  
  361.         mProgressTextPaint.setColor(color);  
  362.     }  
  363.       
  364.     public void setProgressTextSize(int size){  
  365.         if(DEBUG) Log.v(TAG, "setProgressTextSize size = " + size);  
  366.         mProgressTextPaint.setTextSize(size);  
  367.     }  
  368.       
  369.     public void setProgressTextStrokeWidth(int width){  
  370.         if(DEBUG) Log.v(TAG, "setProgressTextStrokeWidth width = " + width);  
  371.         mProgressTextPaint.setStrokeWidth(width);  
  372.     }  
  373.       
  374.     public void setIsShowProgressText(boolean isShow){  
  375.         mIsShowProgressText = isShow;  
  376.     }  
  377.       
  378.     /* 
  379.      * 增加SeekBar change的监听 
  380.      */  
  381.       
  382.     public void setOnSeekBarChangeListener(OnSeekBarChangeListener onSeekBarChangeListener){  
  383.         mOnSeekBarChangeListener = onSeekBarChangeListener;  
  384.     }  
  385. }  


xml文件如下

[html]  view plain copy print ?
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:circle_seekbar="http://schemas.android.com/apk/res/com.lee.circleseekbar"  
  3.     xmlns:tools="http://schemas.android.com/tools"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent" >  
  6.   
  7.     <com.lee.circleseekbar.view.CircleSeekBar  
  8.         android:id="@+id/circle_seekbar"  
  9.         android:layout_width="200dp"  
  10.         android:layout_height="200dp"  
  11.         android:layout_centerInParent="true"  
  12.         android:thumb="@drawable/thumb"  
  13.         circle_seekbar:progress_background="@android:color/darker_gray"  
  14.         circle_seekbar:progress_front="@android:color/holo_blue_light"  
  15.         circle_seekbar:progress_max="1000"  
  16.         circle_seekbar:progress_text_color="@android:color/holo_green_light"  
  17.         circle_seekbar:progress_text_size="30dp"  
  18.         circle_seekbar:progress_text_stroke_width="4dp"  
  19.         circle_seekbar:progress_width="2dp"  
  20.         circle_seekbar:show_progress_text="true" />  
  21.   
  22. RelativeLayout>  


attrs属性文件如下

[html]  view plain copy print ?
  1. xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.   
  4.     <declare-styleable name="CircleSeekBar">  
  5.         <attr name="android:thumb" />  
  6.         <attr name="progress_width" format="dimension" />  
  7.         <attr name="progress_background" format="color" />  
  8.         <attr name="progress_front" format="color" />  
  9.         <attr name="progress_max" format="integer" />  
  10.         <attr name="show_progress_text" format="boolean" />  
  11.         <attr name="progress_text_stroke_width" format="dimension" />  
  12.         <attr name="progress_text_color" format="color" />  
  13.         <attr name="progress_text_size" format="dimension" />  
  14.     declare-styleable>  
  15.   
  16. resources>  

activity文件如下

[java]  view plain copy print ?
  1. package com.lee.circleseekbar;  
  2.   
  3. import android.app.Activity;  
  4. import android.graphics.Color;  
  5. import android.os.Bundle;  
  6. import android.util.Log;  
  7. import android.view.LayoutInflater;  
  8. import android.widget.RelativeLayout;  
  9.   
  10. import com.lee.circleseekbar.view.CircleSeekBar;  
  11. import com.lee.circleseekbar.view.CircleSeekBar.OnSeekBarChangeListener;  
  12.   
  13. public class MainActivity extends Activity {  
  14.   
  15.     private final boolean DEBUG = true;  
  16.     private final String TAG = "MainActivity";  
  17.       
  18.     @Override  
  19.     protected void onCreate(Bundle savedInstanceState) {  
  20.         super.onCreate(savedInstanceState);  
  21.         RelativeLayout mainLayout = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.activity_main, null);  
  22.         setContentView(mainLayout);  
  23.           
  24.         CircleSeekBar circleSeekBar = (CircleSeekBar) findViewById(R.id.circle_seekbar);  
  25.         circleSeekBar.setProgress(100);  
  26.         circleSeekBar.setProgressFrontColor(Color.RED);  
  27.         circleSeekBar.setOnSeekBarChangeListener(new CircleSeekBarOnChangeListener());  
  28.       
  29.         CircleSeekBar circleSeekBar2 = new CircleSeekBar(this);  
  30.         circleSeekBar2.setProgress(10);  
  31.         circleSeekBar2.setProgressFrontColor(Color.RED);  
  32.         circleSeekBar2.setProgressThumb(R.drawable.thumb);  
  33.         circleSeekBar2.setOnSeekBarChangeListener(new CircleSeekBarOnChangeListener());  
  34.         RelativeLayout.LayoutParams circleSeekBarParams = new RelativeLayout.LayoutParams(200200);  
  35.         circleSeekBarParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);  
  36.         mainLayout.addView(circleSeekBar2, circleSeekBarParams);  
  37.     }  
  38.   
  39.     private class CircleSeekBarOnChangeListener implements OnSeekBarChangeListener{  
  40.   
  41.         @Override  
  42.         public void onProgressChanged(int progress) {  
  43.             if(DEBUG) Log.d(TAG, "onProgressChanged progress = " + progress);  
  44.         }  
  45.   
  46.         @Override  
  47.         public void onStartTrackingTouch() {  
  48.             if(DEBUG) Log.d(TAG, "onStartTrackingTouch");  
  49.         }  
  50.   
  51.         @Override  
  52.         public void onStopTrackingTouch() {  
  53.             if(DEBUG) Log.d(TAG, "onStopTrackingTouch");  
  54.         }  
  55.           
  56.     }  
  57.       
  58. }  



Demo下载地址

http://download.csdn.net/detail/ly0904010214/8373347



你可能感兴趣的:(自定义View实现环形SeekBar)