自定义View实现环形SeekBar

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


效果图如下



CircleSeekBar文件如下 

package com.lee.circleseekbar.view;

import com.lee.circleseekbar.R;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

/**
 * 
 * CircleSeekBar
 * 
 * @author lee
 * 
 */

public class CircleSeekBar extends View {

    private final boolean DEBUG = true;
    private final String TAG = "CircleSeekBar";
    
    private Context mContext = null;
    
    private Drawable mThumbDrawable = null;
    private int mThumbHeight = 0;
    private int mThumbWidth = 0;
    private int[] mThumbNormal = null;
    private int[] mThumbPressed = null;
    
    private int mSeekBarMax = 0;
    private Paint mSeekBarBackgroundPaint = null;
    private Paint mSeekbarProgressPaint = null;
    private RectF mArcRectF = null;
    
    private boolean mIsShowProgressText = false;
    private Paint mProgressTextPaint = null;
    private int mProgressTextSize = 0;
    
    private int mViewHeight = 0;
    private int mViewWidth = 0;
    private int mSeekBarSize = 0;
    private int mSeekBarRadius = 0;
    private int mSeekBarCenterX = 0;
    private int mSeekBarCenterY = 0;
    private float mThumbLeft = 0;
    private float mThumbTop = 0;
    
    private float mSeekBarDegree = 0;
    private int mCurrentProgress = 0;
    
    private OnSeekBarChangeListener mOnSeekBarChangeListener = null;
    
    public interface OnSeekBarChangeListener {
        
        void onProgressChanged(int progress);
    
        void onStartTrackingTouch();
        
        void onStopTrackingTouch();
    }
    
