• Tutorials
  • Tips & Tricks
  • Applications
  • News

Android Trainee

  • Tutorials
  • Tips & Tricks
  • Applications
  • News
Home  /  Tutorials  /  Draw Android Line Chart With Animation.
14 July 2015

Draw Android Line Chart With Animation.

Written by admin@androidtrainee
Tutorials android chart animation, chart, chartview, line chart, line chart animation Leave a Comment

Draw Android Line Chart With Animation.

 

—>  AndroidManifest.xml


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.barchart"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="22" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

 

IN Layout Folder

activity_main.xml

 


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff">

<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.androidlinechart.LineView"
android:id="@+id/line_view"/>
</HorizontalScrollView>

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Random"
android:id="@+id/line_button"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />

</RelativeLayout>

Put Images In drawable Folder

popup_blue.9 popup_green.9 popup_red.9

 

MyUtils.java


package com.example.barchart;


import android.content.Context;


public class MyUtils {

public static int dip2px(Context context, float dipValue){
final float scale = context.getResources().getDisplayMetrics().density;
return (int)(dipValue * scale + 0.5f);
}

public static int px2dip(Context context, float pxValue){
final float scale = context.getResources().getDisplayMetrics().density;
return (int)(pxValue / scale + 0.5f);
}

public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}

}

MainActivity.java


package com.example.androidlinechart;

import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;


public class MainActivity extends Activity {
int randomint = 9;
LineView lineView;
Button lineButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lineView = (LineView)findViewById(R.id.line_view);

//must*
ArrayList<String> test = new ArrayList<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);

lineButton = (Button)findViewById(R.id.line_button);
lineButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
randomSet(lineView);

}
});

randomSet(lineView);
}
private void randomSet(LineView lineView){
ArrayList<Integer> dataList = new ArrayList<Integer>();
int random = (int)(Math.random()*9+1);
for (int i=0; i<randomint; i++){
dataList.add((int)(Math.random()*random));
}

ArrayList<Integer> dataList2 = new ArrayList<Integer>();
random = (int)(Math.random()*9+1);
for (int i=0; i<randomint; i++){
dataList2.add((int)(Math.random()*random));
}

ArrayList<Integer> dataList3 = new ArrayList<Integer>();
random = (int)(Math.random()*9+1);
for (int i=0; i<randomint; i++){
dataList3.add((int)(Math.random()*random));
}

ArrayList<ArrayList<Integer>> dataLists = new ArrayList<ArrayList<Integer>>();
dataLists.add(dataList);
dataLists.add(dataList2);


lineView.setDataList(dataLists);
}

}

 

LineView.JAVA


package com.example.androidlinechart;

import java.util.ArrayList;
import java.util.Collections;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.NinePatchDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;



