开源项目WilliamChart的学习笔记1_demo的学习

目前一个项目需要用到图表,并且需要一些酷炫屌炸天的效果,无意中搜索到git上的一个关于图表的开源库WilliamChart (应该是好多库都做了这件事,在次先拜谢各位大神牛们的,希望有一天我也可以做到奋斗

话不多说,先上图:


可以看到,一共四种图表:线性图表,正常的直方图,水平直方图以及栈直方图(就是一个条条分成了三段的那个),很漂亮有木有,关键是他还有很牛逼的动画效果(想要看到牛逼的效果,可以下载demo以及库,运行到真机看看,真的很惊艳)


首先提供WilliamChat项目以及demo的下载地址(页面中Ctrl+F关键字“WilliamChat”)
https://github.com/Trinea/android-open-project


另外,demo还需要support-v7的支持
,下面讲一下怎么使demo在你的机器上跑起来:

a.将demo,WilliamChat,以及v7都导入到eclipse,v7在sdk的extras\android\support\v7\appcompat目录
b.将v7以及WilliamChat项目作为demo的library,右击demo->properties->Android->add,将WilliamChat和v7都加入进来

如果app是在服务器上用hudson编译,需要集成jar包,推荐使用fatjar,官方还是031版本,有大牛提供了032版本,eclipse的luna版本可用。
032版fatjar下载地址如下:
http://download.csdn.net/detail/finnfu/8638761
如果大家实在不想做,我已经做好这个jar包,拿去用吧:
http://download.csdn.net/download/finnfu/8642755



本文首先介绍一下demo,熟悉一下这个的WilliamChat图标库的基本用法,争取下一篇可以分析一下这个开源库的基本实现。
如果已经在真机上跑起来的童鞋可以看到,上面是四个图表,分别是线性图表,正常直方图,横向直方图,栈图表(本文以此为例,其它三个用法一样)
最下面的一排是五个按钮,前四个是设置按钮,最后一个是播放按钮。首先介绍一下各个按钮的作用
右一是play按钮:第一次点击是图表更新的动画,第二次点击是图表消失以及再次出现的动画,动画过程中play按钮为不可点击状态。
右二是动画效果的设置按钮,包括图表消失,出现以及更新的动画,demo中展示了4种,库中一共有10种
右三是图表各项出现/消失顺序的设置按钮,是从左到右/从右到左/从中间到两边
右四是图表出现的起始点/消失的终点设置,动画的开始位置以及结束位置
右五是透明度设置,动画出现/消失的透明度渐变
另外,当点击了各个设置按钮后,play按钮会高亮,此时点击为消失/出现的动画;图表各项都可点击出现数值(tips)


以tackchart为例介绍,贴出其变量的介绍:

<span style="font-size:14px;"><span style="white-space:pre">	</span>/*
	 * StackBar
	 */
	//纵轴的坐标值范围
	private final static int STACKBAR_MAX = 100;
	private final static int STACKBAR_MIN = 0;
	//没一个条条对应的字符串,每个字符串为三个字符,对应每个条条的三小段
	private final static String[] stackBarLabels = { "YAK", "ANT", "GNU",
			"OWL", "APE", "JAY", "COD"};
	//第一行数组为每个条第一小段(最上面)的实际数值,依次类推
	private final static float[][] stackBarValues = {
			{ 30f, 40f, 25f, 25f, 40f, 25f, 25f},
			{ 30f, 30f, 25f, 40f, 25f, 30f, 40f,},
			{ 30f, 30f, 25f, 25f, 25f, 25f, 25f} };
	private static StackBarChartView mStackBarChart;
	private Paint mStackBarThresholdPaint;
	//tips,点击数据区域会显示的数值或者字母
	private TextView mStackBarTooltip;</span>


从设置功能开始:


透明度的设置

<span style="font-size:14px;"><span style="white-space:pre">	</span>/*
	 * 点击设置透明度按钮时调用
	 * @param index 3个透明度等级
	 * setImageAlpha是在API 16中新加的,设置范围是0-255
	 * setAlpha从API 1就用,但是在API16中不推荐使用,设置范围是0f-1f
	 */
	@SuppressLint("NewApi")
	private void setAlpha(int index) {

		switch (index) {
		case 0:
			mCurrAlpha = -1;
			if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
				mAlphaBtn.setImageAlpha(255);
			else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB)
				mAlphaBtn.setAlpha(1f);
			break;
		case 1:
			mCurrAlpha = 2;
			if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
				mAlphaBtn.setImageAlpha(115);
			else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB)
				mAlphaBtn.setAlpha(.6f);
			break;
		case 2:
			mCurrAlpha = 1;
			if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
				mAlphaBtn.setImageAlpha(55);
			else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB)
				mAlphaBtn.setAlpha(.3f);
			break;
		default:
			break;
		}
	}</span>


动画出现消失位置的设置:

<span style="font-size:14px;"><span style="white-space:pre">	</span>/*
	 * 点击位置设置按钮时调用
	 * @param index 9处位置设置
	 * mCurrStartX:0左,1右,-1无效
	 * mCurrStartY:0底,1顶,-1无效
	 * 一共8个组合:
	 * (-1,0):底部    (-1,1):顶部     (0,-1):左边    (1,-1):右边
	 * (0,0):左下       (0,1):左上        (1,0):右下       (1,1):右上
	 */
	private void setEnterPosition(int index) {

		switch (index) {
		case 0:
			mCurrStartX = -1f;
			mCurrStartY = 0f;
			mEnterBtn.setImageResource(R.drawable.enterb);
			break;
		case 1:
			mCurrStartX = 0f;
			mCurrStartY = 0f;
			mEnterBtn.setImageResource(R.drawable.enterbl);
			break;
		case 2:
			mCurrStartX = 0f;
			mCurrStartY = -1f;
			mEnterBtn.setImageResource(R.drawable.enterl);
			break;
		case 3:
			mCurrStartX = 0f;
			mCurrStartY = 1f;
			mEnterBtn.setImageResource(R.drawable.entertl);
			break;
		case 4:
			mCurrStartX = -1f;
			mCurrStartY = 1f;
			mEnterBtn.setImageResource(R.drawable.entert);
			break;
		case 5:
			mCurrStartX = 1f;
			mCurrStartY = 1f;
			mEnterBtn.setImageResource(R.drawable.entertr);
			break;
		case 6:
			mCurrStartX = 1f;
			mCurrStartY = -1f;
			mEnterBtn.setImageResource(R.drawable.enterr);
			break;
		case 7:
			mCurrStartX = 1f;
			mCurrStartY = 0f;
			mEnterBtn.setImageResource(R.drawable.enterbr);
			break;
		case 8:
			mCurrStartX = .5f;
			mCurrStartY = .5f;
			mEnterBtn.setImageResource(R.drawable.enterc);
			break;
		default:
			break;
		}
	}</span>

图表出现/消失/更新的动画效果设置:

<span style="font-size:14px;"><span style="white-space:pre">	</span>/*
	 * 点击设置消失动画的按钮调用
	 * @param index 此处用了4种,library里面一共定义了10种
	 */
	private void setEasing(int index) {

		switch (index) {
		case 0:
			mCurrEasing = new CubicEaseOut();
			mEaseBtn.setImageResource(R.drawable.ease_cubic);
			break;
		case 1:
			mCurrEasing = new QuintEaseOut();
			mEaseBtn.setImageResource(R.drawable.ease_quint);
			break;
		case 2:
			mCurrEasing = new BounceEaseOut();
			mEaseBtn.setImageResource(R.drawable.ease_bounce);
			break;
		case 3:
			mCurrEasing = new ElasticEaseOut();
			mEaseBtn.setImageResource(R.drawable.ease_elastic);
		default:
			break;
		}
	}</span>


图表中各项出现/消失/更新的顺序设置,这里需要之一的是顺序与图表数据个数是相关的,所以在自己实现的时候需要注意:

<span style="font-size:14px;"><span style="white-space:pre">	</span>/*
	 * 点击顺序设置按钮时调用
	 * @param index 4种消失顺序
	 * 顺序采用数组来定义:beginOrder endOrder middleOrder
	 * mCurrOverlapFactor为1代表同时开始
	 * 注意:当图表的数据发生改变的时候,上述定义顺序的数组也要相应更改
	 */
	private void setOverlap(int index) {

		switch (index) {
		case 0:
			mCurrOverlapFactor = 1;
			mCurrOverlapOrder = beginOrder;
			mOrderBtn.setImageResource(R.drawable.ordere);
			break;
		case 1:
			mCurrOverlapFactor = .5f;
			mCurrOverlapOrder = beginOrder;
			mOrderBtn.setImageResource(R.drawable.orderf);
			break;
		case 2:
			mCurrOverlapFactor = .5f;
			mCurrOverlapOrder = endOrder;
			mOrderBtn.setImageResource(R.drawable.orderl);
			break;
		case 3:
			mCurrOverlapFactor = .5f;
			mCurrOverlapOrder = middleOrder;
			mOrderBtn.setImageResource(R.drawable.orderm);
			break;
		default:
			break;
		}
	}</span>


上述四个设置函数都会在下面的函数中调用,重点关注一下调用这个函数传入布尔值就行了:

<span style="font-size:14px;"><span style="white-space:pre">	</span>/*
	 * 图标的消失出场动画以及更新动画都会调用这个函数 
	 * @param boolean newAnim 
	 * 1.为 true的时候调用的当前值mCurr
	 * 2.为false的时候调用的old值
	 * 3.在demo中可以看到,点击了设置以后,动画消失还是之前的效果,动画出现是才是设置后的效果,就因为函数传入的是false
	 */
	private Animation getAnimation(boolean newAnim) {
		if (newAnim)
			return new Animation().setAlpha(mCurrAlpha).setEasing(mCurrEasing)
					.setOverlap(mCurrOverlapFactor, mCurrOverlapOrder)
					.setStartPoint(mCurrStartX, mCurrStartY);
		else
			return new Animation().setAlpha(mOldAlpha).setEasing(mOldEasing)
					.setOverlap(mOldOverlapFactor, mOldOverlapOrder)
					.setStartPoint(mOldStartX, mOldStartY);
	}</span>

getAnimation会在play的onclick以及updateStackBarChart(以stackchart为例)调用,下面来分析一下这这两个函数。在play按钮的onclick中,终点关注一下mNewInstance的变化,初始化为false,点击了设置按钮置为true,点击play置反。dismissAllTooltips为所有的tips消失,dismiss为所有的图表消失,关注一下mExitEndAction

<span style="font-size:14px;"><pre name="code" class="java"><span style="white-space:pre">	</span>/*
	 * 设置play按钮的监听事件,点击发生如下事件
	 * 1.数值显示消失
	 * 2.如果mNewInstance为true则执行图表收起再出现的动画
	 * 3.如果mNewInstance为false则执行图表更新的动画
	 */
	mPlayBtn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				mPlayBtn.setImageResource(R.drawable.play);
				mPlayBtn.setBackgroundResource(R.drawable.button);
				mPlayBtn.setEnabled(false);

				mLineChart.dismissAllTooltips();
				mLineTooltip = null;
				mBarChart.dismissAllTooltips();
				mBarTooltip = null;
				mHorBarChart.dismissAllTooltips();
				mHorBarTooltip = null;
				mStackBarChart.dismissAllTooltips();
				mStackBarTooltip = null;

				if (mNewInstance) {
					mLineChart.dismiss(getAnimation(false).setEndAction(null));
					mBarChart.dismiss(getAnimation(false).setEndAction(null));
					mHorBarChart
							.dismiss(getAnimation(false).setEndAction(null));
					mStackBarChart.dismiss(getAnimation(false).setEndAction(
							mExitEndAction));
				} else {
					updateValues(mLineChart);
					updateValues(mBarChart);
					updateValues(mHorBarChart);
					updateValues(mStackBarChart);
				}
				mNewInstance = !mNewInstance;
			}
		});

	/*
	 * 在动画消失后500毫秒延迟执行
	 * 1.mCurr值赋给old值
	 * 2.调用update使图表出现
	 * */
		private final Runnable mExitEndAction = new Runnable() {
		@Override
		public void run() {
			mHandler.postDelayed(new Runnable() {
				public void run() {
					mOldOverlapFactor = mCurrOverlapFactor;
					mOldOverlapOrder = mCurrOverlapOrder;
					mOldEasing = mCurrEasing;
					mOldStartX = mCurrStartX;
					mOldStartY = mCurrStartY;
					mOldAlpha = mCurrAlpha;
					updateLineChart();
					updateBarChart();
					updateHorBarChart();
					updateStackBarChart();
				}
			}, 500);
		}
	};</span>
 

再来看一下updateStackBarChart函数,各种初始化完毕,最后调用getAnimation函数show出来,可以关注一下mEnterEndAction:

<span style="font-size:14px;"><span style="white-space:pre">	</span>/*
	 * 1.每个数据对应一个bar,bar添加到stackBarSet,stackBarSet添加到mStackBarChart,三个for循环对应三段
	 * 2.以及图表的相关设置:每个图表的每一段的颜色,各种间距,圆角半径,刻度,坐标轴,动画等
	 * */
	private void updateStackBarChart() {

		mStackBarChart.reset();

		BarSet stackBarSet = new BarSet();
		Bar bar;
		
		for (int i = 0; i < stackBarLabels.length; i++) {
			bar = new Bar(stackBarLabels[i], stackBarValues[0][i]);
			if (i == 2)
				bar.setColor(this.getResources().getColor(
						R.color.stackbar_fill1_h));
			else
				bar.setColor(this.getResources().getColor(
						R.color.stackbar_fill1));
			stackBarSet.addBar(bar);
		}
		mStackBarChart.addData(stackBarSet);

		stackBarSet = new BarSet();
		for (int i = 0; i < stackBarLabels.length; i++) {
			bar = new Bar(stackBarLabels[i], stackBarValues[1][i]);
			if (i == 2)
				bar.setColor(this.getResources().getColor(
						R.color.stackbar_fill2_h));
			else
				bar.setColor(this.getResources().getColor(
						R.color.stackbar_fill2));
			stackBarSet.addBar(bar);
		}
		mStackBarChart.addData(stackBarSet);

		stackBarSet = new BarSet();
		for (int i = 0; i < stackBarLabels.length; i++) {
			bar = new Bar(stackBarLabels[i], stackBarValues[2][i]);
			if (i == 2)
				bar.setColor(this.getResources().getColor(
						R.color.stackbar_fill3_h));
			else
				bar.setColor(this.getResources().getColor(
						R.color.stackbar_fill3));
			stackBarSet.addBar(bar);
		}
		mStackBarChart.setBackgroundColor(getResources().getColor(R.color.stackbar_background));
		mStackBarChart.addData(stackBarSet);
		//每个图表之前的间距
		mStackBarChart.setBarSpacing(Tools.fromDpToPx(20));
		//圆角的半径
		mStackBarChart.setRoundCorners(Tools.fromDpToPx(5));
		//step为纵轴的刻度值(与setAxisBorderValues里面的step设置一样),距离左右的边距
		mStackBarChart.setStep(10).setBorderSpacing(Tools.fromDpToPx(5))
				//垂直坐标的最大值最小值以及刻度
				.setAxisBorderValues(STACKBAR_MIN, STACKBAR_MAX, 25)
				//X.Y轴的相关设置,是否有轴线(有/没有),刻度在的位置(里/外/无)
				.setXAxis(false).setXLabels(XController.LabelPosition.OUTSIDE)
				.setYAxis(true).setYLabels(YController.LabelPosition.OUTSIDE)
				//画图的相关设置
				.setThresholdLine(89, mStackBarThresholdPaint)
				//刻度的格式
				.setLabelsFormat(new DecimalFormat("###'%'"))
				//进场动画的设置以及动画结束后play按钮的变化
				.show(getAnimation(true).setEndAction(mEnterEndAction))
				// .show()
				;
	}</span>


在动画执行的过程中play按钮的状态为不可点击,所以需要mEnterEndAction

<span style="font-size:14px;">//在图表出现动画结束后调用,play按钮的置为可点击
	private final Runnable mEnterEndAction = new Runnable() {
		@Override
		public void run() {
			mPlayBtn.setEnabled(true);
		}
	};</span>


现在总体的逻辑应该差不多了,最后来看看在onCreat中做了些什么:

<span style="font-size:14px;"><span style="white-space:pre">		</span>initMenu();

		initLineChart();
		initBarChart();
		initHorBarChart();
		initStackBarChart();

		updateLineChart();
		updateBarChart();
		updateHorBarChart();
		updateStackBarChart();</span>

看一下initMenu(),initStackBarChart() ,updateStackBarChart()
initMenu主要就是设置四大设置按钮以及play按钮的监听,设置的点击事件触发开始介绍的那些设置函数。
updateStackBarChart已经已经介绍过,就是图表的出现,会在play按钮点击事件触发,以及图表消失触发。
initStackBarChart就是图表的一些相关初始化工作以及相关监听器的设置

