android 自定义view之选座功能

效果图:
这里写图片描述

界面比较粗糙,主要看原理。
这个界面主要包括以下几部分
1、座位
2、左边的排数
3、左上方的缩略图
4、缩略图中的红色区域
5、手指移动时跟随移动
6、两个手指缩放时跟随缩放
主要技术点
1、矩阵Matrix
2、GestureDetector与ScaleGestureDetector
3、Bitmap的一下基本用法
4、这里只需要重写view的onDraw就可实现全部功能

可以发现这个其实没什么难度,主要就是一些位置的计算。

为了能便于理解首先把要用到的知识点进行一下梳理
1、矩阵Matrix
Matrix由3*3矩阵中9个值来决定,我们对Matrix的所有设置, 就是对这9个值的操作。
{MSCALE_X,MSKEW_X,MTRANS_X,
MSKEW_Y,MSCALE_Y,MTRANS_Y,
MPERSP_0,MPERSP_1,MPERSP_2}

这是矩阵的9个值,看名字也知道他们是什么意思了。

这里主要用到缩放和平移,下面以缩放为例来了解一下缩放的控制
通过Android提供的api我们可以调用setScale、preScale、postScale来改变MSCALE_X和MSCALE_Y的值达到缩放的效果
所以只要理解setScale、preScale、postScale这三个方法的区别我们就可以简单的进行缩放控制了

1、setScale(sx,sy),首先会将该Matrix设置为对角矩阵,即相当于调用reset()方法,然后在设置该Matrix的MSCALE_X和MSCALE_Y直接设置为sx,sy的值
2、preScale(sx,sy),不会重置Matrix,而是直接与Matrix之前的MSCALE_X和MSCALE_Y值结合起来(相乘),M’ = M * S(sx, sy)。
3、postScale(sx,sy),不会重置Matrix,而是直接与Matrix之前的MSCALE_X和MSCALE_Y值结合起来(相乘),M’ = S(sx, sy) * M。
这么说其实也有些不好理解,举个栗子一看就明白

1、pre….的执行顺序

   Matrix matrix=new Matrix();
   float[] points=new float[]{10.0f,10.0f};
   matrix.preScale(2.0f, 3.0f);
   matrix.preTranslate(8.0f,7.0f); 
   matrix.mapPoints(points);
   Log.i("test", points[0]+"");
   Log.i("test", points[1]+"");

结果为点坐标为(36.0,51.0) 可以得出结论,进行变换的顺序是先执行preTranslate(8.0f,7.0f),在执行的preScale(2.0f,3.0f)。即对于一个Matrix的设置中,所有pre….是倒着向后执行的。

2、post…的执行顺序

 Matrix matrix=new Matrix();
 float[] points=new float[]{10.0f,10.0f};
 matrix.postScale(2.0f, 3.0f);
 matrix.postTranslate(8.0f,7.0f);
 matrix.mapPoints(points);
 Log.i("test", points[0]+"");
 Log.i("test", points[1]+"");

结果为点坐标为(28.0,37.0) 可以得出结论,进行变换的顺序是先执行postScale(2.0f,3.0f),在执行的postTranslate(8.0f,7.0f)。即对于一个Matrix的设置中,所有post….是顺着向前执行的。
这里主要知道set…和post…方法就行,因为只用到了这两个。 自我理解其实和scrollTo、scrollBy类似。

3、Bitmap的一下基本用法 参考: 关于bitmap你不知道的一些事
了解一下bitmap的注意事项即可

下面开始正式画这个选座的功能了
1、画座位:

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /**
         * 如果第一次进入 使座位图居中
         */
        if (mViewH != 0 && mViewW != 0&&isFrist) {
            isFrist = false;
            matrix.setTranslate(-(mViewW-getMeasuredWidth())/2, 0);
        }
        /**
         * 画座位
         */
        drawSeat(canvas);
        /**
         * 画排数
         */
        drawText(canvas);
        /**
         * 画缩略图
         */
        drawOverView(canvas);
        /**
         * 画缩略图选择区域
         */
        drawOvewRect(canvas);

    }
private void drawSeat(Canvas canvas) {
        float zoom = getMatrixScaleX();
        scale1 = zoom;
        tranlateX = getTranslateX();
        tranlateY = getTranslateY();
        /**
         * 使用两层for循环来画出所有座位
         */
        for (int i = 0; i < row; i++) {
            float top = i * SeatHight * scale * scale1 + i * mSpaceY * scale1
                    + tranlateY;
            for (int j = 0; j < column; j++) {
                
                float left = j * SeatWidth * scale * scale1 + j * mSpaceX
                        * scale1 + tranlateX;

                tempMatrix.setTranslate(left, top);
                tempMatrix.postScale(scale, scale, left, top);
                tempMatrix.postScale(scale1, scale1, left, top);
                
                
                /**
                 * 获取每个位置的信息
                 */
                int state = getSeatType(i, j);
                /**
                 * 根据位置信息画不同的位置图片
                 */
                switch (state) {
                case SEAT_TYPE_SOLD:
                    canvas.drawBitmap(SeatLock, tempMatrix, null);
                    break;
                case SEAT_TYPE_SELECTED:
                    canvas.drawBitmap(SeatChecked, tempMatrix, null);
                    break;
                case SEAT_TYPE_AVAILABLE:
                    canvas.drawBitmap(SeatNormal, tempMatrix, null);
                    break;
                case SEAT_TYPE_NOT_AVAILABLE:
                    break;

                }
            }
        }

    }