    public CircleSeekBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        initViewAttrs(attrs);
        mArcRectF = new RectF();
    }
    
    public CircleSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        initViewAttrs(attrs);
        mArcRectF = new RectF();
    }
    
    public CircleSeekBar(Context context) {
        super(context);
        mContext = context;
        initViewDefault();
        mArcRectF = new RectF();
    }
    
    private void initViewAttrs(AttributeSet attrs){
        if(DEBUG) Log.d(TAG, "initView");
        TypedArray localTypedArray = mContext.obtainStyledAttributes(attrs, R.styleable.CircleSeekBar);
        
        //thumb的属性是使用android:thumb属性进行设置的
        //返回的Drawable为一个StateListDrawable类型,即可以实现选中效果的drawable list
        //mThumbNormal和mThumbPressed则是用于设置不同状态的效果,当点击thumb时设置mThumbPressed,否则设置mThumbNormal
        mThumbDrawable = localTypedArray.getDrawable(R.styleable.CircleSeekBar_android_thumb);
        mThumbWidth = mThumbDrawable.getIntrinsicWidth();
        mThumbHeight = mThumbDrawable.getIntrinsicHeight();
        
        mThumbNormal = new int[]{-android.R.attr.state_focused, -android.R.attr.state_pressed, 
                -android.R.attr.state_selected, -android.R.attr.state_checked};
        mThumbPressed = new int[]{android.R.attr.state_focused, android.R.attr.state_pressed, 
                android.R.attr.state_selected, android.R.attr.state_checked};
        
        float progressWidth = localTypedArray.getDimension(R.styleable.CircleSeekBar_progress_width, 5);
        int progressBackgroundColor = localTypedArray.getColor(R.styleable.CircleSeekBar_progress_background, Color.GRAY);
        int progressFrontColor = localTypedArray.getColor(R.styleable.CircleSeekBar_progress_front, Color.BLUE);
        mSeekBarMax = localTypedArray.getInteger(R.styleable.CircleSeekBar_progress_max, 100);
        
        mSeekbarProgressPaint = new Paint();
        mSeekBarBackgroundPaint = new Paint();
        
        mSeekbarProgressPaint.setColor(progressFrontColor);
        mSeekBarBackgroundPaint.setColor(progressBackgroundColor);
        
        mSeekbarProgressPaint.setAntiAlias(true);
        mSeekBarBackgroundPaint.setAntiAlias(true);
        
        mSeekbarProgressPaint.setStyle(Paint.Style.STROKE);
        mSeekBarBackgroundPaint.setStyle(Paint.Style.STROKE);
        
        mSeekbarProgressPaint.setStrokeWidth(progressWidth);
        mSeekBarBackgroundPaint.setStrokeWidth(progressWidth);
        
        mIsShowProgressText = localTypedArray.getBoolean(R.styleable.CircleSeekBar_show_progress_text, false);
        int progressTextStroke = (int) localTypedArray.getDimension(R.styleable.CircleSeekBar_progress_text_stroke_width, 5);
        int progressTextColor = localTypedArray.getColor(R.styleable.CircleSeekBar_progress_text_color, Color.GREEN);
        mProgressTextSize = (int) localTypedArray.getDimension(R.styleable.CircleSeekBar_progress_text_size, 50);
        
        mProgressTextPaint = new Paint();
        mProgressTextPaint.setColor(progressTextColor);
        mProgressTextPaint.setAntiAlias(true);
        mProgressTextPaint.setStrokeWidth(progressTextStroke);
        mProgressTextPaint.setTextSize(mProgressTextSize);
        
        localTypedArray.recycle();
    }
    
    private void initViewDefault(){
        mThumbDrawable = null;
        mThumbWidth = 0;
        mThumbHeight = 0;
        
        mThumbNormal = new int[]{-android.R.attr.state_focused, -android.R.attr.state_pressed, 
                -android.R.attr.state_selected, -android.R.attr.state_checked};
        mThumbPressed = new int[]{android.R.attr.state_focused, android.R.attr.state_pressed, 
                android.R.attr.state_selected, android.R.attr.state_checked};
        
        float progressWidth = 5;
        int progressBackgroundColor = Color.GRAY;
        int progressFrontColor = Color.BLUE;
        mSeekBarMax = 100;
        
        mSeekbarProgressPaint = new Paint();
        mSeekBarBackgroundPaint = new Paint();
        
        mSeekbarProgressPaint.setColor(progressFrontColor);
        mSeekBarBackgroundPaint.setColor(progressBackgroundColor);
        
        mSeekbarProgressPaint.setAntiAlias(true);
        mSeekBarBackgroundPaint.setAntiAlias(true);
        
        mSeekbarProgressPaint.setStyle(Paint.Style.STROKE);
        mSeekBarBackgroundPaint.setStyle(Paint.Style.STROKE);
        
        mSeekbarProgressPaint.setStrokeWidth(progressWidth);
        mSeekBarBackgroundPaint.setStrokeWidth(progressWidth);
        
        mIsShowProgressText = false;
        int progressTextStroke = 5;
        int progressTextColor = Color.GREEN;
        mProgressTextSize = 50;
        
        mProgressTextPaint = new Paint();
        mProgressTextPaint.setColor(progressTextColor);
        mProgressTextPaint.setAntiAlias(true);
        mProgressTextPaint.setStrokeWidth(progressTextStroke);
        mProgressTextPaint.setTextSize(mProgressTextSize);
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if(DEBUG) Log.d(TAG, "onMeasure");
        mViewWidth = getWidth();
        mViewHeight = getHeight();
        
        mSeekBarSize = mViewWidth > mViewHeight ? mViewHeight : mViewWidth;

        mSeekBarCenterX = mViewWidth / 2;
        mSeekBarCenterY = mViewHeight / 2;
        
        mSeekBarRadius = mSeekBarSize / 2 - mThumbWidth / 2;
        
        int left = mSeekBarCenterX - mSeekBarRadius;
        int right = mSeekBarCenterX + mSeekBarRadius;
        int top = mSeekBarCenterY - mSeekBarRadius;
        int bottom = mSeekBarCenterY + mSeekBarRadius;
        mArcRectF.set(left, top, right, bottom);
        
        // 起始位置,三点钟方向
        setThumbPosition(Math.toRadians(mSeekBarDegree));
    }
    
    @Override
    protected synchronized void onDraw(Canvas canvas) {
        canvas.drawCircle(mSeekBarCenterX, mSeekBarCenterY, mSeekBarRadius,
                mSeekBarBackgroundPaint);
        canvas.drawArc(this.mArcRectF, 0.0F, mSeekBarDegree, false, mSeekbarProgressPaint);
        drawThumbBitmap(canvas);
        drawProgressText(canvas);
        
        super.onDraw(canvas);
    }
    
    private void drawThumbBitmap(Canvas canvas) {
        if(null != mThumbDrawable){
            mThumbDrawable.setBounds((int) mThumbLeft, (int) mThumbTop,
                    (int) (mThumbLeft + mThumbWidth), (int) (mThumbTop + mThumbHeight));
            mThumbDrawable.draw(canvas);
        }
    }
    
    private void drawProgressText(Canvas canvas) {
        if (true == mIsShowProgressText){
            float textWidth = mProgressTextPaint.measureText("" + mCurrentProgress);
            canvas.drawText("" + mCurrentProgress, mSeekBarCenterX - textWidth / 2, mSeekBarCenterY
                    + mProgressTextSize / 2, mProgressTextPaint);
        }
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float eventX = event.getX();
        float eventY = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if(null != mOnSeekBarChangeListener){
                    mOnSeekBarChangeListener.onStartTrackingTouch();
                }
                seekTo(eventX, eventY, false);
                break ;
                
            case MotionEvent.ACTION_MOVE:
                seekTo(eventX, eventY, false);
                break ;
                
            case MotionEvent.ACTION_UP:
                if(null != mOnSeekBarChangeListener){
                    mOnSeekBarChangeListener.onStopTrackingTouch();
                }
                seekTo(eventX, eventY, true);
                break ;
        }
        return true;
    }

    private void seekTo(float eventX, float eventY, boolean isUp) {
        if (true == isPointOnThumb(eventX, eventY) && false == isUp) {
            if(null != mThumbDrawable){
                mThumbDrawable.setState(mThumbPressed);
            }
            double radian = Math.atan2(eventY - mSeekBarCenterY, eventX - mSeekBarCenterX);
            /*
             * 由于atan2返回的值为[-pi,pi]
             * 因此需要将弧度值转换一下,使得区间为[0,2*pi]
             */
            if (radian < 0){
                radian = radian + 2*Math.PI;
            }
            if(DEBUG) Log.e(TAG, "seekTo radian = " + radian);
            setThumbPosition(radian);
            
            mSeekBarDegree = (float) Math.round(Math.toDegrees(radian));
            mCurrentProgress = (int) (mSeekBarMax * mSeekBarDegree / 360);
            if(null != mOnSeekBarChangeListener){
                mOnSeekBarChangeListener.onProgressChanged(mCurrentProgress);
            }
            invalidate();
        }else{
            if(null != mThumbDrawable){
                mThumbDrawable.setState(mThumbNormal);
            }
            invalidate();
        }
    }

    private boolean isPointOnThumb(float eventX, float eventY) {
        boolean result = false;
        double distance = Math.sqrt(Math.pow(eventX - mSeekBarCenterX, 2)
                + Math.pow(eventY - mSeekBarCenterY, 2));
        if (distance < mSeekBarSize && distance > (mSeekBarSize / 2 - mThumbWidth)){
            result = true;
        }
        return result;
    }
    
    private void setThumbPosition(double radian) {
        if(DEBUG) Log.v(TAG, "setThumbPosition radian = " + radian);
        double x = mSeekBarCenterX + mSeekBarRadius * Math.cos(radian);
        double y = mSeekBarCenterY + mSeekBarRadius * Math.sin(radian);
        mThumbLeft = (float) (x - mThumbWidth / 2);
        mThumbTop = (float) (y - mThumbHeight / 2);
    }
    
    /*
     * 增加set方法,用于在java代码中调用
     */
    public synchronized void setProgress(int progress) {
        if(DEBUG) Log.v(TAG, "setProgress progress = " + progress);
        if (progress > mSeekBarMax){
            progress = mSeekBarMax;
        }
        if (progress < 0){
            progress = 0;
        }
        mCurrentProgress = progress;
        mSeekBarDegree = (progress * 360 / mSeekBarMax);
        if(DEBUG) Log.d(TAG, "setProgress mSeekBarDegree = " + mSeekBarDegree);
        setThumbPosition(Math.toRadians(mSeekBarDegree));
        
        invalidate();
    }
    
    public synchronized int getProgress(){
        return mCurrentProgress;
    }
    
    public synchronized void setProgressMax(int max){
        if(DEBUG) Log.v(TAG, "setProgressMax max = " + max);
        mSeekBarMax = max;
    }
    
    public synchronized int getProgressMax(){
        return mSeekBarMax;
    }
    
    public void setProgressThumb(int thumbId){
        mThumbDrawable = mContext.getResources().getDrawable(thumbId);
        mThumbWidth = mThumbDrawable.getIntrinsicWidth();
        mThumbHeight = mThumbDrawable.getIntrinsicHeight();
    }
    
    public void setProgressWidth(int width){
        if(DEBUG) Log.v(TAG, "setProgressWidth width = " + width);
        mSeekbarProgressPaint.setStrokeWidth(width);
        mSeekBarBackgroundPaint.setStrokeWidth(width);
    }
    
    public void setProgressBackgroundColor(int color){
        mSeekBarBackgroundPaint.setColor(color);
    }
    
    public void setProgressFrontColor(int color){
        mSeekbarProgressPaint.setColor(color);
    }
    
    public void setProgressTextColor(int color){
        mProgressTextPaint.setColor(color);
    }
    
    public void setProgressTextSize(int size){
        if(DEBUG) Log.v(TAG, "setProgressTextSize size = " + size);
        mProgressTextPaint.setTextSize(size);
    }
    
    public void setProgressTextStrokeWidth(int width){
        if(DEBUG) Log.v(TAG, "setProgressTextStrokeWidth width = " + width);
        mProgressTextPaint.setStrokeWidth(width);
    }
    
    public void setIsShowProgressText(boolean isShow){
        mIsShowProgressText = isShow;
    }
    
    /*
     * 增加SeekBar change的监听
     */
    
    public void setOnSeekBarChangeListener(OnSeekBarChangeListener onSeekBarChangeListener){
        mOnSeekBarChangeListener = onSeekBarChangeListener;
    }
}



