【叨、校长】Android报表工具AChartEngine初探总结

标签: 校长 android 工具 | 发表时间:2013-05-01 22:17 | 作者:
出处:http://www.iteye.com

AChartEngine 是一个安卓系统上制作图表的框架,目前它支持如下的图表类型:
•line chart (折线图)

•area chart (面积图;分区图,对比图)
•scatter chart ( 散点图)
•time chart (时间图;进度表)
•bar chart (条形图;柱状图)
•pie chart ( 饼图)
•bubble chart (气泡图)

•doughnut chart (圆环图)

•range (high-low) bar chart (范围条形图)

•dial chart / gauge (拨号盘/压力表)

•combined (any combination of line, cubic line, scatter, bar, range bar, bubble) chart(组合图)

•cubic line chart (立方折线图)
 
上述所有支持的图表类型,都可以包含多个系列,都支持水平(默认)或垂直方式展示图表,并且支持许多其他的自定义功能。所有图表都可以建立为一个view,也可以建立为一个用于启动activity的intent.

这个模型和绘图过程的代码进行了很好优化处理,它可以处理和显示值的数量非常大。

目前为止AchartEngine最新版是1.0.0版本。

 

以下代码基于1.0.0版本: 

 

 

1.0.0版本相对以前的使用简单了很多!代码也很简洁!

下面是我整理、摸索出来的使用方法:

折线图:

首先通过阅读官方代码可以发现有俩个很重要的类:

1、接口IDemoChart:

/**
 * Copyright (C) 2009, 2010 SC 4ViewSoft SRL
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.achartengine.chartdemo.demo.chart;

import android.content.Context;
import android.content.Intent;

/**
 * Defines the demo charts.
 */
public interface IDemoChart {
  /** A constant for the name field in a list activity. */
  String NAME = "name";
  /** A constant for the description field in a list activity. */
  String DESC = "desc";

  /**
   * Returns the chart name.
   * 
   * @return the chart name
   */
  String getName();

  /**
   * Returns the chart description.
   * 
   * @return the chart description
   */
  String getDesc();

  /**
   * Executes the chart demo.
   * 
   * @param context the context
   * @return the built intent
   */
  Intent execute(Context context);

}

 

 

这个接口对实现chart图标的方法进行了抽象!

我们可以通过实现这个接口的方法来很方便的实现我们想要的Chart

在官方Demo中有一个类对此进行了实现、我们只要经常该类就可以很方便的实现我们自己的chart而不像之前版本那样的繁琐!

类AbstractDemoChart:

 

/**
 * Copyright (C) 2009, 2010 SC 4ViewSoft SRL
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.achartengine.chartdemo.demo.chart;

import java.util.Date;
import java.util.List;

import org.achartengine.chart.PointStyle;
import org.achartengine.model.CategorySeries;
import org.achartengine.model.MultipleCategorySeries;
import org.achartengine.model.TimeSeries;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.DefaultRenderer;
import org.achartengine.renderer.SimpleSeriesRenderer;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;

/**
 * An abstract class for the demo charts to extend. It contains some methods for
 * building datasets and renderers.
 */
public abstract class AbstractDemoChart implements IDemoChart {

  /**
   * Builds an XY multiple dataset using the provided values.
   * 
   * @param titles the series titles
   * @param xValues the values for the X axis
   * @param yValues the values for the Y axis
   * @return the XY multiple dataset
   */
  protected XYMultipleSeriesDataset buildDataset(String[] titles, List<double[]> xValues,
      List<double[]> yValues) {
    XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
    addXYSeries(dataset, titles, xValues, yValues, 0);
    return dataset;
  }

  public void addXYSeries(XYMultipleSeriesDataset dataset, String[] titles, List<double[]> xValues,
      List<double[]> yValues, int scale) {
    int length = titles.length;
    for (int i = 0; i < length; i++) {
      XYSeries series = new XYSeries(titles[i], scale);
      double[] xV = xValues.get(i);
      double[] yV = yValues.get(i);
      int seriesLength = xV.length;
      for (int k = 0; k < seriesLength; k++) {
        series.add(xV[k], yV[k]);
      }
      dataset.addSeries(series);
    }
  }

