|
package com.electric.chargingpile.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.widget.ProgressBar;
import com.electric.chargingpile.R;
/**
* Created by demon on 2017/5/18.
*/
public class SaundProgressBar extends ProgressBar {
private Drawable m_indicator;
private int m_offset = 5;
private TextPaint m_textPaint;
private Formatter m_formatter;
public SaundProgressBar(Context context) {
this(context, null);
}
public SaundProgressBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SaundProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// create a default progress bar indicator text paint used for drawing
// the
// text on to the canvas
m_textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
m_textPaint.density = getResources().getDisplayMetrics().density;
m_textPaint.setColor(Color.WHITE);
m_textPaint.setTextAlign(Align.CENTER);
m_textPaint.setTextSize(10);
m_textPaint.setFakeBoldText(true);
// get the styleable attributes as defined in the xml
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.SaundProgressBar, defStyle, 0);
if (a != null) {
m_textPaint.setTextSize(a.getDimension(
R.styleable.SaundProgressBar_textSize, 10));
m_textPaint.setColor(a.getColor(
R.styleable.SaundProgressBar_textColor, Color.WHITE));
int alignIndex = (a.getInt(R.styleable.SaundProgressBar_textAlign,
1));
if (alignIndex == 0) {
m_textPaint.setTextAlign(Align.LEFT);
} else if (alignIndex == 1) {
m_textPaint.setTextAlign(Align.CENTER);
} else if (alignIndex == 2) {
m_textPaint.setTextAlign(Align.RIGHT);
}
int textStyle = (a
.getInt(R.styleable.SaundProgressBar_textStyle, 1));
if (textStyle == 0) {
m_textPaint.setTextSkewX(0.0f);
m_textPaint.setFakeBoldText(false);
} else if (textStyle == 1) {
m_textPaint.setTextSkewX(0.0f);
m_textPaint.setFakeBoldText(true);
} else if (textStyle == 2) {
m_textPaint.setTextSkewX(-0.25f);
m_textPaint.setFakeBoldText(false);
}
m_indicator = a
.getDrawable(R.styleable.SaundProgressBar_progressIndicator);
m_offset = (int) a.getDimension(
R.styleable.SaundProgressBar_offset, 0);
a.recycle();
}
}
/**
* Sets the drawable used as a progress indicator
*
* @param indicator
*/
public void setProgressIndicator(Drawable indicator) {
m_indicator = indicator;
}
/**
* The text formatter is used for customizing the presentation of the text
* displayed in the progress indicator. The default text format is X% where
* X is [0,100]. To use the formatter you must provide an object which
* implements the {@linkplain SaundProgressBar.Formatter} interface.
*
* @param formatter
*/
public void setTextFormatter(Formatter formatter) {
m_formatter = formatter;
}
/**
* The additional offset is for tweaking the position of the indicator.
*
* @param offset
*/
public void setOffset(int offset) {
m_offset = offset;
}
/**
* Set the text color
*
* @param color
*/
public void setTextColor(int color) {
m_textPaint.setColor(color);
}
/**
* Set the text size.
*
* @param size
*/
public void setTextSize(float size) {
m_textPaint.setTextSize(size);
}
/**
* Set the text bold.
*
* @param bold
*/
public void setTextBold(boolean bold) {
m_textPaint.setFakeBoldText(true);
}
/**
* Set the alignment of the text.
*
* @param align
*/
public void setTextAlign(Align align) {
m_textPaint.setTextAlign(align);
}
/**
* Set the paint object used to draw the text on to the canvas.
*
* @param paint
*/
public void setPaint(TextPaint paint) {
m_textPaint = paint;
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// if we have an indicator we need to adjust the height of the view to
// accomodate the indicator
if (m_indicator != null) {
final int width = getMeasuredWidth();
final int height = getMeasuredHeight() + getIndicatorHeight();
// make the view the original height + indicator height size
setMeasuredDimension(width, height);
}
}
private int getIndicatorWidth() {
if (m_indicator == null) {
return 0;
}
Rect r = m_indicator.copyBounds();
int width = r.width();
return width;
}
private int getIndicatorHeight() {
if (m_indicator == null) {
return 0;
}
Rect r = m_indicator.copyBounds();
int height = r.height();
return height;
}
@Override
protected synchronized void onDraw(Canvas canvas) {
Drawable progressDrawable = getProgressDrawable();
// If we have an indicator then we'll need to adjust the drawable bounds
// for
// the progress bar and its layers (if the drawable is a layer
// drawable).
// This will ensure the progress bar gets drawn in the correct position
if (m_indicator != null) {
if (progressDrawable != null
&& progressDrawable instanceof LayerDrawable) {
LayerDrawable d = (LayerDrawable) progressDrawable;
for (int i = 0; i < d.getNumberOfLayers(); i++) {
d.getDrawable(i).getBounds().top = getIndicatorHeight();
// thanks to Dave [dave@pds-uk.com] for point out a bug
// which eats up
// a lot of cpu cycles. It turns out the issue was linked to
// calling
// getIntrinsicHeight which proved to be very cpu intensive.
d.getDrawable(i).getBounds().bottom = d.getDrawable(i)
.getBounds().height()
+ getIndicatorHeight();
}
} else if (progressDrawable != null) {
// It's not a layer drawable but we still need to adjust the
// bounds
progressDrawable.getBounds().top = m_indicator
.getIntrinsicHeight();
// thanks to Dave[dave@pds-uk.com] -- see note above for
// explaination.
progressDrawable.getBounds().bottom = progressDrawable
.getBounds().height() + getIndicatorHeight();
}
}
// update the size of the progress bar and overlay
updateProgressBar();
super.onDraw(canvas);
// Draw the indicator to match the far right position of the progress
// bar
if (m_indicator != null) {
canvas.save();
int dx = 0;
// get the position of the progress bar's right end
if (progressDrawable != null
&& progressDrawable instanceof LayerDrawable) {
LayerDrawable d = (LayerDrawable) progressDrawable;
Drawable progressBar = d.findDrawableByLayerId(R.id.progress);
dx = progressBar.getBounds().right;
} else if (progressDrawable != null) {
dx = progressDrawable.getBounds().right;
}
// adjust for any additional offset
dx = dx - getIndicatorWidth() / 2 - m_offset + getPaddingLeft();
// translate the canvas to the position where we should draw the
// indicator
canvas.translate(dx, 0);
m_indicator.draw(canvas);
canvas.drawText(
m_formatter != null ? m_formatter.getText(getProgress())
: Math.round(getScale(getProgress()) * 100.0f)
+ "%", getIndicatorWidth() / 2,
getIndicatorHeight() / 2 + 1, m_textPaint);
// restore canvas to original
canvas.restore();
}
}
@Override
public synchronized void setProgress(int progress) {
super.setProgress(progress);
// the setProgress super will not change the details of the progress bar
// anymore so we need to force an update to redraw the progress bar
invalidate();
}
private float getScale(int progress) {
float scale = getMax() > 0 ? (float) progress / (float) getMax() : 0;
return scale;
}
/**
* Instead of using clipping regions to uncover the progress bar as the
* progress increases we increase the drawable regions for the progress bar
* and pattern overlay. Doing this gives us greater control and allows us to
* show the rounded cap on the progress bar.
*/
private void updateProgressBar() {
Drawable progressDrawable = getProgressDrawable();
if (progressDrawable != null
&& progressDrawable instanceof LayerDrawable) {
LayerDrawable d = (LayerDrawable) progressDrawable;
final float scale = getScale(getProgress());
// get the progress bar and update it's size
Drawable progressBar = d.findDrawableByLayerId(R.id.progress);
final int width = d.getBounds().right - d.getBounds().left;
if (progressBar != null) {
Rect progressBarBounds = progressBar.getBounds();
progressBarBounds.right = progressBarBounds.left
+ (int) (width * scale + 0.5f);
progressBar.setBounds(progressBarBounds);
}
// get the pattern overlay
Drawable patternOverlay = d.findDrawableByLayerId(R.id.pattern);
if (patternOverlay != null) {
if (progressBar != null) {
// we want our pattern overlay to sit inside the bounds of
// our progress bar
Rect patternOverlayBounds = progressBar.copyBounds();
final int left = patternOverlayBounds.left;
final int right = patternOverlayBounds.right;
patternOverlayBounds.left = (left + 1 > right) ? left
: left + 1;
patternOverlayBounds.right = (right > 0) ? right - 1
: right;
patternOverlay.setBounds(patternOverlayBounds);
} else {
// we don't have a progress bar so just treat this like the
// progress bar
Rect patternOverlayBounds = patternOverlay.getBounds();
patternOverlayBounds.right = patternOverlayBounds.left
+ (int) (width * scale + 0.5f);
patternOverlay.setBounds(patternOverlayBounds);
}
}
}
}
/**
* You must implement this interface if you wish to present a custom
* formatted text to be used by the Progress Indicator. The default format
* is X% where X [0,100]
*
* @author jsaund
*/
public interface Formatter {
public String getText(int progress);
}
}
|