1. 程式人生 > >Android 自定義柱狀圖及屬性動畫

Android 自定義柱狀圖及屬性動畫

前段時間公司專案中用到了統計圖,網上找了些資料和框架都不能滿足我的需求,沒辦法,只有自己寫了。

近來清閒,將其抽出一個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