在上一篇中,我们见到了一个很简单的坐标轴,并且可以实现拖动等效果。本文在此基础上进行进一步深入探讨。仅供初学者学习,大牛看了请别乱喷哦~~谢谢~~
图为放大前和放大并拖动后
控件功能
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 ListmPointsList; private List 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();
源码下载: