Android绘图机制(四)——使用HelloCharts开源框架搭建一系列炫酷图表,柱形图,折线图,饼状图和动画特效,抽丝剥茧带你认识图表之美


这里为什么不继续把自定义View写下去呢,因为最近项目很急,个人能力也有限,所以就在网上找到一些开源的框架使用,不是MPAndroidChart,也不是AChartEngine ,而是HelloCharts

开源地 址:https://github.com/lecho/hellocharts-android

这次主要是翻译了一些注释和简化了一下项目的结构,我也是照着开源项目来的,但是我相信你看完本篇,也是可以直接把项目拿过来用的,最好是使用我提供的的Demo,这样就跟本文一一对照了,我们一步步来

首先我们来看看官方的

一.官方截图

1.Bubble Chart

2.Combo Chart

3.Line Column Dependency

4.Pie Chart

5.Preview Column Chart

6.Tempo line chart

二.项目架构

这里我们首先来看一下我排版的思维导图

上面比较明确的说明了,首先我们主页是一个ListView



然后跳转一个Activity

@Override
        public void onItemClick(AdapterView<?> adapter, View view,
                int position, long id) {
            Intent intent;

            switch (position) {
            case 0:
                // Line Chart;
                intent = new Intent(getActivity(), LineChartActivity.class);
                startActivity(intent);
                break;
            case 1:
                // Column Chart;
                intent = new Intent(getActivity(), ColumnChartActivity.class);
                startActivity(intent);
                break;
            case 2:
                // Pie Chart;
                intent = new Intent(getActivity(), PieChartActivity.class);
                startActivity(intent);
                break;
            case 3:
                // Bubble Chart;
                intent = new Intent(getActivity(), BubbleChartActivity.class);
                startActivity(intent);
                break;
            case 4:
                // Preview Line Chart;
                intent = new Intent(getActivity(),
                        PreviewLineChartActivity.class);
                startActivity(intent);
                break;
            case 5:
                // Preview Column Chart;
                intent = new Intent(getActivity(),
                        PreviewColumnChartActivity.class);
                startActivity(intent);
                break;
            case 6:
                // Combo Chart;
                intent = new Intent(getActivity(),
                        ComboLineColumnChartActivity.class);
                startActivity(intent);
                break;
            case 7:
                // Line Column Dependency;
                intent = new Intent(getActivity(),
                        LineColumnDependencyActivity.class);
                startActivity(intent);
                break;
            case 8:
                // Tempo line chart;
                intent = new Intent(getActivity(), TempoChartActivity.class);
                startActivity(intent);
                break;
            case 9:
                // Speed line chart;
                intent = new Intent(getActivity(), SpeedChartActivity.class);
                startActivity(intent);
                break;
            case 10:
                // Good Bad filled line chart;
                intent = new Intent(getActivity(), GoodBadChartActivity.class);
                startActivity(intent);
                break;
            case 11:
                // Good Bad filled line chart;
                intent = new Intent(getActivity(),
                        ViewPagerChartsActivity.class);
                startActivity(intent);
                break;
            default:
                break;
            }
        }

Activity继承的是FragmentActivity,我们在FragmentActivity内部编写一个Fragment这样就可以绑定主Activity而不用继承自View去多写一个类了

public static class PlaceholderFragment extends Fragment 

所以你看到的项目也是非常简洁的



项目只有一个主Activity——MainActivity和一个关于软件的AboutActivity,然后就是十二个对应的类了

好了,我们可以编写了

三,实现图标

1.折线图

简单的实现折线图是可以的,这里多了几个注意的地方


从这里我们就可以看出,其实图表操作主要还是看menu菜单,所以,我们先实现

我们fragment要绑定的布局

fragment_line_chart

<lecho.lib.hellocharts.view.LineChartView
        android:id="@+id/chart"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </lecho.lib.hellocharts.view.LineChartView>

LineChartActivity

package lecho.lib.hellocharts.samples;

import java.util.ArrayList;
import java.util.List;

