自定义漂亮的seekBar,内带popwindow及指示器

本文介绍如何自定义一个带有背景图片、Thumb和动画效果的SeekBar,包括点的数量自定义和回调功能。作者分享了实现过程,并提供了源码下载链接。

看到了一个App自定义的SeekBar很有趣,尝试着自己做一个。

不废话,先上图,看是否是你期望的:

增加了背景图片的自定义,增加了Thumb的自定义。当然还有动画效果。会动画滑动到附近的点。具体代码稍后奉上...

啊。。。乱七八糟事儿太多,这几天都没时间整理

这个demo的点可以自定义数量,并增加回调。我就不写原理了...直接把代码传上去...有时间再写...

也可以尝试IndicatorSeekBar这个,GitHub上的,2017年出的,写这篇博客是2016年。那时候还没有....

源码已发布,地址:https://download.csdn.net/download/explorerqp/11373353

首先需要画出seekbar的背景drawable:

 

package com.zkbc.tougu.demo;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;

/**
 * 自定义seekBar背景图
 * Created by Rock Lee on 2016/6/21.
 */
public class MySeekBarDrawable extends Drawable {

    int pointCount;
    int startColor;
    int centerColor;
    int endColor;

    private Paint paint;//画笔(背景)
    LinearGradient shader;

    private Paint paintCircle;//画笔-外圆
    private Paint paintCircleCenter;//画笔-内圆
    RectF rectF;//矩形上下左右的坐标
    int colors[] = new int[3];//三个颜色渐变(shader)
    float positions[] = new float[3];//渐变色的三个点(shader)

    public MySeekBarDrawable(int pointCount, int startColor, int centerColor, int endColor) {
        this.pointCount = pointCount * 2;//增加两点之间的中线
        this.startColor = startColor;
        this.centerColor = centerColor;
        this.endColor = endColor;

        paint = new Paint();
        paintCircle = new Paint();
        paintCircleCenter = new Paint();
        rectF = new RectF();
    }

    @Override
    public void draw(Canvas canvas) {
        final Rect bounds = getBounds();
        // 第1个点
        colors[0] = startColor;
        positions[0] = 0;

        // 第2个点
        colors[1] = centerColor;
        positions[1] = 0.5f;

        // 第3个点
        colors[2] = endColor;
        positions[2] = 1;

        //是否有中间色
        if (centerColor == 0) {
            shader = new LinearGradient(0f, 0f, bounds.width(), bounds.height(), startColor, endColor, Shader.TileMode.MIRROR);
        } else {
            shader = new LinearGradient(0f, 0f, bounds.width(), bounds.height(), colors, positions, Shader.TileMode.MIRROR);
        }
        paint.setShader(shader);
        paint.setStrokeCap(Paint.Cap.ROUND);// 圆角
        paint.setAntiAlias(true); // 消除锯齿

        paintCircle.setShader(shader);
//        paintCircle.setStyle(Paint.Style.STROKE); // 设置空心
//        paintCircle.setStrokeWidth(bounds.height()/2); // 设置笔画的宽度
        paintCircle.setAntiAlias(true); // 消除锯齿

        paintCircleCenter.setAntiAlias(true); // 消除锯齿
        paintCircleCenter.setColor(Color.WHITE);

        float lineHeight = bounds.height()/4.0f;

        rectF.set(0, bounds.centerY() - lineHeight, bounds.width(), bounds.centerY() + lineHeight);
        //绘制圆角矩形
        canvas.drawRoundRect(rectF, lineHeight, lineHeight, paint);

        float section = (float) bounds.width() / pointCount;

        for (int i = 1; i < pointCount; i++) {
            paint.setShader(null);
            paint.setColor(Color.WHITE);
//            paint.setStrokeWidth(1);

            float cx = section * i;//X轴圆心坐标

            if (i % 2 == 0) {
                canvas.drawLine(cx, bounds.centerY() - lineHeight, cx, bounds.centerY() + lineHeight, paint);
            } else {
                canvas.drawCircle(cx, bounds.centerY(), lineHeight*2, paintCircle);
                canvas.drawCircle(cx, bounds.centerY(), lineHeight, paintCircleCenter);
            }
        }
    }