  /**
   * Builds an XY multiple series renderer.
   * 
   * @param colors the series rendering colors
   * @param styles the series point styles
   * @return the XY multiple series renderers
   */
  protected XYMultipleSeriesRenderer buildRenderer(int[] colors, PointStyle[] styles) {
    XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
    setRenderer(renderer, colors, styles);
    return renderer;
  }

  protected void setRenderer(XYMultipleSeriesRenderer renderer, int[] colors, PointStyle[] styles) {
    renderer.setAxisTitleTextSize(16);
    renderer.setChartTitleTextSize(20);
    renderer.setLabelsTextSize(15);
    renderer.setLegendTextSize(15);
    renderer.setPointSize(5f);
    renderer.setMargins(new int[] { 20, 30, 15, 20 });
    int length = colors.length;
    for (int i = 0; i < length; i++) {
      XYSeriesRenderer r = new XYSeriesRenderer();
      r.setColor(colors[i]);
      r.setPointStyle(styles[i]);
      renderer.addSeriesRenderer(r);
    }
  }

  /**
   * Sets a few of the series renderer settings.
   * 
   * @param renderer the renderer to set the properties to
   * @param title the chart title
   * @param xTitle the title for the X axis
   * @param yTitle the title for the Y axis
   * @param xMin the minimum value on the X axis
   * @param xMax the maximum value on the X axis
   * @param yMin the minimum value on the Y axis
   * @param yMax the maximum value on the Y axis
   * @param axesColor the axes color
   * @param labelsColor the labels color
   */
  protected void setChartSettings(XYMultipleSeriesRenderer renderer, String title, String xTitle,
      String yTitle, double xMin, double xMax, double yMin, double yMax, int axesColor,
      int labelsColor) {
    renderer.setChartTitle(title);
    renderer.setXTitle(xTitle);
    renderer.setYTitle(yTitle);
    renderer.setXAxisMin(xMin);
    renderer.setXAxisMax(xMax);
    renderer.setYAxisMin(yMin);
    renderer.setYAxisMax(yMax);
    renderer.setAxesColor(axesColor);
    renderer.setLabelsColor(labelsColor);
  }

  /**
   * Builds an XY multiple time dataset using the provided values.
   * 
   * @param titles the series titles
   * @param xValues the values for the X axis
   * @param yValues the values for the Y axis
   * @return the XY multiple time dataset
   */
  protected XYMultipleSeriesDataset buildDateDataset(String[] titles, List<Date[]> xValues,
      List<double[]> yValues) {
    XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
    int length = titles.length;
    for (int i = 0; i < length; i++) {
      TimeSeries series = new TimeSeries(titles[i]);
      Date[] xV = xValues.get(i);
      double[] yV = yValues.get(i);
      int seriesLength = xV.length;
      for (int k = 0; k < seriesLength; k++) {
        series.add(xV[k], yV[k]);
      }
      dataset.addSeries(series);
    }
    return dataset;
  }

  /**
   * Builds a category series using the provided values.
   * 
   * @param titles the series titles
   * @param values the values
   * @return the category series
   */
  protected CategorySeries buildCategoryDataset(String title, double[] values) {
    CategorySeries series = new CategorySeries(title);
    int k = 0;
    for (double value : values) {
      series.add("Project " + ++k, value);
    }

    return series;
  }

  /**
   * Builds a multiple category series using the provided values.
   * 
   * @param titles the series titles
   * @param values the values
   * @return the category series
   */
  protected MultipleCategorySeries buildMultipleCategoryDataset(String title,
      List<String[]> titles, List<double[]> values) {
    MultipleCategorySeries series = new MultipleCategorySeries(title);
    int k = 0;
    for (double[] value : values) {
      series.add(2007 + k + "", titles.get(k), value);
      k++;
    }
    return series;
  }