import lecho.lib.hellocharts.animation.ChartAnimationListener;
import lecho.lib.hellocharts.gesture.ZoomType;
import lecho.lib.hellocharts.listener.LineChartOnValueSelectListener;
import lecho.lib.hellocharts.model.Axis;
import lecho.lib.hellocharts.model.Line;
import lecho.lib.hellocharts.model.LineChartData;
import lecho.lib.hellocharts.model.PointValue;
import lecho.lib.hellocharts.model.ValueShape;
import lecho.lib.hellocharts.model.Viewport;
import lecho.lib.hellocharts.util.ChartUtils;
import lecho.lib.hellocharts.view.Chart;
import lecho.lib.hellocharts.view.LineChartView;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

public class LineChartActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_line_chart);
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, new PlaceholderFragment()).commit();
        }
    }

    /**
     * A fragment containing a line chart.
     */
    public static class PlaceholderFragment extends Fragment {

        private LineChartView chart;
        private LineChartData data;
        private int numberOfLines = 1;
        private int maxNumberOfLines = 4;
        private int numberOfPoints = 12;

        float[][] randomNumbersTab = new float[maxNumberOfLines][numberOfPoints];

        private boolean hasAxes = true;
        private boolean hasAxesNames = true;
        private boolean hasLines = true;
        private boolean hasPoints = true;
        private ValueShape shape = ValueShape.CIRCLE;
        private boolean isFilled = false;
        private boolean hasLabels = false;
        private boolean isCubic = false;
        private boolean hasLabelForSelected = false;
        private boolean pointsHaveDifferentColor;

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            setHasOptionsMenu(true);
            View rootView = inflater.inflate(R.layout.fragment_line_chart,
                    container, false);

            chart = (LineChartView) rootView.findViewById(R.id.chart);
            chart.setOnValueTouchListener(new ValueTouchListener());

            // Generate some randome values.
            generateValues();

            generateData();

            // Disable viewpirt recalculations, see toggleCubic() method for
            // more info.
            chart.setViewportCalculationEnabled(false);

            resetViewport();

            return rootView;
        }

        // MENU
        @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
            inflater.inflate(R.menu.line_chart, menu);
        }

    //menu的操作
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            int id = item.getItemId();
            if (id == R.id.action_reset) {
                reset();
                generateData();
                return true;
            }
            if (id == R.id.action_add_line) {
                addLineToData();
                return true;
            }
            if (id == R.id.action_toggle_lines) {
                toggleLines();
                return true;
            }
            if (id == R.id.action_toggle_points) {
                togglePoints();
                return true;
            }
            if (id == R.id.action_toggle_cubic) {
                toggleCubic();
                return true;
            }
            if (id == R.id.action_toggle_area) {
                toggleFilled();
                return true;
            }
            if (id == R.id.action_point_color) {
                togglePointColor();
                return true;
            }
            if (id == R.id.action_shape_circles) {
                setCircles();
                return true;
            }
            if (id == R.id.action_shape_square) {
                setSquares();
                return true;
            }
            if (id == R.id.action_shape_diamond) {
                setDiamonds();
                return true;
            }
            if (id == R.id.action_toggle_labels) {
                toggleLabels();
                return true;
            }
            if (id == R.id.action_toggle_axes) {
                toggleAxes();
                return true;
            }
            if (id == R.id.action_toggle_axes_names) {
                toggleAxesNames();
                return true;
            }
            if (id == R.id.action_animate) {
                prepareDataAnimation();
                chart.startDataAnimation();
                return true;
            }
            if (id == R.id.action_toggle_selection_mode) {
                toggleLabelForSelected();

                Toast.makeText(
                        getActivity(),
                        "Selection mode set to "
                                + chart.isValueSelectionEnabled()
                                + " select any point.", Toast.LENGTH_SHORT)
                        .show();
                return true;
            }
            if (id == R.id.action_toggle_touch_zoom) {
                chart.setZoomEnabled(!chart.isZoomEnabled());
                Toast.makeText(getActivity(),
                        "IsZoomEnabled " + chart.isZoomEnabled(),
                        Toast.LENGTH_SHORT).show();
                return true;
            }
            if (id == R.id.action_zoom_both) {
                chart.setZoomType(ZoomType.HORIZONTAL_AND_VERTICAL);
                return true;
            }
            if (id == R.id.action_zoom_horizontal) {
                chart.setZoomType(ZoomType.HORIZONTAL);
                return true;
            }
            if (id == R.id.action_zoom_vertical) {
                chart.setZoomType(ZoomType.VERTICAL);
                return true;
            }
            return super.onOptionsItemSelected(item);
        }

        private void generateValues() {
            for (int i = 0; i < maxNumberOfLines; ++i) {
                for (int j = 0; j < numberOfPoints; ++j) {
                    randomNumbersTab[i][j] = (float) Math.random() * 100f;
                }
            }
        }

        private void reset() {
            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();
        }

        private void resetViewport() {
            // Reset viewport height range to (0,100)
            final Viewport v = new Viewport(chart.getMaximumViewport());
            v.bottom = 0;
            v.top = 100;
            v.left = 0;
            v.right = numberOfPoints - 1;
            chart.setMaximumViewport(v);
            chart.setCurrentViewport(v);
        }

        private void generateData() {

            List<Line> lines = new ArrayList<Line>();
            for (int i = 0; i < numberOfLines; ++i) {

                List<PointValue> values = new ArrayList<PointValue>();
                for (int j = 0; j < numberOfPoints; ++j) {
                    values.add(new PointValue(j, randomNumbersTab[i][j]));
                }

                Line line = new Line(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 = new LineChartData(lines);

            if (hasAxes) {
                Axis axisX = new Axis();
                Axis axisY = new Axis().setHasLines(true);
                if (hasAxesNames) {
                    axisX.setName("Axis X");
                    axisY.setName("Axis Y");
                }
                data.setAxisXBottom(axisX);
                data.setAxisYLeft(axisY);
            } else {
                data.setAxisXBottom(null);
                data.setAxisYLeft(null);
            }

            data.setBaseValue(Float.NEGATIVE_INFINITY);
            chart.setLineChartData(data);

        }

        /**
         * Adds lines to data, after that data should be set again with
         * {@link LineChartView#setLineChartData(LineChartData)}. Last 4th line
         * has non-monotonically x values.
         */
        private void addLineToData() {
            if (data.getLines().size() >= maxNumberOfLines) {
                Toast.makeText(getActivity(), "Samples app uses max 4 lines!",
                        Toast.LENGTH_SHORT).show();
                return;
            } else {
                ++numberOfLines;
            }

            generateData();
        }

        private void toggleLines() {
            hasLines = !hasLines;

            generateData();
        }

        private void togglePoints() {
            hasPoints = !hasPoints;

            generateData();
        }

        private void toggleCubic() {
            isCubic = !isCubic;

            generateData();

            if (isCubic) {
                /**
                 * 手动设置高一点最大立方行,因为有时超过或低于最大值/最小值。为此使用Viewport.inest()
                 * 方法并传递负值作为dy参数或手动设置顶部和底部的值
                 * 。在这个例子中我知道Y值(0100)范围内我手动设置视口高度范围(105)
                 * 。让这个作品在动画应该使用Chart.setViewportCalculationEnabled
                 * (false)之前修改窗口。记住你叫setLineChartData后设置窗口()。
                 */
                final Viewport v = new Viewport(chart.getMaximumViewport());
                v.bottom = -5;
                v.top = 105;
                // You have to set max and current viewports separately.
                chart.setMaximumViewport(v);
                // I changing current viewport with animation in this case.
                chart.setCurrentViewportWithAnimation(v);
            } else {
                // If not cubic restore viewport to (0,100) range.
                final Viewport v = new Viewport(chart.getMaximumViewport());
                v.bottom = 0;
                v.top = 100;

                /**
                 * 你必须单独设置最大和当前视窗。在这种情况下,如果我想要动画我必须先设置currentviewport和使用动画侦听器。
                 * 最大视窗将onAnimationFinished方法。
                 */
                chart.setViewportAnimationListener(new ChartAnimationListener() {

                    @Override
                    public void onAnimationStarted() {
                        // TODO Auto-generated method stub

                    }

                    @Override
                    public void onAnimationFinished() {
                        // 设置最大viewpirt和删除侦听器。
                        chart.setMaximumViewport(v);
                        chart.setViewportAnimationListener(null);

                    }
                });
                // 设置当前viewpirt动画;
                chart.setCurrentViewportWithAnimation(v);
            }

        }

        private void toggleFilled() {
            isFilled = !isFilled;

            generateData();
        }

        private void togglePointColor() {
            pointsHaveDifferentColor = !pointsHaveDifferentColor;

            generateData();
        }

        private void setCircles() {
            shape = ValueShape.CIRCLE;

            generateData();
        }

        private void setSquares() {
            shape = ValueShape.SQUARE;

            generateData();
        }

        private void setDiamonds() {
            shape = ValueShape.DIAMOND;

            generateData();
        }

        private void toggleLabels() {
            hasLabels = !hasLabels;

            if (hasLabels) {
                hasLabelForSelected = false;
                chart.setValueSelectionEnabled(hasLabelForSelected);
            }

            generateData();
        }

        private void toggleLabelForSelected() {
            hasLabelForSelected = !hasLabelForSelected;

            chart.setValueSelectionEnabled(hasLabelForSelected);

            if (hasLabelForSelected) {
                hasLabels = false;
            }

            generateData();
        }

        private void toggleAxes() {
            hasAxes = !hasAxes;

            generateData();
        }

        private void toggleAxesNames() {
            hasAxesNames = !hasAxesNames;

            generateData();
        }

        /**
         * 动画值你必须改变目标的值,然后调用{ @link图表#
         * startDataAnimation()}方法(不要混淆View.animate())。如果你操作数据之前设置你不必叫{ @link
         * LineChartView # setLineChartData(LineChartData)}。
         */
        private void prepareDataAnimation() {
            for (Line line : data.getLines()) {
                for (PointValue value : line.getValues()) {
                    // 这里我只修改目标X Y值,但可以修改目标。
                    value.setTarget(value.getX(), (float) Math.random() * 100);
                }
            }
        }

        private class ValueTouchListener implements
                LineChartOnValueSelectListener {

            @Override
            public void onValueSelected(int lineIndex, int pointIndex,
                    PointValue value) {
                Toast.makeText(getActivity(), "选择: " + value,
                        Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onValueDeselected() {
                // TODO Auto-generated method stub

            }

        }
    }
}

从上面其实可以很容易的就看出,我们一个activity然后add了一个fragment,而一个fragment的实现,有一个xml,在xml中就要用上自定义的view的标签了,而所有的操作,都在menu的菜单上,每个菜单对应的是一个方法,看上去是很多的代码,其实只要你耐心一下,你会发现很多的规律,而且这里也只是才去了随机数,项目中的数据可改性还是很大的,下面的就不一一说明了,现在打字都很卡,编辑器的原因,然道是内容太多了?可是这个原理也不好分章节去写,毕竟有12个重复的写法,那样就太无聊了,我们还是来直接说一下我上次的这个demo的结构



这个我整理过,所以你只要导入hellocharts-library和hellocharts-samples,然后让hellocharts-samples依赖hellocharts-library就可以直接运行了

我们接下来再放几张运行图就直接上Demo吧











Demo下载地址:http://download.csdn.net/detail/qq_26787115/9406897

Android绘图机制(四)——使用HelloCharts开源框架搭建一系列炫酷图表,柱形图,折线图,饼状图和动画特效,抽丝剥茧带你认识图表之美的更多相关文章

  1. Android图表库MPAndroidChart(八)——饼状图的扩展:折线饼状图

    Android图表库MPAndroidChart(八)--饼状图的扩展:折线饼状图 我们接着上文,饼状图的扩展,增加折现的说明,来看下我们要实现的效果 因为之前对MPAndroidChart的熟悉,所 ...

  2. Android图表库MPAndroidChart(七)—饼状图可以再简单一点

    Android图表库MPAndroidChart(七)-饼状图可以再简单一点 接上文,今天实现的是用的很多的,作用在统计上的饼状图,我们看下今天的效果 这个效果,我们实现,和之前一样的套路,我先来说下 ...

  3. Android群英传》读书笔记 (3) 第六章 Android绘图机制与处理技巧 + 第七章 Android动画机制与使用技巧

    第六章 Android绘图机制与处理技巧 1.屏幕尺寸信息屏幕大小:屏幕对角线长度,单位“寸”:分辨率:手机屏幕像素点个数,例如720x1280分辨率:PPI(Pixels Per Inch):即DP ...

  4. Android群英传笔记——第六章:Android绘图机制与处理技巧

    Android群英传笔记--第六章:Android绘图机制与处理技巧 一直在情调,时间都是可以自己调节的,不然世界上哪有这么多牛X的人 今天就开始读第六章了,算日子也刚好一个月了,一个月就读一半,这效 ...

  5. Android绘图机制(一)——自定义View的基础属性和方法

    Android绘图机制(一)--自定义View的基础属性和方法 自定义View看起来,确实看起来高深莫测,很多Android开发都不是特别在行这一块,这里面的逻辑以及一些绘画都是有一点难的,说一下我目 ...

  6. Android图片加载与缓存开源框架:Android Glide

    <Android图片加载与缓存开源框架:Android Glide> Android Glide是一个开源的图片加载和缓存处理的第三方框架.和Android的Picasso库类似,个人感觉 ...

  7. Android绘图机制(三)——自定义View的实现方式以及半弧圆新控件

    Android绘图机制(三)--自定义View的三种实现方式以及实战项目操作 在Android绘图机制(一)--自定义View的基础属性和方法 里说过,实现自定义View有三种方式,分别是 1.对现有 ...

  8. Android绘图机制(二)——自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解

    Android绘图机制(二)--自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解 我们要想画好一些炫酷的View,首先我们得知道怎么去画一些基础的图案,比如矩形,圆 ...

  9. 开源分享三(炫酷的Android Loading动画)

    开源分享三(炫酷的Android Loading动画) 分享GitHub上的一些Loading,为了提升产品用户体验,一个好的Loading必然是不可缺少的,对于一些耗时需要用户等待的页面来说会转移用 ...

随机推荐

  1. Struts 1之DispatchAction

    DispatchAction是struts 1 的内置通用分发器 import org.apache.struts.actions.DispatchAction; public class UserA ...

  2. How to generate the complex data regularly to Ministry of Transport of P.R.C by DB Query Analyzer

    How to generate the complex data regularly to Ministry of Transport of P.R.C by DB Query Analyzer 1 ...

  3. EBS总账(GL)模块常用表

     select * from gl_sets_of_books 总帐 select * from gl_code_combinations gcc wheregcc.summary_flag='Y ...

  4. JAVA之旅(三十五)——完结篇,终于把JAVA写完了,真感概呐!

    JAVA之旅(三十五)--完结篇,终于把JAVA写完了,真感概呐! 这篇博文只是用来水经验的,写这个系列是因为我自己的java本身也不是特别好,所以重温了一下,但是手比较痒于是就写出了这三十多篇博客了 ...

  5. JVM常量池和八种基本数据及字符串

    迄今为止看到的对常量池和字符串最为透彻的解释,赞一个! 常量池(constant_pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据.它包括了关于类.方法.接口等中的常量, ...

  6. [Python]程序性能分析

    有些脚本发现比预期要慢的多,就需要找到瓶颈,然后做相应的优化,参考A guide to analyzing Python performance,也可以说是翻译. 指标 运行时间 时间瓶颈 内存使用 ...

  7. FFmpeg示例程序合集-批量编译脚本

    此前做了一系列有关FFmpeg的示例程序,组成了<最简单的FFmpeg示例程序合集>,其中包含了如下项目:simplest ffmpeg player:                   ...

  8. 使用maven将项目打成jar包

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  9. android sdutio常用快捷键

    快捷键 说明 F2 定位到高亮错误或警告的位置 F4 若选中项目,打开 Project Struture F5 复制文件 Alt+F3 选中文本,逐个往下查找相同文本,并高亮显示 Alt+F1 可以将 ...

  10. 并发编程之ThreadLocal、Volatile、synchronized、Atomic关键字扫盲

    前言 对于ThreadLocal.Volatile.synchronized.Atomic这四个关键字,我想一提及到大家肯定都想到的是解决在多线程并发环境下资源的共享问题,但是要细说每一个的特点.区别 ...