xml文件如下

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:circle_seekbar="http://schemas.android.com/apk/res/com.lee.circleseekbar"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.lee.circleseekbar.view.CircleSeekBar
        android:id="@+id/circle_seekbar"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:thumb="@drawable/thumb"
        circle_seekbar:progress_background="@android:color/darker_gray"
        circle_seekbar:progress_front="@android:color/holo_blue_light"
        circle_seekbar:progress_max="1000"
        circle_seekbar:progress_text_color="@android:color/holo_green_light"
        circle_seekbar:progress_text_size="30dp"
        circle_seekbar:progress_text_stroke_width="4dp"
        circle_seekbar:progress_width="2dp"
        circle_seekbar:show_progress_text="true" />

</RelativeLayout>


attrs属性文件如下

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="CircleSeekBar">
        <attr name="android:thumb" />
        <attr name="progress_width" format="dimension" />
        <attr name="progress_background" format="color" />
        <attr name="progress_front" format="color" />
        <attr name="progress_max" format="integer" />
        <attr name="show_progress_text" format="boolean" />
        <attr name="progress_text_stroke_width" format="dimension" />
        <attr name="progress_text_color" format="color" />
        <attr name="progress_text_size" format="dimension" />
    </declare-styleable>

</resources>

activity文件如下

package com.lee.circleseekbar;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.RelativeLayout;

