Android绘图机制(四)——使用HelloCharts开源框架搭建一系列炫酷图表,柱形图,折线图,饼状图和动画特效,抽丝剥茧带你认识图表之美
一.官方截图
二.项目架构
这里我们首先来看一下我排版的思维导图
然后跳转一个Activity
@Override
publicvoidonItemClick(AdapterView>adapter,Viewview,
intposition,longid){
Intentintent;
switch(position){
case0:
//LineChart;
intent=newIntent(getActivity(),LineChartActivity.class);
startActivity(intent);
break;
case1:
//ColumnChart;
intent=newIntent(getActivity(),ColumnChartActivity.class);
startActivity(intent);
break;
case2:
//PieChart;
intent=newIntent(getActivity(),PieChartActivity.class);
startActivity(intent);
break;
case3:
//BubbleChart;
intent=newIntent(getActivity(),BubbleChartActivity.class);
startActivity(intent);
break;
case4:
//PreviewLineChart;
intent=newIntent(getActivity(),
PreviewLineChartActivity.class);
startActivity(intent);
break;
case5:
//PreviewColumnChart;
intent=newIntent(getActivity(),
PreviewColumnChartActivity.class);
startActivity(intent);
break;
case6:
//ComboChart;
intent=newIntent(getActivity(),
ComboLineColumnChartActivity.class);
startActivity(intent);
break;
case7:
//LineColumnDependency;
intent=newIntent(getActivity(),
LineColumnDependencyActivity.class);
startActivity(intent);
break;
case8:
//Tempolinechart;
intent=newIntent(getActivity(),TempoChartActivity.class);
startActivity(intent);
break;
case9:
//Speedlinechart;
intent=newIntent(getActivity(),SpeedChartActivity.class);
startActivity(intent);
break;
case10:
//GoodBadfilledlinechart;
intent=newIntent(getActivity(),GoodBadChartActivity.class);
startActivity(intent);
break;
case11:
//GoodBadfilledlinechart;
intent=newIntent(getActivity(),
ViewPagerChartsActivity.class);
startActivity(intent);
break;
default:
break;
}
}
Activity继承的是FragmentActivity,我们在FragmentActivity内部编写一个Fragment这样就可以绑定主Activity而不用继承自View去多写一个类了
publicstaticclassPlaceholderFragmentextendsFragment
所以你看到的项目也是非常简洁的
项目只有一个主Activity——MainActivity和一个关于软件的AboutActivity,然后就是十二个对应的类了
好了,我们可以编写了
三,实现图标
1.折线图
从这里我们就可以看出,其实图表操作主要还是看menu菜单,所以,我们先实现
我们fragment要绑定的布局
fragment_line_chart
android:id="@+id/chart"
android:layout_width="match_parent"
android:layout_height="match_parent">
LineChartActivity
packagelecho.lib.hellocharts.samples;
importjava.util.ArrayList;
importjava.util.List;
importlecho.lib.hellocharts.animation.ChartAnimationListener;
importlecho.lib.hellocharts.gesture.ZoomType;
importlecho.lib.hellocharts.listener.LineChartOnValueSelectListener;
importlecho.lib.hellocharts.model.Axis;
importlecho.lib.hellocharts.model.Line;
importlecho.lib.hellocharts.model.LineChartData;
importlecho.lib.hellocharts.model.PointValue;
importlecho.lib.hellocharts.model.ValueShape;
importlecho.lib.hellocharts.model.Viewport;
importlecho.lib.hellocharts.util.ChartUtils;
importlecho.lib.hellocharts.view.Chart;
importlecho.lib.hellocharts.view.LineChartView;
importandroid.os.Bundle;
importandroid.support.v4.app.Fragment;
importandroid.support.v4.app.FragmentActivity;
importandroid.view.LayoutInflater;
importandroid.view.Menu;
importandroid.view.MenuInflater;
importandroid.view.MenuItem;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.Toast;
publicclassLineChartActivityextendsFragmentActivity{
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_line_chart);
if(savedInstanceState==null){
getSupportFragmentManager().beginTransaction()
.add(R.id.container,newPlaceholderFragment()).commit();
}
}
/
Afragmentcontainingalinechart.
/
publicstaticclassPlaceholderFragmentextendsFragment{
privateLineChartViewchart;
privateLineChartDatadata;
privateintnumberOfLines=1;
privateintmaxNumberOfLines=4;
privateintnumberOfPoints=12;
float[][]randomNumbersTab=newfloat[maxNumberOfLines][numberOfPoints];
privatebooleanhasAxes=true;
privatebooleanhasAxesNames=true;
privatebooleanhasLines=true;
privatebooleanhasPoints=true;
privateValueShapeshape=ValueShape.CIRCLE;
privatebooleanisFilled=false;
privatebooleanhasLabels=false;
privatebooleanisCubic=false;
privatebooleanhasLabelForSelected=false;
privatebooleanpointsHaveDifferentColor;
publicPlaceholderFragment(){
}
@Override
publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,
BundlesavedInstanceState){
setHasOptionsMenu(true);
ViewrootView=inflater.inflate(R.layout.fragment_line_chart,
container,false);
chart=(LineChartView)rootView.findViewById(R.id.chart);
chart.setOnValueTouchListener(newValueTouchListener());
//Generatesomerandomevalues.
generateValues();
generateData();
//Disableviewpirtrecalculations,seetoggleCubic()methodfor
//moreinfo.
chart.setViewportCalculationEnabled(false);
resetViewport();
returnrootView;
}
//MENU
@Override
publicvoidonCreateOptionsMenu(Menumenu,MenuInflaterinflater){
inflater.inflate(R.menu.line_chart,menu);
}
//menu的操作
@Override
publicbooleanonOptionsItemSelected(MenuItemitem){
intid=item.getItemId();
if(id==R.id.action_reset){
reset();
generateData();
returntrue;
}
if(id==R.id.action_add_line){
addLineToData();
returntrue;
}
if(id==R.id.action_toggle_lines){
toggleLines();
returntrue;
}
if(id==R.id.action_toggle_points){
togglePoints();
returntrue;
}
if(id==R.id.action_toggle_cubic){
toggleCubic();
returntrue;
}
if(id==R.id.action_toggle_area){
toggleFilled();
returntrue;
}
if(id==R.id.action_point_color){
togglePointColor();
returntrue;
}
if(id==R.id.action_shape_circles){
setCircles();
returntrue;
}
if(id==R.id.action_shape_square){
setSquares();
returntrue;
}
if(id==R.id.action_shape_diamond){
setDiamonds();
returntrue;
}
if(id==R.id.action_toggle_labels){
toggleLabels();
returntrue;
}
if(id==R.id.action_toggle_axes){
toggleAxes();
returntrue;
}
if(id==R.id.action_toggle_axes_names){
toggleAxesNames();
returntrue;
}
if(id==R.id.action_animate){
prepareDataAnimation();
chart.startDataAnimation();
returntrue;
}
if(id==R.id.action_toggle_selection_mode){
toggleLabelForSelected();
Toast.makeText(
getActivity(),
"Selectionmodesetto"
+chart.isValueSelectionEnabled()
+"selectanypoint.",Toast.LENGTH_SHORT)
.show();
returntrue;
}
if(id==R.id.action_toggle_touch_zoom){
chart.setZoomEnabled(!chart.isZoomEnabled());
Toast.makeText(getActivity(),
"IsZoomEnabled"+chart.isZoomEnabled(),
Toast.LENGTH_SHORT).show();
returntrue;
}
if(id==R.id.action_zoom_both){
chart.setZoomType(ZoomType.HORIZONTAL_AND_VERTICAL);
returntrue;
}
if(id==R.id.action_zoom_horizontal){
chart.setZoomType(ZoomType.HORIZONTAL);
returntrue;
}
if(id==R.id.action_zoom_vertical){
chart.setZoomType(ZoomType.VERTICAL);
returntrue;
}
returnsuper.onOptionsItemSelected(item);
}
privatevoidgenerateValues(){
for(inti=0;i for(intj=0;j randomNumbersTab[i][j]=(float)Math.random()100f;
}
}
}
privatevoidreset(){
numberOfLines=1;
hasAxes=true;
hasAxesNames=true;
hasLines=true;
hasPoints=true;
shape=ValueShape.CIRCLE;
isFilled=false;
hasLabels=false;
isCubic=false;
hasLabelForSelected=false;
pointsHaveDifferentColor=false;
chart.setValueSelectionEnabled(hasLabelForSelected);
resetViewport();
}
privatevoidresetViewport(){
//Resetviewportheightrangeto(0,100)
finalViewportv=newViewport(chart.getMaximumViewport());
v.bottom=0;
v.top=100;
v.left=0;
v.right=numberOfPoints-1;
chart.setMaximumViewport(v);
chart.setCurrentViewport(v);
}
privatevoidgenerateData(){
Listlines=newArrayList();
for(inti=0;i
Listvalues=newArrayList();
for(intj=0;j values.add(newPointValue(j,randomNumbersTab[i][j]));
}
Lineline=newLine(values);
line.setColor(ChartUtils.COLORS[i]);
line.setShape(shape);
line.setCubic(isCubic);
line.setFilled(isFilled);
line.setHasLabels(hasLabels);
line.setHasLabelsOnlyForSelected(hasLabelForSelected);
line.setHasLines(hasLines);
line.setHasPoints(hasPoints);
if(pointsHaveDifferentColor){
line.setPointColor(ChartUtils.COLORS[(i+1)
%ChartUtils.COLORS.length]);
}
lines.add(line);
}
data=newLineChartData(lines);
if(hasAxes){
AxisaxisX=newAxis();
AxisaxisY=newAxis().setHasLines(true);
if(hasAxesNames){
axisX.setName("AxisX");
axisY.setName("AxisY");
}
data.setAxisXBottom(axisX);
data.setAxisYLeft(axisY);
}else{
data.setAxisXBottom(null);
data.setAxisYLeft(null);
}
data.setBaseValue(Float.NEGATIVE_INFINITY);
chart.setLineChartData(data);
}
/
Addslinestodata,afterthatdatashouldbesetagainwith
{@linkLineChartView#setLineChartData(LineChartData)}.Last4thline
hasnon-monotonicallyxvalues.
/
privatevoidaddLineToData(){
if(data.getLines().size()>=maxNumberOfLines){
Toast.makeText(getActivity(),"Samplesappusesmax4lines!",
Toast.LENGTH_SHORT).show();
return;
}else{
++numberOfLines;
}
generateData();
}
privatevoidtoggleLines(){
hasLines=!hasLines;
generateData();
}
privatevoidtogglePoints(){
hasPoints=!hasPoints;
generateData();
}
privatevoidtoggleCubic(){
isCubic=!isCubic;
generateData();
if(isCubic){
/
手动设置高一点最大立方行,因为有时超过或低于最大值/最小值。为此使用Viewport.inest()
方法并传递负值作为dy参数或手动设置顶部和底部的值
。在这个例子中我知道Y值(0100)范围内我手动设置视口高度范围(105)
。让这个作品在动画应该使用Chart.setViewportCalculationEnabled
(false)之前修改窗口。记住你叫setLineChartData后设置窗口()。
/
finalViewportv=newViewport(chart.getMaximumViewport());
v.bottom=-5;
v.top=105;
//Youhavetosetmaxandcurrentviewportsseparately.
chart.setMaximumViewww.shanxiwang.netwport(v);
//Ichangingcurrentviewportwithanimationinthiscase.
chart.setCurrentViewportWithAnimation(v);
}else{
//Ifnotcubicrestoreviewportto(0,100)range.
finalViewportv=newViewport(chart.getMaximumViewport());
v.bottom=0;
v.top=100;
/
你必须单独设置最大和当前视窗。在这种情况下,如果我想要动画我必须先设置currentviewport和使用动画侦听器。
最大视窗将onAnimationFinished方法。
/
chart.setViewportAnimationListener(newChartAnimationListener(){
@Override
publicvoidonAnimationStarted(){
//TODOAuto-generatedmethodstub
}
@Override
publicvoidonAnimationFinished(){
//设置最大viewpirt和删除侦听器。
chart.setMaximumViewport(v);
chart.setViewportAnimationListener(null);
}
});
//设置当前viewpirt动画;
chart.setCurrentViewportWithAnimation(v);
}
}
privatevoidtoggleFilled(){
isFilled=!isFilled;
generateData();
}
privatevoidtogglePointColor(){
pointsHaveDifferentColor=!pointsHaveDifferentColor;
generateData();
}
privatevoidsetCircles(){
shape=ValueShape.CIRCLE;
generateData();
}
privatevoidsetSquares(){
shape=ValueShape.SQUARE;
generateData();
}
privatevoidsetDiamonds(){
shape=ValueShape.DIAMOND;
generateData();
}
privatevoidtoggleLabels(){
hasLabels=!hasLabels;
if(hasLabels){
hasLabelForSelected=false;
chart.setValueSelectionEnabled(hasLabelForSelected);
}
generateData();
}
privatevoidtoggleLabelForSelected(){
hasLabelForSelected=!hasLabelForSelected;
chart.setValueSelectionEnabled(hasLabelForSelected);
if(hasLabelForSelected){
hasLabels=false;
}
generateData();
}
privatevoidtoggleAxes(){
hasAxes=!hasAxes;
generateData();
}
privatevoidtoggleAxesNames(){
hasAxesNames=!hasAxesNames;
generateData();
}
/
动画值你必须改变目标的值,然后调用{@link图表#
startDataAnimation()}方法(不要混淆View.animate())。如果你操作数据之前设置你不必叫{@link
LineChartView#setLineChartData(LineChartData)}。
/
privatevoidprepareDataAnimation(){
for(Lineline:data.getLines()){
for(PointValuevalue:line.getValues()){
//这里我只修改目标XY值,但可以修改目标。
value.setTarget(value.getX(),(float)Math.random()100);
}
}
}
privateclassValueTouchListenerimplements
LineChartOnValueSelectListener{
@Override
publicvoidonValueSelected(intlineIndex,intpointIndex,
PointValuevalue){
Toast.makeText(getActivity(),"选择:"+value,
Toast.LENGTH_SHORT).show();
}
@Override
publicvoidonValueDeselected(){
//TODOAuto-generatedmethodstub
}
}
}
}
从上面其实可以很容易的就看出,我们一个activity然后add了一个fragment,而一个fragment的实现,有一个xml,在xml中就要用上自定义的view的标签了,而所有的操作,都在menu的菜单上,每个菜单对应的是一个方法,看上去是很多的代码,其实只要你耐心一下,你会发现很多的规律,而且这里也只是才去了随机数,项目中的数据可改性还是很大的,下面的就不一一说明了,现在打字都很卡,编辑器的原因,然道是内容太多了?可是这个原理也不好分章节去写,毕竟有12个重复的写法,那样就太无聊了,我们还是来直接说一下我上次的这个demo的结构
这个我整理过,所以你只要导入hellocharts-library和hellocharts-samples,然后让hellocharts-samples依赖hellocharts-library就可以直接运行了
我们接下来再放几张运行图就直接上Demo吧
|
|