public class LineView extends View {
private int mViewHeight;
//drawBackground
private boolean autoSetDataOfGird = true;
private boolean autoSetGridWidth = true;
private int dataOfAGird = 10;
private int bottomTextHeight = 0;
private ArrayList<String> bottomTextList = new ArrayList<String>();

private ArrayList<ArrayList<Integer>> dataLists;
private ArrayList<Integer> dataList;

private ArrayList<Integer> xCoordinateList = new ArrayList<Integer>();
private ArrayList<Integer> yCoordinateList = new ArrayList<Integer>();

private ArrayList<ArrayList<Dot>> drawDotLists = new ArrayList<ArrayList<Dot>>();
private ArrayList<Dot> drawDotList = new ArrayList<Dot>();

private Paint bottomTextPaint = new Paint();
private int bottomTextDescent;

//popup
private Paint popupTextPaint = new Paint();
private final int bottomTriangleHeight = 12;
public boolean showPopup = true;

private Dot pointToSelect;
private Dot selectedDot;

private int topLineLength = MyUtils.dip2px(getContext(), 12);;

private int sideLineLength = MyUtils.dip2px(getContext(),45)/3*2;
private int backgroundGridWidth = MyUtils.dip2px(getContext(),45);

//Constants
private final int popupTopPadding = MyUtils.dip2px(getContext(),2);
private final int popupBottomMargin = MyUtils.dip2px(getContext(),5);
private final int bottomTextTopMargin = MyUtils.sp2px(getContext(),5);
private final int bottomLineLength = MyUtils.sp2px(getContext(), 22);
private final int DOT_INNER_CIR_RADIUS = MyUtils.dip2px(getContext(), 2);
private final int DOT_OUTER_CIR_RADIUS = MyUtils.dip2px(getContext(),5);
private final int MIN_TOP_LINE_LENGTH = MyUtils.dip2px(getContext(),12);
private final int MIN_VERTICAL_GRID_NUM = 4;
private final int MIN_HORIZONTAL_GRID_NUM = 1;
private final int BACKGROUND_LINE_COLOR = Color.parseColor("#EEEEEE");
private final int BOTTOM_TEXT_COLOR = Color.parseColor("#9B9A9B");

public static final int SHOW_POPUPS_All = 1;
public static final int SHOW_POPUPS_MAXMIN_ONLY = 2;
public static final int SHOW_POPUPS_NONE = 3;

private int showPopupType = SHOW_POPUPS_NONE;
public void setShowPopup(int popupType) {
this.showPopupType = popupType;
}


private Boolean drawDotLine = false;

private String[] colorArray = {"#e74c3c","#2980b9","#1abc9c"};

private int[] popupColorArray = {R.drawable.popup_red,R.drawable.popup_blue,R.drawable.popup_green};

// onDraw optimisations
private final Point tmpPoint = new Point();

public void setDrawDotLine(Boolean drawDotLine) {
this.drawDotLine = drawDotLine;
}

private Runnable animator = new Runnable() {
@Override
public void run() {
boolean needNewFrame = false;
for(ArrayList<Dot> data : drawDotLists){
for(Dot dot : data){
dot.update();
if(!dot.isAtRest()){
needNewFrame = true;
}
}
}
if (needNewFrame) {
postDelayed(this, 25);
}
invalidate();
}
};

public LineView(Context context){
this(context,null);
}
public LineView(Context context, AttributeSet attrs){
super(context, attrs);
popupTextPaint.setAntiAlias(true);
popupTextPaint.setColor(Color.WHITE);
popupTextPaint.setTextSize(MyUtils.sp2px(getContext(), 13));
popupTextPaint.setStrokeWidth(5);
popupTextPaint.setTextAlign(Paint.Align.CENTER);

bottomTextPaint.setAntiAlias(true);
bottomTextPaint.setTextSize(MyUtils.sp2px(getContext(),12));
bottomTextPaint.setTextAlign(Paint.Align.CENTER);
bottomTextPaint.setStyle(Paint.Style.FILL);
bottomTextPaint.setColor(BOTTOM_TEXT_COLOR);
}


public void setBottomTextList(ArrayList<String> bottomTextList){
this.dataList = null;
this.bottomTextList = bottomTextList;

Rect r = new Rect();
int longestWidth = 0;
String longestStr = "";
bottomTextDescent = 0;
for(String s:bottomTextList){
bottomTextPaint.getTextBounds(s,0,s.length(),r);
if(bottomTextHeight<r.height()){
bottomTextHeight = r.height();
}
if(autoSetGridWidth&&(longestWidth<r.width())){
longestWidth = r.width();
longestStr = s;
}
if(bottomTextDescent<(Math.abs(r.bottom))){
bottomTextDescent = Math.abs(r.bottom);
}
}

if(autoSetGridWidth){
if(backgroundGridWidth<longestWidth){
backgroundGridWidth = longestWidth+(int)bottomTextPaint.measureText(longestStr,0,1);
}
if(sideLineLength<longestWidth/2){
sideLineLength = longestWidth/2;
}
}

refreshXCoordinateList(getHorizontalGridNum());
}


public void setDataList(ArrayList<ArrayList<Integer>> dataLists){
selectedDot = null;
this.dataLists = dataLists;
for(ArrayList<Integer> list : dataLists){
if(list.size() > bottomTextList.size()){
throw new RuntimeException("dacer.LineView error:" +
" dataList.size() > bottomTextList.size() !!!");
}
}
int biggestData = 0;
for(ArrayList<Integer> list : dataLists){
if(autoSetDataOfGird){
for(Integer i:list){
if(biggestData<i){
biggestData = i;
}
}
}
dataOfAGird = 1;
while(biggestData/10 > dataOfAGird){
dataOfAGird *= 10;
}
}

refreshAfterDataChanged();
showPopup = true;
setMinimumWidth(0); // It can help the LineView reset the Width,
// I don't know the better way..
postInvalidate();
}

private void refreshAfterDataChanged(){
int verticalGridNum = getVerticalGridlNum();
refreshTopLineLength(verticalGridNum);
refreshYCoordinateList(verticalGridNum);
refreshDrawDotList(verticalGridNum);
}

private int getVerticalGridlNum(){
int verticalGridNum = MIN_VERTICAL_GRID_NUM;
if(dataLists != null && !dataLists.isEmpty()){
for(ArrayList<Integer> list : dataLists){
for(Integer integer:list){
if(verticalGridNum<(integer+1)){
verticalGridNum = integer+1;
}
}
}
}
return verticalGridNum;
}

private int getHorizontalGridNum(){
int horizontalGridNum = bottomTextList.size()-1;
if(horizontalGridNum<MIN_HORIZONTAL_GRID_NUM){
horizontalGridNum = MIN_HORIZONTAL_GRID_NUM;
}
return horizontalGridNum;
}

private void refreshXCoordinateList(int horizontalGridNum){
xCoordinateList.clear();
for(int i=0;i<(horizontalGridNum+1);i++){
xCoordinateList.add(sideLineLength + backgroundGridWidth*i);
}

}

private void refreshYCoordinateList(int verticalGridNum){
yCoordinateList.clear();
for(int i=0;i<(verticalGridNum+1);i++){
yCoordinateList.add(topLineLength +
((mViewHeight-topLineLength-bottomTextHeight-bottomTextTopMargin-
bottomLineLength-bottomTextDescent)*i/(verticalGridNum)));
}
}

private void refreshDrawDotList(int verticalGridNum){
if(dataLists != null && !dataLists.isEmpty()){
if(drawDotLists.size() == 0){
for(int k = 0; k < dataLists.size(); k++){
drawDotLists.add(new ArrayList<LineView.Dot>());
}
}
for(int k = 0; k < dataLists.size(); k++){
int drawDotSize = drawDotLists.get(k).isEmpty()? 0:drawDotLists.get(k).size();

for(int i=0;i<dataLists.get(k).size();i++){
int x = xCoordinateList.get(i);
int y = yCoordinateList.get(verticalGridNum - dataLists.get(k).get(i));
if(i>drawDotSize-1){

drawDotLists.get(k).add(new Dot(x, 0, x, y, dataLists.get(k).get(i),k));
}else{

drawDotLists.get(k).set(i, drawDotLists.get(k).get(i).setTargetData(x,y,dataLists.get(k).get(i),k));
}
}

int temp = drawDotLists.get(k).size() - dataLists.get(k).size();
for(int i=0; i<temp; i++){
drawDotLists.get(k).remove(drawDotLists.get(k).size()-1);
}
}
}
removeCallbacks(animator);
post(animator);
}

private void refreshTopLineLength(int verticalGridNum){
// For prevent popup can't be completely showed when backgroundGridHeight is too small.
// But this code not so good.
if((mViewHeight-topLineLength-bottomTextHeight-bottomTextTopMargin)/
(verticalGridNum+2)<getPopupHeight()){
topLineLength = getPopupHeight()+DOT_OUTER_CIR_RADIUS+DOT_INNER_CIR_RADIUS+2;
}else{
topLineLength = MIN_TOP_LINE_LENGTH;
}
}

@Override
protected void onDraw(Canvas canvas) {
drawBackgroundLines(canvas);
drawLines(canvas);
drawDots(canvas);


for(int k=0; k < drawDotLists.size(); k++){
int MaxValue = Collections.max(dataLists.get(k));
int MinValue = Collections.min(dataLists.get(k));
for(Dot d: drawDotLists.get(k)){
if(showPopupType == SHOW_POPUPS_All)
drawPopup(canvas, String.valueOf(d.data), d.setupPoint(tmpPoint),popupColorArray[k%3]);
else if(showPopupType == SHOW_POPUPS_MAXMIN_ONLY){
if(d.data == MaxValue)
drawPopup(canvas, String.valueOf(d.data), d.setupPoint(tmpPoint),popupColorArray[k%3]);
if(d.data == MinValue)
drawPopup(canvas, String.valueOf(d.data), d.setupPoint(tmpPoint),popupColorArray[k%3]);
}
}
}

if(showPopup && selectedDot != null){
drawPopup(canvas,
String.valueOf(selectedDot.data),
selectedDot.setupPoint(tmpPoint),popupColorArray[selectedDot.linenumber%3]);
}
}


private void drawPopup(Canvas canvas,String num, Point point,int PopupColor){
boolean singularNum = (num.length() == 1);
int sidePadding = MyUtils.dip2px(getContext(),singularNum? 8:5);
int x = point.x;
int y = point.y-MyUtils.dip2px(getContext(),5);
Rect popupTextRect = new Rect();
popupTextPaint.getTextBounds(num,0,num.length(),popupTextRect);
Rect r = new Rect(x-popupTextRect.width()/2-sidePadding,
y - popupTextRect.height()-bottomTriangleHeight-popupTopPadding*2-popupBottomMargin,
x + popupTextRect.width()/2+sidePadding,
y+popupTopPadding-popupBottomMargin);

NinePatchDrawable popup = (NinePatchDrawable)getResources().getDrawable(PopupColor);
popup.setBounds(r);
popup.draw(canvas);
canvas.drawText(num, x, y-bottomTriangleHeight-popupBottomMargin, popupTextPaint);
}

private int getPopupHeight(){
Rect popupTextRect = new Rect();
popupTextPaint.getTextBounds("9",0,1,popupTextRect);
Rect r = new Rect(-popupTextRect.width()/2,
- popupTextRect.height()-bottomTriangleHeight-popupTopPadding*2-popupBottomMargin,
+ popupTextRect.width()/2,
+popupTopPadding-popupBottomMargin);
return r.height();
}


private void drawDots(Canvas canvas){
Paint bigCirPaint = new Paint();
bigCirPaint.setAntiAlias(true);
Paint smallCirPaint = new Paint(bigCirPaint);
smallCirPaint.setColor(Color.parseColor("#FFFFFF"));
if(drawDotLists!=null && !drawDotLists.isEmpty()){
for(int k=0; k < drawDotLists.size(); k++){
bigCirPaint.setColor(Color.parseColor(colorArray[k%3]));
for(Dot dot : drawDotLists.get(k)){
canvas.drawCircle(dot.x,dot.y,DOT_OUTER_CIR_RADIUS,bigCirPaint);
canvas.drawCircle(dot.x,dot.y,DOT_INNER_CIR_RADIUS,smallCirPaint);
}
}
}
}


private void drawLines(Canvas canvas){
Paint linePaint = new Paint();
linePaint.setAntiAlias(true);
linePaint.setStrokeWidth(MyUtils.dip2px(getContext(), 2));
for(int k = 0; k<drawDotLists.size(); k ++){
linePaint.setColor(Color.parseColor(colorArray[k%3]));
for(int i=0; i<drawDotLists.get(k).size()-1; i++){
canvas.drawLine(drawDotLists.get(k).get(i).x,
drawDotLists.get(k).get(i).y,
drawDotLists.get(k).get(i+1).x,
drawDotLists.get(k).get(i+1).y,
linePaint);
}
}
}


private void drawBackgroundLines(Canvas canvas){
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(MyUtils.dip2px(getContext(),1f));
paint.setColor(BACKGROUND_LINE_COLOR);
PathEffect effects = new DashPathEffect(
new float[]{10,5,10,5}, 1);

//draw vertical lines
for(int i=0;i<xCoordinateList.size();i++){
canvas.drawLine(xCoordinateList.get(i),
0,
xCoordinateList.get(i),
mViewHeight - bottomTextTopMargin - bottomTextHeight-bottomTextDescent,
paint);
}

//draw dotted lines
paint.setPathEffect(effects);
Path dottedPath = new Path();
for(int i=0;i<yCoordinateList.size();i++){
if((yCoordinateList.size()-1-i)%dataOfAGird == 0){
dottedPath.moveTo(0, yCoordinateList.get(i));
dottedPath.lineTo(getWidth(), yCoordinateList.get(i));
canvas.drawPath(dottedPath, paint);
}
}
//draw bottom text
if(bottomTextList != null){
for(int i=0;i<bottomTextList.size();i++){
canvas.drawText(bottomTextList.get(i), sideLineLength+backgroundGridWidth*i, mViewHeight-bottomTextDescent, bottomTextPaint);
}
}

if(!drawDotLine){
//draw solid lines
for(int i=0;i<yCoordinateList.size();i++){
if((yCoordinateList.size()-1-i)%dataOfAGird == 0){
canvas.drawLine(0,yCoordinateList.get(i),getWidth(),yCoordinateList.get(i),paint);
}
}
}
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int mViewWidth = measureWidth(widthMeasureSpec);
mViewHeight = measureHeight(heightMeasureSpec);
//        mViewHeight = MeasureSpec.getSize(measureSpec);
refreshAfterDataChanged();
setMeasuredDimension(mViewWidth,mViewHeight);
}

private int measureWidth(int measureSpec){
int horizontalGridNum = getHorizontalGridNum();
int preferred = backgroundGridWidth*horizontalGridNum+sideLineLength*2;
return getMeasurement(measureSpec, preferred);
}

private int measureHeight(int measureSpec){
int preferred = 0;
return getMeasurement(measureSpec, preferred);
}

private int getMeasurement(int measureSpec, int preferred){
int specSize = MeasureSpec.getSize(measureSpec);
int measurement;
switch(MeasureSpec.getMode(measureSpec)){
case MeasureSpec.EXACTLY:
measurement = specSize;
break;
case MeasureSpec.AT_MOST:
measurement = Math.min(preferred, specSize);
break;
default:
measurement = preferred;
break;
}
return measurement;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
pointToSelect = findPointAt((int) event.getX(), (int) event.getY());
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (pointToSelect != null) {
selectedDot = pointToSelect;
pointToSelect = null;
postInvalidate();
}
}

return true;
}

