序
- 此Demo单纯实现自定义框的部分
- 初入自定义的伙伴可此来一观,应有助益。
- 虚心务实、共勉共进。
目录
简介
-
思路分析(理论指导实践。不管代码,从逻辑上先分析,逻辑通透后,在逐步实现编码。)
首先,终极目标,要做什么,最终的样子,肯定要烂熟,画猫画狗,先定论(这里,我们要使用自定义控件知识,编写一个可手势控制的矩形框)
-
其次,目标确定了,然而不能一蹴而就,一口吃不成胖子,就需要拆分目标。针对我们的目标,一个手势控制的矩形框,可拆分如下↓
可控制的意思是,手势移动、手势缩放。
a.点击框内,拖拽,移动矩形。(此处衍生出两个问题,1.怎么知道点击焦点在矩形内。2.手势移动时,矩形怎么跟随移动。)b. 点击边框线,拖拽,单边缩放矩形(此处衍生出两个问题,1.怎么知道点击焦点在边框线上。2.手势拖拽时,矩形怎么单边缩放。)
c. 点击边角,拖拽,双边缩放矩形(此处衍生出两个问题,1.怎么知道点击焦点在边角上。2.手势拖拽时,矩形怎么双边缩放。)
最后,理解以上,方向即正确了,细节也明确了,实现起来,相信已不是难事。
-
好,接下来,我们针对每一个细节问题,逐一击破。
a .移动缩放判断
矩形怎么移动?
1. 矩形是怎么绘制到界面上的,矩形的位置大小怎么确认。经了解,一般用坐标点确认,四个点(A、B、C、D),四对坐标,分别确认矩形的四个顶点。当然。函数中,作为形参的坐标点,事实上只需要左上点坐标,和右下点坐标即可。即,两点确定一个矩形。如图,矩形S由点A和点D确认。2. 矩形移动的本质到底是什么。从上面我们能知道,矩形的位置大小皆由一对坐标确认,所以,只要矩形的坐标发生改变,矩形的位置大小也就相应改变了。因而,移动的本质,就是坐标点的变化。
3. 坐标点怎么变化,手势点击屏幕,移动,自然,手指和屏幕接触的点也有坐标,而矩形需要跟随手指移动,所以,只需要,实时获取手指在屏幕上的当前的坐标点与在屏幕上的上一次坐标点的差值,然后,矩形的左上(A)、右下(D)两点坐标分别减去或加上这个插值,重绘,即可。如图,矩形S移动至S'的位置,A点到A'的坐标变化是这样的,我们知道,android的坐标系,原点在左上角,X坐标,往右为正(增 ),向左为负(减),Y坐标,向下为正(增 ),上移为负(减 ),遂,A'的X坐标=A的X坐标+AE'的长度。A'的Y坐标=A的Y坐标-E'A'的长度,D到D'依葫芦画瓢。
4. 矩形移动的过程就是不断计算重绘的过程,每一次移动(只要达到系统认为的移动最小值)都会根据手势位置去计算新矩形的位置(A'和D'的位置)
1. 单边缩放,顾名思义,即,矩形的一边( 宽或高)的长度发生变化,变长或缩短的过程。如图,即是矩形S(ABDC)到矩形S'(A'B'C'D')的过程。我们知道,矩形的位置大小由一对坐标点确定(点A和点D),所以,不管如何去单边缩放,本质就是改变这两点的坐标,如图,我们发现D点的坐标不变,A点坐标只改变X值 Y不变,而X值的改变大小就是AA'的长度。所以,我们单边缩放的思路是,实时获取手指在屏幕上的当前的坐标点与在屏幕上的上一次坐标点的X或Y的差值,然后点A或点D的X或Y实时加上或减去这个插值,重绘即可。2.注意,单边缩放,只需改变其中一点的X或者Y值就可以了。同时改变X、Y值,就是双边缩放了
1. 双边缩放,顾名思义,即,同矩形的两边(一宽和一高)的长度发生变化,变成或缩短的过程,如图,矩形S到矩形S’,就是一次双边缩放,矩形S的边AB和边AC同时改变至边A'B'和A‘C'位置。本质就是点A的X,Y坐标同时改变,就是双边缩放了,所以我们双边缩放的思路是,实时获取手指在屏幕上的当前的坐标点与在屏幕上的上一次坐标点的X和Y的差值,然后点A或点D的X和Y实时加上或减去这个插值,重绘即可。2.注意,双边缩放,需要同时改变X和Y坐标才行。
b. 焦点判断
怎么知道点击焦点在矩形内?
怎么知道点击焦点在边框线上?
怎么知道点击焦点在边角上?
-
准备工作
- 画笔
android里面,画笔的关键字是Piant
,构造初始化即可(new Paint
),一般作为参数传入画布(Canvas)中,绘制的颜色、透明度、是否有边框、边框粗细等、都由画笔确认,可以重复使用。为了代码的易于理解,这里每种对象都定义一只对应的画笔,如下
/*画笔*/ /*边框画笔*/private Paint mRectPaint; /*边角线画笔*/private Paint mCornerPaint; /*文字画笔*/private Paint mTextPaint; /*测绘线画笔*/private Paint mMappingLinePaint;
- 画布
Android里面,画布的关键字是Canvas
,一般自定义控件的onDraw
方法里持有一个,可直接使用。这里的画布,并非真正意义上的画布,它代表 一组绘制规则(是点、还是圆、弧形、还是图,以及绘制的位置、大小等),对应的规则有对应的函数,调用传参即可,图形就是根据这组规则绘制到屏幕上的
@Override protected void onDraw(Canvas mCanvas) { super.onDraw(mCanvas); }
- 矩形绘制函数
canvas.drawRect(float left, float top, float right, float bottom, Paint paint)
left:矩形左上角以父控件左上角为原点的x坐标
top: 矩形左上角以父控件左上角为原点的y坐标
right: 矩形右下角以父控件左上角为原点的x坐标
bottom: 矩形右下角以父控件左上角为原点的y坐标
paint: 画笔
/**绘制矩形*/ canvas.drawRect(mRect_FourCorner_coordinate[0][0], mRect_FourCorner_coordinate[0][1], mRect_FourCorner_coordinate[3][0], mRect_FourCorner_coordinate[3][1], mRectPaint);
- 画笔
源码分析
-
变量(注释应该够是浅显了)
/*控件宽*/private float mViewWidth; /*控件高*/private float mViewHeight; /*上下文*/private Context mContext; /*自定框相关*/ /*矩形边长*/private float mRectLength = 200f * mDensity; /*矩形四个边角坐标*/private float[][] mRect_FourCorner_coordinate; /*边角线长度*/private float mCornerLength = 30f * mDensity; /*边角线偏移值*/private float mCornerOffset = 5 * mDensity; /*0-不动 1-拖动 2-边角缩放 3-边框缩放*/ /*矩形操作状态*/private int mOperatingStatus = 0; /*0-左 1-上 2-右 3-下*/ /*边框线点击-操作状态*/private int mBorderlineStatus = -1; /*0-左上角 1-左下角 2-右上角 3-右下角*/ /*边角点击-操作状态*/private int mCornerStatus = -1; /*是否绘制坐标点*/private boolean mIsDrawPonit; /*是否绘制测绘线*/private boolean mISDrawMapLine; /*是否开启动画*/private boolean mIsOpenAnima;
-
函数
-
onSizeChanged
:在这获取父控件的宽、高,用于计算矩形的四个坐标点。四个坐标点用一个浮点型二维数组表示,也在此处初始化。后面绘制边角、测绘线都使用此二维数组坐标值,矩形的移动、缩放,也是是通过改变此二维数组的元素值来变化的。/** * 获取控件宽、高 */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); /*获取控件宽高*/ mViewWidth = getWidth(); mViewHeight = getHeight(); /*初始化矩形四边角坐标*/ mRect_FourCorner_coordinate = new float[][]{ {(mViewWidth - mRectLength) / 2, (mViewHeight - mRectLength) / 2},//左上角 {(mViewWidth - mRectLength) / 2, (mViewHeight + mRectLength) / 2},//左下角 {(mViewWidth + mRectLength) / 2, (mViewHeight - mRectLength) / 2},//右上角 {(mViewWidth + mRectLength) / 2, (mViewHeight + mRectLength) / 2},//右下角 }; }
-
toolDrawCorner
:这个方法,用以绘制矩形的四个边角,绘制边角使用canvas.drawLine
方法,本质是画线,通过改变画笔的粗细,达到边角矩形的呈现效果 。/** * 绘制边角 */ private void toolDrawCorner(Canvas canvas) { /*绘制边角*/ /*左上-横*/ canvas.drawLine(mRect_FourCorner_coordinate[0][0] - mCornerOffset, mRect_FourCorner_coordinate[0][1] , mRect_FourCorner_coordinate[0][0] + mCornerLength, mRect_FourCorner_coordinate[0][1], mCornerPaint); /*左上-竖*/ canvas.drawLine(mRect_FourCorner_coordinate[0][0], mRect_FourCorner_coordinate[0][1] - mCornerOffset , mRect_FourCorner_coordinate[0][0], mRect_FourCorner_coordinate[0][1] + mCornerLength, mCornerPaint); /*左下-横*/ canvas.drawLine(mRect_FourCorner_coordinate[1][0] - mCornerOffset, mRect_FourCorner_coordinate[1][1] , mRect_FourCorner_coordinate[1][0] + mCornerLength, mRect_FourCorner_coordinate[1][1], mCornerPaint); /*左上下-竖*/ canvas.drawLine(mRect_FourCorner_coordinate[1][0], mRect_FourCorner_coordinate[1][1] - mCornerLength , mRect_FourCorner_coordinate[1][0], mRect_FourCorner_coordinate[1][1] + mCornerOffset, mCornerPaint); /*右上-横*/ canvas.drawLine(mRect_FourCorner_coordinate[2][0] - mCornerLength, mRect_FourCorner_coordinate[2][1] , mRect_FourCorner_coordinate[2][0] + mCornerOffset, mRect_FourCorner_coordinate[2][1], mCornerPaint); /*右上-竖*/ canvas.drawLine(mRect_FourCorner_coordinate[2][0], mRect_FourCorner_coordinate[2][1] - mCornerOffset , mRect_FourCorner_coordinate[2][0], mRect_FourCorner_coordinate[2][1] + mCornerLength, mCornerPaint); /*右下-横*/ canvas.drawLine(mRect_FourCorner_coordinate[3][0] - mCornerLength, mRect_FourCorner_coordinate[3][1] , mRect_FourCorner_coordinate[3][0] + mCornerOffset, mRect_FourCorner_coordinate[3][1], mCornerPaint); /*右下-竖*/ canvas.drawLine(mRect_FourCorner_coordinate[3][0], mRect_FourCorner_coordinate[3][1] - mCornerLength , mRect_FourCorner_coordinate[3][0], mRect_FourCorner_coordinate[3][1] + mCornerOffset, mCornerPaint); }
-
toolDrawMapLine
:这个方法,用以绘制测绘线条。/** * 绘制测绘线 */ private void toolDrawMapLine(Canvas canvas) { /*绘制横线*/ /*绘制第一根线-位于矩形框的3分之一处*/ canvas.drawLine(mRect_FourCorner_coordinate[0][0] , mRect_FourCorner_coordinate[0][1] + (mRect_FourCorner_coordinate[1][1] - mRect_FourCorner_coordinate[0][1]) / 3 , mRect_FourCorner_coordinate[2][0] , mRect_FourCorner_coordinate[0][1] + (mRect_FourCorner_coordinate[1][1] - mRect_FourCorner_coordinate[0][1]) / 3 , mMappingLinePaint); /*绘制第二根线-位于矩形框的3分之二处*/ canvas.drawLine(mRect_FourCorner_coordinate[0][0] , mRect_FourCorner_coordinate[0][1] + (mRect_FourCorner_coordinate[1][1] - mRect_FourCorner_coordinate[0][1]) / 3 * 2 , mRect_FourCorner_coordinate[2][0] , mRect_FourCorner_coordinate[0][1] + (mRect_FourCorner_coordinate[1][1] - mRect_FourCorner_coordinate[0][1]) / 3 * 2 , mMappingLinePaint); /*绘制竖线*/ /*绘制第一根线-位于矩形框的3分之一处*/ canvas.drawLine(mRect_FourCorner_coordinate[0][0] + (mRect_FourCorner_coordinate[2][0] - mRect_FourCorner_coordinate[0][0]) / 3 , mRect_FourCorner_coordinate[0][1] , mRect_FourCorner_coordinate[0][0] + (mRect_FourCorner_coordinate[2][0] - mRect_FourCorner_coordinate[0][0]) / 3 , mRect_FourCorner_coordinate[1][1] , mMappingLinePaint); /*绘制第二根线-位于矩形框的3分之二处*/ canvas.drawLine(mRect_FourCorner_coordinate[0][0] + (mRect_FourCorner_coordinate[2][0] - mRect_FourCorner_coordinate[0][0]) / 3 * 2 , mRect_FourCorner_coordinate[0][1] , mRect_FourCorner_coordinate[0][0] + (mRect_FourCorner_coordinate[2][0] - mRect_FourCorner_coordinate[0][0]) / 3 * 2 , mRect_FourCorner_coordinate[1][1] , mMappingLinePaint); }
-
总结
- 以上
跋
- 初次撰文,篇幅冗余,废话颇多。
- 以上,也算是一些心得了
-
可直接看源码,注释详尽。
源码链接