使用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組合。