自定义View_柱形图-玩一把牛逼的~
两个月没有写过文章了,最近比较忙,今天抽个时间写一篇,最近做的一个项目中有一个关于柱形图的,而且不是传统的单列柱形图,而是双列柱形图,类似于下面这种效果~
双列柱状图.png然后我就本能的去GitHub上搜一些图表的框架,后来总结以下特点:
1.很难吻合自己的需求,总是很多地方不一样;
2.框架要么很全[复杂],会存在很多不需要的东西;要么很单一,不符合自己的需要;
3.使用起来复杂。等等
而且我这个双列柱状图好像还没有类似的框架,百度上搜也是寥寥无几,怎么办呢,自己造一个吧!于是这篇博客诞生了~照例先放个GitHub传送门:https://github.com/SuperKotlin/DoubleLineChatView
然后看一下今天要实现的效果图:
其实这样的柱状图绘制起来特别简单,难处理的是里面的计算和一些细节等等。
1.自定义我们需要的属性:
首先为了一定的扩展性,我们要先思考哪些属性需要自定义,这里我想到了这些
<declare-styleable name="DoubleLineChatView">
<!--左边的柱状图的颜色-->
<attr name="chatview_left_background_color" format="color" />
<!--右边的柱状图的颜色-->
<attr name="chatview_right_background_color" format="color" />
<!--字体(左数据)的颜色-->
<attr name="chatview_left_text_data_color" format="color" />
<!--字体(右数据)的颜色-->
<attr name="chatview_right_text_data_color" format="color" />
<!--字体(数据)的大小-->
<attr name="chatview_text_data_size" format="dimension" />
<!--(xy轴)的颜色-->
<attr name="chatview_text_xy_color" format="color" />
<!--(xy轴)的字体大小-->
<attr name="chatview_text_xy_size" format="dimension" />
<!--左右柱状图之间的小距离-->
<attr name="chatview_line_small_distance" format="dimension" />
<!--柱状图的宽度-->
<attr name="chatview_line_width" format="dimension" />
<!--柱状图之间的大距离-->
<attr name="chatview_line_big_distance" format="dimension" />
<!--Y轴数据的宽度,当数据字符串过大的时候需要调整这个宽度-->
<attr name="chatview_y_distance" format="dimension" />
<!--X轴数据的宽度-->
<attr name="chatview_x_distance" format="dimension" />
<!--动画时长-->
<attr name="chatview_animation_duration" format="integer" />
<!--是否显示坐标轴的箭头-->
<attr name="chatview_show_arrow" format="boolean" />
<!--是否显示Y坐标轴的间断标记-->
<attr name="chatview_show_y_interval" format="boolean" />
</declare-styleable>
画个草图解释一下:
属性点示意图.pngOK,这就是大致上我能想到的需求了,还有就是这里解释一下X轴和Y轴的数值显示,X轴上一般显示某某文字,比如项目名称等等,Y轴上面的数据是自动计算的,根据最大值计算显示,计算方式下面会讲。
声明相关变量和获取自定义属性值我就不详细讲了,之前博客讲过很多,这里重要讲一下绘制过程中需要考虑的东西。
假设现在有两组数据,这里默认我都写成了0,还有一组字符串数组是X轴的值;
/**
* 左边柱状图数据
*/
private int[] mDataLeft = {0, 0, 0, 0};
/**
* 右边柱状图数据
*/
private int[] mDataRight = {0, 0, 0, 0};
/**
* X轴底部文字
*/
private String[] mDataTextX = {"", "", "", ""};
计算:
想要绘制出Y轴的数字,那么必须计算,这里我是从0开始显示,后面分成5等分,那么Y轴肯定有个最大值,这个最大值的定义规则如下:先找出两个数据数组中的最大值,然后判断这个最大值是不是5的整倍数,如果不是则自加加到是5的整倍数为止;
/**
* 数据
*
* @param dataLeft 左边柱状图数据
* @param dataRight 右边柱状图数据
* @param dataTextX X轴文字数组
*/
public void setData(int[] dataLeft, int[] dataRight, String[] dataTextX) {
this.mDataLeft = dataLeft;
this.mDataRight = dataRight;
this.mDataTextX = dataTextX;
//找出两个数组中的最大值
int maxLeft = getMax(mDataLeft);
int maxRight = getMax(mDataRight);
mMaxData = maxLeft > maxRight ? maxLeft : maxRight;
Log.i(TAG, "mMaxCount=" + mMaxData);
//计算Y轴坐标最大值,根据当前最大数据并且是5的整倍数计算
while (mMaxData % 5 != 0) {
mMaxData++;
}
Log.i(TAG, "mMaxCount=" + mMaxData);
}
绘制:
第一步:绘制柱状图drawLineData(canvas);,为什么不先绘制XY轴,因为这里有个覆盖的问题,XY轴和柱形图的覆盖问题,虽然差别不大,但是影响美观。
/**
* 绘制柱状图
*/
private void drawLineData(Canvas canvas) {
for (int i = 0; i < mDataLeft.length; i++) {
float startX = mYDistance + mBigDistance + mLineWidth / 2 + (i * (mBigDistance + mSmallDistance + 2 * mLineWidth));
float endY = ((mViewHeight - mXDistance)) - (mDataLeft[i] * (mViewHeight - 2 * mXDistance)) / mMaxData;
canvas.drawLine(startX, mViewHeight - mXDistance, startX, endY, mPaintLeft);
String text = mDataLeft[i] + "";
float textWidth = mPaintTextLeft.measureText(text, 0, text.length());
canvas.drawText(text, startX - textWidth / 2, endY - 15, mPaintTextLeft);
}
for (int i = 0; i < mDataRight.length; i++) {
float startX = mYDistance + mBigDistance + mSmallDistance + mLineWidth + mLineWidth / 2 + (i * (mBigDistance + mSmallDistance + 2 * mLineWidth));
float endY = ((mViewHeight - mXDistance)) - (mDataRight[i] * (mViewHeight - 2 * mXDistance)) / mMaxData;
canvas.drawLine(startX, mViewHeight - mXDistance, startX, endY, mPaintRight);
String text = mDataRight[i] + "";
float textWidth = mPaintTextRight.measureText(text, 0, text.length());
canvas.drawText(text, startX - textWidth / 2, endY - 15, mPaintTextRight);
}
}
这里绘制是使用了drawLine方法,那么就需要起始点和终点的坐标值,这里的坐标都是有规律的,讲一大堆不如来个草图(这是左柱状图的计算方式,右柱状图同理)~
drawLine的坐标计算草图.png第二步:绘制坐标轴drawLineXY(canvas);
/**
* 绘制坐标轴
*/
private void drawLineXY(Canvas canvas) {
canvas.drawLine(mYDistance, mViewHeight - mXDistance, mYDistance, 15, mPaintTextXY);
canvas.drawLine(mYDistance, mViewHeight - mXDistance, mViewWidth - 15, mViewHeight - mXDistance, mPaintTextXY);
if (mIsShowArrow) {
//Y轴箭头
canvas.drawLine(mYDistance, 15, mYDistance - 10, 25, mPaintTextXY);
canvas.drawLine(mYDistance, 15, mYDistance + 10, 25, mPaintTextXY);
//X轴箭头
canvas.drawLine(mViewWidth - 15, mViewHeight - mXDistance, mViewWidth - 25, mViewHeight - mXDistance - 10, mPaintTextXY);
canvas.drawLine(mViewWidth - 15, mViewHeight - mXDistance, mViewWidth - 25, mViewHeight - mXDistance + 10, mPaintTextXY);
}
}
第三步:绘制X坐标值drawLineX(canvas);
/**
* 绘制X坐标值
*/
private void drawLineX(Canvas canvas) {
for (int i = 0; i < mDataTextX.length; i++) {
//绘制进度数字
String text = mDataTextX[i];
//获取文字宽度
float textWidth = mPaintTextXY.measureText(text, 0, text.length());
float dx = (mYDistance + mBigDistance + mSmallDistance / 2 + mLineWidth + (i * (mBigDistance + mSmallDistance + 2 * mLineWidth))) - textWidth / 2;
Paint.FontMetricsInt fontMetricsInt = mPaintTextXY.getFontMetricsInt();
float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
float baseLine = mViewHeight - mXDistance / 2 + dy;
canvas.drawText(text, dx, baseLine, mPaintTextXY);
}
}
第四步:绘制Y坐标值drawLineY(canvas);
/**
* 绘制Y坐标值
* 这里的坐标值是根据最大值计算出来对应的间隔,然后从0显示出6个数据
*/
private void drawLineY(Canvas canvas) {
for (int i = 0; i < 6; i++) {
//绘制进度数字
String text = (mMaxData / 5 * i) + "";
//获取文字宽度
float textWidth = mPaintTextXY.measureText(text, 0, text.length());
float dx = mYDistance / 2 - textWidth / 2;
Paint.FontMetricsInt fontMetricsInt = mPaintTextXY.getFontMetricsInt();
float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
float baseLine = (mViewHeight - mXDistance) - (i * (mViewHeight - 2 * mXDistance) / 5) + dy;
canvas.drawText(text, dx, baseLine, mPaintTextXY);
if (mIsShowArrowYInterval) canvas.drawLine(mYDistance, (mViewHeight - mXDistance)
- (i * (mViewHeight - 2 * mXDistance) / 5), mYDistance + 10,
(mViewHeight - mXDistance) - (i * (mViewHeight - 2 * mXDistance) / 5), mPaintTextXY);
}
}
最后再处理一下动画这块就差不多了:
/**
* 开启动画,并且绘制图表
*/
public void start() {
AnimatorSet set = new AnimatorSet();
for (int i = 0; i < mDataLeft.length; i++) {
ValueAnimator animator = ValueAnimator.ofInt(0, mDataLeft[i]);
final int finalI = i;
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mDataLeft[finalI] = (int) valueAnimator.getAnimatedValue();
invalidate();
}
});
set.playTogether(animator);
}
for (int i = 0; i < mDataRight.length; i++) {
ValueAnimator animator = ValueAnimator.ofInt(0, mDataRight[i]);
final int finalI = i;
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mDataRight[finalI] = (int) valueAnimator.getAnimatedValue();
invalidate();
}
});
set.playTogether(animator);
}
set.setInterpolator(new DecelerateInterpolator());
set.setDuration(mAnimationDuration);
set.start();
}
好的,写到这里就可以在Activity中使用了,下面上全家福咯,注释很详细。
DoubleLineChatView.java
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
/**
* Created by zhuyong on 2017/10/12.
*/
public class DoubleLineChatView extends View {
private static final String TAG = "DoubleLineChatView";
/**
* 上下文
*/
private Context mContext;
/**
* 左边柱状图画笔
*/
private Paint mPaintLeft;
/**
* 右边柱状图画笔
*/
private Paint mPaintRight;
/**
* 左边柱状图文字画笔
*/
private Paint mPaintTextLeft;
/**
* 右边柱状图文字画笔
*/
private Paint mPaintTextRight;
/**
* XY轴画笔
*/
private Paint mPaintTextXY;
/**
* 左边柱状图数据
*/
private int[] mDataLeft = {0, 0, 0, 0};
/**
* 右边柱状图数据
*/
private int[] mDataRight = {0, 0, 0, 0};
/**
* X轴底部文字
*/
private String[] mDataTextX = {"", "", "", ""};
/**
* Y轴文字宽度
*/
private float mYDistance;
/**
* X轴底部高度
*/
private float mXDistance;
/**
* 柱状图宽度
*/
private float mLineWidth;
/**
* 颜色字体等变量
*/
private int mLeftLineBackGroundColor = Color.RED;
private int mRightLineBackGroundColor = Color.BLUE;
private int mLeftLineTextColor = Color.RED;
private int mRightLineTextColor = Color.BLUE;
private float mLineTextSize;
private int mLineXYColor = Color.BLACK;
private float mLineXYSize;
/**
* 是否显示XY轴箭头
*/
private boolean mIsShowArrow = true;
/**
* 是否显示Y轴间隔标记
*/
private boolean mIsShowArrowYInterval = true;
/**
* 动画时长,默认1秒
*/
private int mAnimationDuration = 1000;
/**
* View宽度
*/
private int mViewWidth;
/**
* View高度
*/
private int mViewHeight;
/**
* 柱状图的Y轴最大数据,等于或者大于两个数组中的最大值
*/
private int mMaxData;
/**
* 两组柱状图之间的距离(偏大距离)
*/
private float mBigDistance;
/**
* 两个柱状图之间的距离(偏小距离)
*/
private float mSmallDistance;
public DoubleLineChatView(Context context) {
this(context, null);
}
public DoubleLineChatView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DoubleLineChatView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
//获取自定义属性
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DoubleLineChatView);
//柱状图的宽度
mLineWidth = array.getDimension(R.styleable.DoubleLineChatView_chatview_line_width, dip2px(mContext, 30));
//左边柱状图的背景色
mLeftLineBackGroundColor = array.getColor(R.styleable.DoubleLineChatView_chatview_left_background_color, mLeftLineBackGroundColor);
//右边柱状图的背景色
mRightLineBackGroundColor = array.getColor(R.styleable.DoubleLineChatView_chatview_right_background_color, mRightLineBackGroundColor);
//左边柱状图的数据字体颜色
mLeftLineTextColor = array.getColor(R.styleable.DoubleLineChatView_chatview_left_text_data_color, mLeftLineTextColor);
//右边柱状图的数据字体颜色
mRightLineTextColor = array.getColor(R.styleable.DoubleLineChatView_chatview_right_text_data_color, mRightLineTextColor);
//柱状图的数据字体大小
mLineTextSize = array.getDimension(R.styleable.DoubleLineChatView_chatview_text_data_size, sp2px(mContext, 14));
//XY轴的颜色以及字体颜色
mLineXYColor = array.getColor(R.styleable.DoubleLineChatView_chatview_text_xy_color, mLineXYColor);
//XY轴的字体大小
mLineXYSize = array.getDimension(R.styleable.DoubleLineChatView_chatview_text_xy_size, sp2px(mContext, 14));
//两组柱状图之间的距离(偏大距离)
mBigDistance = array.getDimension(R.styleable.DoubleLineChatView_chatview_line_big_distance, dip2px(mContext, 20));
//两个柱状图之间的距离(偏小距离)
mSmallDistance = array.getDimension(R.styleable.DoubleLineChatView_chatview_line_small_distance, dip2px(mContext, 5));
//是否显示XY轴的箭头
mIsShowArrow = array.getBoolean(R.styleable.DoubleLineChatView_chatview_show_arrow, true);
//是否显示Y轴的数据间隔标志
mIsShowArrowYInterval = array.getBoolean(R.styleable.DoubleLineChatView_chatview_show_y_interval, true);
//柱状图生长动画时间,默认1秒
mAnimationDuration = array.getInteger(R.styleable.DoubleLineChatView_chatview_animation_duration, 1000);
//Y轴数据的宽度,也就是Y轴距离左边的宽度,这个要根据数据字符串的长度进行调整
mYDistance = array.getDimension(R.styleable.DoubleLineChatView_chatview_y_distance, dip2px(mContext, 40));
//X轴数据的高度,也就是X轴距离底部的距离
mXDistance = array.getDimension(R.styleable.DoubleLineChatView_chatview_x_distance, dip2px(mContext, 40));
array.recycle();
initView();
}
/**
* 初始化画笔
*/
private void initView() {
//左边柱状图
mPaintLeft = new Paint();
mPaintLeft.setColor(mLeftLineBackGroundColor);
mPaintLeft.setStrokeWidth(mLineWidth);
mPaintLeft.setAntiAlias(true);
//右边柱状图
mPaintRight = new Paint();
mPaintRight.setColor(mRightLineBackGroundColor);
mPaintRight.setStrokeWidth(mLineWidth);
mPaintRight.setAntiAlias(true);
//左边柱状图字体数据
mPaintTextLeft = new Paint();
mPaintTextLeft.setColor(mLeftLineTextColor);
mPaintTextLeft.setTextSize(mLineTextSize);
mPaintTextLeft.setAntiAlias(true);
//右边柱状图字体数据
mPaintTextRight = new Paint();
mPaintTextRight.setColor(mRightLineTextColor);
mPaintTextRight.setTextSize(mLineTextSize);
mPaintTextRight.setAntiAlias(true);
//XY轴
mPaintTextXY = new Paint();
mPaintTextXY.setStrokeWidth(3);
mPaintTextXY.setColor(mLineXYColor);
mPaintTextXY.setTextSize(mLineXYSize);
mPaintTextXY.setAntiAlias(true);
}
/**
* 数据
*
* @param dataLeft 左边柱状图数据
* @param dataRight 右边柱状图数据
* @param dataTextX X轴文字数组
*/
public void setData(int[] dataLeft, int[] dataRight, String[] dataTextX) {
this.mDataLeft = dataLeft;
this.mDataRight = dataRight;
this.mDataTextX = dataTextX;
//找出两个数组中的最大值
int maxLeft = getMax(mDataLeft);
int maxRight = getMax(mDataRight);
mMaxData = maxLeft > maxRight ? maxLeft : maxRight;
Log.i(TAG, "mMaxCount=" + mMaxData);
//计算Y轴坐标最大值,根据当前最大数据并且是5的整倍数计算
while (mMaxData % 5 != 0) {
mMaxData++;
}
Log.i(TAG, "mMaxCount=" + mMaxData);
}
/**
* 找出数组中的最大值
*
* @param arr 目标数组
* @return 最大值
*/
private static int getMax(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setDimension(heightMeasureSpec);
}
/**
* 重新赋予宽高
*/
private void setDimension(int heightMeasureSpec) {
//
mViewWidth = (int) (mYDistance + mBigDistance + (mDataLeft.length * (mLineWidth * 2 + mBigDistance + mSmallDistance)));
mViewHeight = MeasureSpec.getSize(heightMeasureSpec);
Log.i(TAG, "mViewWidth=" + mViewWidth + "px");
Log.i(TAG, "mViewHeight=" + mViewHeight + "px");
setMeasuredDimension(mViewWidth, mViewHeight);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**
* 绘制柱状图
*/
drawLineData(canvas);
/**
* 绘制坐标轴
*/
drawLineXY(canvas);
/**
* 绘制X坐标值
*/
drawLineX(canvas);
/**
* 绘制Y坐标值
*/
drawLineY(canvas);
}
/**
* 绘制X坐标值
*/
private void drawLineX(Canvas canvas) {
for (int i = 0; i < mDataTextX.length; i++) {
//绘制进度数字
String text = mDataTextX[i];
//获取文字宽度
float textWidth = mPaintTextXY.measureText(text, 0, text.length());
float dx = (mYDistance + mBigDistance + mSmallDistance / 2 + mLineWidth + (i * (mBigDistance + mSmallDistance + 2 * mLineWidth))) - textWidth / 2;
Paint.FontMetricsInt fontMetricsInt = mPaintTextXY.getFontMetricsInt();
float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
float baseLine = mViewHeight - mXDistance / 2 + dy;
canvas.drawText(text, dx, baseLine, mPaintTextXY);
}
}
/**
* 绘制Y坐标值
* 这里的坐标值是根据最大值计算出来对应的间隔,然后从0显示出6个数据
*/
private void drawLineY(Canvas canvas) {
for (int i = 0; i < 6; i++) {
//绘制进度数字
String text = (mMaxData / 5 * i) + "";
//获取文字宽度
float textWidth = mPaintTextXY.measureText(text, 0, text.length());
float dx = mYDistance / 2 - textWidth / 2;
Paint.FontMetricsInt fontMetricsInt = mPaintTextXY.getFontMetricsInt();
float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
float baseLine = (mViewHeight - mXDistance) - (i * (mViewHeight - 2 * mXDistance) / 5) + dy;
canvas.drawText(text, dx, baseLine, mPaintTextXY);
if (mIsShowArrowYInterval) canvas.drawLine(mYDistance, (mViewHeight - mXDistance)
- (i * (mViewHeight - 2 * mXDistance) / 5), mYDistance + 10,
(mViewHeight - mXDistance) - (i * (mViewHeight - 2 * mXDistance) / 5), mPaintTextXY);
}
}
/**
* 绘制坐标轴
*/
private void drawLineXY(Canvas canvas) {
canvas.drawLine(mYDistance, mViewHeight - mXDistance, mYDistance, 15, mPaintTextXY);
canvas.drawLine(mYDistance, mViewHeight - mXDistance, mViewWidth - 15, mViewHeight - mXDistance, mPaintTextXY);
if (mIsShowArrow) {
//Y轴箭头
canvas.drawLine(mYDistance, 15, mYDistance - 10, 25, mPaintTextXY);
canvas.drawLine(mYDistance, 15, mYDistance + 10, 25, mPaintTextXY);
//X轴箭头
canvas.drawLine(mViewWidth - 15, mViewHeight - mXDistance, mViewWidth - 25, mViewHeight - mXDistance - 10, mPaintTextXY);
canvas.drawLine(mViewWidth - 15, mViewHeight - mXDistance, mViewWidth - 25, mViewHeight - mXDistance + 10, mPaintTextXY);
}
}
/**
* 绘制柱状图
*/
private void drawLineData(Canvas canvas) {
for (int i = 0; i < mDataLeft.length; i++) {
float startX = mYDistance + mBigDistance + mLineWidth / 2 + (i * (mBigDistance + mSmallDistance + 2 * mLineWidth));
float endY = (mViewHeight - mXDistance) - (mDataLeft[i] * (mViewHeight - 2 * mXDistance)) / mMaxData;
canvas.drawLine(startX, mViewHeight - mXDistance, startX, endY, mPaintLeft);
String text = mDataLeft[i] + "";
float textWidth = mPaintTextLeft.measureText(text, 0, text.length());
canvas.drawText(text, startX - textWidth / 2, endY - 15, mPaintTextLeft);
}
for (int i = 0; i < mDataRight.length; i++) {
float startX = mYDistance + mBigDistance + mSmallDistance + mLineWidth + mLineWidth / 2 + (i * (mBigDistance + mSmallDistance + 2 * mLineWidth));
float endY = ((mViewHeight - mXDistance)) - (mDataRight[i] * (mViewHeight - 2 * mXDistance)) / mMaxData;
canvas.drawLine(startX, mViewHeight - mXDistance, startX, endY, mPaintRight);
String text = mDataRight[i] + "";
float textWidth = mPaintTextRight.measureText(text, 0, text.length());
canvas.drawText(text, startX - textWidth / 2, endY - 15, mPaintTextRight);
}
}
/**
* 开启动画,并且绘制图表
*/
public void start() {
AnimatorSet set = new AnimatorSet();
for (int i = 0; i < mDataLeft.length; i++) {
ValueAnimator animator = ValueAnimator.ofInt(0, mDataLeft[i]);
final int finalI = i;
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mDataLeft[finalI] = (int) valueAnimator.getAnimatedValue();
invalidate();
}
});
set.playTogether(animator);
}
for (int i = 0; i < mDataRight.length; i++) {
ValueAnimator animator = ValueAnimator.ofInt(0, mDataRight[i]);
final int finalI = i;
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mDataRight[finalI] = (int) valueAnimator.getAnimatedValue();
invalidate();
}
});
set.playTogether(animator);
}
set.setInterpolator(new DecelerateInterpolator());
set.setDuration(mAnimationDuration);
set.start();
}
/**
* sp转px
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
/**
* dp转px
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context="com.zhuyong.doublelinechatview.MainActivity">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="15dp"
android:background="#DEDEDE"
android:scrollbars="none">
<com.zhuyong.doublelinechatview.DoubleLineChatView
android:id="@+id/line_chat_one"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:chatview_animation_duration="0"
app:chatview_left_background_color="@color/colorAccent"
app:chatview_left_text_data_color="@color/colorAccent"
app:chatview_line_big_distance="20dp"
app:chatview_line_small_distance="3dp"
app:chatview_line_width="25dp"
app:chatview_right_background_color="@color/colorPrimaryDark"
app:chatview_right_text_data_color="@color/colorPrimaryDark"
app:chatview_show_arrow="true"
app:chatview_show_y_interval="true"
app:chatview_text_data_size="14sp"
app:chatview_text_xy_color="@color/black"
app:chatview_text_xy_size="14sp"
app:chatview_x_distance="40dp"
app:chatview_y_distance="45dp" />
</HorizontalScrollView>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="15dp">
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始动画" />
<LinearLayout
android:id="@+id/llayout_uncheck"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:gravity="center"
android:orientation="horizontal">
<View
android:layout_width="15dp"
android:layout_height="15dp"
android:background="@color/yellow" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="未审核数" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@+id/llayout_uncheck"
android:layout_marginTop="5dp"
android:gravity="center"
android:orientation="horizontal">
<View
android:layout_width="15dp"
android:layout_height="15dp"
android:background="@color/green" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="已审核数" />
</LinearLayout>
</RelativeLayout>
<com.zhuyong.doublelinechatview.DoubleLineChatView
android:id="@+id/line_chat_two"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="15dp"
app:chatview_animation_duration="1000"
app:chatview_left_background_color="@color/yellow"
app:chatview_left_text_data_color="@color/yellow"
app:chatview_line_big_distance="20dp"
app:chatview_line_small_distance="3dp"
app:chatview_line_width="25dp"
app:chatview_right_background_color="@color/green"
app:chatview_right_text_data_color="@color/green"
app:chatview_show_arrow="true"
app:chatview_show_y_interval="true"
app:chatview_text_data_size="14sp"
app:chatview_text_xy_color="@color/black"
app:chatview_text_xy_size="14sp"
app:chatview_x_distance="40dp"
app:chatview_y_distance="45dp" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
/**
* 第一组数据
*/
private int[] mDataLeft = {60, 180, 130, 10, 299, 45, 199, 20, 250};
private int[] mDataRight = {151, 65, 280, 66, 105, 88, 198, 299, 45};
private String[] mDataTextX = {"项目1", "项目2", "项目3", "项目4", "项目5", "项目6", "项目7", "项目8", "项目9"};
/**
* 第二组数据
*/
private int[] mDataLeftTwo = {60, 181, 130, 100};
private int[] mDataRightTwo = {151, 65, 40, 20};
private String[] mDataTextXTwo = {"测试1", "测试2", "测试3", "测试4"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final DoubleLineChatView doubleLineChatViewOne = (DoubleLineChatView) findViewById(R.id.line_chat_one);
doubleLineChatViewOne.setData(mDataLeft, mDataRight, mDataTextX);
doubleLineChatViewOne.start();
final DoubleLineChatView doubleLineChatViewTwo = (DoubleLineChatView) findViewById(R.id.line_chat_two);
doubleLineChatViewTwo.setData(mDataLeftTwo, mDataRightTwo, mDataTextXTwo);
findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
doubleLineChatViewTwo.start();
}
});
}
}
GitHub传送门:源码