From 3a13eb27e68ef53755aa4b4da24766bf5686103f Mon Sep 17 00:00:00 2001 From: wangdongdong Date: Wed, 19 Apr 2017 15:06:10 +0800 Subject: [PATCH] add progressbar Signed-off-by: wangdongdong --- .../statebutton/sample/MainActivity.java | 14 + sample/src/main/res/layout/activity_main.xml | 65 +++ .../statebutton/CircleTextProgressbar.java | 441 ++++++++++++++++++ .../java/mazouri/statebutton/StateButton.java | 129 ++++- statebutton/src/main/res/values/attrs.xml | 20 + 5 files changed, 656 insertions(+), 13 deletions(-) create mode 100644 statebutton/src/main/java/mazouri/statebutton/CircleTextProgressbar.java diff --git a/sample/src/main/java/com/mazouri/statebutton/sample/MainActivity.java b/sample/src/main/java/com/mazouri/statebutton/sample/MainActivity.java index de18111..12e1ba6 100644 --- a/sample/src/main/java/com/mazouri/statebutton/sample/MainActivity.java +++ b/sample/src/main/java/com/mazouri/statebutton/sample/MainActivity.java @@ -30,6 +30,7 @@ public class MainActivity extends AppCompatActivity { StateButton stateButton13; StateButton stateButton14; StateButton stateButton15; + StateButton stateButton16; private Timer mTimer; private TimerTask mTimerTask; @@ -54,6 +55,7 @@ protected void onCreate(Bundle savedInstanceState) { stateButton13 = (StateButton) findViewById(R.id.state_button13); stateButton14 = (StateButton) findViewById(R.id.state_button14); stateButton15 = (StateButton) findViewById(R.id.state_button15); + stateButton16 = (StateButton) findViewById(R.id.state_button16); setup(); } @@ -149,6 +151,18 @@ public void onClick(View v) { showDisableToast(stateButton15); } }); + stateButton16.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + stateButton16.setState(StateButton.BUTTON_STATES.LOADING); + } + }); + stateButton16.setCountdownProgressListener(new StateButton.OnCountdownListener() { + @Override + public void onProgress(int progress) { + stateButton16.setProgressText(progress + ""); + } + }); } private void changState(StateButton stateButton) { diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index 373c35e..d133d95 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -291,4 +291,69 @@ app:selectedTextColor="#ffffff" app:state="disabled"/> + + + + + + + + + + diff --git a/statebutton/src/main/java/mazouri/statebutton/CircleTextProgressbar.java b/statebutton/src/main/java/mazouri/statebutton/CircleTextProgressbar.java new file mode 100644 index 0000000..71bf86c --- /dev/null +++ b/statebutton/src/main/java/mazouri/statebutton/CircleTextProgressbar.java @@ -0,0 +1,441 @@ +/* + * Copyright 2016 Yan Zhenjie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mazouri.statebutton; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.support.annotation.ColorInt; +import android.util.AttributeSet; +import android.util.Log; + +public class CircleTextProgressbar extends android.support.v7.widget.AppCompatTextView { + + /** + * 外部轮廓的颜色。 + */ + private int outLineColor = Color.BLACK; + + /** + * 外部轮廓的宽度。 + */ + private int outLineWidth = 2; + + /** + * 内部圆的颜色。 + */ + private ColorStateList inCircleColors = ColorStateList.valueOf(Color.TRANSPARENT); + /** + * 中心圆的颜色。 + */ + private int circleColor; + + /** + * 进度条的颜色。 + */ + private int progressLineColor = Color.BLUE; + + /** + * 进度条的宽度。 + */ + private int progressLineWidth = 8; + + /** + * 进度更新时间间隔 + */ + private int progressChangeDuration = 0; + + /** + * 画笔。 + */ + private Paint mPaint = new Paint(); + + /** + * 进度条的矩形区域。 + */ + private RectF mArcRect = new RectF(); + + /** + * 进度。 + */ + private int progress = 100; + + /** + * 最大进度值 + */ + private int maxProgress = 100; + /** + * 进度条类型。 + */ + private ProgressType mProgressType = ProgressType.COUNT_BACK; + /** + * 进度倒计时时间。 + */ + private long timeMillis = 3000; + + /** + * View的显示区域。 + */ + final Rect bounds = new Rect(); + /** + * 进度条通知。 + */ + private OnCountdownProgressListener mCountdownProgressListener; + /** + * Listener what。 + */ + private int listenerWhat = 0; + + public CircleTextProgressbar(Context context) { + this(context, null); + } + + public CircleTextProgressbar(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CircleTextProgressbar(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initialize(context, attrs); + } + + /** + * 初始化。 + * + * @param context 上下文。 + * @param attributeSet 属性。 + */ + private void initialize(Context context, AttributeSet attributeSet) { + mPaint.setAntiAlias(true); + TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.CircleTextProgressbar); + ColorStateList colorStateList = typedArray.getColorStateList(R.styleable.CircleTextProgressbar_inCircleColor); + inCircleColors = colorStateList == null ? ColorStateList.valueOf(Color.TRANSPARENT) : colorStateList; + circleColor = inCircleColors.getColorForState(getDrawableState(), Color.TRANSPARENT); + + progressLineColor = typedArray.getColor(R.styleable.CircleTextProgressbar_progressLineColor, Color.TRANSPARENT); + progressLineWidth = typedArray.getDimensionPixelSize(R.styleable.CircleTextProgressbar_progressLineWidth, 8); + progressChangeDuration = typedArray.getInt(R.styleable.CircleTextProgressbar_progressChangeDuration, 0); + maxProgress = typedArray.getInt(R.styleable.CircleTextProgressbar_maxProgress, 100); + maxProgress ++; //for count_back + + typedArray.recycle(); + } + + /** + * 设置外部轮廓的颜色。 + * + * @param outLineColor 颜色值。 + */ + public void setOutLineColor(@ColorInt int outLineColor) { + this.outLineColor = outLineColor; + invalidate(); + } + + /** + * 设置外部轮廓的颜色。 + * + * @param outLineWidth 颜色值。 + */ + public void setOutLineWidth(@ColorInt int outLineWidth) { + this.outLineWidth = outLineWidth; + invalidate(); + } + + /** + * 设置圆形的填充颜色。 + * + * @param inCircleColor 颜色值。 + */ + public void setInCircleColor(@ColorInt int inCircleColor) { + this.inCircleColors = ColorStateList.valueOf(inCircleColor); + invalidate(); + } + + /** + * 是否需要更新圆的颜色。 + */ + private void validateCircleColor() { + int circleColorTemp = inCircleColors.getColorForState(getDrawableState(), Color.TRANSPARENT); + if (circleColor != circleColorTemp) { + circleColor = circleColorTemp; + invalidate(); + } + } + + /** + * 设置进度条颜色。 + * + * @param progressLineColor 颜色值。 + */ + public void setProgressColor(@ColorInt int progressLineColor) { + this.progressLineColor = progressLineColor; + invalidate(); + } + + /** + * 设置进度条线的宽度。 + * + * @param progressLineWidth 宽度值。 + */ + public void setProgressLineWidth(int progressLineWidth) { + this.progressLineWidth = progressLineWidth; + invalidate(); + } + + public void setProgressChangeDuration(int progressChangeDuration) { + this.progressChangeDuration = progressChangeDuration; + } + + public void setMaxProgress(int maxProgress) { + this.maxProgress = ++maxProgress; + } + + /** + * 设置进度。 + * + * @param progress 进度。 + */ + public void setProgress(int progress) { + this.progress = validateProgress(progress); + invalidate(); + } + + /** + * 验证进度。 + * + * @param progress 你要验证的进度值。 + * @return 返回真正的进度值。 + */ + private int validateProgress(int progress) { + if (progress > maxProgress) { + progress = maxProgress; + } else if (progress < 0) + progress = 0; + return progress; + } + + /** + * 拿到此时的进度。 + * + * @return 进度值,最大100,最小0。 + */ + public int getProgress() { + return progress; + } + + /** + * 设置倒计时总时间。 + * + * @param timeMillis 毫秒。 + */ + public void setTimeMillis(long timeMillis) { + this.timeMillis = timeMillis; + invalidate(); + } + + /** + * 拿到进度条计时时间。 + * + * @return 毫秒。 + */ + public long getTimeMillis() { + return this.timeMillis; + } + + /** + * 设置进度条类型。 + * + * @param progressType {@link ProgressType}. + */ + public void setProgressType(ProgressType progressType) { + this.mProgressType = progressType; + resetProgress(); + invalidate(); + } + + /** + * 重置进度。 + */ + private void resetProgress() { + switch (mProgressType) { + case COUNT: + progress = 0; + break; + case COUNT_BACK: + progress = maxProgress; + break; + } + } + + /** + * 拿到进度条类型。 + * + * @return + */ + public ProgressType getProgressType() { + return mProgressType; + } + + /** + * 设置进度监听。 + * + * @param mCountdownProgressListener 监听器。 + */ + public void setCountdownProgressListener(int what, OnCountdownProgressListener mCountdownProgressListener) { + this.listenerWhat = what; + this.mCountdownProgressListener = mCountdownProgressListener; + } + + /** + * 开始。 + */ + public void start() { + stop(); + post(progressChangeTask); + } + + /** + * 重新开始。 + */ + public void reStart() { + resetProgress(); + start(); + } + + /** + * 停止。 + */ + public void stop() { + removeCallbacks(progressChangeTask); + } + + @Override + protected void onDraw(Canvas canvas) { + //获取view的边界 + getDrawingRect(bounds); + + int size = bounds.height() > bounds.width() ? bounds.width() : bounds.height(); + float outerRadius = size / 2; + + //画内部背景 + int circleColor = inCircleColors.getColorForState(getDrawableState(), 0); + mPaint.setStyle(Paint.Style.FILL); + mPaint.setColor(circleColor); + canvas.drawCircle(bounds.centerX(), bounds.centerY(), outerRadius - outLineWidth, mPaint); + + //画边框圆 +// mPaint.setStyle(Paint.Style.STROKE); +// mPaint.setStrokeWidth(outLineWidth); +// mPaint.setColor(outLineColor); +// canvas.drawCircle(bounds.centerX(), bounds.centerY(), outerRadius - outLineWidth / 2, mPaint); + + //画字 + Paint paint = getPaint(); + paint.setColor(getCurrentTextColor()); + paint.setAntiAlias(true); + paint.setTextAlign(Paint.Align.CENTER); + float textY = bounds.centerY() - (paint.descent() + paint.ascent()) / 2; + canvas.drawText(getText().toString(), bounds.centerX(), textY, paint); + + //画进度条 + mPaint.setColor(progressLineColor); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeWidth(progressLineWidth); + mPaint.setStrokeCap(Paint.Cap.ROUND); + int deleteWidth = progressLineWidth + outLineWidth; + mArcRect.set(bounds.left + deleteWidth / 2, bounds.top + deleteWidth / 2, bounds.right - deleteWidth / 2, bounds.bottom - deleteWidth / 2); + + Log.d("progress", "progress : " + progress + ", maxProgress : " + maxProgress); + canvas.drawArc(mArcRect, 0, 360 * progress / maxProgress, false, mPaint); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); +// int lineWidth = 4 * (outLineWidth + progressLineWidth); + int width = getMeasuredWidth(); + int height = getMeasuredHeight(); +// int size = (width > height ? width : height) + lineWidth; +// Log.d("ttttt", "width" + width + ", height : " + height + ", size : " + size); +// setMeasuredDimension(size, size); + setMeasuredDimension(width, height); + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + validateCircleColor(); + } + + /** + * 进度更新task。 + */ + private Runnable progressChangeTask = new Runnable() { + @Override + public void run() { + removeCallbacks(this); + switch (mProgressType) { + case COUNT: + progress += 1; + break; + case COUNT_BACK: + progress -= 1; + break; + } + if (progress >= 0 && progress <= maxProgress) { + if (mCountdownProgressListener != null) { + mCountdownProgressListener.onProgress(listenerWhat, progress); + } + invalidate(); + postDelayed(progressChangeTask, progressChangeDuration == 0 ? timeMillis / 100 : progressChangeDuration); + } else + progress = validateProgress(progress); + } + }; + + /** + * 进度条类型。 + */ + public enum ProgressType { + /** + * 顺数进度条,从0-100; + */ + COUNT, + + /** + * 倒数进度条,从100-0; + */ + COUNT_BACK; + } + + /** + * 进度监听。 + */ + public interface OnCountdownProgressListener { + + /** + * 进度通知。 + * + * @param progress 进度值。 + */ + void onProgress(int what, int progress); + } +} diff --git a/statebutton/src/main/java/mazouri/statebutton/StateButton.java b/statebutton/src/main/java/mazouri/statebutton/StateButton.java index 6830410..cfe25fd 100644 --- a/statebutton/src/main/java/mazouri/statebutton/StateButton.java +++ b/statebutton/src/main/java/mazouri/statebutton/StateButton.java @@ -6,8 +6,10 @@ import android.graphics.Typeface; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; import android.util.TypedValue; import android.view.ViewGroup; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; @@ -19,6 +21,8 @@ public class StateButton extends RelativeLayout { public static final int ID_ICON =0x1000001; + public static final int ID_PROGRESS =0x1000002; + public static final int ID_FRAME = 0x1000003; private TextView mTextView; private ImageView mIconView; @@ -28,6 +32,7 @@ public class StateButton extends RelativeLayout { private String disabledText = ""; private String enabledText = ""; private String selectedText = ""; + private String loadingText = ""; /** * Texts color for the different states @@ -35,6 +40,7 @@ public class StateButton extends RelativeLayout { private int disabledTextColor; private int enabledTextColor; private int selectedTextColor; + private int loadingTextColor; /** * Backgrounds for different states @@ -42,6 +48,7 @@ public class StateButton extends RelativeLayout { private int disabledBackground; private int enabledBackground; private int selectedBackground; + private int loadingBackground; /** * Icons color for different states @@ -57,6 +64,25 @@ public class StateButton extends RelativeLayout { private boolean enabledIconVisibility; private boolean selectedIconVisibility; + /** + * 进度条的颜色。 + */ + private int progressLineColor = Color.BLUE; + + private int progressTextColor; + + /** + * 进度条的宽度。 + */ + private int progressLineWidth = 8; + + /** + * 进度更新时间间隔 + */ + private int progressChangeDuration = 0; + + private int maxProgress = 100; + /** * Previous state of button */ @@ -66,6 +92,8 @@ public class StateButton extends RelativeLayout { * Current state of the button */ private BUTTON_STATES currentState; + private CircleTextProgressbar mCircleTextProgressbar; + private FrameLayout mFrameLayout; public StateButton(Context context) { this(context, null); @@ -85,16 +113,19 @@ public StateButton(Context context, AttributeSet attrs, int defStyleAttr) { disabledText = a.getString(R.styleable.StateButton_disabledText); enabledText = a.getString(R.styleable.StateButton_enabledText); selectedText = a.getString(R.styleable.StateButton_selectedText); + loadingText = a.getString(R.styleable.StateButton_loadingText); // Texts color disabledTextColor = a.getColor(R.styleable.StateButton_disabledTextColor, Color.WHITE); enabledTextColor = a.getColor(R.styleable.StateButton_enabledTextColor, Color.WHITE); selectedTextColor = a.getColor(R.styleable.StateButton_selectedTextColor, Color.WHITE); + loadingTextColor = a.getColor(R.styleable.StateButton_loadingTextColor, Color.WHITE); // Backgrounds disabledBackground = a.getResourceId(R.styleable.StateButton_disabledBackground, 0); enabledBackground = a.getResourceId(R.styleable.StateButton_enabledBackground, 0); selectedBackground = a.getResourceId(R.styleable.StateButton_selectedBackground, 0); + loadingBackground = a.getResourceId(R.styleable.StateButton_loadingBackground, 0); // Icons disabledIcon = a.getResourceId(R.styleable.StateButton_disabledIcon, 0); @@ -113,29 +144,50 @@ public StateButton(Context context, AttributeSet attrs, int defStyleAttr) { int iconHeight = a.getDimensionPixelSize(R.styleable.StateButton_iconHeight, dip2px(30f)); int drawablePadding = a.getDimensionPixelSize(R.styleable.StateButton_drawablePadding, dip2px(10f)); + progressLineColor = a.getColor(R.styleable.StateButton_barProgressLineColor, Color.TRANSPARENT); + progressTextColor = a.getColor(R.styleable.StateButton_barProgressTextColor, Color.WHITE); + progressLineWidth = a.getDimensionPixelSize(R.styleable.StateButton_barProgressLineWidth, 8); + progressChangeDuration = a.getInt(R.styleable.StateButton_barProgressChangeDuration, 0); + maxProgress = a.getInt(R.styleable.StateButton_barMaxProgress, 100); + a.recycle(); oldState = currentState; -// LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); -// View viewRoot = inflater.inflate(R.layout.statebutton_layout, this, true); -// mIconView = (ImageView) viewRoot.findViewById(R.id.icon); -// mTextView = (TextView) viewRoot.findViewById(R.id.text_view); - mIconView = new ImageView(context, attrs); LayoutParams lpIcon = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - lpIcon.topMargin = iconTopMargin; lpIcon.width = iconWidth; lpIcon.height = iconHeight; - lpIcon.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE); mIconView.setScaleType(ImageView.ScaleType.FIT_CENTER); mIconView.setId(ID_ICON); - addView(mIconView, lpIcon); +// addView(mIconView, lpIcon); + + mCircleTextProgressbar = new CircleTextProgressbar(context, attrs); + LayoutParams lpProgressbar = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + lpProgressbar.width = iconWidth; + lpProgressbar.height = iconHeight; + mCircleTextProgressbar.setId(ID_PROGRESS); + mCircleTextProgressbar.setProgressColor(progressLineColor); + mCircleTextProgressbar.setProgressLineWidth(progressLineWidth); + mCircleTextProgressbar.setProgressChangeDuration(progressChangeDuration); + mCircleTextProgressbar.setMaxProgress(maxProgress); + mCircleTextProgressbar.setProgressType(CircleTextProgressbar.ProgressType.COUNT_BACK); + mCircleTextProgressbar.setVisibility(GONE); + + mFrameLayout = new FrameLayout(context, attrs); + LayoutParams lpFrame = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + lpFrame.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE); + lpFrame.topMargin = iconTopMargin; + mFrameLayout.addView(mIconView, lpIcon); + mFrameLayout.addView(mCircleTextProgressbar, lpProgressbar); + mFrameLayout.setId(ID_FRAME); + addView(mFrameLayout, lpFrame); mTextView = new TextView(context, attrs); LayoutParams lpText = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); lpText.topMargin = drawablePadding; - lpText.addRule(RelativeLayout.BELOW, ID_ICON); + lpText.addRule(RelativeLayout.BELOW, ID_FRAME); lpText.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE); mTextView.setDuplicateParentStateEnabled(true); @@ -188,6 +240,14 @@ private void setup() { attrWrapper.iconVisibility = selectedIconVisibility ? VISIBLE : INVISIBLE; attrWrapper.clickable = true; break; + case 3: //loading + attrWrapper.text = loadingText; + attrWrapper.textColor = loadingTextColor; + attrWrapper.background = loadingBackground; + attrWrapper.icon = 0; + attrWrapper.iconVisibility = GONE; + attrWrapper.clickable = false; + break; } if (attrWrapper.background != 0) { @@ -195,10 +255,10 @@ private void setup() { } if (attrWrapper.icon != 0) { - mIconView.setVisibility(VISIBLE); + mFrameLayout.setVisibility(VISIBLE); mIconView.setImageResource(attrWrapper.icon); } else { - mIconView.setVisibility(GONE); + mFrameLayout.setVisibility(GONE); } if (!TextUtils.isEmpty(attrWrapper.text)) { @@ -365,7 +425,49 @@ public int dip2px(float dipValue) { public void setState(BUTTON_STATES state) { this.oldState = this.currentState; this.currentState = state; - setup(); + if (state == BUTTON_STATES.LOADING) { + mCircleTextProgressbar.setVisibility(VISIBLE); + mIconView.setVisibility(GONE); + mCircleTextProgressbar.reStart(); + } else { + mCircleTextProgressbar.setVisibility(GONE); + mIconView.setVisibility(VISIBLE); + setup(); + } + } + + public void resetStateFromLoading() { + setState(oldState); + } + + public void setProgressText(String progressText) { + mCircleTextProgressbar.setText(progressText); + mCircleTextProgressbar.setTextColor(progressTextColor); + } + + public void setCountdownProgressListener(final OnCountdownListener onCountdownListener) { + mCircleTextProgressbar.setCountdownProgressListener(0, new CircleTextProgressbar.OnCountdownProgressListener() { + @Override + public void onProgress(int what, int progress) { + onCountdownListener.onProgress(progress); + if (progress == 0) { + resetStateFromLoading(); + } + } + }); + } + + /** + * 进度监听。 + */ + public interface OnCountdownListener { + + /** + * 进度通知。 + * + * @param progress 进度值。 + */ + void onProgress(int progress); } /** @@ -384,7 +486,8 @@ public enum BUTTON_STATES { DISABLED(0), ENABLED(1), - SELECTED(2); + SELECTED(2), + LOADING(3); private final int value; diff --git a/statebutton/src/main/res/values/attrs.xml b/statebutton/src/main/res/values/attrs.xml index 987257b..e47f173 100644 --- a/statebutton/src/main/res/values/attrs.xml +++ b/statebutton/src/main/res/values/attrs.xml @@ -6,14 +6,17 @@ + + + @@ -23,11 +26,20 @@ + + + + + + + + + @@ -40,4 +52,12 @@ + + + + + + + + \ No newline at end of file