这里其实没什么难度,主要就是使用两层for循环,一层画行,一层画列
另外要注意的就是当前位置的计算 top = (当前位置i)(座位图标大小SeatHight 它本身的缩放比scale缩放时的缩放比scale1)+(当前位置i 垂直方向的间距mSpaceY*缩放时的缩放比scale1)+垂直方向移动是的移动距离
2、画排数

/**
     * 画排数
     */
    private void drawText(Canvas canvas) {
        mTextPaint.setColor(bacColor);
        RectF rectF = new RectF();
        rectF.top = getTranslateY() - mNumberHeight/2;
        rectF.bottom = getTranslateY()+ mViewH* getMatrixScaleX() + mNumberHeight/2;
        rectF.left = 0;
        rectF.right = mTextWidth;
        
        
        canvas.drawRoundRect(rectF, mTextWidth/2, mTextWidth/2, mTextPaint);
        mTextPaint.setColor(Color.WHITE);
         for (int i = 0; i < row; i++) {
             float top = (i *SeatHight*scale + i * mSpaceY) * getMatrixScaleX() + getTranslateY();
             float bottom = (i * SeatHight*scale + i * mSpaceY + SeatHight) * getMatrixScaleX() + getTranslateY();
             float baseline = (bottom + top  - lineNumberPaintFontMetrics.bottom - lineNumberPaintFontMetrics.top ) / 2-6;
             canvas.drawText(lineNumbers.get(i), mTextWidth / 2, baseline, mTextPaint);
          }     
    }

3、画缩略图

private void drawOverView(Canvas canvas) {
        /**
         * 1、先画张背景图片
         */
        mBitMapOverView = Bitmap.createBitmap((int)mOverViewWidth,(int)mOverViewHight,Bitmap.Config.ARGB_8888);
        Canvas OverViewCanvas = new Canvas(mBitMapOverView);
        Paint paint = new Paint();
        paint.setColor(bacColor);
        scaleoverX = mOverViewWidth / mViewW;
        scaleoverY = mOverViewHight / mViewH;
        float tempX = mViewW * scaleoverX;
        float tempY = mViewH * scaleoverY;
        OverViewCanvas.drawRect(0, 0, (float)tempX, (float)tempY, paint);
        
        Matrix tempoverMatrix = new Matrix();
        /**
         * 2、和画座位图一样在缩略图中画座位
         */
        for (int i = 0; i < row; i++) {
            float top =  i * SeatHight * scale * scaleoverY+ i * mSpaceY * scaleoverY;
            for (int j = 0; j < column; j++) {
                float left = j * SeatWidth * scale * scaleoverX + j * mSpaceX * scaleoverX+mTextWidth*scaleoverX;
                tempoverMatrix.setTranslate(left, top);
                tempoverMatrix.postScale(scale*scaleoverX, scale*scaleoverY, left, top);
                
                int state = getSeatType(i, j);
                switch (state) {
                case SEAT_TYPE_SOLD:
                    OverViewCanvas.drawBitmap(SeatLock, tempoverMatrix, null);
                    break;
                case SEAT_TYPE_SELECTED:
                    OverViewCanvas.drawBitmap(SeatChecked, tempoverMatrix, null);
                    break;
                case SEAT_TYPE_AVAILABLE:
                    OverViewCanvas.drawBitmap(SeatNormal, tempoverMatrix, null);
                    break;
                case SEAT_TYPE_NOT_AVAILABLE:
                    break;

                }
                
                
            }
        }
        
        canvas.drawBitmap(mBitMapOverView,0,0,null);
        
    }