  /**
   * Builds a category renderer to use the provided colors.
   * 
   * @param colors the colors
   * @return the category renderer
   */
  protected DefaultRenderer buildCategoryRenderer(int[] colors) {
    DefaultRenderer renderer = new DefaultRenderer();
    renderer.setLabelsTextSize(15);
    renderer.setLegendTextSize(15);
    renderer.setMargins(new int[] { 20, 30, 15, 0 });
    for (int color : colors) {
      SimpleSeriesRenderer r = new SimpleSeriesRenderer();
      r.setColor(color);
      renderer.addSeriesRenderer(r);
    }
    return renderer;
  }

  /**
   * Builds a bar multiple series dataset using the provided values.
   * 
   * @param titles the series titles
   * @param values the values
   * @return the XY multiple bar dataset
   */
  protected XYMultipleSeriesDataset buildBarDataset(String[] titles, List<double[]> values) {
    XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
    int length = titles.length;
    for (int i = 0; i < length; i++) {
      CategorySeries series = new CategorySeries(titles[i]);
      double[] v = values.get(i);
      int seriesLength = v.length;
      for (int k = 0; k < seriesLength; k++) {
        series.add(v[k]);
      }
      dataset.addSeries(series.toXYSeries());
    }
    return dataset;
  }

  /**
   * Builds a bar multiple series renderer to use the provided colors.
   * 
   * @param colors the series renderers colors
   * @return the bar multiple series renderer
   */
  protected XYMultipleSeriesRenderer buildBarRenderer(int[] colors) {
    XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
    renderer.setAxisTitleTextSize(16);
    renderer.setChartTitleTextSize(20);
    renderer.setLabelsTextSize(15);
    renderer.setLegendTextSize(15);
    int length = colors.length;
    for (int i = 0; i < length; i++) {
      SimpleSeriesRenderer r = new SimpleSeriesRenderer();
      r.setColor(colors[i]);
      renderer.addSeriesRenderer(r);
    }
    return renderer;
  }

}

 

 

通过阅读这个类的源码我们可以发现、对Chart实现了封装!我们只要简单的继承该类、重写以下方法即可实现我们自己的Chart!如下代码所示:折线图的实现:

这是我自己的一个业务Chart实现:

package com.example.shishi;

import java.util.ArrayList;
import java.util.HashMap;

import org.achartengine.ChartFactory;
import org.achartengine.chart.AbstractChart;
import org.achartengine.chart.PointStyle;
import org.achartengine.chartdemo.demo.chart.AbstractDemoChart;
import org.achartengine.renderer.XYMultipleSeriesRenderer;

import com.example.service.GetSSCDate;
import com.example.service.GetSSCDate.DateStruct;

import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Paint.Align;

public class ShishiTrendChart extends AbstractDemoChart {
	
	private int _type;
	private ArrayList<String> _data;

	/**
	 * 构造方法可以实现自定义的初始化工作:如:数据的获得
	 * @param type
	 * @param data
	 */
	public ShishiTrendChart(int type,ArrayList<String> data) {
		this._data =data;
		this._type=type;
	}

	@Override
	public String getName() {
		return "号码走势图";
	}

	@Override
	public String getDesc() {
		return null;
	}

	
	
