前段时间公司项目中用到了统计图,网上找了些资料和框架都不能满足我的需求,没办法,只有自己写了。
近来清闲,将其抽出一个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