Android-自定义手势裁剪框

草图.gif

  • 此Demo单纯实现自定义框的部分
  • 初入自定义的伙伴可此来一观,应有助益。
  • 虚心务实、共勉共进。

目录

纲要图.png

简介

  • 思路分析(理论指导实践。不管代码,从逻辑上先分析,逻辑通透后,在逐步实现编码。)

    • 首先,终极目标,要做什么,最终的样子,肯定要烂熟,画猫画狗,先定论(这里,我们要使用自定义控件知识,编写一个可手势控制的矩形框

    • 其次,目标确定了,然而不能一蹴而就,一口吃不成胖子,就需要拆分目标。针对我们的目标,一个手势控制的矩形框,可拆分如下↓

      可控制的意思是,手势移动、手势缩放。
      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);
          }
      

总结

  • 以上

  • 初次撰文,篇幅冗余,废话颇多。
  • 以上,也算是一些心得了
  • 可直接看源码,注释详尽。
    源码链接
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容