1. 程式人生 > >自定義漂亮的seekBar,內帶popwindow及指示器

自定義漂亮的seekBar,內帶popwindow及指示器

看到了一個App自定義的SeekBar很有趣,嘗試著自己做一個。

不廢話,先上圖,看是否是你期望的:


增加了背景圖片的自定義,增加了Thumb的自定義。當然還有動畫效果。會動畫滑動到附近的點。具體程式碼稍後奉上...

啊。。。亂七八糟事兒太多,這幾天都沒時間整理

這個demo的點可以自定義數量,並增加回調。我就不寫原理了...直接把程式碼傳上去...有時間再寫...

首先需要畫出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);
            }
        });
    }