在上一篇中,我们见到了一个很简单的坐标轴,并且可以实现拖动等效果。本文在此基础上进行进一步深入探讨。仅供初学者学习,大牛看了请别乱喷哦~~谢谢~~
图为放大前和放大并拖动后
控件功能
1. 支持坐标轴单位长度的放大缩小
2. 放大缩小基于坐标轴中间位置缩放,即中间位置的那个点为参考点,在程序中需要记录它的物理坐标和逻辑坐标,并且逻辑坐标随着拖动和缩放而不断变化,其他所有坐标的物理逻辑坐标之间的转换都是以此作为参考点的,这样可以使得缩放的时候让人感觉在用google地图(夸张了点)。
3. 支持内容拖动
4. 支持坐标轴名称和单位设置
5. 支持标题设置
6. 支持边距和标题高度设置
7. 支持添加任意多条不同折线,且可以由用户决定用什么样的Paint。
实现缩放和移动的关键为参考点的变化和坐标转换。
1. 坐标轴密度K
/*
* 横轴纵轴密度、长度和比例。
*/
private float mXValuePerPix, mYValuePerPix;
private float mXScale, mYScale;
mXValuePerPix / mXScale 为X轴的密度
mYValuePerPix / mYScale 为Y轴的密度
2. 移动的时候参考点的变化
/*
* 用于保存拖动时的上一个点的位置
*/
int x0, y0;
/*
* 拖动事件监听
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
/*
* (x,y)点为发生事件时的点,它的坐标值为相对于该控件左上角的距离
*/
int x = (int) event.getX();
int y = (int) event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN: // 按下
x0 = x;
y0 = y;
Log.i("down", "(" + x0 + "," + y0 + ")");
break;
case MotionEvent.ACTION_MOVE: // 拖动
/*
* (x-x0, y-y0)为物理坐标相对运动矢量
* mXValuePerPix / mXScale 为X轴的密度
* mYValuePerPix / mYScale 为Y轴的密度
* 即每个像素长度代表的长度
*
* 向右和向上移动的时候,坐标值都是变大的,而参考点是不动的所以相对来说是变小的
* 但是物理Y轴的矢量应该先取反,因为物理坐标点的坐标轴是向下Y增大的。
*/
mPointBaseValue.x -= (x - x0) * mXValuePerPix / mXScale;
mPointBaseValue.y -= -(y - y0) * mYValuePerPix / mYScale;
x0 = x;
y0 = y;
Log.i("move", "(" + x0 + "," + y0 + ")");
invalidate();
break;
case MotionEvent.ACTION_UP: // 弹起
break;
}
/*
* 注意:这里一定要返回true 返回false和super.onTouchEvent(event)都会本监听只能检测到按下消息
* 这是因为false和super.onTouchEvent(event)的处理都是告诉系统该控件不能处理这样的消息,
* 最终系统会将这些事件交给它的父容器处理。
*/
return true;
}
3. 坐标转换
/**
* 逻辑坐标转化为屏幕坐标
* 将逻辑坐标logPointF点转换为物理坐标
*/
private PointF point2Physical(PointF logPointF) {
PointF physicalPointF = new PointF();
physicalPointF.set((logPointF.x - mPointBaseValue.x) * mXScale
/ mXValuePerPix + mPointBase.x,
-(logPointF.y - mPointBaseValue.y) * mYScale / mYValuePerPix
+ mPointBase.y);
return physicalPointF;
}
/**
* 物理坐标转化为逻辑坐标
* 将物理坐标phyPointF点转换为逻辑坐标
*/
private PointF point2Logical(PointF phyPointF) {
float x = (phyPointF.x - mPointBase.x) * mXValuePerPix / mXScale
+ mPointBaseValue.x;
float y = (mPointBase.y - phyPointF.y) * mYValuePerPix / mYScale
+ mPointBaseValue.y;
PointF logicalPointF = new PointF(x, y);
return logicalPointF;
}
4. 程序中所用的成员变量一览
/*
* 颜料
*/
private Paint mPaint;
/*
* 数据集合
*/
private List<PointF[]> mPointsList;
private List<Paint> mPaintList;
/*
* 标题
*/
private boolean mHasTitle;
private String mTitle;
private int mTitleHeight;
private PointF mTitlePoint;
/*
* 边距
*/
private int mLeftPad, mRightPad, mBottomPad, mTopPad;
/*
* 横轴纵轴密度、长度和比例。
*/
private float mXValuePerPix, mYValuePerPix;
private int mXLen, mYLen;
private float mXScale, mYScale;
/*
* 横轴纵轴标识和单位
*/
private String mXAxisPrickle, mYAxisPrickle;
private String mXAxisName = "X", mYAxisName = "Y";
/*
* 圆心(坐标值是相对与控件的左上角的)
*/
// private PointF mPointZero = new PointF();
/*
* 参考坐标
*/
private PointF mPointBase = new PointF();
private PointF mPointBaseValue = new PointF();
/*
* 交叉点坐标中心点
*/
private PointF mPointOrigin = new PointF();
源码下载:CoordinatesTest.zip