因为要帮同学做个空心扇形统计图,要求使用自定义View,在帮她的同时,我也学了自定义View的写法。
自定义View
自定义View毫无疑问要继承 android.view.View
,这里我重写了 onDraw(Canvas)
、 onMeasure(int, int)
方法和构造方法
代码如下:
package com.shyling.customviewdemo; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; import java.util.ArrayList; public class PieChart extends View { private Paint mPaint,mCenterPaint; private float mRadius; private int mBackgroundColor; private String mText; private RectF mRectF; private int mTotalValue = 0; private ArrayList<PieChartDataItem> mValues; public PieChart(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); mRectF = new RectF(); mCenterPaint = new Paint(); mValues = new ArrayList<PieChartDataItem>(); //读取xml中的配置 TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.PieChart); mRadius = typedArray.getDimension(R.styleable.PieChart_Radius, 0); mBackgroundColor = typedArray.getColor(R.styleable.PieChart_Background,Color.WHITE); typedArray.recycle(); //设置反锯齿 mPaint.setAntiAlias(true); mCenterPaint.setAntiAlias(true); mCenterPaint.setTextSize(79); mCenterPaint.setTextAlign(Paint.Align.CENTER); } /* 绘制View @param canvas#画布 */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(mBackgroundColor); int degrees = 0; for(PieChartDataItem pieChartDataItem:mValues){ mPaint.setColor(pieChartDataItem.getColor()); canvas.drawArc(mRectF, degrees, pieChartDataItem.getDegrees(), true, mPaint); degrees+=pieChartDataItem.getDegrees(); } canvas.translate(mRadius / 2, mRadius / 2); mCenterPaint.setColor(Color.WHITE); canvas.drawCircle(0, 0, mRadius / 4, mCenterPaint); mCenterPaint.setColor(Color.BLACK); canvas.drawText(this.getText(), 0, 0, mCenterPaint); } /* 设置View中心文本内容 @param text#文本内容 */ public void setText(String text){ this.mText = text; } /* 获得View中心文本 @return text */ public String getText(){ if(this.mText==null){ return ""; }else{ return this.mText; } } /* 添加Item @param itemTitle#item标题 @param itemValue#item值 @param itemColor#item颜色 @see #addItem(PieChartDataItem) */ public void addItem(String itemTitle,int itemValue,int itemColor){ addItem(new PieChartDataItem(itemTitle, itemValue, itemColor)); } /* 添加item @param pieChartDataItem#PieChartDataItem实例 */ public void addItem(PieChartDataItem pieChartDataItem){ if(pieChartDataItem.getValue()<0){ throw new NumberFormatException("itemValue must big than zero"); } mTotalValue +=pieChartDataItem.getValue(); mValues.add(pieChartDataItem); } /* 重置View数据 */ public void reset(){ mTotalValue=0; mValues.clear(); } /* 获得View内全部数据 @return PieChartDataItem[] */ public PieChartDataItem[] getValues(){ PieChartDataItem[] pieChartDataItems = new PieChartDataItem[mValues.size()]; mValues.toArray(pieChartDataItems); return pieChartDataItems; } /* 对VIew内数据进行处理,生成对应角度 */ public void cal(){ for(PieChartDataItem pieChartDataItem : mValues){ pieChartDataItem.setDegrees(360*pieChartDataItem.getValue()/mTotalValue+1); } this.invalidate(); } /* 初始化View大小,根据长宽中最小值确定圆半径 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); mRadius = Math.min(width,height); mRectF.set(0, 0, mRadius, mRadius); setMeasuredDimension((int)mRadius, (int)mRadius); } }
Item Model为:
package com.shyling.customviewdemo; import android.graphics.Color; /** * Created by shy on 2015/11/3. */ public class PieChartDataItem { private String title; private int value; private float degrees; public PieChartDataItem(String title, int value,int color) { this.color = color; this.title = title; this.value = value; } private int color; public int getColor() { return color; } public void setColor(int color) { this.color = color; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } protected void setDegrees(float degrees){ this.degrees = degrees; } protected float getDegrees(){ return this.degrees; } }
在onDraw中,通过对画布(Canvas)的操作来绘制图形,在onMeasue中对View的大小进行设定。
自定义View属性
在构造方法中,对AttributeSet进行解析来加载xml中的自定义属性。
为了自定义属性。首先需要在资源文件中加入
<declare-styleable name="PieChart"> <attr name="Radius" format="dimension" /> <attr name="Background" format="color" /> </declare-styleable>
其中PieChart为类名,attr为各个属性,name为名称,format为属性类型,与代码中相对应,例如:integer,float,dimension,color等。
主Activity
代码很简单,仅仅为了使用自定义View
package com.shyling.customviewdemo; import android.graphics.Color; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { PieChart pieChart; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); pieChart = (PieChart) findViewById(R.id.piechart); pieChart.addItem("Title1", 100, Color.GREEN); pieChart.addItem("Title2", 110, Color.BLUE); pieChart.addItem("Title3", 100, Color.YELLOW); pieChart.addItem(new PieChartDataItem("Title4", 60, Color.LTGRAY)); pieChart.addItem("Title5", 120, Color.MAGENTA); pieChart.setText("Text"); pieChart.cal(); } }
布局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.shyling.customviewdemo.MainActivity"> <com.shyling.customviewdemo.PieChart android:id="@+id/piechart" app:Radius="300dp" app:background="@color/piechart_background" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
效果图: