前段时间公司项目中用到了统计图,网上找了些资料和框架都不能满足我的需求,没办法,只有自己写了。
近来清闲,将其抽出一个demo了,欢迎大家交流指正。
效果图先行
实现方案有两个,一是自定义控件,二是使用属性动画。属性动画在api11以上版本才有,在11版本以下使用可以引入nineoldandroids框架。
由于属性动画比较简单,这里主要说下自定义控件
自定义控件源码如下:
/** * 工程名: histogram * 文件名: HistogramView.java * 包名: com.style.histogram * 日期: 2014-4-21下午8:23:38 * Copyright (c) 2014 * */ package com.style.histogram; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.RelativeLayout.LayoutParams; /** * 类名: HistogramView <br/> * 功能: 柱状图. <br/> * 日期: 2014-4-21 下午8:23:38 <br/> * * @author msl * @version */ public class HistogramView extends View implements Runnable{ private static final String TAG = HistogramView.class.getSimpleName(); private int comWidth; //控件宽度 private int comHeight;//控件高度 private View rateView;//进度条 private View rateTopView; //进度条顶部View private String rateBackgroundColor;//进图条背景颜色 private int rateBackgroundId; //进图条背景图片id private Bitmap rataBackgroundBitmap; private boolean isHasRateTopView; //进度条顶部View private int rateHeight; //进度条的高 private int rateWidth; //进度条的宽 private int rateAnimValue;//进度条动画高度 private int orientation; //设置柱状图方向 private double progress;//设置进度 1为最大值 private boolean isAnim = true; //是否动画显示统计条 private Handler handler = new Handler();//动画handler private int animRate = 1; //动画速度 以每1毫秒计 private int animTime = 1;//动画延迟执行时间 private Canvas canvas;//画布 public HistogramView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public HistogramView(Context context, AttributeSet attrs) { super(context, attrs); } public HistogramView(Context context) { super(context); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //初始化控件和进度的条相关参数 comWidth = w; comHeight = h; if(orientation==LinearLayout.HORIZONTAL){ rateWidth = (int) (w*progress); rateHeight = h; }else{ rateHeight = (int) (h*progress); rateWidth = w; } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); this.canvas = canvas; Paint paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Paint.Style.FILL); Log.d(TAG, "onDraw rateBackgroundColor===="+rateBackgroundColor); if(rateBackgroundColor!=null){ drawViewWithColor(paint,isAnim); }else if(rateBackgroundId!=-1){ drawViewWithBitmap(paint, isAnim); } } /** * * drawViewWithColor:(绘制颜色进度条). <br/> * @author msl * @param paint * @param isAnim * @since 1.0 */ private void drawViewWithColor(Paint paint,boolean isAnim){ paint.setColor(Color.parseColor(rateBackgroundColor)); Log.d(TAG, "rateBackgroundColor===="+rateBackgroundColor); if(isAnim){ handler.postDelayed(this, animTime); if(orientation==LinearLayout.HORIZONTAL){//水平方向柱状图 canvas.drawRect(0, 0, rateAnimValue, comHeight, paint); }else{//垂直方向柱状图 canvas.drawRect(0, comHeight-rateAnimValue, comWidth, comHeight, paint); } }else { if(orientation==LinearLayout.HORIZONTAL){//水平方向无动画柱状图 canvas.drawRect(0, 0, rateWidth, comHeight, paint); }else{//垂直方向无动画柱状图 canvas.drawRect(0, comHeight-rateHeight, comWidth, comHeight, paint); } } } /** * * drawViewWithBitmap:(绘制图片进度条). <br/> * @author msl * @param paint * @param isAnim * @since 1.0 */ private void drawViewWithBitmap(Paint paint,boolean isAnim){ Log.d(TAG, "bitmap===="+rataBackgroundBitmap); RectF dst = null; if(isAnim){ handler.postDelayed(this, animTime); if(orientation==LinearLayout.HORIZONTAL){//水平方向柱状图 dst = new RectF(0, 0, rateAnimValue, comHeight); canvas.drawBitmap(rataBackgroundBitmap, null, dst, paint); }else{//垂直方向柱状图 dst = new RectF(0, comHeight-rateAnimValue, comWidth, comHeight); canvas.drawBitmap(rataBackgroundBitmap, null, dst, paint); } }else { if(orientation==LinearLayout.HORIZONTAL){//水平方向无动画柱状图 dst = new RectF(0, 0, rateWidth, comHeight); canvas.drawBitmap(rataBackgroundBitmap, null, dst, paint); }else{//垂直方向无动画柱状图 dst = new RectF(0, comHeight-rateHeight, comWidth, comHeight); canvas.drawBitmap(rataBackgroundBitmap, null, dst, paint); } } } public double getProgress() { return progress; } public void setProgress(double progress) { this.progress = progress; } public View getRateView() { return rateView; } public void setRateView(View rateView) { this.rateView = rateView; } public int getRateHeight() { return rateHeight; } public void setRateHeight(int rateHeight) { this.rateHeight = rateHeight; } public int getRateWidth() { return rateWidth; } public void setRateWidth(int rateWidth) { this.rateWidth = rateWidth; } public int getOrientation() { return orientation; } public void setOrientation(int orientation) { this.orientation = orientation; } public boolean isAnim() { return isAnim; } public void setAnim(boolean isAnim) { this.isAnim = isAnim; } public int getAnimRate() { return animRate; } public void setAnimRate(int animRate) { this.animRate = animRate; } public String getRateBackgroundColor() { return rateBackgroundColor; } public void setRateBackgroundColor(String rateBackgroundColor) { this.rateBackgroundColor = rateBackgroundColor; rateBackgroundId = -1; rataBackgroundBitmap = null; } public int getRateBackgroundId() { return rateBackgroundId; } public void setRateBackgroundId(int rateBackground) { this.rateBackgroundId = rateBackground; rataBackgroundBitmap = BitmapFactory.decodeResource(getResources(), rateBackgroundId); rateBackgroundColor = null; } /** * *刷新界面 * @see java.lang.Runnable#run() */ @Override public void run() { if(orientation==LinearLayout.HORIZONTAL&&(rateAnimValue<=rateWidth)){ rateAnimValue+=animRate; invalidate(); }else if(orientation==LinearLayout.VERTICAL&&(rateAnimValue<=rateHeight)){ rateAnimValue+=animRate; invalidate(); }else{ handler.removeCallbacks(this); rateAnimValue = 0; } } }
因为需要有动画效果,故HistogramView实现Runable接口,结合Handler的postDelayed方法,使柱状条慢慢增长。
1、首先我们在onSizeChanged方法中初始化控件的相关参数,控件的宽高,柱状条的宽高。当然也可以在其它方法中初始化。这个随个人喜好。
2、接下来在onDraw方法中绘制我们的柱状条。代码挺简单的,没啥好说的。主要用到了两个方法
如果我们的柱状条为颜色值,则用drawRect,它有四个参数,分别是所要绘制区域的四个顶点坐标。
如果我们的柱状条为一张图片,使用drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
bitmap表示所要绘制上去的图片,src为图片的绘制区域,如果为null表示整张图片
dst表示图片在控件上的绘制区域,paint为我们的画笔。
使用
布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="@dimen/activity_vertical_margin" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <com.style.histogram.HistogramView android:id="@+id/hv1" android:layout_width="50dp" android:layout_height="100dp" android:background="@drawable/asset_column_bg" /> <com.style.histogram.HistogramView android:id="@+id/hv2" android:layout_width="50dp" android:layout_height="100dp" android:layout_marginLeft="20dp" android:background="#ee8833" /> <com.style.histogram.HistogramView android:id="@+id/hv3" android:layout_width="50dp" android:layout_height="100dp" android:layout_marginLeft="20dp" android:background="@drawable/asset_column_bg" /> <com.style.histogram.HistogramView android:id="@+id/hv4" android:layout_width="50dp" android:layout_height="100dp" android:layout_marginLeft="20dp" android:background="#ee8833" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:orientation="vertical" > <com.style.histogram.HistogramView android:id="@+id/hv5" android:layout_width="match_parent" android:layout_height="50dp" android:background="#ee8833" /> <com.style.histogram.HistogramView android:id="@+id/hv6" android:layout_marginTop="20dp" android:layout_width="match_parent" android:layout_height="50dp" android:background="#ee8833" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:orientation="vertical" > <RelativeLayout android:layout_width="50dp" android:layout_height="100dp" android:background="@drawable/asset_column_bg"> <View android:layout_width="50dp" android:layout_height="0dp" android:background="@drawable/dq_column" android:layout_alignParentBottom="true" android:id="@+id/v1"/> </RelativeLayout> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="20dp" android:background="#ee8833"> <View android:layout_width="0dp" android:layout_height="50dp" android:background="#123456" android:id="@+id/v2"/> </FrameLayout> </LinearLayout> </LinearLayout>
java代码
package com.style.histogram; import android.os.Bundle; import android.view.View; import android.widget.LinearLayout; import android.animation.ObjectAnimator; import android.app.Activity; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); HistogramView hv1 = (HistogramView) findViewById(R.id.hv1); hv1.setProgress(0.4); hv1.setRateBackgroundId(R.drawable.dq_column); hv1.setOrientation(LinearLayout.VERTICAL); HistogramView hv2 = (HistogramView) findViewById(R.id.hv2); hv2.setProgress(0.4); hv2.setRateBackgroundColor("#123456"); hv2.setOrientation(LinearLayout.VERTICAL); HistogramView hv3 = (HistogramView) findViewById(R.id.hv3); hv3.setProgress(0.4); hv3.setRateBackgroundId(R.drawable.dq_column); hv3.setOrientation(LinearLayout.VERTICAL); hv3.setAnim(false); HistogramView hv4 = (HistogramView) findViewById(R.id.hv4); hv4.setProgress(0.4); hv4.setRateBackgroundColor("#123456"); hv4.setOrientation(LinearLayout.VERTICAL); hv4.setAnim(false); HistogramView hv5 = (HistogramView) findViewById(R.id.hv5); hv5.setProgress(0.4); hv5.setRateBackgroundColor("#123456"); hv5.setOrientation(LinearLayout.HORIZONTAL); HistogramView hv6 = (HistogramView) findViewById(R.id.hv6); hv6.setProgress(0.4); hv6.setRateBackgroundColor("#123456"); hv6.setOrientation(LinearLayout.HORIZONTAL); hv6.setAnim(false); View v1 = findViewById(R.id.v1); ObjectAnimator.ofInt(new ViewWrapper(v1), "height", 30).setDuration(4000).start(); View v2 = findViewById(R.id.v2); ObjectAnimator.ofInt(new ViewWrapper(v2), "width", 200).setDuration(4000).start(); } class ViewWrapper{ private View mTarget; public ViewWrapper(View target) { mTarget = target; } public int getWidth() { return mTarget.getLayoutParams().width; } public void setWidth(int width) { mTarget.getLayoutParams().width = width; mTarget.requestLayout(); } public int getHeight() { return mTarget.getLayoutParams().height; } public void setHeight(int height) { mTarget.getLayoutParams().height = height; mTarget.requestLayout(); } } }
没啥难的,demo下载:github