自定義漂亮的seekBar,內帶popwindow及指示器
阿新 • • 發佈:2019-02-08
看到了一個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); } }); }