android 自定义view之选座功能

作者: solary2016 | 来源:发表于2016-11-07 17:59 被阅读827次
    效果图: 这里写图片描述

    界面比较粗糙,主要看原理。
    这个界面主要包括以下几部分
    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;
                    }
    

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

    相关文章

      网友评论

      本文标题:android 自定义view之选座功能

      本文链接:https://www.haomeiwen.com/subject/zqvouttx.html