	@Override
	public Intent execute(Context context) {
		String[] legends = { "万", "千", "百", "十", "个" };// 图例名称数组
		String[] legend =new String[_type];// 图例名称数组
		for(int i=0;i<_type;i++){
			legend[i]=legends[4-i];
		}
		ArrayList<double[]> x = new ArrayList<double[]>();
		ArrayList<double[]> y = new ArrayList<double[]>();
		int size = _data.size();
		for(int j=0;j<legend.length;j++){//有几条曲线、就有几个x轴
			double[] xValues = new double[100];
			for (int i = 0; i < 100; i++) {
				xValues[i] = i + 1;
			}
			x.add(xValues);
		}
		for (int i = 1; i <= _type; i++) {//有几条曲线、就有几个y轴的坐标
			double[] yValues = new double[100];
			for (int j = 0; j < size; j++) {
				yValues[j] = getNum(_data.get(j), i);
				
			}
			y.add(yValues);
		}

		int[] color = new int[] { Color.GRAY, Color.GREEN, Color.CYAN,
				Color.YELLOW, Color.GREEN };// 设置每条曲线的颜色 默认是5条
		
		int[] colors =new int[_type];// 实际曲线数量
		for(int i=0;i<_type;i++){
			colors[i]=color[4-i];
		}
		
		PointStyle[] style = new PointStyle[] { PointStyle.POINT,
				PointStyle.DIAMOND, PointStyle.TRIANGLE, PointStyle.SQUARE,
				PointStyle.CIRCLE };//设置默认曲线的点的形状
		
		PointStyle[] styles =new PointStyle[_type];// 实际曲线
		for(int i=0;i<_type;i++){
			styles[i]=style[4-i];
		}
		
		XYMultipleSeriesRenderer renderer = buildRenderer(colors, styles);//

		setChartSettings(renderer, getName(), "期号", "号码", 0, 100, 0, 9,
				Color.GRAY, Color.GRAY);

		
		renderer.setXLabels(10);// 设置x轴显示10个点,根据setChartSettings的最大值和最小值自动计算点的间隔
		renderer.setYLabels(10);// 设置y轴显示10个点,根据setChartSettings的最大值和最小值自动计算点的间隔
		renderer.setShowGrid(true);// 是否显示网格
		
		renderer.setApplyBackgroundColor(true);
		renderer.setBackgroundColor(Color.BLACK);
		renderer.setMarginsColor(Color.BLACK);
		
		renderer.setXLabelsAlign(Align.RIGHT);// 刻度线与刻度标注之间的相对位置关系
		renderer.setYLabelsAlign(Align.CENTER);// 刻度线与刻度标注之间的相对位置关系
		renderer.setZoomButtonsVisible(true);// 是否显示放大缩小按钮
		renderer.setPanLimits(new double[] { 0, 100, -1, 10 }); // 设置拖动时X轴Y轴允许的最大值最小值.
	//	renderer.setZoomLimits(new double[] {  0, 100, -10, 20 });// 设置放大缩小时X轴Y轴允许的最大最小值.
		
		Intent intent = ChartFactory.getLineChartIntent(context,
				buildDataset(legend, x, y), renderer,
				getName());// 构建Intent
		return intent;
	}

	
	
	public double getNum(String s, int i) {
		s = s.split(",")[5-i];
		return Double.parseDouble(s);
	}
	
}

 这样的代码是不是比以前版本的实现清晰、简洁了很多!

 

以上代码根据官方实例得出!所以务必要写入官方的俩个实现类:public interface IDemoChart,public abstract class AbstractDemoChart implements IDemoChart这俩个类官方源码已经给出!复制到自己的项目当中就行!

对于饼图、柱状图就更简单了!

参考下列代码:

饼图:

package org.achartengine.chartdemo.demo.chart;

import org.achartengine.ChartFactory;
import org.achartengine.renderer.DefaultRenderer;

import android.content.Context;
import android.content.Intent;
import android.graphics.Color;

public class BudgetPieChart extends AbstractDemoChart {
  public String getName() {
    return "Budget chart";
  }

  public String getDesc() {
    return "The budget per project for this year (pie chart)";
  }
  public Intent execute(Context context) {
    double[] values = new double[] { 12, 14, 11, 10, 19 };//饼图分层5块,每块代表的数值
    int[] colors = new int[] { Color.BLUE, Color.GREEN, Color.MAGENTA, Color.YELLOW, Color.CYAN };//每块饼图的颜色
    DefaultRenderer renderer = buildCategoryRenderer(colors);
    renderer.setZoomButtonsVisible(true);//设置显示放大缩小按钮
    renderer.setZoomEnabled(true);//设置允许放大缩小.
    renderer.setChartTitleTextSize(20);//设置图表标题的文字大小
    return ChartFactory.getPieChartIntent(context, buildCategoryDataset("Project budget", values),
        renderer, "Budget");//构建Intent, buildCategoryDataset是调用AbstraDemoChart的构建方法.  
  }

}

 柱状图:

 

