您好,欢迎来到源码搜藏!分享精神,快乐你我!提示:担心找不到本站?在百度搜索“源码搜藏”,网址永远不丢失!
  • 首 页
  • 在线工具
  • andriod 打造炫酷的淘宝电影在线选座功能

    时间:2016-08-10 21:15 来源:互联网 作者:源码搜藏 浏览:收藏 挑错 推荐 打印

    • 源码类别:缩放ZoomControls
    • 源码大小:1.4 MB
    • 编码格式:gbk
    • 授权方式:免费源码
    • 运行环境:php+mysql,asp+access,VC+MSSQL,VS+MSSQL,eclipse,Android studio,其他
    • 官方网址:暂无
    • 欢迎加入QQ群讨论学习
    • Android学习交流
    • 缩放ZoomControls下载排行

    不知道大家有没有跟我一样的感觉,看了那么多的介绍Android自定义控件原理、事件分发机制的书籍,文章,教程,依然还是不能随心所欲的自定义控件。甚至是看了再忘,忘了再看,很尴尬有木有。有的时候真正遇到了事件冲突一脸懵逼有木有。其实导致这些问题原因很简单,一句话就可以说明问题了“纸上得来终觉浅,绝知此事要躬行。”正如这篇介绍文章里所说的,从不会到会,秘诀是重复。我们需要一遍一遍仔细的阅读理解,并用代码实践来验证,学到的这些概念流程知识,这样才会在脑海里留下比较深刻的印象,才能自如的应用学到的知识。学习view的绘制原理,事件分发机制的目的是为了自定义控件,所以学了这些知识后,就需要通过实战多自定义几个控件,来不停的应用,消化这些知识。当你真正自己写了几个自定义控件后,你会发现view的绘制原理,事件分发机制这些东西都是死的,真正麻烦的是绘制逻辑,绘图逻辑,计算逻辑以及一些相关的数学知识。 
    下面开始正题,不知道大家有没有用过,淘宝电影客户端(淘票票)买过电影票,纵观各类在线选座app的在线选座功能 淘宝在线选座功能用户体验最好,用起来最顺手,夸张点说已经到了炉火纯青的地步,下面我们看一下效果:

    效果分析:

    整个控件分成几个部分,座位图区域、座位缩略图区域、行号区域、屏幕区域 
    1、座位图可以自由的移动缩放,放大缩小移动后会自动回弹到合适的位置,选中座位会自动放大到合适比例。 
    2、行号部分跟着座位图缩放以及上下移动,屏幕区域跟着座位图左右移动缩放。 
    3、当手指按下的时候会出现缩略图,缩略图上有个红色的方框表示,当前能看到的区域,并且跟随缩略图的移动。

    涉及到的知识点:

    view的绘制原理、事件分发机制这些就不说了,这些是基础,这里并不打算介绍,网上有非常多的这方面的资料。 
    1、矩阵Matrix使用,通过Matrix来进行移动、缩放 
    2、弹性移动、弹性缩放。 
    3、手势监听的使用通过GestureDetector、ScaleGestureDetector来获得缩放比例幅度。

    编码实现

    通过以下几个核心部分来介绍,其他部分都是类似的思路实现 
    1、绘制座位图 
    2、座位图的缩放和移动 
    3、座位图自动回弹自动缩放 
    4、缩略图部分的绘制实现 
    至于其他部分比如影院荧幕,左侧的行号部分思路跟座位图的实现思路是一致的。

    1、绘制座位图

    座位图实际上就是个二维矩阵,有行数和列数,我们只需要根据行数和列数加上一定的间距绘制即可。

    座位图实际上就是个二维矩阵,有行数和列数,我们只需要根据行数和列数加上一定的间距绘制即可。

    void drawSeat() {
            for (int i = 0; i < row; i++) {
                for (int j = 0; j < column; j++) {
    
                    int left = j * seatBitmap.getWidth() + j * spacing;
                    int top = i * seatBitmap.getHeight() + i * verSpacing;
    
                    int seatType = getSeatType(i, j);
                    switch (seatType) {
                        case SEAT_TYPE_AVAILABLE:
                            seatCanvas.drawBitmap(seatBitmap, left, top, paint);
                            break;
                        case SEAT_TYPE_NOT_AVAILABLE:
    
                            break;
                        case SEAT_TYPE_SELECTED:
                            seatCanvas.drawBitmap(checkedSeatBitmap, left, top, paint);
    
                            break;
                        case SEAT_TYPE_SOLD:
                            seatCanvas.drawBitmap(seatSoldBitmap, left, top, paint);
                            break;
                    }
    
                }
            }
            isNeedDrawSeatBitmap = false;
    
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    getSeatType()方法是用来判断当前的座位是否可用,是否已经卖出去,是否已经选中,根据这些状态绘制不同的座位图。

    2、座位图的缩放和移动

    移动缩放功能使用 Matirx来实现,Matrix在android中可以用来对图片进行缩放、移动、旋转等变换。  matrix本身是一个3*3的矩阵,矩阵中的每一个值都代表一个变换属性,如下

    MSCALE_X MSKEW_X MTRANS_X  MSKEW_Y MSCALE_Y MTRANS_Y  MPERSP_0 MPERSP_1 MPERSP_2

    实际上就是一个有9个元素的数组  float[] value=new float[9];  通过 matrix.getValues(value);可以获得具体的值。  value[0]表示的是缩放的x值  value[1]表示的是斜切的x值  value[2] 表示x轴上平移的值  value[3]表示的是斜切的y值  value[4]表示的y轴上的缩放比例  value[5]表示的是y轴上的平移的值

    Matrix类有一些方法可以对这些值进行改变  setScale(float sx, float sy, float px, float py) :设置x轴和y周上的缩放比例,px,py表示缩放的中心点。  setTranslate(float dx, float dy) :设置x轴和y周上的偏移量。  与之对应的还有这么两个方法:  postScale(float sx, float sy, float px, float py)  postTranslate(float dx, float dy)  那么post跟set有什么区别呢,简单理解就是set直接,把之前的值给覆盖了,而post是在之前的值的基础上进行变换。比如现在你已经向左移动了10个像素,这时候你用setTranslate(5,5)这个时候直接变成了移动5个像素了,而用post就是在10的基础上在移动5个像素就变成15了。  以上是Matrix的使用方法,Canvas对象有个drawBitmap方法可以接收一个matrix,这样就可以在绘图的时候使用matrix进行变换了。

    座位图平移缩放的实现思路:

    要做两件事情来实现座位图的缩放移动  1、获取平移的值和放大缩小的比例  重写onTouchEvent方法来计算获取移动的x值和y值  使用ScaleGestureDetector这个类来帮我们获取放大缩小的比例,使用非常简单,创建一个ScaleGestureDetector的对象,然后在onTouchEvent方法了调用一下ScaleGestureDetector.onTouchEvent(event);即可  2、根据获取到的值对座位图进行平移和缩放  获取到了平移的值和缩放的比例后,使用matrix.postScale(x,y)和matrix.postTrans(x,y)进行对应的变换即可,变换完了调用view的invalidate方法让view重新绘制matrix即可生效。  下面只列出了核心代码,省掉了一些逻辑,完整代码请到github中查看。

    ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.OnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                float scaleFactor = detector.getScaleFactor();
                matrix.postScale(scaleFactor, scaleFactor, scaleX, scaleY);
                invalidate();
                return true;
            }
        });
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    onTouchEvent处理逻辑

     @Override
        public boolean onTouchEvent(MotionEvent event) {
            int y = (int) event.getY();
            int x = (int) event.getX();
            scaleGestureDetector.onTouchEvent(event);
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    downX = x;
                    downY = y; 
                    break;
                case MotionEvent.ACTION_MOVE:
    
                        int downDX = Math.abs(x - downX);
                        int downDY = Math.abs(y - downY);
                        if (downDX > 10 || downDY > 10) {
                            int dx = x - lastX;
                            int dy = y - lastY;
                            matrix.postTranslate(dx, dy);
                            invalidate();
                        }
    
                    break;
                case MotionEvent.ACTION_UP:
                    ....
                    break;
            }
            lastY = y;
            lastX = x;
    
            return true;
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    onDraw的时候

     @Override
        protected void onDraw(Canvas canvas) {
            .....
            canvas.drawBitmap(seat, matrix, paint);
            .....
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3、座位图的自动回弹自动缩放效果的实现

    为什么要自动回弹呢,因为你操作的时候有可能把座位图移到屏幕外,缩放的时候把图缩放的比较小,或者比较大,这个时候程序通过计算给你自动的移动到一个比较合适的位置,比较合适的缩放大小。这样就有着不错的使用体验。

    自动回弹实现的思路:  当我们手指屏幕上移动然后抬起的时候会触发MotionEvent.ACTION_UP事件,这个时候我们可以通过matrix对象来获取当前的移动的位置,如果当前移动的值不符合我们的规则,我们就将座位图按照规则移动到指定位置。

    移动规则如下(参考的淘票票客户端的移动逻辑)  座位图整个大小不超过控件大小的时候:  往左边滑动,自动回弹到行号右边  往右边滑动,自动回弹到右边  往上,下滑动,自动回弹到顶部  座位图整个大小超过控件大小的时候:  往左侧滑动,回弹到最右边,往右侧滑回弹到最左边  往上滑动,回弹到底部,往下滑动回弹到顶部  以上的移动规则的实现大家可以查看具体源码中的autoScroll()方法的实现,这里就不贴出来了。

    移动和缩放涉及到一个弹性移动和缩放的问题,所谓弹性移动就是有动画效果的移动。因为如果你当前在100,100 这个位置,你需要移动到800,100这个位置,如果你直接移动到800这个位置,而不是通过,先移动到110,在移动到120。。。一直到800这样一段一段的移动。那么移动效果将是非常僵硬的,刷的闪过去的感觉,效果非常不好。  弹性移动的实现思路就是:  比如要从100,100 移动到800,100这个位置,很明显 x轴要移动700个像素。那么把这700个像素的移动我们分成10次移动来实现,每次移动700/10=70个像素,两次移动之间间隔50毫秒,就跟帧动画似的,这样就会有一个弹性的动画效果。  通过handler来实现代码如下:

    int FRAME_COUNT = 10;
        int time = 15;
        int count;
        Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
    
                if (count < FRAME_COUNT) {
                    count++;
                    MoveInfo moveInfo = (MoveInfo) msg.obj;
                    float moveXLength = moveInfo.moveXLength;
                    float moveYLength = moveInfo.moveYLength;
                    float xValue = moveXLength / FRAME_COUNT;
                    float yValue = moveYLength / FRAME_COUNT;
    
                    matrix.postTranslate(xValue, yValue);
                    invalidate();
                    Message message = Message.obtain();
                    message.obj = msg.obj;
                    //循环调用
                    handler.sendMessageDelayed(message, time);
                } else {
                    count = 0;
                }
    
                return true;
     }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    弹性缩放的原理跟弹性移动的原理一致

    4、缩略图部分的绘制实现

    缩略图是座位图的缩小版,缩略图的宽高和座位图的宽高有一定比例,比如五分之一。当然这是可以根据效果来调整的。之所以必须是座位图的一定比例,是因为缩略图上有一个动态的红色方框表示当前可见的座位区域,这个红色方框是需要根据座位图的移动而移动的。比例确定后,就这可以根据座位图的移动来移动红色方框。举个例子来说-如果当前座位图向上移动了100个像素,那么缩略图中对应的红色方框部分向下移动 100/4=250个像素即可。

    绘制概览图代码:

    Bitmap drawOverview() {
    
            //计算概览图上代表座位的正方形宽度、高度 
            //规则 :座位图上座位的宽度/缩放比例
            float rectSize = seatBitmap.getHeight() / overviewScale;
            //计算概览图的宽度和高度 
            rectW = column * rectWidth + (column - 1) * overviewSpacing + overviewSpacing * 2;
            rectH = row * rectSize + (row - 1) * overviewVerSpacing + overviewVerSpacing * 2;
            Canvas canvas = new Canvas(overviewBitmap);
            //绘制透明灰色背景
            canvas.drawRect(0, 0, rectW, rectH, paint);
    
            paint.setColor(Color.WHITE);
            //循环绘制
            for (int i = 0; i < row; i++) {
                float top = i * rectSize + i * overviewVerSpacing + overviewVerSpacing;
                for (int j = 0; j < column; j++) {
    
                    //获取座位是什么状态 已经售出、未选中、已经选中
                    int seatType = getSeatType(i, j);
                    switch (seatType) {
                        case SEAT_TYPE_AVAILABLE:
                            paint.setColor(Color.WHITE);
                            break;
                        case SEAT_TYPE_NOT_AVAILABLE:
                            continue;
                        case SEAT_TYPE_SELECTED:
                            paint.setColor(overview_checked);
                            break;
                        case SEAT_TYPE_SOLD:
                            paint.setColor(overview_sold);
                            break;
                    }
    
                    float left;
    
                    left = j * rectWidth + j * overviewSpacing + overviewSpacing;
                    canvas.drawRect(left, top, left + rectWidth, top + rectSize, paint);
                }
            }
    
    
    
            return overviewBitmap;
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    绘制概览图上的红色方框

    /**
         * 绘制概览图
         */
        void drawOverviewBorder(Canvas canvas) {
    
            //绘制红色框
            int left = (int) -getTranslateX();//获取当前座位图x轴上的平移位置
            if (left < 0) {
                left = 0;
            }
            left /= overviewScale; //overviewScale 表示概览图占座位图的比例
            left /= getMatrixScaleX();//getMatrixScaleX() 获取当前座位图的缩放比例
    
            //判断当前座位图的宽度是否超出 整个控件的宽度,如果超出了,那么超出的部分就看不见。表示当前能看到的位置的红色方框就不能把超出的部分框进来。
            int currentWidth = (int) (getTranslateX() + (column * seatBitmap.getWidth() + spacing * (column - 1)) * getMatrixScaleX());
            if (currentWidth > getWidth()) {
                currentWidth = currentWidth - getWidth();
            } else {
                currentWidth = 0;
            }
            int right = (int) (rectW - currentWidth / overviewScale / getMatrixScaleX());
    
            float top = -getTranslateY()+headHeight;
            if (top < 0) {
                top = 0;
            }
            top /= overviewScale;
            top /= getMatrixScaleY();
            if (top > 0) {
                top += overviewVerSpacing;
            }
    
            //判断当前座位图的宽度是否超出 整个控件的高度,如果超出了,那么超出的部分就看不见。表示当前能看到的位置的红色方框就不能把超出的部分框进来。
            int currentHeight = (int) (getTranslateY() + (row * seatBitmap.getHeight() + verSpacing * (row - 1)) * getMatrixScaleY());
            if (currentHeight > getHeight()) {
                currentHeight = currentHeight - getHeight();
            } else {
                currentHeight = 0;
            }
    
            int bottom = (int) (rectH - currentHeight / overviewScale / getMatrixScaleY());
    
            canvas.drawRect(left, top, right, bottom, redBorderPaint);
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    控件性能优化

     

    千辛万苦终于把控件做出来了,结果一运行卡的不要不要的。特别是行数列数一多,卡顿的懵逼了。这个时候呢我们要对性能进行优化,总结下来主要从以下几个方面:  1、避免在onDraw中创建对象,分配内存,把paint对象的创建放在初始化函数里面。这一步其实非常重要因为我们使用canvas绘图的时候需要paint对象,往往不同的地方需要不同paint,这样一来,创建的paint对象就比较多了,在加上onDraw方法可能会执行多次。频繁的创建对象会造成gc,导致卡顿。当然了不止是paint对象,其他的对象创建也要能少则少。  2、避免不必要的绘制逻辑,在需要的时候才绘制。这个需要我们根据控件的绘制逻辑来进行调整,同样也是非常重要。  3、总的原则就是想尽一切办法把onDraw方法的执行控制在16ms以内,就不会卡了。


    andriod 打造炫酷的淘宝电影在线选座功能转载请注明出处http://www.codesocang.com/kj/zoom/33263.html 源码搜藏网所有源码来自用户上传分享,版权问题及牵扯到商业纠纷均与源码搜藏网无关
    标签:
    上一篇:没有了
    下一篇:没有了