使用RecycleView进行画图表。大概几年前,我在想,怎么搞一个一个折线图的图表,突发灵感使用RecycleView试试如何。后来就写了一个Demo。构造过程主要是:
- 把图表拆成Item来表示
- 编写Adapter进行组装
把图标拆成Item来表示
拆分情况有三种,对于Item为Index为0;Item的Index为size-1;其他。如下图表示:
对于浅颜色的是Item外的Item,可以定义为LeftItem,MidItem,RightItem。如果要弄成曲线也是很直观的如下图:
Item的绘制代码
package com.owant.space.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by owant on 2018/6/8.
*/
public class DrawItemView extends View {
private Paint mPaint;
private int mWidth;
private int mHeight;
private static int default_dy = 10;
private static int default_dx = 250;
private int mPosition;
private Path mPath;
private int[] mData;
LocationPoint leftPoint = new LocationPoint();
LocationPoint midPoint = new LocationPoint();
LocationPoint rightPoint = new LocationPoint();
private boolean selected = false;
public DrawItemView(Context context) {
this(context, null, 0);
}
public DrawItemView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public DrawItemView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPath = new Path();
mPath.reset();
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setAntiAlias(true);
mPaint.setStrokeJoin(Paint.Join.ROUND);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
default_dx = mWidth / 2;
System.out.println(mWidth);
System.out.println(mHeight);
int left = this.mPosition - 1;
int right = this.mPosition + 1;
midPoint.x = mWidth / 2;
midPoint.y = this.mData[mPosition] * default_dy;
if (mPosition == 0) {
rightPoint.x = mWidth + mWidth / 2;
rightPoint.y = mData[right] * default_dy;
mPath.moveTo(midPoint.x, midPoint.y);
mPath.cubicTo(midPoint.x + default_dx, midPoint.y,
rightPoint.x - default_dx, rightPoint.y,
rightPoint.x, rightPoint.y);
mPath.lineTo(rightPoint.x, mHeight);
mPath.lineTo(midPoint.x, mHeight);
mPath.close();
} else if (mPosition == mData.length - 1) {
leftPoint.x = -mWidth / 2;
leftPoint.y = mData[left] * default_dy;
mPath.moveTo(leftPoint.x, leftPoint.y);
mPath.cubicTo(leftPoint.x + default_dx, leftPoint.y,
midPoint.x - default_dx, midPoint.y,
midPoint.x, midPoint.y);
mPath.lineTo(midPoint.x, mHeight);
mPath.lineTo(leftPoint.x, mHeight);
mPath.close();
} else {
leftPoint.x = -mWidth / 2;
leftPoint.y = mData[left] * default_dy;
rightPoint.x = mWidth + mWidth / 2;
rightPoint.y = mData[right] * default_dy;
mPath.moveTo(leftPoint.x, leftPoint.y);
mPath.cubicTo(leftPoint.x + default_dx, leftPoint.y,
midPoint.x - default_dx, midPoint.y,
midPoint.x, midPoint.y);
mPath.cubicTo(midPoint.x + default_dx, midPoint.y,
rightPoint.x - default_dx, rightPoint.y,
rightPoint.x, rightPoint.y);
mPath.lineTo(rightPoint.x, mHeight);
mPath.lineTo(leftPoint.x, mHeight);
mPath.close();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
selected = true;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_OUTSIDE:
selected = false;
break;
default:
break;
}
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
LinearGradient linearGradient = new LinearGradient(
0, 0,
mWidth, mHeight,
Color.RED, Color.YELLOW,
Shader.TileMode.MIRROR
);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setShader(linearGradient);
canvas.drawPath(mPath, mPaint);
if (selected) {
mPaint.setColor(Color.WHITE);
canvas.drawCircle(midPoint.x, midPoint.y, 10, mPaint);
}
}
public void setDataSource(int[] data, int position) {
this.mData = data;
this.mPosition = position;
}
}
Adapter进行组装
组装按照常规进行即可。简单代码如下
package com.owant.space.adapter;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.owant.space.R;
import com.owant.space.view.DrawItemView;
/**
* Created by owant on 2018/6/8.
*/
public class DataSourceAdapter extends RecyclerView.Adapter<DataSourceAdapter.ViewHolder> {
private Context mContext;
private int[] mDataSource;
public DataSourceAdapter(Context context, int[] dataSource) {
this.mContext = context;
this.mDataSource = dataSource;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, null);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.setIsRecyclable(false);
holder.drawItemView.setDataSource(mDataSource, position);
holder.tv.setText(position + "");
}
@Override
public int getItemCount() {
return mDataSource.length;
}
@Override
public long getItemId(int position) {
return position;
}
static class ViewHolder extends RecyclerView.ViewHolder {
DrawItemView drawItemView;
TextView tv;
public ViewHolder(View itemView) {
super(itemView);
drawItemView = itemView.findViewById(R.id.item_div);
tv = itemView.findViewById(R.id.item_id);
}
}
}
效果
组装起来的效果还行,具体要项目用还需要进行修改。
总结
好处使用到了RecycleView,方便复用Item,简化了计算,只计算一个Item即可。坏处就是可以用一个View进行绘制的,变成了多个Item组合。