package org.achartengine.chartdemo.demo.chart;

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

import org.achartengine.ChartFactory;
import org.achartengine.chart.BarChart.Type;
import org.achartengine.renderer.XYMultipleSeriesRenderer;

import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Paint.Align;

public class SalesStackedBarChart extends AbstractDemoChart {
  public String getName() {
    return "Sales stacked bar chart";
  }

  public String getDesc() {
    return "The monthly sales for the last 2 years (stacked bar chart)";
  }

  public Intent execute(Context context) {
    String[] titles = new String[] { "2008", "2007" };//图例
    List<double[]> values = new ArrayList<double[]>();
    values.add(new double[] { 14230, 12300, 14240, 15244, 15900, 19200, 22030, 21200, 19500, 15500,
        12600, 14000 });//第一种柱子的数值
    values.add(new double[] { 5230, 7300, 9240, 10540, 7900, 9200, 12030, 11200, 9500, 10500,
        11600, 13500 });//第二中柱子的数值
    int[] colors = new int[] { Color.BLUE, Color.CYAN };//两种柱子的颜色
    XYMultipleSeriesRenderer renderer = buildBarRenderer(colors);//调用AbstractDemoChart中的方法构建renderer.
    setChartSettings(renderer, "Monthly sales in the last 2 years", "Month", "Units sold", 0.5,
        12.5, 0, 24000, Color.GRAY, Color.LTGRAY);//调用AbstractDemoChart中的方法设置renderer的一些属性.
    renderer.getSeriesRendererAt(0).setDisplayChartValues(true);//设置柱子上是否显示数量值
    renderer.getSeriesRendererAt(1).setDisplayChartValues(true);//设置柱子上是否显示数量值
    renderer.setXLabels(12);//X轴的近似坐标数
    renderer.setYLabels(5);//Y轴的近似坐标数
    renderer.setXLabelsAlign(Align.LEFT);//刻度线与X轴坐标文字左侧对齐
    renderer.setYLabelsAlign(Align.LEFT);//Y轴与Y轴坐标文字左对齐
    renderer.setPanEnabled(true, false);//允许左右拖动,但不允许上下拖动.
    // renderer.setZoomEnabled(false);
    renderer.setZoomRate(1.1f);//放大的倍率
    renderer.setBarSpacing(0.5f);//柱子间宽度
    return ChartFactory.getBarChartIntent(context, buildBarDataset(titles, values), renderer,
        Type.STACKED);//构建Intent, buildBarDataset是调用AbstractDemochart中的方法.
  }
}

 可以发现都是继承了抽象类AbstractDemoChart而实现的!

 

很简单吧!

以下是一些技巧:

如何设置背景色:

renderer.setApplyBackgroundColor(true);//是否可以自定义背景色
		renderer.setBackgroundColor(Color.BLACK); //chart内部的背景色
		renderer.setMarginsColor(Color.BLACK);//chart边缘部分的背景色

 以上所述由校长整理、研究所得!

 

 

 

 



已有 2 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [校长 android 工具] 推荐:

【叨、校长】Android报表工具AChartEngine初探总结

- - ITeye博客
AChartEngine 是一个安卓系统上制作图表的框架,目前它支持如下的图表类型:. •line chart (折线图). •area chart (面积图;分区图,对比图). •scatter chart ( 散点图). •time chart (时间图;进度表). •bar chart (条形图;柱状图).

优秀的Android SSH工具

- 牛牛 - Solidot
Peter Jackson 写道 Secure Shell(SSH)是工作在应用层和传输层之上的网络安全协议,利用128位公钥加密接收和发送的信息. SSH能保证两个主机之间的通信不被中间人拦截. 作为一种安全保护协议,SSH常被用于安全数据通信和远程命令执行. 其它常用用途包括使用SSH文件传输协议或安全复制协议在机器之间建立安全隧道或传输文件.