private Dot findPointAt(int x, int y) {
if (drawDotLists.isEmpty()) {
return null;
}

final int width = backgroundGridWidth/2;
final Region r = new Region();

for (ArrayList<Dot> data : drawDotLists) {
for (Dot dot : data) {
final int pointX = dot.x;
final int pointY = dot.y;

r.set(pointX - width, pointY - width, pointX + width, pointY + width);
if (r.contains(x, y)){
return dot;
}
}
}

return null;
}



class Dot{
int x;
int y;
int data;
int targetX;
int targetY;
int linenumber;
int velocity = MyUtils.dip2px(getContext(),18);

Dot(int x,int y,int targetX,int targetY,Integer data,int linenumber){
this.x = x;
this.y = y;
this.linenumber = linenumber;
setTargetData(targetX, targetY,data,linenumber);
}

Point setupPoint(Point point) {
point.set(x, y);
return point;
}

Dot setTargetData(int targetX,int targetY,Integer data,int linenumber){
this.targetX = targetX;
this.targetY = targetY;
this.data = data;
this.linenumber = linenumber;
return this;
}

boolean isAtRest(){
return (x==targetX)&&(y==targetY);
}

void update(){
x = updateSelf(x, targetX, velocity);
y = updateSelf(y, targetY, velocity);
}

private int updateSelf(int origin, int target, int velocity){
if (origin < target) {
origin += velocity;
} else if (origin > target){
origin-= velocity;
}
if(Math.abs(target-origin)<velocity){
origin = target;
}
return origin;
}
}
}

 

 

 

admin@androidtrainee

 Previous Article Draw Android pie Chart With Animation.
Next Article   Draw Android Clock Pie Chart With Animation.

Related Posts

  • Android New Quick Action Animation.

    July 15, 2015
  • Android satellite menu Animation.

    July 15, 2015
  • Android Staggered Grid & List View.

    July 14, 2015

Leave a Reply

Cancel reply

Tags

admob Advertising Networks AerServ Airpush android android ads android Advertising Networks Android App android chart animation Android GridView android L android lollipop androidmapv2 AppBrain AppFlood Appia AppKey Appnext AppOptim Appwiz chart chartview Epom Market google place api GridView Image Loader InMobi LeadBolt location map mapv2 mapv2 api material design Minimob Mobicow MobileCore MobiMicro NativeX Pingjam RevMob StarApplication startapp TapContext touched location Widdit

Count per Day

  • 347Reads yesterday:
  • 463828Total visitors:
  • 48Visitors today:
  • 2421Visitors per month:
  • 0Visitors currently online:
© Copyright 2014. Theme by BloomPixel.
Posting....