詳解android繪制動畫效果的曲線圖的實現

iwfb6234 8年前發布 | 22K 次閱讀 Android開發 移動開發

 安卓繪制統計圖可以用androidchart,也可以自己繪制,不像iosandroid能找到的開源庫在UI方面都很差,要做出吸引人地方還是需要自己繪制。

本文給出最常用的曲線圖的繪制方法。

繪制曲線圖首先需要畫好橫豎坐標軸建立坐標系,比如坐標系中的100距離應該在canvas中繪制多長,這個是需要計算的,其實坐標體系的建立是最復雜的,我看過很多第三方庫的建立方法都不一樣,有的要靈活一些,有的比較死板。至于繪制曲線要么是用Canvas.drawLine方法,要么是用Path.lineTo方法,看你自己的習慣。

 

為了做出一個外觀良好的曲線圖,我參考了兩個開源代碼,第一個的曲線圖繪制限制較多,使用范圍太窄,但是有數據變化時的動畫效果。第二個的適用范圍很廣,他能根據數據集合自動計算橫縱坐標的個數,在canvas上單元格的距離,只需輸入坐標點就能自動建立坐標體系繪制曲線,但是沒有動畫效果。

先講第一個LineView

 

LineViewdemo可以在這里下載,lineview其實只是github項目的一部分,我是將其提取出來了的,個人覺得他的其他部分沒有參考價值。作者好像是個韓國人。

 

LineView的曲線繪制沒有什么可取的部分,我想學習的是他實現動畫效果的方法,設計的很好,但具體實現還需要改進,讓動畫更流暢。

 

Lineview的調用方法:

xml中添加lineview控件

<HorizontalScrollView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:id="@+id/horizontalScrollView"

android:layout_alignParentRight="true"

android:layout_above="@+id/line_button">

<view

android:layout_width="wrap_content"

android:layout_height="200dp"

class="com.example.widget.LineView"

android:id="@+id/line_view"/>

</HorizontalScrollView>

activity代碼中獲取lineview對象:

finalLineView lineView = (LineView)findViewById(R.id.line_view);

添加橫坐標:

int randomint = 9;

ArrayList<String>test =newArrayList<String>();

for (int i=0;i<randomint; i++){

test.add(String.valueOf(i+1));

}

lineView.setBottomTextList(test);

允許繪制坐標點:

lineView.setDrawDotLine(true);

lineView.setShowPopup(LineView.SHOW_POPUPS_NONE);

ArrayList<Integer> dataList = newArrayList<Integer>();

intrandom = (int)(Math.random()*9+1);

for (int i=0;i<randomint; i++){

dataList.add((int)(Math.random()*random));

}

添加縱坐標的值:

ArrayList<ArrayList<Integer>>dataLists = newArrayList<ArrayList<Integer>>();

dataLists.add(dataList);

lineView.setDataList(dataLists);

 

從其用法中可以看出,lineview需要提前設定橫坐標的范圍,而且縱坐標的值必須和lineView.setBottomTextList(test)中添加的值一一對應(讀lineview源碼可以知道),使用起來很不方便,我覺得作者僅僅是做出了一條曲線而已,而不太關注是否有用。和很多曲線圖的開源代碼一樣lineview允許一次繪制幾根顏色不同的曲線。

只需在上面的代碼中為dataLists再添加一個list成員就行。

 

Lineview的實現

 

Lineviewview的直接子類public class LineView extends View,因為其繪制曲線的方法本身沒什么亮點就不講了,簡單提一下其measure方法。重點講他是如何實現動畫效果的。

其實我也不是很明白measure過程中有些代碼,自己看吧:

 

@Override

protectedvoidonMeasure(int widthMeasureSpec, intheightMeasureSpec) {

intmViewWidth = measureWidth(widthMeasureSpec);

mViewHeight =measureHeight(heightMeasureSpec);

refreshAfterDataChanged();

setMeasuredDimension(mViewWidth,mViewHeight);

}

privateintmeasureWidth(intmeasureSpec){

inthorizontalGridNum = getHorizontalGridNum();

intpreferred = backgroundGridWidth*horizontalGridNum+sideLineLength*2;

returngetMeasurement(measureSpec, preferred);

}

privateintmeasureHeight(intmeasureSpec){

intpreferred = 0;

returngetMeasurement(measureSpec,preferred);

}

privateintgetMeasurement(intmeasureSpec, intpreferred){

intspecSize = MeasureSpec.getSize(measureSpec);

intmeasurement;

switch(MeasureSpec.getMode(measureSpec)){

caseMeasureSpec.EXACTLY:

measurement = specSize;

break;

caseMeasureSpec.AT_MOST:

measurement = Math.min(preferred,specSize);

break;

default:

measurement = preferred;

break;

}

returnmeasurement;

}

 

動畫:

如何才能展現出一條曲線的變化過程呢,曲線在波動其實是縱坐標y值在上下波動,橫坐標x值是沒有變化的,但是不要緊這個辦法即使x值變化也應該可以展示出動畫來,只是可能動畫有點亂亂的感覺。

一條曲線的動畫其實是多個個點的值在不斷變化引起的,因此傳統的android動畫滿足不了這么細致的需求,得自己想辦法。

假如我們有這樣的9個點(1,2),(2,0),(3,0),(4,2),(5,1),(6,0),(7,1),(8,2),(9,1)則用lineview可以得到如下的曲線圖:

我們先預設最初的點為(1,0), (2,0), (3,0), (4,0), (5,0), (6,0), (7,0), (8,0), (9,0)

然后每隔一段時間就讓每個點的y+1,這樣就能得到動畫效果。

 

private Runnable animator = new Runnable() {

@Override

public void run() {

boolean needNewFrame = false;

for(ArrayList<Dot> data :drawDotLists){

for(Dotdot : data){

dot.update();

if(!dot.isAtRest()){

needNewFrame = true;

}

}

}

if (needNewFrame) {

postDelayed(this, 10);

}

invalidate();

}

};

 

其中postDelayed(this, 10);表示每隔10毫秒執行一次值變化,同時刷新曲線圖;dot.update();表示更新該點的值。有意思的是表示坐標的dot類,他不僅僅包含了坐標值,還有目標值:

class Dot{

intx;

inty;

intdata;

inttargetX;

inttargetY;

intlinenumber;

intvelocity =MyUtils.dip2px(getContext(),2);

Dot(int x,int y,inttargetX,int targetY,Integer data,intlinenumber){

this.x = x;

this.y = y;

this.linenumber =linenumber;

setTargetData(targetX,targetY,data,linenumber);

}

Point getPoint(){

returnnewPoint(x,y);

}

Dot setTargetData(inttargetX,int targetY,Integer data,intlinenumber){

this.targetX =targetX;

this.targetY =targetY;

this.data =data;

this.linenumber =linenumber;

returnthis;

}

booleanisAtRest(){

return (x==targetX)&&(y==targetY);

}

voidupdate(){

x =updateSelf(x, targetX, velocity);

y =updateSelf(y, targetY, velocity);

}

privateintupdateSelf(int origin, inttarget, int velocity){

if(origin < target) {

origin += velocity;

} elseif(origin > target){

origin-= velocity;

}

if(Math.abs(target-origin)<velocity){

origin = target;

}

returnorigin;

}

}

 

 

 本文由用戶 iwfb6234 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!