Android数据库导出工具—ADEL

- - FreebuF.COM
ADEL(Android Data Extractor Lite)这款工具可自动导出Android 2.x移动设备的SQLite数据库,并且释放下载文件中的存储内容. 以下数据库会被ADEL导出 telephone and SIM-card information (e. IMSI and serial number) telephone book and call lists, calendar entries, SMS messages, GPS locations from different sources on the smartphone.

Android Killer--安卓反编译工具

- - CSDN博客推荐文章
一个朋友写的工具,挺方便好用,发到此处,留给新手同学们学习使用. Android Killer 是一款可视化的安卓应用逆向工具,集Apk反编译、Apk打包、Apk签名,编码互转,ADB通信(应用安装-卸载-运行-设备文件管理)等特色功能于一 身,支持logcat日志输出,语法高亮,基于关键字(支持单行代码或多行代码段)项目内搜索,可自定义外部工具;吸收融汇多种工具功能与特点,打造一站 式逆向工具操作体验,大大简化了用户在安卓应用/游戏修改过程中的各类繁琐工作.

Mifare Classic Tool (MCT),Android平台RFID工具

- - FreebuF.COM
最近逛国外坛子的时候发现一新RFID神器,把介绍翻一下. Mifare Classic Tool (MCT) - 一款用来读取/编辑/分析Mifare Classic卡片的安卓软件. 大家好, 这几个月我开发了一款安卓NFC工具, 希望大家用的高兴, 同时希望大家能在github 上一同完善这款工具.

Android 自动化测试工具初探

- - IT瘾-geek
Android 自动化测试工具初探.    这段几乎都编写代码,没有新的心得体会.唯一由感想的是在测试上.由于策划的变动,接口的完善等因素,总在不停的修改功能,修改代码.由于项目中的代码都经过了好多少,又没有很好的架构规划.所以在修改或测试的时候难免会有遗漏的地方,这个时候就在想android是不是也应该有自动化测试工具来辅助测试.使得功能更完善点.本期的创新文档只能算是对自动化创新工具的一种简介..

android 应用的网络请求工具

- - ITeye博客
       Android应用开发中一般会用到访问网络请求,可以使用socket,webservice等. 这里我只是用java中URL,传送数据方式采用json,不考虑数据加密问题. 封装的工具类很简单,只提供post,get方法. 由于androidUI主线程中不允许启动线程,这里使用FutrueTask启动线程获得返回结果.

玩转android sqlLite---(附android DB的图行工具)

- - CSDN博客推荐文章
捣鼓android一年多了,总结一下sqlLite的开发经验. sqlLite就像个迷你数据库,配套的操作工具还不完善,操作起来不直观. 不像oracle、mysql那样有图形化的操作工作. 偶然在网上发现一款操作sqlLite的图形化工具  ----  SQLiteSpy(后附上链接). 嘿嘿,虽然功能还显简单,但对开发者来说,起码说看到比较直观的界面.

Android开发之2016_最流行的Android组件、工具、框架大全

- - IT瘾-geek
MaterialDesigh资源整理. light_sky@weibo 整理的MaterialDesigh资源,很多资料. 填写一个表单, 选择你要集成的一些Android开源项目,自动给你生成一个应用程序. Android Button Maker是一个可以在线生成按钮代码的工具. Android API提供了XML文件定义的几何形状的Drawable资源,包括颜色、边界和梯度.

Google正式推出Android 3.2开发工具

- star - cnBeta.COM
根据Android开发者网站消息称,近日Google正式对其开发者工具版本进行更新,新版开发者工具将主要针对Android 3.2版本的开发应用. 这是Google继优先为Motorola Xoom提供Android 3.2更新之后,首次对旧版本开发者工具全面更新. Google表示,新版开发者工具对比此前版本,有以下几个亮点:.