已上架公司项目,目前运行稳定。
GqLineChartView的代码
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.Rect;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import java.util.ArrayList;
/**
* 作者:Mr.Lee on 2017-7-6 09:46
* 邮箱:569932357@qq.com
*/
public class GqLineChartView extends View {
private Paint paint;
private TextPaint mTextPaint;
// 上下间隔
private int verticalSpace = dipToPx(getContext(), 28f);//默认上下间隔 约为55px
//垂直默认分块数
private int verticalNum = 5;
// 左下角Y
private int mHeigh = verticalSpace * verticalNum+1;//默认高度
//水平默认分块数
private int horizontalNum =1;
// 水平间隔
private int horizontalSpace;
//水平padding
private int horizontalPadding = dipToPx(getContext(), 15f);
//垂直padding
private int verticalPadding = dipToPx(getContext(),5);
//垂直值域高度
private int valuesHeigh;
//外yuan半径
private int mCircleOut=4;
//内圆半径
private int mCircleInside=3;
private int mWidth;//控件宽度
private int mIndex; //第几个被选中
private int mHeighView=mHeigh+dipToPx(getContext(), 8f)+dipToPx(getContext(), 17f);
private ArrayList<Float> listX =new ArrayList<>() ;
private ArrayList<Float> listY =new ArrayList<>() ;
private ArrayList<Float> mValues = new ArrayList<>(); //值
private ArrayList<String> mXString = new ArrayList<>(); //值
public GqLineChartView(Context context) {
this(context, null, 0);
}
public GqLineChartView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GqLineChartView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public static int dipToPx(Context context, float dip) {
float density = context.getResources().getDisplayMetrics().density;
return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
}
private void init() {
paint = new Paint();
mTextPaint=new TextPaint();
WindowManager manager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
// 方法1,获取屏幕的默认分辨率
Display display = manager.getDefaultDisplay(); // getWindowManager().getDefaultDisplay();
mWidth=display.getWidth()-dipToPx(getContext(),16f)*2;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(mWidth,mHeighView);
}
@Override
protected void onDraw(Canvas canvas) {
// 画数中间横线
DashPathEffect effects = new DashPathEffect(new float[] { 5, 5, 5, 5 }, 1);
paint.setColor(Color.parseColor("#ffe2dd"));
paint.setStrokeWidth(1);
for (int i = 0; i < verticalNum; i++) {
float x1 = 0;
float y = (i + 1) * verticalSpace;
float x2 = getMeasuredWidth();
canvas.drawLine(x1, y, x2, y, paint);
}
if (mValues.size() <1 || listX.size()<=0 || listY.size()<=0) {
return;
}
// 画数据折线
paint.setColor(Color.parseColor("#ff3e19"));
paint.setStrokeWidth(4);
paint.setStyle(Paint.Style.STROKE);
Path path = new Path();
for (int i = 0; i < mValues.size(); i++) {
if (i == 0) {
path.moveTo(listX.get(i), listY.get(i));
} else {
path.lineTo(listX.get(i), listY.get(i));
}
}
canvas.drawPath(path, paint);
//画坐标
mTextPaint.setStrokeWidth(5);
mTextPaint.setTextSize(dipToPx(getContext(),11));
mTextPaint.setStyle(Paint.Style.FILL);
for (int i = 0; i < mXString.size(); i++) {
if (i == mIndex) {
if(i==0){
mTextPaint.setTextAlign(Paint.Align.LEFT);
}else if(i==mValues.size()-1){
mTextPaint.setTextAlign(Paint.Align.RIGHT);
}else {
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
String str=mXString.get(i);
mTextPaint.setColor(Color.parseColor("#ffffff"));
Paint linePaint=new Paint();
linePaint.setColor(Color.parseColor("#ff3e19"));
linePaint.setStrokeWidth(dipToPx(getContext(), 16f));
linePaint.setStrokeCap(Paint.Cap.ROUND);
Rect rect = new Rect();
paint.getTextBounds(str, 0, str.length(), rect);
int w = rect.width();
if(i==0){
canvas.drawLine(listX.get(i),mHeigh+dipToPx(getContext(), 8f)+dipToPx(getContext(), 8f),listX.get(i)+dipToPx(getContext(), w),mHeigh+dipToPx(getContext(), 8f)+dipToPx(getContext(), 8f), linePaint);
}else if(i==mValues.size()-1){
canvas.drawLine(listX.get(i)-dipToPx(getContext(), w),mHeigh+dipToPx(getContext(), 8f)+dipToPx(getContext(), 8f),listX.get(i),mHeigh+dipToPx(getContext(), 8f)+dipToPx(getContext(), 8f), linePaint);
}else {
canvas.drawLine(listX.get(i)-dipToPx(getContext(), w/2),mHeigh+dipToPx(getContext(), 8f)+dipToPx(getContext(), 8f),listX.get(i)+dipToPx(getContext(), w/2),mHeigh+dipToPx(getContext(), 8f)+dipToPx(getContext(), 8f), linePaint);
}
// canvas.drawLine(listX.get(i)-dipToPx(getContext(), w/2),mHeigh+dipToPx(getContext(), 8f)+dipToPx(getContext(), 8f),listX.get(i)+dipToPx(getContext(), w/2),mHeigh+dipToPx(getContext(), 8f)+dipToPx(getContext(), 8f), linePaint);
canvas.drawText(str,listX.get(i),mHeigh+dipToPx(getContext(), 8f)+dipToPx(getContext(), 12f), mTextPaint);
} else {
mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setColor(Color.parseColor("#000000"));
canvas.drawText(mXString.get(i),listX.get(i),mHeigh+dipToPx(getContext(), 8f)+dipToPx(getContext(), 12f), mTextPaint);
}
}
// 画被选中的竖线
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.parseColor("#ff8e78"));
paint.setPathEffect(effects);
paint.setStrokeWidth(2);
Path path_vertical = new Path();
path_vertical.moveTo(listX.get(mIndex),mHeigh+dipToPx(getContext(), 8f));
path_vertical.lineTo(listX.get(mIndex), verticalSpace*2f);
canvas.drawPath(path_vertical, paint);
// 画被选中的竖线头部的圆
paint.reset();
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(2);
paint.setColor(Color.parseColor("#ff8e78"));
canvas.drawCircle(listX.get(mIndex), verticalSpace*2f-dipToPx(getContext(),mCircleInside), dipToPx(getContext(),mCircleInside), paint);
//画文字
if(mIndex==0){
mTextPaint.setTextAlign(Paint.Align.LEFT);
}else if(mIndex==mValues.size()-1){
mTextPaint.setTextAlign(Paint.Align.RIGHT);
}else{
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
mTextPaint.setStrokeWidth(5);
mTextPaint.setTextSize(dipToPx(getContext(),11));
mTextPaint.setColor(Color.parseColor("#666666"));
mTextPaint.setStyle(Paint.Style.FILL);
canvas.drawText("预计回款金额", listX.get(mIndex),verticalSpace*2f-dipToPx(getContext(),mCircleInside)*2-15, mTextPaint);
mTextPaint.setTextSize(dipToPx(getContext(),14));
mTextPaint.setColor(Color.parseColor("#ff3e19"));
mTextPaint.setFakeBoldText(true);
canvas.drawText(String.valueOf(mValues.get(mIndex)), listX.get(mIndex),verticalSpace*2f-dipToPx(getContext(),mCircleInside)*2-20-dipToPx(getContext(),11), mTextPaint);
//画阴影
Path path_shadow = new Path();
paint.setColor(Color.parseColor("#10ff3e19"));
paint.setStyle(Paint.Style.FILL);
path_shadow.moveTo(listX.get(0)-dipToPx(getContext(),mCircleOut), listY.get(0));
for (int i = 0; i < mValues.size(); i++) {
path_shadow.lineTo(listX.get(i), listY.get(i));
}
path_shadow.lineTo(listX.get(listX.size()-1)+dipToPx(getContext(),mCircleOut), listY.get(listX.size()-1));
path_shadow.lineTo(listX.get(listX.size()-1)+dipToPx(getContext(),mCircleOut), mHeigh);
path_shadow.lineTo(listX.get(0)-dipToPx(getContext(),mCircleOut), mHeigh);
path_shadow.close();
canvas.drawPath(path_shadow, paint);
// 画出转折点圆圈
paint.setStyle(Paint.Style.FILL);
for (int i = 0;i <mValues.size(); i++) {
if(i==mIndex){
// 画外圆
paint.setColor(Color.parseColor("#19ff3e19"));
canvas.drawCircle(listX.get(i), listY.get(i), dipToPx(getContext(),10), paint);
// 画外圆
paint.setColor(Color.parseColor("#21ff3e19"));
canvas.drawCircle(listX.get(i), listY.get(i), dipToPx(getContext(),7), paint);
// 画外圆
paint.setColor(Color.WHITE);
canvas.drawCircle(listX.get(i), listY.get(i), dipToPx(getContext(),mCircleOut), paint);
// 画中心点为白色
paint.setColor(Color.parseColor("#ff3e19"));
canvas.drawCircle(listX.get(i), listY.get(i), dipToPx(getContext(),mCircleInside), paint);
continue;
}
// 画外圆
paint.setColor(Color.parseColor("#ff3e19"));
canvas.drawCircle(listX.get(i), listY.get(i), dipToPx(getContext(),mCircleOut), paint);
// 画中心点为白色
paint.setColor(Color.WHITE);
canvas.drawCircle(listX.get(i), listY.get(i), dipToPx(getContext(),mCircleInside), paint);
}
}
/**
*
* @param values 传入的值的集合
* @param index 第几个被选中 从0开始
*/
public void setValues(ArrayList<Float> values,int index,ArrayList<String> xString) {
this.mValues=values;
this.mIndex=index;
this.mXString=xString;
if(null==mValues || mValues.size()<0){
invalidate();
return;
}
listX.clear();
listY.clear();
if(mValues.size()!=1){
horizontalNum=mValues.size()-1;
horizontalSpace = (mWidth - horizontalPadding * 2) / horizontalNum;
}else{
horizontalSpace = (mWidth - horizontalPadding * 2);
}
verticalSpace = mHeigh / verticalNum;
float max = 0;
for (int i = 0; i < mValues.size(); i++) {
if (mValues.get(i) > max) {
max = mValues.get(i);
}
}
valuesHeigh = verticalSpace * 3 - verticalPadding * 2;
for (int i = 0; i < mValues.size(); i++) {
listX.add(Float.valueOf(horizontalPadding + i * horizontalSpace));
if(max==0){
listY.add((float)( mHeigh - verticalPadding));
continue;
}
listY.add(mHeigh - verticalPadding - (mValues.get(i) / max * valuesHeigh));
}
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_DOWN:
for (int i=0 ;i<mValues.size();i++){
if(listX.get(i)-dipToPx(getContext(),20f)<event.getX()&&event.getX()<=listX.get(i)+dipToPx(getContext(),20f)){
if(event.getY()>listY.get(i)-dipToPx(getContext(),20f)&&event.getY()<=mHeighView){
if(i==mIndex){
return true;
}
setValues(mValues,i,mXString);
}
return true;
}
}
break;
case MotionEvent.ACTION_UP:
break;
}
//这句话不要修改
return super.onTouchEvent(event);
}
}
在布局中使用
//布局
<com.gq.android.views.GqLineChartView
android:id="@+id/lcv_return"
android:layout_marginTop="@dimen/px_20"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="@dimen/px_20"
/>
在代码设置
//代码
lcv.setValues(mValues,0,mXString);