    @Override
    public void setAlpha(int alpha) {
        paint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        paint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return 1 - paint.getAlpha();
    }

    /**
     * MySeekBarDrawable Builder
     */
    public static class Builder {

        /**
         * 分割段数
         */
        int pointCount;

        /**
         * 起始颜色
         */
        int startColor;

        /**
         * 中间色
         */
        int centerColor;

        /**
         * 结束颜色
         */
        int endColor;

        /**
         * Sets the seekBar point count.
         *
         * @returns This Builder
         */
        public Builder setPointCount(int pointCount) {
            this.pointCount = pointCount;
            return this;
        }

        /**
         * Sets the seekBar start color.
         *
         * @param startColor start color in #AARRGGBB format.
         * @returns This Builder
         */
        public Builder setStartColor(int startColor) {
            this.startColor = startColor;
            return this;
        }

        /**
         * Sets the seekBar center color.
         *
         * @param centerColor center color in #AARRGGBB format.
         * @returns This Builder
         */
        public Builder setCenterColor(int centerColor) {
            this.centerColor = centerColor;
            return this;
        }

        /**
         * Sets the seekBar end color.
         *
         * @param endColor end color in #AARRGGBB format.
         * @returns This Builder
         */
        public Builder setEndColor(int endColor) {
            this.endColor = endColor;
            return this;
        }

        /**
         * Creates a new MySeekBarDrawable with the requested parameters
         *
         * @return New MySeekBarDrawableInstance
         */
        public MySeekBarDrawable create() {
            return new MySeekBarDrawable(pointCount, startColor, centerColor, endColor);
        }
    }
}


第二步:自定义seekbar,并将我们的自定义drawable附上:

 

 

 

package com.zkbc.tougu.demo;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.SeekBar;
import android.widget.TextView;

import com.zkbc.tougu.R;

/**
 * 自定义seekBar
 * Created by Rock Lee on 2016/6/21.
 */
public class MyNiceSeekBar extends SeekBar implements SeekBar.OnSeekBarChangeListener {

    private int pointCount;

    private int startColor;

    private int centerColor;

    private int endColor;

    public final int oneLength = 100;//每个隔断的刻度
    int risk;

    TextView centerText;
    Drawable thumbDrawable;
    View thumb;

    MySeekBarDrawable drawable;

    SeekBarInterface barInterface;//参数回调

    SeekBarOnDrawListener onDrawListener;//seekBar绘画监听

    private boolean mPopupStyle;//是否显示pop
    private boolean mThumbStyle;
    private int xOffset;
    private PopupWindow mPopup;
    private TextView mPopupTextView;
    private int mYLocationOffset;

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

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