<span style="font-size:14px;">/*
	 * 1.图表空白区域的点击事件监听 
	 * 2.图表图形(数据)区域的点击事件监听 
	 * 3.画笔的设置
	 */
	private void initStackBarChart() {

		mStackBarChart = (StackBarChartView) findViewById(R.id.stackbarchart);
		mStackBarChart.setOnEntryClickListener(stackBarEntryListener);
		mStackBarChart.setOnClickListener(stackBarClickListener);

		mStackBarThresholdPaint = new Paint();
		mStackBarThresholdPaint.setColor(this.getResources().getColor(
				R.color.stackbar_line));
		mStackBarThresholdPaint.setPathEffect(new DashPathEffect(new float[] {
				10, 20 }, 0));
		mStackBarThresholdPaint.setStyle(Paint.Style.STROKE);
		mStackBarThresholdPaint.setAntiAlias(true);
		mStackBarThresholdPaint.setStrokeWidth(Tools.fromDpToPx(.75f));
	}</span>

说一下上面两个监听器:stackBarEntryListener和stackBarClickListener:

<span style="font-size:14px;">/*
	 * 图表图形(数据)区域的监听器
	 * 1.首次点击调用showStackBarTooltip,出现tip
	 * 2.非首次点击调用,调用dismissStackBarTooltip,消除之前的tip,产生新的tip
	 */
	private final OnEntryClickListener stackBarEntryListener = new OnEntryClickListener() {
		@Override
		public void onClick(int setIndex, int entryIndex, Rect rect) {
			if (mStackBarTooltip == null)
				showStackBarTooltip(setIndex, entryIndex, rect);
			else
				dismissStackBarTooltip(setIndex, entryIndex, rect);
		}
	};</span>
<span style="font-size:14px;">/*
	 * 图表空白区域的监听,点击会消除tip
	 * 其中第二个参数传入-1,所以只会消除tips
	 * */
	private final OnClickListener stackBarClickListener = new OnClickListener() {
		@Override
		public void onClick(View v) {
			if (mStackBarTooltip != null)
				dismissStackBarTooltip(-1, -1, null);
		}
	};</span>

调用showStackBarTooltip和dismissStackBarTooltip

<span style="font-size:14px;">/*
	 * 第一次点击图表(此时没有tip),用来在点击位置new一个tip并显示
	 * @param entryIndex 横轴坐标的字符串在barLabels字符数组中的下表
	 * @param setIndex 字符串的的字符的下表
	 * @param rect 为栈图表上的矩形区域,将其layoutParams设置给mStackBarTooltip
	 */
	@SuppressLint("NewApi")
	private void showStackBarTooltip(int setIndex, int entryIndex, Rect rect) {

		mStackBarTooltip = (TextView) getLayoutInflater().inflate(
				R.layout.stackbar_tooltip, null);
		mStackBarTooltip.setText("" + stackBarLabels[entryIndex].charAt(setIndex));

		LayoutParams layoutParams = new LayoutParams(rect.width(),
				rect.height());
		layoutParams.leftMargin = rect.left;
		layoutParams.topMargin = rect.top;
		mStackBarTooltip.setLayoutParams(layoutParams);

		if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
			mStackBarTooltip.setPivotX(0);
			mStackBarTooltip.setAlpha(0);
			mStackBarTooltip.setScaleX(0);
			mStackBarTooltip.animate().setDuration(200).alpha(1).scaleX(1)
					.setInterpolator(enterInterpolator);
		}

		mStackBarChart.showTooltip(mStackBarTooltip);
	}

	/*
	 * 点击空白区域/数据区域都会调用
	 * 如果点击数据区域,会传入setIndex,entryIndex,rect,所以是tips在原位置消失,新位置出现
	 * 点击空白区域时,参数为-1和null,所以只会执行tips消失的操作
	 * */
	@SuppressLint("NewApi")
	private void dismissStackBarTooltip(final int setIndex,
			final int entryIndex, final Rect rect) {

		if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
			mStackBarTooltip.animate().setDuration(100).scaleX(0).alpha(0)
					.setInterpolator(exitInterpolator)
					.withEndAction(new Runnable() {
						@Override
						public void run() {
							mStackBarChart.removeView(mStackBarTooltip);
							mStackBarTooltip = null;
							if (entryIndex != -1)
								showStackBarTooltip(setIndex, entryIndex, rect);
						}
					});
		} else {
			mStackBarChart.dismissTooltip(mStackBarTooltip);
			mStackBarTooltip = null;
			if (entryIndex != -1)
				showStackBarTooltip(setIndex, entryIndex, rect);
		}
	}</span>



以上,这个demo基本介绍完了,其它三种图表类似。有时间再研究一下这个库的实现,到时候再与大家分享。








  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值