package com.duguang.baseanimation.ui.canvas.chart.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import com.duguang.baseanimation.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
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;
import android.view.WindowManager;
/**
* Created by Dacer on 11/4/13.
*/
public class LineView extends View {
private int mViewHeight;
// drawBackground
private String[] colors=new String[]{"#f27302","#9dcd19","#0096e0","#843dfa","#eeb445","#dd1ae9","#07a616","#036ad5","#fa2942","#04bdae"};
// private boolean autoSetDataOfGird = false;
private boolean autoSetGridWidth = true;
// private int dataOfAGird = 10;
private int bottomTextHeight = 0;
private ArrayList<String> bottomTextList;
// private ArrayList<Integer> dataList;
private ArrayList<ArrayList<Integer>> dataLists=new ArrayList<ArrayList<Integer>>();
private ArrayList<Integer> xCoordinateList = new ArrayList<Integer>();
private ArrayList<ArrayList<Integer>> yCoordinateLists = new ArrayList<ArrayList<Integer>>();
// private ArrayList<Dot> drawDotList = new ArrayList<Dot>();
private ArrayList<ArrayList<Dot>> drawDotLists=new ArrayList<ArrayList<Dot>>();
private Paint bottomTextPaint = new Paint();
private int bottomTextDescent;
private Context c = getContext();
// popup
private Paint popupTextPaint = new Paint();
private final int bottomTriangleHeight = 12;
private boolean showPopup = false;
private Dot selectedDot;
private int topLineLength = MyUtils.dip2px(getContext(), 2); // | | �?this
// -+-+-
private int sideLineLength = MyUtils.dip2px(getContext(), 20) / 3 * 2;// --+--+--+--+--+--+--
// �?this �?
private int backgroundGridWidth =0;
// 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(), 0);
private final int DOT_OUTER_CIR_RADIUS = MyUtils.dip2px(getContext(), 2);
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 int BACKGROUND_LINE_COLOR = Color.parseColor("#EEEEEE");
private int BOTTOM_TEXT_COLOR = Color.parseColor("#9B9A9B");
private int biggestData=0;
private int middleHight;
private int marginTop=MyUtils.dip2px(getContext(), 22);
private Runnable animator = new Runnable() {
@Override
public void run() {
boolean needNewFrame = false;
for (ArrayList<Dot> drawDotList : drawDotLists) {
for (Dot dot : drawDotList) {
dot.update();
if (!dot.isAtRest()) {
needNewFrame = true;
}
}
}
if (needNewFrame) {
postDelayed(this, 20);
}
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(c, 13));
popupTextPaint.setStrokeWidth(5);
popupTextPaint.setTextAlign(Paint.Align.CENTER);
bottomTextPaint.setAntiAlias(true);
bottomTextPaint.setTextSize(MyUtils.sp2px(c, 12));
bottomTextPaint.setTextAlign(Paint.Align.CENTER);
bottomTextPaint.setStyle(Paint.Style.FILL);
bottomTextPaint.setColor(BOTTOM_TEXT_COLOR);
}
/**
* dataList will be reset when called is method.
*
* @param bottomTextList
* The String ArrayList in the bottom.
*/
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) {
int size = bottomTextList.size();
if(size<6){
WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
int width=wm.getDefaultDisplay().getWidth();
backgroundGridWidth=(int) ((float)width/size);
}else{
backgroundGridWidth = longestWidth+ (int) bottomTextPaint.measureText(longestStr, 0, 1);
}
Log.i("backgroundGridWidth=", backgroundGridWidth+"");
if (sideLineLength < longestWidth / 2) {
sideLineLength = longestWidth / 2;
}
}
// refreshXCoordinateList(getHorizontalGridNum());
}
public void addDataList(ArrayList<ArrayList<Integer>> all){
for (ArrayList<Integer> dataList : all) {
dataLists.add(dataList);
if (dataLists.get(0).size() != bottomTextList.size()) {
throw new RuntimeException("纵横数量不一致");
}
// if (autoSetDataOfGird) {
// for (Integer i : dataList) {
// if (biggestData < i) {
// biggestData = i;
// }
// }
// while (biggestData / 10 > dataOfAGird) {
// dataOfAGird *= 10;
// }
// }
}
// refreshAfterDataChanged();
showPopup = false;
setMinimumWidth(0); // It can help the LineView reset the Width,I don't know the better way..
postInvalidate();
}
/**
*
* @param dataList
* The Integer ArrayList for showing, dataList.size() must <
* bottomTextList.size()
*/
// public void setDataList(ArrayList<Integer> dataList) {
// this.dataList = dataList;
// if (dataList.size() > bottomTextList.size()) {
// throw new RuntimeException("dacer.LineView error:"
// + " dataList.size() > bottomTextList.size() !!!");
// }
// if (autoSetDataOfGird) {
// int biggestData = 0;
// for (Integer i : dataList) {
// if (biggestData < i) {
// biggestData = i;
// }
// }
// dataOfAGird = 1;
// while (biggestData / 10 > dataOfAGird) {
// dataOfAGird *= 10;
// }
// }
// refreshAfterDataChanged();
// showPopup = false;
// 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);
// mViewHeight=measureHeight(MeasureSpec.EXACTLY);
refreshXCoordinateList(getHorizontalGridNum());
refreshYCoordinateList();
refreshDrawDotList();
}
// 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() {
middleHight = mViewHeight - topLineLength - bottomTextHeight- bottomTextTopMargin - bottomLineLength - bottomTextDescent;
yCoordinateLists.clear();
ArrayList<Integer> yCoordinateList;
for (ArrayList<Integer> dataList : dataLists) {
yCoordinateList = new ArrayList<Integer>();
for (Integer integer : dataList) {
yCoordinateList.add((100-integer)*middleHight/100+topLineLength+marginTop);
}
yCoordinateLists.add(yCoordinateList);
}
// for (int i = 0; i < verticalGridNum ; i++) {
// yCoordinateList.add(topLineLength+ middleHight*i/100);
// }
// System.out.println(yCoordinateList);
}
private void refreshDrawDotList() {
for (int k=0;k<dataLists.size();k++) {
ArrayList<Integer> dataList=dataLists.get(k) ;
if (drawDotLists.size()<dataLists.size()) {
ArrayList<Dot> drawDotList = new ArrayList<Dot>();
for (int i = 0; i < dataList.size(); i++) {
// int drawDotSize=drawDotList.isEmpty() ? 0 : drawDotList.size();
int x = xCoordinateList.get(i);
int y = yCoordinateLists.get(k).get(i);
drawDotList.add(new Dot(x, 0, x, y, dataList.get(i)));
// if (i > drawDotSize - 1) {
// drawDotList.add(new Dot(x, 0, x, y, dataList.get(i)));
// } else {
// drawDotList.set(i,drawDotList.get(i).setTargetData(x, y,dataList.get(i)));
// }
}
drawDotLists.add(drawDotList);
}
removeCallbacks(animator);
post(animator);
}
// Log.d("A", "drawDotLists"+drawDotLists.size());
// Log.d("A", "dataLists"+dataLists.size());
}
// private void refreshTopLineLength(int verticalGridNum) {
// 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);
if (showPopup && selectedDot != null) {
drawPopup(canvas, String.valueOf(selectedDot.data),selectedDot.getPoint());
}
}
/**
*
* @param canvas
* The canvas you need to draw on.
* @param point
* The Point consists of the x y coordinates from left bottom to
* right top. Like is 锟� * 3 2 1 0 1 2 3 4 5
*/
private void drawPopup(Canvas canvas, String num, Point point) {
boolean singularNum = (num.length() == 1);
int sidePadding = MyUtils.dip2px(c, singularNum ? 8 : 5);
int x = point.x;
int y = point.y - MyUtils.dip2px(c, 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(R.drawable.icon_canvas_charts_popup_red);
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);
if (drawDotLists != null && !drawDotLists.isEmpty()) {
for (int i=0;i<drawDotLists.size();i++) {
ArrayList<Dot> drawDotList=drawDotLists.get(i);
bigCirPaint.setColor(Color.parseColor(colors[i]));
for (Dot dot : drawDotList) {
canvas.drawCircle(dot.x, dot.y, DOT_OUTER_CIR_RADIUS,bigCirPaint);
}
}
}
}
private void drawLines(Canvas canvas) {
Paint linePaint = new Paint();
linePaint.setAntiAlias(true);
linePaint.setStrokeWidth(MyUtils.dip2px(c, 1));
for (int j=0;j<drawDotLists.size();j++) {
ArrayList<Dot> drawDotList=drawDotLists.get(j);
linePaint.setColor(Color.parseColor(colors[j]));
for (int i = 0; i < drawDotList.size() - 1; i++) {
canvas.drawLine(drawDotList.get(i).x, drawDotList.get(i).y,
drawDotList.get(i + 1).x, drawDotList.get(i + 1).y,
linePaint);
}
}
}
private void drawBackgroundLines(Canvas canvas) {
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(MyUtils.dip2px(c, 1f));
paint.setColor(BACKGROUND_LINE_COLOR);
// PathEffect effects = new DashPathEffect(new float[] { 10, 5, 10, 5 }, 1);
int hight2bottom=mViewHeight - bottomTextTopMargin - bottomTextHeight- bottomTextDescent;
// draw vertical lines
for (int i = 0; i < xCoordinateList.size(); i++) {
canvas.drawLine(xCoordinateList.get(i), 0, xCoordinateList.get(i),hight2bottom, paint);
}
for (float i = 0; i < 6; i++) {
float y=topLineLength+marginTop+i/5*middleHight;
canvas.drawLine(0, y, getWidth(),y, paint);
}
// 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);
// }
// }
// draw bottom text
if (bottomTextList != null) {
for (int i = 0; i < bottomTextList.size(); i++) {
// if(i%7==0){
// bottomTextPaint.setTextSize(MyUtils.sp2px(c, 12));
// }else{
// bottomTextPaint.setTextSize(MyUtils.sp2px(c, 0));
// }
canvas.drawText(bottomTextList.get(i), sideLineLength+ backgroundGridWidth * i, mViewHeight- bottomTextDescent, bottomTextPaint);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int mViewWidth = measureWidth(widthMeasureSpec);
mViewHeight = measureHeight(heightMeasureSpec);
setMeasuredDimension(mViewWidth, mViewHeight);
refreshAfterDataChanged();
}
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) {
Point point = new Point();
point.x = (int) event.getX();
point.y = (int) event.getY();
Region r = new Region();
int width = backgroundGridWidth / 2;
if (drawDotLists != null || !drawDotLists.isEmpty()) {
for (ArrayList<Dot> drawDotList : drawDotLists) {
for (Dot dot : drawDotList) {
r.set(dot.x - width, dot.y - width, dot.x + width, dot.y
+ width);
if (r.contains(point.x, point.y)
&& event.getAction() == MotionEvent.ACTION_DOWN) {
selectedDot = dot;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (r.contains(point.x, point.y)) {
showPopup = true;
}
}
}
}
}
if (event.getAction() == MotionEvent.ACTION_DOWN
|| event.getAction() == MotionEvent.ACTION_UP) {
postInvalidate();
}
return true;
}
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;
}
class Dot {
int x;
int y;
int data;
int targetX;
int targetY;
int velocity = MyUtils.dip2px(c, 20);
Dot(int x, int y, int targetX, int targetY, Integer data) {
this.x = x;
this.y = y;
setTargetData(targetX, targetY, data);
}
Point getPoint() {
return new Point(x, y);
}
Dot setTargetData(int targetX, int targetY, Integer data) {
this.targetX = targetX;
this.targetY = targetY;
this.data = data;
return this;
}
boolean isAtRest() {
return (x == targetX) && (y == targetY);
}
void update() {
x = updateSelf(x, targetX, velocity);
y = updateSelf(y, targetY, velocity);
}
}
}