    public MyNiceSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);

    }

    private void init(Context context, AttributeSet attrs) {

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

        pointCount = mTypedArray.getInteger(R.styleable.MySeekBar_pointCount, 5);
        startColor = mTypedArray.getColor(R.styleable.MySeekBar_startColor, Color.GREEN);
        centerColor = mTypedArray.getColor(R.styleable.MySeekBar_centerColor, 0);
        endColor = mTypedArray.getColor(R.styleable.MySeekBar_endColor, Color.RED);
        mPopupStyle = mTypedArray.getBoolean(R.styleable.MySeekBar_popupStyle, false);
        mThumbStyle = mTypedArray.getBoolean(R.styleable.MySeekBar_popupStyle, false);
        xOffset = (int) mTypedArray.getDimension(R.styleable.MySeekBar_xOffset, 0);
        mYLocationOffset = (int) mTypedArray.getDimension(R.styleable.MySeekBar_yOffset, 0);

        mTypedArray.recycle();

        setMax(pointCount * 2 * oneLength);

        setProgress(3 * oneLength);
        setOnSeekBarChangeListener(this);

        drawable = new MySeekBarDrawable.Builder()
                .setPointCount(pointCount)
                .setStartColor(startColor)
                .setCenterColor(centerColor)
                .setEndColor(endColor).create();
        setProgressDrawable(drawable);
        initThumb((getProgress() / (2 * oneLength) + 1) + "");
        initHintPopup();
    }

    public void initThumb(String risk) {
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        thumb = inflater.inflate(R.layout.seekbar_thumb, null);
        centerText = (TextView) thumb.findViewById(R.id.text1);
        if (mThumbStyle) {
            centerText.setText(risk);
        }
        thumbDrawable = convertViewToDrawable(thumb);
        setThumb(thumbDrawable);
    }

    public static Drawable convertViewToDrawable(View view) {
        view.measure(
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        view.layout(0, 0, view.getMeasuredHeight(), view.getMeasuredHeight());
        view.setDrawingCacheEnabled(true);
        Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache(true));
        Drawable drawable = new BitmapDrawable(null, bitmap);
        view.destroyDrawingCache();
        view.setDrawingCacheEnabled(false);
        return drawable;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
    }

    @Override
    protected synchronized void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (onDrawListener != null) {
            onDrawListener.onDrawListener();
        }
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        //进度改变时调用
        risk = getTargetProgress(seekBar) / (2 * oneLength) + 1;
        String popupText;
        if (barInterface != null) {
            int targetProgress = getTargetProgress(seekBar);
            popupText = barInterface.progressChangeCallBack(this, getProgress(), targetProgress);
            mPopupTextView.setText(popupText != null ? popupText : String.valueOf(targetProgress));
        }
        if (mPopupStyle && mPopup != null) {
            showPopup();
            mPopup.update(this, (int) getXPosition(getProgress(), mPopup.getContentView()), -(this.getHeight() + mPopup.getContentView().getMeasuredHeight() + mYLocationOffset), -1, -1);
        } else {
            hidePopup();
        }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        //进度条开始拖动的时候调用
        showPopup();
        centerText.setText("");
        thumbDrawable = convertViewToDrawable(thumb);
        setThumb(thumbDrawable);
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        //进度条停止拖动的时候调用
        int targetProgress = getTargetProgress(seekBar);
        dodo(seekBar.getProgress(), targetProgress);
        if (mThumbStyle) {
            centerText.setText(risk + "");
        }
        thumbDrawable = convertViewToDrawable(thumb);
        setThumb(thumbDrawable);
    }

    /**
     * 赋值+执行动画
     *
     * @param progressText   当前滑动的点
     * @param targetProgress 自动滑动到目标点
     */
    public void dodo(int progressText, int targetProgress) {
        AnimatorSet animation = new AnimatorSet();

        ObjectAnimator progressAnimation = ObjectAnimator.ofInt(this, "progress",
                progressText, targetProgress);
        progressAnimation.setDuration(300);
        progressAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
        //增加动画监听
        progressAnimation.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                hidePopup();
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                hidePopup();
            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        animation.playTogether(progressAnimation);
        animation.start();
    }

    /**
     * 选中回调
     *
     * @param barInterface 回调监听
     */
    public void setSeekBarCallBack(SeekBarInterface barInterface) {
        this.barInterface = barInterface;
    }

    /**
     * 绘画监听接口
     *
     * @param onDrawListener 绘画监听实现接口
     */
    public void setOnDrawListener(SeekBarOnDrawListener onDrawListener) {
        this.onDrawListener = onDrawListener;
    }

    /**
     * 是否显示pop
     * @param style 是否显示pop
     */
    public void setPopupStyle(boolean style) {
        mPopupStyle = style;
    }

    private void initHintPopup() {
        String popupText = null;

        if (barInterface != null) {
            int targetProgress = getTargetProgress(this);
            popupText = barInterface.progressChangeCallBack(this, getProgress(), targetProgress);
        }

        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        final View undoView = inflater.inflate(R.layout.popup, null);
        mPopupTextView = (TextView) undoView.findViewById(R.id.text);
        mPopupTextView.setText(popupText != null ? popupText : String.valueOf(getProgress()));

        mPopup = new PopupWindow(undoView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, false);
        mPopup.getContentView().measure(0, 0);//获取测量后的popWindow高度

        mPopup.setAnimationStyle(R.style.fade_animation);

    }

    private void showPopup() {
        if (mPopupStyle) {
            mPopup.showAsDropDown(this, (int) getXPosition(getProgress(), mPopup.getContentView()), -(this.getHeight() + mPopup.getContentView().getMeasuredHeight() + mYLocationOffset));
        }
    }

    private void hidePopup() {
        if (mPopup != null && mPopup.isShowing()) {
            mPopup.dismiss();
        }
    }

    /**
     * 获取X坐标
     *
     * @param progress 进度
     * @param v        在seekBar上显示的View
     * @return X
     */
    private float getXPosition(int progress, View v) {
        //seekBar总宽度包含ThumbOffset两边
        float val = (((float) progress * ((float) getWidth())) / (float) getMax());
        int textWidth = v.getMeasuredWidth();
        float textCenter = (textWidth / 2.0f) + xOffset;
        return val - textCenter;
    }

    /**
     * 获取目标进度
     *
     * @return 返回目标进度
     */
    private int getTargetProgress(SeekBar seekBar) {
        int targetProgress;//进度参数回调默认值

        int proNow = seekBar.getProgress();//当前点
        int yu = proNow % oneLength;//取余数
        if ((proNow / oneLength) % 2 == 0) {
            //是否为满进度,满进度回退一格
            targetProgress = proNow == getMax() ? proNow - oneLength : proNow + oneLength - yu;
        } else {
            targetProgress = proNow - yu;
        }
        return targetProgress;
    }

    /**
     * 设置推荐坐标
     *
     * @param recommendPosition 显示在第几个点,从0开始
     * @param ll_recommend
     */
    public void setRecommendPosition(int recommendPosition, View ll_recommend) {
        int progress = oneLength + recommendPosition * 2 * oneLength;
        float x = getXPosition(progress, ll_recommend);
        LinearLayout.LayoutParams pa = (LinearLayout.LayoutParams) ll_recommend.getLayoutParams();
        pa.setMargins((int) x, 0, 0, 0);
        ll_recommend.setLayoutParams(pa);
    }
    public int getRisk() {
        return risk;
    }

    public void setRisk(int risk) {
        this.risk = risk;
    }

    /**
     * 是否显示Thumb数字
     * @param mThumbStyle
     */
    public void setThumbStyle(boolean mThumbStyle) {
        this.mThumbStyle = mThumbStyle;
    }

}

 

使用方式:

 

 

private void initSeekbar() {
        recommend = 3;
        mSeekBar1.initThumb((mSeekBar1.getProgress() / (2 * mSeekBar1.oneLength)) + "");
        mSeekBar1.setSeekBarCallBack(new SeekBarInterface() {
            @Override
            public String progressChangeCallBack(MyNiceSeekBar myNiceSeekBar, int progress, int targetProgress) {
                risk = myNiceSeekBar.getRisk();
                myNiceSeekBar.setRisk(risk);
                
                //返回Pop上需要显示的字符串
                return "节点" + risk;
            }
        });
        mSeekBar1.setOnDrawListener(new SeekBarOnDrawListener() {
            @Override
            public void onDrawListener() {
                mSeekBar1.setRecommendPosition(recommend, ll_recommend);
            }
        });
    }

 

 

 

 

 

 

 

 

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值