前几天突然需要做两种图表——柱形图、折线图,于是第一反应是先看看网上有没有现成的,结果有是有,但都不是想要的,而且大多数不是用纯android代码完成,不过HTML5似乎完成这类工作要容易得多,单是非我们所擅长。
**知识点:**android自定义view、图形图像、Fragment、MVC模式。
折线图:
代码,注释很详细,直接看代码就行了:
001.
public
class
LineChartView
extends
View {
002.
003.
private
int
width;
004.
private
int
height;
005.
private
float
maxValue;
//传入数据的最大值
006.
private
int
dataNum;
//数据总数
007.
008.
private
Paint mPaintBg;
//报表背景画笔
009.
private
Paint mPaintCoveredBg;
//用于画数据覆盖部分
010.
private
Paint mPaintChartLine;
//用于画报表的网格
011.
private
Paint mPaintDataLine;
//用于画数据连线
012.
private
Paint mPaintTextDate;
//用于画日期文本
013.
private
Paint mPaintCircle;
//用于画空心圆
014.
private
Paint mPaintFilledCircle;
//用于画实心圆
015.
private
Paint mPaintTextValue;
//用于画数据访问量值
016.
017.
private
Path path;
018.
019.
private
HashMap<Integer, PageViewData> dataTotal;
//用于得存储传入的总数据
020.
021.
//用一个setPaints()方法,设定所有画笔的属性,增加代码灵活性
022.
public
void
setPaints(
int
bgColor,
int
coveredBgColor,
int
chartLineColor
023.
,
int
dataLineColor,
int
textDateColor,
int
filledCircleColor,
int
circleColor,
int
textValueColor) {
024.
025.
mPaintBg.setColor(bgColor);
026.
mPaintBg.setStyle(Paint.Style.FILL);
027.
028.
mPaintCoveredBg.setColor(coveredBgColor);
029.
mPaintCoveredBg.setStyle(Paint.Style.FILL);
030.
031.
mPaintCircle.setColor(circleColor);
032.
mPaintCircle.setStyle(Paint.Style.STROKE);
033.
mPaintCircle.setStrokeWidth(
5
);
034.
mPaintCircle.setAntiAlias(
true
);
035.
036.
mPaintFilledCircle.setColor(filledCircleColor);
037.
mPaintFilledCircle.setStyle(Paint.Style.FILL);
038.
mPaintFilledCircle.setAntiAlias(
true
);
039.
040.
mPaintChartLine.setColor(chartLineColor);
041.
mPaintChartLine.setStyle(Paint.Style.STROKE);
042.
mPaintChartLine.setAntiAlias(
true
);
043.
044.
mPaintDataLine.setColor(dataLineColor);
045.
mPaintDataLine.setStyle(Paint.Style.STROKE);
046.
mPaintDataLine.setStrokeWidth(SizeConvert.dip2px(getContext(),
5
));
047.
mPaintDataLine.setAntiAlias(
true
);
048.
049.
mPaintTextDate.setColor(textDateColor);
050.
mPaintTextDate.setTextSize(SizeConvert.dip2px(getContext(),
10
));
051.
mPaintTextDate.setTextAlign(Paint.Align.CENTER);
052.
mPaintTextDate.setAntiAlias(
true
);
053.
054.
mPaintTextValue.setColor(textValueColor);
055.
mPaintTextValue.setTextSize(SizeConvert.dip2px(getContext(),
12
));
056.
mPaintTextValue.setTextAlign(Paint.Align.CENTER);
057.
mPaintTextValue.setAntiAlias(
true
);
058.
059.
//重绘
060.
invalidate();
061.
}
062.
063.
064.
//用于设定传入的总数据
065.
public
void
setDataTotal(HashMap<Integer, PageViewData> dataTotal) {
066.
this
.dataTotal = dataTotal;
067.
invalidate();
068.
}
069.
070.
public
LineChartView(Context context) {
071.
super
(context);
072.
}
073.
074.
public
LineChartView(Context context, AttributeSet attrs) {
075.
super
(context, attrs);
076.
mPaintBg =
new
Paint();
077.
mPaintCoveredBg =
new
Paint();
078.
mPaintCircle =
new
Paint();
079.
mPaintChartLine =
new
Paint();
080.
mPaintDataLine =
new
Paint();
081.
mPaintTextDate =
new
Paint();
082.
mPaintTextValue =
new
Paint();
083.
mPaintFilledCircle =
new
Paint();
084.
path =
new
Path();
085.
086.
dataTotal =
new
HashMap<>();
087.
}
088.
089.
@Override
090.
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
091.
super
.onMeasure(widthMeasureSpec, heightMeasureSpec);
092.
//当总数据已经传入,即不为空时,根据总数据中数据个数设定view的总宽
093.
if
(dataTotal !=
null
) {
094.
width = (dataTotal.size() -
1
) * xAddedNum + chartMarginHorizontal *
2
;
095.
getMaxValue(dataTotal);
096.
}
097.
height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
098.
setMeasuredDimension(width, height);
099.
}
100.
101.
/**
102.
* 用于得到总数据中最大数据
103.
* @param dataTotal 总数据
104.
*/
105.
private
void
getMaxValue(HashMap<Integer, PageViewData> dataTotal) {
106.
maxValue =
0
;
107.
dataNum =
0
;
108.
for
(
int
key : dataTotal.keySet()) {
109.
if
(dataTotal.get(key).getPageViewValue() > maxValue) {
110.
maxValue = dataTotal.get(key).getPageViewValue();
111.
}
112.
dataNum++;
113.
}
114.
}
115.
116.
private
int
mChartHeight;
//折线图的高
117.
private
int
mChartWidth;
//折线图的
118.
private
int
startX = SizeConvert.dip2px(getContext(),
10
);
//开始绘制的x坐标
119.
private
int
startY = SizeConvert.dip2px(getContext(),
5
);
//开始绘制的y坐标
120.
private
int
chartMarginBottom = SizeConvert.dip2px(getContext(),
30
);
//折线图距离父控件底部距离
121.
private
int
chartMarginHorizontal = SizeConvert.dip2px(getContext(),
12
);
//折线图距离父控件左右的距离
122.
private
int
valueAlignLeft = SizeConvert.dip2px(getContext(),
0
);
//value参数文本距离左边距离
123.
private
int
dateAlignLeft = SizeConvert.dip2px(getContext(),
0
);
//date参数文本距离左边距离
124.
private
int
valueAlignBottom = SizeConvert.dip2px(getContext(),
5
);
//value参数文本距离底部距离
125.
private
int
dateAlignBottom = SizeConvert.dip2px(getContext(),
10
);
//date参数文本距离底部距离
126.
private
int
xAddedNum = SizeConvert.dip2px(getContext(),
60
);
//绘制折线图时每次移动的x轴距离
127.
private
int
yAddedNum;
//绘制折线图时每次移动的y轴距离
128.
private
boolean
isDrawFirst;
//是否是第一次绘制
129.
private
float
circleFilledRadius = SizeConvert.dip2px(getContext(),
5
);
//外圆半径
130.
private
float
circleRadius = SizeConvert.dip2px(getContext(),
3
);
//内圆半径
131.
132.
private
float
firstX;
//第一个点的x轴坐标
133.
private
float
firstY;
//第一个点的y轴坐标
134.
135.
@Override
136.
protected
void
onDraw(Canvas canvas) {
137.
super
.onDraw(canvas);
138.
isDrawFirst =
true
;
139.
mChartHeight = height - chartMarginBottom;
140.
yAddedNum = mChartHeight /
4
;
141.
mChartWidth = width - chartMarginHorizontal *
2
;
142.
143.
canvas.drawRect(startX, startY, startX + mChartWidth, startY + mChartHeight, mPaintBg);
144.
for
(
int
key : dataTotal.keySet()) {
145.
float
value = dataTotal.get(key).getPageViewValue();
146.
if
(isDrawFirst) {
147.
//当第一次绘制时得到第一个点的横纵坐标
148.
firstX = startX;
149.
firstY = startY + (1f - value / ((
int
) maxValue *
1
.5f)) * mChartHeight;
150.
path.moveTo(firstX, firstY);
151.
isDrawFirst =
false
;
152.
}
153.
//每循环一次,将path线性相位一次
154.
path.lineTo(startX, startY + (1f - value / ((
int
) maxValue *
1
.5f)) * mChartHeight);
155.
startX += xAddedNum;
156.
}
157.
//重新给startX赋值
158.
startX = SizeConvert.dip2px(getContext(),
10
);
159.
//画出折线
160.
canvas.drawPath(path, mPaintDataLine);
161.
//画出折线以下部分的颜色
162.
path.lineTo(startX + mChartWidth, startY + mChartHeight);
163.
path.lineTo(startX, startY + mChartHeight);
164.
path.lineTo(firstX, firstY);
165.
canvas.drawPath(path, mPaintCoveredBg);
166.
167.
//画出每个点的圆圈,和对应的文本
168.
for
(
int
key : dataTotal.keySet()) {
169.
int
date = dataTotal.get(key).getDate();
170.
float
value = dataTotal.get(key).getPageViewValue();
171.
canvas.drawCircle(startX, startY + (1f - value / ((
int
) maxValue *
1
.5f)) * mChartHeight, circleFilledRadius, mPaintFilledCircle);
172.
canvas.drawCircle(startX, startY + (1f - value / ((
int
) maxValue *
1
.5f)) * mChartHeight, circleRadius, mPaintCircle);
173.
canvas.drawText(date +
""
, startX + dateAlignLeft, height - dateAlignBottom, mPaintTextDate);
174.
canvas.drawText(value +
""
, startX + valueAlignLeft, startY + (1f - value / ((
int
) maxValue *
1
.5f)) * mChartHeight - valueAlignBottom, mPaintTextValue);
175.
176.
startX += xAddedNum;
177.
}
178.
179.
//在次使startX回到初始值
180.
startX = SizeConvert.dip2px(getContext(),
10
);
181.
/**
182.
* 画出网格
183.
*/
184.
//竖线
185.
for
(
int
i =
0
; i < dataNum; i++) {
186.
canvas.drawLine(startX + i * xAddedNum, startY, startX + i * xAddedNum, startY + mChartHeight, mPaintChartLine);
187.
}
188.
189.
//横线
190.
for
(
int
i =
0
; i <
5
; i++) {
191.
canvas.drawLine(startX, startY + i * yAddedNum, startX + mChartWidth, startY + i * yAddedNum, mPaintChartLine);
192.
}
193.
194.
path.reset();
195.
196.
}
197.
}
柱形图:
代码:
01.
/**
02.
* 柱形图
03.
*/
04.
public
class
HistogramView
extends
View {
05.
06.
private
int
width;
07.
private
int
height;
08.
09.
private
Paint mBgPaint;
10.
private
Paint mHistogramBgPaint;
11.
private
Paint mHistogramPaint;
12.
private
Paint mTextPaint;
13.
14.
private
HashMap<Integer,HistogramData> dataTotal;
15.
16.
public
void
setPaints(
int
bgColor,
int
histogramBgColor,
int
histogramColor,
int
textColor){
17.
18.
mBgPaint.setColor(bgColor);
19.
mBgPaint.setStyle(Paint.Style.FILL);
20.
21.
mHistogramBgPaint.setColor(histogramBgColor);
22.
mHistogramBgPaint.setStyle(Paint.Style.FILL);
23.
24.
mHistogramPaint.setColor(histogramColor);
25.
mHistogramPaint.setStyle(Paint.Style.FILL);
26.
27.
mTextPaint.setColor(textColor);
28.
mTextPaint.setTextSize(SizeConvert.dip2px(getContext(),
9
));
29.
mTextPaint.setTextAlign(Paint.Align.CENTER);
30.
31.
invalidate();
32.
};
33.
34.
public
void
setDataTotal(HashMap<Integer, HistogramData> dataTotal) {
35.
this
.dataTotal = dataTotal;
36.
invalidate();
37.
}
38.
39.
public
HistogramView(Context context) {
40.
super
(context);
41.
}
42.
43.
public
HistogramView(Context context, AttributeSet attrs) {
44.
super
(context, attrs);
45.
mHistogramBgPaint =
new
Paint();
46.
mHistogramPaint =
new
Paint();
47.
mTextPaint =
new
Paint();
48.
mBgPaint =
new
Paint();
49.
50.
dataTotal =
new
HashMap<>();
51.
}
52.
53.
@Override
54.
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
55.
super
.onMeasure(widthMeasureSpec, heightMeasureSpec);
56.
if
(dataTotal!=
null
){
57.
width = dataTotal.size()*SizeConvert.dip2px(getContext(),
40
);
58.
}
59.
height = getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec);
60.
setMeasuredDimension(width, height);
61.
}
62.
63.
private
int
mHistogramHeight;
64.
int
startX = SizeConvert.dip2px(getContext(),
20
);
65.
int
startY = SizeConvert.dip2px(getContext(),
20
);
66.
int
radius =
45
;
67.
int
greyValueAlignLeft = SizeConvert.dip2px(getContext(),
5
);
68.
int
nameAlignLeft = SizeConvert.dip2px(getContext(),
15
);
69.
int
greyValueAlignBottom = SizeConvert.dip2px(getContext(),
5
);
70.
int
nameAlignBottom = SizeConvert.dip2px(getContext(),
15
);
71.
int
histogramWidth = SizeConvert.dip2px(getContext(),
8
);
72.
int
xAddedNum = SizeConvert.dip2px(getContext(),
40
);
73.
int
histogramAlignTop = SizeConvert.dip2px(getContext(),
2
);
74.
75.
@Override
76.
protected
void
onDraw(Canvas canvas) {
77.
super
.onDraw(canvas);
78.
mHistogramHeight = height-SizeConvert.dip2px(getContext(),
40
);
79.
canvas.drawRect(
0
,startY,width,mHistogramHeight,mBgPaint);
80.
for
(
int
key:dataTotal.keySet()){
81.
String name = dataTotal.get(key).getName();
82.
int
greyValue = dataTotal.get(key).getGreyValue();
83.
84.
canvas.drawText(greyValue +
"%"
, startX + greyValueAlignLeft, startY-greyValueAlignBottom , mTextPaint);
85.
canvas.drawRect(startX, startY+histogramAlignTop, startX + histogramWidth, mHistogramHeight, mHistogramBgPaint);
86.
canvas.drawRect(startX, startY+(1f - greyValue / 100f) * (mHistogramHeight - startY-histogramAlignTop)+histogramAlignTop, startX + histogramWidth, mHistogramHeight, mHistogramPaint);
87.
88.
canvas.save();
89.
canvas.rotate(-radius, startX,height);
90.
canvas.drawText(name, startX + nameAlignLeft, height-nameAlignBottom, mTextPaint);
91.
canvas.restore();
92.
startX+=xAddedNum;
93.
}
94.
startX = SizeConvert.dip2px(getContext(),
20
);
95.
}
96.
97.
}
Fragment
001.
public
class
FragmentChart
extends
Fragment
implements
View.OnClickListener{
002.
003.
private
RelativeLayout mItemFirst;
004.
private
RelativeLayout mItemSecond;
005.
private
RelativeLayout mItemThird;
006.
private
RelativeLayout mItemForth;
007.
008.
private
HistogramView mHistogramView;
009.
private
HashMap<Integer,HistogramData> mDataHistogramTotal;
010.
private
boolean
isShowHistogram =
false
;
011.
012.
private
LineChartView mLineChartView;
013.
private
HashMap<Integer,PageViewData> mDataPageView;
014.
private
boolean
isShowSecondItem =
false
;
015.
@Nullable
016.
@Override
017.
public
View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
018.
View view = inflater.inflate(R.layout.content_chart,
null
);
019.
mItemFirst = (RelativeLayout) view.findViewById(R.id.first_page_item_1);
020.
mItemSecond = (RelativeLayout) view.findViewById(R.id.first_page_item_2);
021.
mItemThird = (RelativeLayout) view.findViewById(R.id.first_page_item_3);
022.
mItemForth = (RelativeLayout) view.findViewById(R.id.first_page_item_4);
023.
024.
mHistogramView = (HistogramView) view.findViewById(R.id.histogram_view_item_1);
025.
mLineChartView = (LineChartView) view.findViewById(R.id.line_chart_view_item_2);
026.
027.
mItemFirst.setOnClickListener(
this
);
028.
mItemSecond.setOnClickListener(
this
);
029.
mItemThird.setOnClickListener(
this
);
030.
mItemForth.setOnClickListener(
this
);
031.
032.
initDataHistogram();
033.
initDataPageView();
034.
035.
return
view;
036.
}
037.
038.
private
void
initDataPageView() {
039.
mDataPageView =
new
HashMap<>();
040.
mDataPageView.put(
1
,
new
PageViewData(
1
,
9
,
16
));
041.
mDataPageView.put(
2
,
new
PageViewData(
2
,
10
,
18
));
042.
mDataPageView.put(
3
,
new
PageViewData(
3
,
11
,
33
));
043.
mDataPageView.put(
4
,
new
PageViewData(
4
,
12
,
97
));
044.
mDataPageView.put(
5
,
new
PageViewData(
5
,
13
,
46
));
045.
mDataPageView.put(
6
,
new
PageViewData(
6
,
14
,
55
));
046.
mDataPageView.put(
7
,
new
PageViewData(
7
,
15
,
11
));
047.
mDataPageView.put(
8
,
new
PageViewData(
8
,
16
,
22
));
048.
mDataPageView.put(
9
,
new
PageViewData(
9
,
17
,
8
));
049.
mDataPageView.put(
10
,
new
PageViewData(
10
,
18
,
19
));
050.
mDataPageView.put(
11
,
new
PageViewData(
11
,
16
,
22
));
051.
052.
mLineChartView.setDataTotal(mDataPageView);
053.
mLineChartView.setPaints(Color.argb(
255
,
225
,
250
,
250
),
054.
Color.argb(
255
,
234
,
234
,
250
), Color.argb(
255
,
74
,
208
,
204
),
055.
Color.argb(
255
,
105
,
210
,
249
),Color.argb(
255
,
203
,
203
,
203
)
056.
,Color.argb(
255
,
255
,
255
,
255
),Color.argb(
255
,
105
,
210
,
249
),Color.argb(
255
,
105
,
210
,
249
));
057.
}
058.
059.
private
void
initDataHistogram(){
060.
mDataHistogramTotal =
new
HashMap<>();
061.
mDataHistogramTotal.put(
1
,
new
HistogramData(
1
,
"海萌"
,
100
));
062.
mDataHistogramTotal.put(
2
,
new
HistogramData(
2
,
"涛涛"
,
18
));
063.
mDataHistogramTotal.put(
3
,
new
HistogramData(
3
,
"火风"
,
17
));
064.
mDataHistogramTotal.put(
4
,
new
HistogramData(
4
,
"周杰伦"
,
16
));
065.
mDataHistogramTotal.put(
5
,
new
HistogramData(
5
,
"王宝强"
,
15
));
066.
mDataHistogramTotal.put(
6
,
new
HistogramData(
6
,
"林俊杰"
,
14
));
067.
mDataHistogramTotal.put(
7
,
new
HistogramData(
7
,
"孙悟空"
,
11
));
068.
mDataHistogramTotal.put(
8
,
new
HistogramData(
8
,
"钟航"
,
10
));
069.
mDataHistogramTotal.put(
9
,
new
HistogramData(
9
,
"小明"
,
8
));
070.
mDataHistogramTotal.put(
10
,
new
HistogramData(
10
,
"小红"
,
5
));
071.
072.
mHistogramView.setDataTotal(mDataHistogramTotal);
073.
mHistogramView.setPaints(Color.argb(
255
,
250
,
250
,
250
),
074.
Color.argb(
255
,
238
,
238
,
238
), Color.argb(
255
,
240
,
141
,
77
),
075.
Color.argb(
255
,
168
,
168
,
168
));
076.
}
077.
078.
@Override
079.
public
void
onClick(View v) {
080.
switch
(v.getId()){
081.
case
R.id.first_page_item_1:
082.
isShowHistogram = !isShowHistogram;
083.
if
(isShowHistogram) {
084.
mHistogramView.setVisibility(View.VISIBLE);
085.
}
else
{
086.
mHistogramView.setVisibility(View.GONE);
087.
}
088.
break
;
089.
case
R.id.first_page_item_2:
090.
isShowSecondItem= !isShowSecondItem;
091.
if
(isShowSecondItem) {
092.
mLineChartView.setVisibility(View.VISIBLE);
093.
}
else
{
094.
mLineChartView.setVisibility(View.GONE);
095.
}
096.
break
;
097.
case
R.id.first_page_item_3:
098.
break
;
099.
case
R.id.first_page_item_4:
100.
break
;
101.
}
102.
}
103.
}
主活动:
01.
public
class
MainActivity
extends
FragmentActivity {
02.
03.
private
FragmentChart mFragmentChart;
04.
private
RadioGroup mRadioGroup;
05.
private
FragmentTransaction transaction;
06.
07.
@Override
08.
protected
void
onCreate(Bundle savedInstanceState) {
09.
super
.onCreate(savedInstanceState);
10.
setContentView(R.layout.activity_main);
11.
mFragmentChart =
new
FragmentChart();
12.
mFragmentManager = getSupportFragmentManager();
13.
transaction = mFragmentManager.beginTransaction();
14.
transaction.add(R.id.frame_layout, mFragmentChart);
15.
transaction.commit();
16.
mRadioGroup = (RadioGroup) findViewById(R.id.radiogroup);
17.
18.
mRadioGroup.setOnCheckedChangeListener(
new
RadioGroup.OnCheckedChangeListener() {
19.
@Override
20.
public
void
onCheckedChanged(RadioGroup group,
int
checkedId) {
21.
22.
switch
(checkedId) {
23.
case
R.id.button1:
24.
transaction = mFragmentManager.beginTransaction();
25.
transaction.hide(mFragmentChat);
26.
transaction.hide(mFragmentSetting);
27.
transaction.hide(mFragmentWork);
28.
transaction.show(mFragmentChart);
29.
transaction.commit();
30.
break
;
31.
32.
}
33.
}
34.
});
35.
}
36.
}
activity_main:
01.
<LinearLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
02.
03.
android:layout_width=
"match_parent"
04.
android:layout_height=
"match_parent"
05.
android:orientation=
"vertical"
06.
>
07.
08.
<FrameLayout
09.
android:id=
"@+id/frame_layout"
10.
android:layout_width=
"match_parent"
11.
android:layout_height=
"wrap_content"
12.
android:layout_weight=
"1"
>
13.
</FrameLayout>
14.
15.
<RadioGroup
16.
android:id=
"@+id/radiogroup"
17.
android:layout_width=
"match_parent"
18.
android:layout_height=
"60dp"
19.
android:layout_alignParentBottom=
"true"
20.
android:background=
"@color/light_gray"
21.
android:checkedButton=
"@+id/button1"
22.
android:gravity=
"center_vertical"
23.
android:orientation=
"horizontal"
24.
android:padding=
"2dp"
>
25.
26.
<RadioButton
27.
android:id=
"@+id/button1"
28.
android:layout_width=
"0dp"
29.
android:layout_height=
"wrap_content"
30.