import com.lee.circleseekbar.view.CircleSeekBar;
import com.lee.circleseekbar.view.CircleSeekBar.OnSeekBarChangeListener;

public class MainActivity extends Activity {

    private final boolean DEBUG = true;
    private final String TAG = "MainActivity";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        RelativeLayout mainLayout = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.activity_main, null);
        setContentView(mainLayout);
        
        CircleSeekBar circleSeekBar = (CircleSeekBar) findViewById(R.id.circle_seekbar);
        circleSeekBar.setProgress(100);
        circleSeekBar.setProgressFrontColor(Color.RED);
        circleSeekBar.setOnSeekBarChangeListener(new CircleSeekBarOnChangeListener());
    
        CircleSeekBar circleSeekBar2 = new CircleSeekBar(this);
        circleSeekBar2.setProgress(10);
        circleSeekBar2.setProgressFrontColor(Color.RED);
        circleSeekBar2.setProgressThumb(R.drawable.thumb);
        circleSeekBar2.setOnSeekBarChangeListener(new CircleSeekBarOnChangeListener());
        RelativeLayout.LayoutParams circleSeekBarParams = new RelativeLayout.LayoutParams(200, 200);
        circleSeekBarParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        mainLayout.addView(circleSeekBar2, circleSeekBarParams);
    }

    private class CircleSeekBarOnChangeListener implements OnSeekBarChangeListener{

        @Override
        public void onProgressChanged(int progress) {
            if(DEBUG) Log.d(TAG, "onProgressChanged progress = " + progress);
        }

        @Override
        public void onStartTrackingTouch() {
            if(DEBUG) Log.d(TAG, "onStartTrackingTouch");
        }

        @Override
        public void onStopTrackingTouch() {
            if(DEBUG) Log.d(TAG, "onStopTrackingTouch");
        }
        
    }
    
}



Demo下载地址

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



  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值