4、缩略图中的红色区域

    private void drawOvewRect(Canvas canvas) {

        OverRectPaint = new Paint();
        OverRectPaint.setColor(Color.RED);
        OverRectPaint.setStyle(Paint.Style.STROKE);
        OverRectPaint.setStrokeWidth(overRectLineWidth);
        int tempViewW ;
        int tempViewH;
        if(getMeasuredWidth()<mViewW){
            tempViewW = getMeasuredWidth();
        }else{
            tempViewW = mViewW;
        }
        if(getMeasuredHeight()<mViewH){
            tempViewH = getMeasuredHeight();
        }else{
            tempViewH = mViewH;
        }
        
        try{
            Rect rect ;
            if(getMatrixScaleX()>= 1.0f){
                 rect = new Rect((int)(scaleoverX*Math.abs(getTranslateX())/getMatrixScaleX()), 
                                     (int)(scaleoverY*Math.abs(getTranslateY())/getMatrixScaleX()),
                                     (int)(scaleoverX*Math.abs(getTranslateX())/getMatrixScaleX()+tempViewW*scaleoverX/getMatrixScaleX()),
                                     (int)(scaleoverY*Math.abs(getTranslateY())/getMatrixScaleX()+tempViewH*scaleoverY/getMatrixScaleX()));
            }else{
                 rect = new Rect((int)(scaleoverX*Math.abs(getTranslateX())), 
                         (int)(scaleoverY*Math.abs(getTranslateY())),
                         (int)(scaleoverX*Math.abs(getTranslateX())+tempViewW*scaleoverX),
                         (int)(scaleoverY*Math.abs(getTranslateY())+tempViewH*scaleoverY));
            }
        canvas.drawRect(rect, OverRectPaint);
        }catch(Exception e){
            e.printStackTrace();
        }
        
        
        
    }

5、手指移动时跟随移动

/**
                 * 移动事件 这里只是简单判断了一下,需要更细致的进行条件判断
                 */
                public boolean onScroll(MotionEvent e1, MotionEvent e2,
                        float distanceX, float distanceY) {
                    float tempMViewW = column * SeatWidth*scale*scale1+(column -1)*mSpaceX*scale1+mTextWidth-getWidth();
                    float tempmViewH = row * SeatHight * scale * scale1 + (row -1) * mSpaceY * scale1 - getHeight();
                    
                    
                    if((getTranslateX()>mTextWidth+mSpaceX)&& distanceX<0){
                        distanceX = 0.0f;
                    }
                    if((Math.abs(getTranslateX())>tempMViewW)&&(distanceX>0)){
                        distanceX = 0.0f;
                    }
                    
                    
                    if((getTranslateY()>0)&&distanceY<0){
                        distanceY=0.0f;
                    }
                    if((Math.abs(getTranslateY())>tempmViewH)&&(distanceY>0)){
                        distanceY = 0.0f;
                    }                   
                    matrix.postTranslate(-distanceX, -distanceY);               
                    invalidate();
                    return false;

                }
                /**
                 * 单击事件
                 */
                public boolean onSingleTapConfirmed(MotionEvent e) {
                    int x = (int) e.getX();
                    int y = (int) e.getY();

                    for (int i = 0; i < row; i++) {
                        for (int j = 0; j < column; j++) {
                            int tempX = (int) ((j * SeatWidth * scale + j * mSpaceX) * getMatrixScaleX() + getTranslateX());
                            int maxTemX = (int) (tempX + SeatWidth * scale * getMatrixScaleX());

                            int tempY = (int) ((i * SeatHight * scale + i * mSpaceX) * getMatrixScaleY() + getTranslateY());
                            int maxTempY = (int) (tempY + SeatHight * scale * getMatrixScaleY());
                            
                            
                            if (x >= tempX && x <= maxTemX && y >= tempY
                                    && y <= maxTempY) {
                                int id = getID(i, j);
                                int index = isHave(id);
                                if (index >= 0) {
                                    remove(index);
                                } else {
                                    addChooseSeat(i, j);
                                    

                                }
                                float currentScaleY = getMatrixScaleY();
                                if (currentScaleY < 1.7f) {
                                    scaleX = x;
                                    scaleY = y;
                                    /**
                                     * 选中时进行缩放操作
                                     */
                                    zoomAnimate(currentScaleY, 1.9f);
                                }
                                invalidate();
                                break;

                            }
                        }
                    }

                    return super.onSingleTapConfirmed(e);
                }

6、两个手指缩放时跟随缩放

public boolean onScale(ScaleGestureDetector detector) {
                    float scaleFactor = detector.getScaleFactor();
                    //scaleX = detector.getCurrentSpanX();
                    //scaleY = detector.getCurrentSpanY();
                    //直接判断大于2会导致获取的matrix缩放比例继续执行一次从而导致变成2.000001之类的数从而使
                    //判断条件一直为真从而不会执行缩小动作
                    //判断相乘大于2 可以是当前获得的缩放比例即使是1.9999之类的数如果继续放大即使乘以1.0001也会比2大从而
                    //避免上述问题。
                
                     if (getMatrixScaleY() * scaleFactor > 2) {
                            scaleFactor = 2 / getMatrixScaleY();
                      }
                      if (getMatrixScaleY() * scaleFactor < 0.8) {
                            scaleFactor = 0.8f / getMatrixScaleY();
                      }
                    matrix.postScale(scaleFactor, scaleFactor);
                    

                    invalidate();

                    return true;
                }

至此这个比较粗糙的选座功能就实现了,有时间会继续优化下细节问题。

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

推荐阅读更多精彩内容