用途說(shuō)明:
這是一個(gè)自定義的圓環(huán)圖像怒详,支持動(dòng)畫展示卖毁,可以自定義圓環(huán)的顏色和占比毫目,主要用以展示一些數(shù)據(jù)占比方面展示的android圓環(huán)奸腺。
圓環(huán)實(shí)現(xiàn)思路:
android的自定義圓環(huán)實(shí)現(xiàn)有很多種方法侥衬,這里只介紹我實(shí)現(xiàn)的思路诗祸。主要思路是先畫一個(gè)大圓,然后再畫一個(gè)與大圓同圓心的小圓轴总,然后小圓的顏色可以設(shè)置為背景色直颅,這樣看上去就是一個(gè)圓環(huán)了。
實(shí)現(xiàn)效果:
使用方法:
1.布局文件中直接使用自定義圓環(huán)(RingView)怀樟,控件的寬和高需要固定的尺寸
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical">
<com.wuden.zxingproject.widgets.RingView
android:id="@+id/rvRingView"
android:layout_gravity="center"
android:layout_width="300dp"
android:layout_height="300dp" />
</LinearLayout>
2.在對(duì)應(yīng)的activty中調(diào)用一些方法來(lái)實(shí)現(xiàn)你的需求即可
public class TestActivity extends AppCompatActivity {
@Bind(R.id.rvRingView)
RingView mRvRingView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_test1);
ButterKnife.bind(this);
mRvRingView.setAnglesData("12.2","230","6799.01","1","111","200");//直接設(shè)置String類型的數(shù)據(jù)
// mRvRingView.setAnglesData(12.2,230,6799.01,1,111,200);//直接設(shè)置double類型的數(shù)據(jù)
// mRvRingView.setAngles(20, 40, 100, 180, 20);//設(shè)置的是角度
// mRvRingView.setRingStartAngle(-90);//設(shè)置圓環(huán)的開始角度功偿,不設(shè)置默認(rèn)是-90
//設(shè)置畫筆的顏色,支持字符串和資源文件可變參數(shù)往堡。
mRvRingView.initPaint("#123456", "#fea123", "#fefefe", "#78da10", "#1121de", "#aacc18");//支持字符串
// mRvRingView.initPaint(R.color.color_first_part,R.color.color_second_part,
// R.color.color_third_part,R.color.color_fourth_part,
// R.color.color_fifth_part,R.color.color_sixth_part);
// mRvRingView.setInnerCirclePaintColor("#ffffff");//內(nèi)圓的畫筆顏色械荷,默認(rèn)#ffffff
mRvRingView.setRingStrokeWidth(40);//圓環(huán)的環(huán)寬,默認(rèn)20
// mRvRingView.showViewWithAnimation(1000);//自定義動(dòng)畫時(shí)長(zhǎng)展示圓環(huán)
// mRvRingView.showViewWithoutAnimation();//展示圓環(huán)不帶動(dòng)畫
mRvRingView.showViewWithAnimation();//動(dòng)畫展示圓環(huán)虑灰,默認(rèn)2s
}
}
3.自定義view的源碼
public class RingView extends View {
private static final int CIRCLE_ANGLE = 360;//圓環(huán)的角度
private static final int RING_STROKE_WIDTH = 20;//默認(rèn)圓環(huán)的寬度為20dp
private Paint mNoAssetsPaint, mInnerCirclePaint;
private ArrayList<Paint> mPaints;
private int mRingStrokeWidth;//圓環(huán)的寬度
private int mCanvasWidth, mCanvasHeight;
private RectF mRingRect, mInnerRect;
private int mDensity;//手機(jī)屏幕密度
private int mNoDataPaintColor = Color.parseColor("#cccccc");//沒(méi)有數(shù)據(jù)的paint的顏色
private int mInnerCirclePaintColor = Color.parseColor("#ffffff");//內(nèi)圓的paint的顏色
private ArrayList<Integer> mAngles;//傳入的數(shù)據(jù)
private boolean mHasData = false;
private ArrayList<Integer> mLevelStartAngles;//每段圓弧的起始角度值
private int mMoveAngle;//圓弧移動(dòng)的角度
private int mRingStartAngle = -90;//圓環(huán)的起始角度
private RingAnimation mRingAnim;
public RingView(Context context) {
super(context);
init(context);
}
public RingView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public RingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context ctx) {
mDensity = (int) ctx.getResources().getDisplayMetrics().density;
mRingStrokeWidth = RING_STROKE_WIDTH * mDensity;
mPaints = new ArrayList<Paint>();
mAngles = new ArrayList<Integer>();
mLevelStartAngles = new ArrayList<Integer>();
mNoAssetsPaint = new Paint();
mNoAssetsPaint.setAntiAlias(true);
mNoAssetsPaint.setStyle(Paint.Style.FILL);
mNoAssetsPaint.setColor(mNoDataPaintColor);
mInnerCirclePaint = new Paint();
mInnerCirclePaint.setAntiAlias(true);
mInnerCirclePaint.setStyle(Paint.Style.FILL);
mInnerCirclePaint.setColor(mInnerCirclePaintColor);
mRingAnim = new RingAnimation();
}
@Override
protected void onDraw(Canvas canvas) {
if (mCanvasWidth == 0) {
initRect();
}
if (!mHasData) {//沒(méi)有數(shù)據(jù)
mMoveAngle = CIRCLE_ANGLE;
drawRingView(canvas, mRingStartAngle, mMoveAngle, mNoAssetsPaint);
} else {
int _level = 0;//圓弧的段數(shù)
for (int _i = 0; _i < mAngles.size(); _i++) {//計(jì)算需要畫幾段圓弧
if (mMoveAngle < mLevelStartAngles.get(1)) {
_level = 1;
} else if (mMoveAngle > mLevelStartAngles.get(_i) && mMoveAngle <= mLevelStartAngles.get(_i + 1)) {
_level = _i + 1;
}
}
drawRing(_level, canvas);
}
canvas.drawArc(mInnerRect, mRingStartAngle, CIRCLE_ANGLE, true, mInnerCirclePaint);//畫內(nèi)部的圓
}
/**
*
* @param level 圓環(huán)的段數(shù)
* @param canvas
*/
private void drawRing(int level, Canvas canvas) {
if (level <= 0) {
drawRingView(canvas, mRingStartAngle, CIRCLE_ANGLE, mNoAssetsPaint);
return;
}
if (mAngles.size() > mPaints.size()) {
int _temp = mAngles.size() - mPaints.size();
for (int _i = 0; _i < _temp; _i++) {
mPaints.add(mNoAssetsPaint);
}
}
for (int _i = 0; _i < level; _i++) {
if (_i == level - 1) {
drawRingView(canvas, mRingStartAngle + mLevelStartAngles.get(_i),
mMoveAngle - mLevelStartAngles.get(_i), mPaints.get(_i));
} else {
drawRingView(canvas, mRingStartAngle + mLevelStartAngles.get(_i), mAngles.get(_i), mPaints.get(_i));
}
}
}
/**
*
* @param canvas
* @param startAngle 開始的角度
* @param sweepAngle 旋轉(zhuǎn)的角度
* @param paint 畫筆
*/
private void drawRingView(Canvas canvas, int startAngle, int sweepAngle, Paint paint) {
if (sweepAngle != 0) {
canvas.drawArc(mRingRect, startAngle, sweepAngle, true, paint);
}
}
public void setNoDataPaintColor(int color) {
mNoAssetsPaint.setColor(getResources().getColor(color));
}
public void setNoDataPaintColor(String color) {
mNoAssetsPaint.setColor(Color.parseColor(color));
}
public void setInnerCirclePaintColor(int colorId) {
mInnerCirclePaint.setColor(getResources().getColor(colorId));
}
public void setInnerCirclePaintColor(String color){
mInnerCirclePaint.setColor(Color.parseColor(color));
}
public void initPaint(ArrayList<Integer> colors) {
mPaints.clear();
for (int _i = 0; _i < colors.size(); _i++) {
Paint _paint = new Paint();
_paint.setAntiAlias(true);
_paint.setStyle(Paint.Style.FILL);
_paint.setColor(colors.get(_i));
mPaints.add(_paint);
}
}
public void initPaint(String... colors) {
ArrayList<Integer> _colors = new ArrayList<Integer>();
for (int _i = 0; _i < colors.length; _i++) {
_colors.add(Color.parseColor(colors[_i]));
}
initPaint(_colors);
}
public void initPaint(int... colorIds) {
ArrayList<Integer> _colors = new ArrayList<Integer>();
for (int _i = 0; _i < colorIds.length; _i++) {
_colors.add(getResources().getColor(colorIds[_i]));
}
initPaint(_colors);
}
private void initRect() {
mCanvasWidth = getWidth();
mCanvasHeight = getHeight();
mInnerRect = new RectF(mRingStrokeWidth, mRingStrokeWidth, mCanvasWidth - mRingStrokeWidth, mCanvasHeight - mRingStrokeWidth);
mRingRect = new RectF(0, 0, mCanvasWidth, mCanvasHeight);
}
/**
* 設(shè)置圓環(huán)起始的角度
* @param angle
*/
public void setRingStartAngle(int angle){
mRingStartAngle = angle;
}
/**
* 設(shè)置圓環(huán)的環(huán)寬
*
* @param width
*/
public void setRingStrokeWidth(int width) {
mRingStrokeWidth = width * mDensity;
invalidate();
}
/**
* 所需要顯示的數(shù)據(jù)的角度
*
* @param angles
*/
public void setAngles(int... angles) {
ArrayList<Integer> _angles = new ArrayList<Integer>();
for (int _i = 0; _i < angles.length; _i++) {
_angles.add(angles[_i]);
}
setAngles(_angles);
}
/**
* 所需要顯示的數(shù)據(jù)的角度
*
* @param angles
*/
public void setAngles(ArrayList<Integer> angles) {
mAngles.clear();
mAngles.addAll(angles);
mLevelStartAngles.clear();
mLevelStartAngles.add(0);
int _angle = 0;
for (int _i = 0; _i < mAngles.size(); _i++) {
_angle += mAngles.get(_i);
mLevelStartAngles.add(_angle);
if (mAngles.get(_i) > 0) {
mHasData = true;
}
}
}
/**
* 設(shè)置數(shù)據(jù)來(lái)計(jì)算角度并繪制圓環(huán)
*
* @param data
*/
public void setAnglesData(BigDecimal... data) {
BigDecimal _total = new BigDecimal("0.00");
for (int _i = 0; _i < data.length; _i++) {
_total = _total.add(data[_i]);
}
if (_total.compareTo(BigDecimal.valueOf(0)) == 0) {
mHasData = false;
return;
}
BigDecimal[] _dbData = new BigDecimal[data.length];
for (int _i = 0; _i < data.length; _i++) {
_dbData[_i] = data[_i].divide(_total, 10, ROUND_HALF_UP).multiply(BigDecimal.valueOf(360));
}
int[] _intData = new int[data.length];
for (int _i = 0; _i < data.length; _i++) {
//數(shù)值小于1且大于0的吨瞎,就直接定1,否則轉(zhuǎn)int類型穆咐,確保小數(shù)據(jù)也能出現(xiàn)在圓環(huán)上
_intData[_i] = _dbData[_i].compareTo(BigDecimal.valueOf(1.0)) < 0 && _dbData[_i].compareTo(BigDecimal.valueOf(0)) > 0 ?
1 : _dbData[_i].intValue();
}
//所有數(shù)據(jù)加起來(lái)可能會(huì)不滿360也可能會(huì)超出360颤诀,由于精度的問(wèn)題
//處理方案是把缺少的度數(shù)(有正也有負(fù))加在最大的值上字旭,這樣圖形出現(xiàn)的誤差會(huì)不明顯
int _remind = 360;//剩余的角度
int _maxPosition = -1, _max = _intData[0];
for (int _i = 0; _i < _intData.length; _i++) {
_remind = _remind - _intData[_i];
if (_max <= _intData[_i]) {
_maxPosition = _i;
}
}
_intData[_maxPosition] += _remind;//將缺少的度數(shù)加載最大值上
//將最終的數(shù)據(jù)設(shè)置到圓環(huán)上
setAngles(_intData);
}
public void setAnglesData(String... data) {
BigDecimal[] _bdData = new BigDecimal[data.length];
for (int _i = 0; _i < data.length; _i++) {
_bdData[_i] = new BigDecimal(TextUtils.isEmpty(data[_i]) ? "0" : data[_i]);
}
setAnglesData(_bdData);
}
public void setAnglesData(double... data) {
BigDecimal[] _bdData = new BigDecimal[data.length];
for (int _i = 0; _i < data.length; _i++) {
_bdData[_i] = BigDecimal.valueOf(data[_i]);
}
setAnglesData(_bdData);
}
/**
* 自定義動(dòng)畫時(shí)間的圓環(huán)
*
* @param animTime
*/
public void showViewWithAnimation(int animTime) {
startAnimation(animTime);
}
/**
* 默認(rèn)時(shí)間(2000)的圓環(huán)
*/
public void showViewWithAnimation() {
startAnimation(-1);
}
/**
* 不帶動(dòng)畫的圓環(huán)
*/
public void showViewWithoutAnimation() {
mMoveAngle = CIRCLE_ANGLE;
invalidate();
}
private void startAnimation(int animTime) {
mRingAnim.setDuration(animTime <= 0 ? 2000 : animTime);
startAnimation(mRingAnim);
}
private class RingAnimation extends Animation {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
mMoveAngle = (int) (interpolatedTime * CIRCLE_ANGLE);
invalidate();
}
}
}
4.代碼實(shí)現(xiàn)的一些注意點(diǎn)
1)控件的寬和高必須是固定的,不然無(wú)法顯示崖叫。
2)畫筆顏色的數(shù)組長(zhǎng)度必須大于或等于數(shù)據(jù)數(shù)組的長(zhǎng)度遗淳,不然超出的數(shù)據(jù)將由默認(rèn)的沒(méi)有數(shù)據(jù)的顏色顯示。
3)在設(shè)置畫筆顏色時(shí)心傀,使用的字符串形式的顏色必須嚴(yán)格遵循顏色的書寫方式屈暗,不然會(huì)出現(xiàn)無(wú)法正確顯示view。例如:不支持“#fff”脂男,支持“#ffffff”养叛。