目录
一、简介
通过安卓自定义View实现根据手指点击或移动轨迹绘制出折(曲)线图并循环滚动。
二、实现
获取手指点击和移动的y坐标存入数组,设定好x间隔,根据坐标数组和间隔绘制折线路径,并使用定时器Timer实现循环移动数组内容位置重新绘制折线来实现。(此方法实现会有肉眼可见的帧数问题)
三、代码
attrs.xml:
<!--自动滚动折线波动图-->
<declare-styleable name="WaveView">
<!--折线间隔宽度-->
<attr name="lineSpaceWidth" format="integer"/>
<!--折线宽度-->
<attr name="lineWidth" format="integer"/>
<!--折线颜色-->
<attr name="lineColor" format="color"/>
</declare-styleable>
自定义WaveView.java:
/**
* author:created by mj
* Date:2022/5/17 11:05
* Description:自定义view用来实现手绘折线波动图的展示
*/
public class WaveView extends androidx.appcompat.widget.AppCompatImageView {
//折线宽度、颜色、间隔宽度
private float lineWidth;
private int lineColor;
private int lineSpaceWidth;
private Paint linePaint;
private Path path;
//定时器用来数据循环显示
private Timer timer;
private TimerTask timerTask;
//数组长度
private int row;
//绘制开始纵坐标位置
private float startY;
//折线坐标数组
private float[] dataArray;
private float[] tempData;
private float mHeight = 0;
@RequiresApi(api = Build.VERSION_CODES.M)
public WaveView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
@RequiresApi(api = Build.VERSION_CODES.M)
public WaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
//初始化控件自定义属性和默认值以及画笔等方法
@RequiresApi(api = Build.VERSION_CODES.M)
private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.WaveView);
lineWidth = typedArray.getInteger(R.styleable.WaveView_lineWidth, 5);
lineSpaceWidth = typedArray.getInteger(R.styleable.WaveView_lineSpaceWidth, 20);
lineColor = typedArray.getColor(R.styleable.WaveView_lineColor, context.getColor(R.color.hand_drawn_color));
this.setWillNotDraw(false);
//资源回收
typedArray.recycle();
//初始化画笔
linePaint = new Paint();
linePaint.setStyle(Paint.Style.STROKE);
linePaint.setStrokeWidth(lineWidth);
linePaint.setColor(lineColor);
linePaint.setStrokeCap(Paint.Cap.ROUND);
linePaint.setAntiAlias(true);
path = new Path();
//初始化定时器
timer = new Timer();
}
@SuppressLint("DrawAllocation")
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//设置长度和宽度以及初始位置
mHeight = getMeasuredHeight();
//组件高度和宽度
float mWidth = getMeasuredWidth();
startY = mHeight / 1.25f;
//根据间隔设置最多绘制多少个数据点
row = (int) (mWidth / lineSpaceWidth) + 1;
//初始化数据数组
dataArray = new float[row];
tempData = new float[row];
for (int i = 0; i < row; i++) {
dataArray[i] = startY;
}
//定时器循环执行数据改变实现滚动
timerTask = new TimerTask() {
@Override
public void run() {
//改变数组内容位置
tempData = dataArray;
//数组内容向前移动一位
float temp = tempData[0];
System.arraycopy(tempData, 1, tempData, 0, tempData.length - 1);
tempData[tempData.length - 1] = temp;
dataArray = tempData;
postInvalidate();
}
};
timer.schedule(timerTask, 0, 100);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawWaveLine(canvas);
}
//绘制折线
private void drawWaveLine(Canvas canvas) {
path.reset();
path.moveTo(0, dataArray[0]);
for (int i = 0; i < dataArray.length - 1; i++) {
path.lineTo(i * lineSpaceWidth, dataArray[i]);
}
canvas.drawPath(path, linePaint);
}
public float[] getTempData() {
return tempData;
}
public void setTempData(float[] tempData) {
this.tempData = tempData;
}
}
布局中添加组件
<com.example.androidproject.WaveView
android:id="@+id/view_drawnShow"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:lineColor="@color/hand_drawn_color"
app:lineSpaceWidth="20"
app:lineWidth="10"
/>
界面中添加数据
private WaveView showView;
showView = findViewById(R.id.view_drawnShow);
private final View.OnTouchListener touchListener = new View.OnTouchListener() {
//手指点击和移动事件
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
//添加坐标数据
tempData = showView.getTempData();
//控制最大高度
if (event.getY() < 0)
tempData[tempData.length - 1] = toolbar.getHeight()/4f;
else
tempData[tempData.length - 1] = event.getY();
showView.setTempData(tempData);
break;
default:
break;
}
return true;
}
};
四、实现效果