美文网首页UIAndroid
Android 电影票在线选座

Android 电影票在线选座

作者: 遇见编程 | 来源:发表于2021-07-21 11:01 被阅读0次

    SeatTable.java

    import android.animation.Animator;
    import android.animation.TypeEvaluator;
    import android.animation.ValueAnimator;
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Matrix;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.Point;
    import android.graphics.RectF;
    import android.graphics.Typeface;
    import android.os.Handler;
    import android.text.TextPaint;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.view.ScaleGestureDetector;
    import android.view.View;
    import android.view.animation.DecelerateInterpolator;
    import android.widget.Toast;
    import java.util.ArrayList;
    import java.util.Collections;
    
    public class SeatTable extends View {
        private final boolean DBG = false;
    
        Paint paint = new Paint();
        Paint overviewPaint=new Paint();
        Paint lineNumberPaint;
        float lineNumberTxtHeight;
    
        /**
         * 设置行号 默认显示 1,2,3....数字
         * @param lineNumbers
         */
        public void setLineNumbers(ArrayList<String> lineNumbers) {
            this.lineNumbers = lineNumbers;
            invalidate();
        }
    
        /**
         * 用来保存所有行号
         */
        ArrayList<String> lineNumbers = new ArrayList<>();
    
        Paint.FontMetrics lineNumberPaintFontMetrics;
        Matrix matrix = new Matrix();
    
        /**
         * 座位水平间距
         */
        int spacing;
    
        /**
         * 座位垂直间距
         */
        int verSpacing;
    
        /**
         * 行号宽度
         */
        int numberWidth;
    
        /**
         * 行数
         */
        int row;
    
        /**
         * 列数
         */
        int column;
    
        /**
         * 可选时座位的图片
         */
        Bitmap seatBitmap;
    
        /**
         * 选中时座位的图片
         */
        Bitmap checkedSeatBitmap;
    
        /**
         * 座位已经售出时的图片
         */
        Bitmap seatSoldBitmap;
    
        Bitmap overviewBitmap;
    
        int lastX;
        int lastY;
    
        /**
         * 整个座位图的宽度
         */
        int seatBitmapWidth;
    
        /**
         * 整个座位图的高度
         */
        int seatBitmapHeight;
    
        /**
         * 标识是否需要绘制座位图
         */
        boolean isNeedDrawSeatBitmap = true;
    
        /**
         * 概览图白色方块高度
         */
        float rectHeight;
    
        /**
         * 概览图白色方块的宽度
         */
        float rectWidth;
    
        /**
         * 概览图上方块的水平间距
         */
        float overviewSpacing;
    
        /**
         * 概览图上方块的垂直间距
         */
        float overviewVerSpacing;
    
        /**
         * 概览图的比例
         */
        float overviewScale = 4.8f;
    
        /**
         * 荧幕高度
         */
        float screenHeight;
    
        /**
         * 荧幕默认宽度与座位图的比例
         */
        float screenWidthScale = 0.5f;
    
        /**
         * 荧幕最小宽度
         */
        int defaultScreenWidth;
    
        /**
         * 标识是否正在缩放
         */
        boolean isScaling;
        float scaleX, scaleY;
    
        /**
         * 是否是第一次缩放
         */
        boolean firstScale = true;
    
        /**
         * 最多可以选择的座位数量
         */
        int maxSelected = Integer.MAX_VALUE;
    
        private SeatChecker seatChecker;
    
        /**
         * 荧幕名称
         */
        private String screenName = "";
    
        /**
         * 整个概览图的宽度
         */
        float rectW;
    
        /**
         * 整个概览图的高度
         */
        float rectH;
    
        Paint headPaint;
        Bitmap headBitmap;
    
        /**
         * 是否第一次执行onDraw
         */
        boolean isFirstDraw = true;
    
        /**
         * 标识是否需要绘制概览图
         */
        boolean isDrawOverview = false;
    
        /**
         * 标识是否需要更新概览图
         */
        boolean isDrawOverviewBitmap = true;
    
        int overview_checked;
        int overview_sold;
        int txt_color;
        int seatCheckedResID;
        int seatSoldResID;
        int seatAvailableResID;
    
        boolean isOnClick;
    
        /**
         * 座位已售
         */
        private static final int SEAT_TYPE_SOLD = 1;
    
        /**
         * 座位已经选中
         */
        private static final int SEAT_TYPE_SELECTED = 2;
    
        /**
         * 座位可选
         */
        private static final int SEAT_TYPE_AVAILABLE = 3;
    
        /**
         * 座位不可用
         */
        private static final int SEAT_TYPE_NOT_AVAILABLE = 4;
    
        private int downX, downY;
        private boolean pointer;
    
        /**
         * 顶部高度,可选,已选,已售区域的高度
         */
        float headHeight;
    
        Paint pathPaint;
        RectF rectF;
    
        /**
         * 头部下面横线的高度
         */
        int borderHeight = 1;
        Paint redBorderPaint;
    
        /**
         * 默认的座位图宽度,如果使用的自己的座位图片比这个尺寸大或者小,会缩放到这个大小
         */
        private float defaultImgW = 40;
    
        /**
         * 默认的座位图高度
         */
        private float defaultImgH = 34;
    
        /**
         * 座位图片的宽度
         */
        private int seatWidth;
    
        /**
         * 座位图片的高度
         */
        private int seatHeight;
    
        public SeatTable(Context context) {
            super(context);
        }
    
        public SeatTable(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context,attrs);
        }
    
        private void init(Context context,AttributeSet attrs){
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SeatTableView);
            overview_checked = typedArray.getColor(R.styleable.SeatTableView_overview_checked, Color.parseColor("#5A9E64"));
            overview_sold = typedArray.getColor(R.styleable.SeatTableView_overview_sold, Color.RED);
            txt_color=typedArray.getColor(R.styleable.SeatTableView_txt_color,Color.WHITE);
            seatCheckedResID = typedArray.getResourceId(R.styleable.SeatTableView_seat_checked, R.drawable.seat_green);
            seatSoldResID = typedArray.getResourceId(R.styleable.SeatTableView_overview_sold, R.drawable.seat_sold);
            seatAvailableResID = typedArray.getResourceId(R.styleable.SeatTableView_seat_available, R.drawable.seat_gray);
            typedArray.recycle();
        }
    
        public SeatTable(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context,attrs);
        }
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    
        float xScale1 = 1;
        float yScale1 = 1;
    
        private void init() {
            spacing = (int) dip2Px(5);
            verSpacing = (int) dip2Px(10);
            defaultScreenWidth = (int) dip2Px(80);
    
            seatBitmap = BitmapFactory.decodeResource(getResources(), seatAvailableResID);
    
            float scaleX = defaultImgW / seatBitmap.getWidth();
            float scaleY = defaultImgH / seatBitmap.getHeight();
            xScale1 = scaleX;
            yScale1 = scaleY;
    
            seatHeight= (int) (seatBitmap.getHeight()*yScale1);
            seatWidth= (int) (seatBitmap.getWidth()*xScale1);
    
            checkedSeatBitmap = BitmapFactory.decodeResource(getResources(), seatCheckedResID);
            seatSoldBitmap = BitmapFactory.decodeResource(getResources(), seatSoldResID);
    
            seatBitmapWidth = (int) (column * seatBitmap.getWidth()*xScale1 + (column - 1) * spacing);
            seatBitmapHeight = (int) (row * seatBitmap.getHeight()*yScale1 + (row - 1) * verSpacing);
            paint.setColor(Color.RED);
            numberWidth = (int) dip2Px(20);
    
            screenHeight = dip2Px(20);
            headHeight = dip2Px(30);
    
            headPaint = new Paint();
            headPaint.setStyle(Paint.Style.FILL);
            headPaint.setTextSize(24);
            headPaint.setColor(Color.WHITE);
            headPaint.setAntiAlias(true);
    
            pathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            pathPaint.setStyle(Paint.Style.FILL);
            pathPaint.setColor(Color.parseColor("#e2e2e2"));
    
            redBorderPaint = new Paint();
            redBorderPaint.setAntiAlias(true);
            redBorderPaint.setColor(Color.RED);
            redBorderPaint.setStyle(Paint.Style.STROKE);
            redBorderPaint.setStrokeWidth(getResources().getDisplayMetrics().density * 1);
    
            rectF = new RectF();
    
            rectHeight = seatHeight / overviewScale;
            rectWidth = seatWidth / overviewScale;
            overviewSpacing = spacing / overviewScale;
            overviewVerSpacing = verSpacing / overviewScale;
    
            rectW = column * rectWidth + (column - 1) * overviewSpacing + overviewSpacing * 2;
            rectH = row * rectHeight + (row - 1) * overviewVerSpacing + overviewVerSpacing * 2;
            overviewBitmap = Bitmap.createBitmap((int) rectW, (int) rectH, Bitmap.Config.ARGB_4444);
    
            lineNumberPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            lineNumberPaint.setColor(bacColor);
            lineNumberPaint.setTextSize(getResources().getDisplayMetrics().density * 16);
            lineNumberTxtHeight = lineNumberPaint.measureText("4");
            lineNumberPaintFontMetrics = lineNumberPaint.getFontMetrics();
            lineNumberPaint.setTextAlign(Paint.Align.CENTER);
    
            if(lineNumbers==null){
                lineNumbers=new ArrayList<>();
            }else if(lineNumbers.size()<=0) {
                for (int i = 0; i < row; i++) {
                    lineNumbers.add((i + 1) + "");
                }
            }
    
    
            matrix.postTranslate(numberWidth + spacing, headHeight + screenHeight + borderHeight + verSpacing);
    
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            long startTime = System.currentTimeMillis();
            if (row <= 0 || column == 0) {
                return;
            }
    
            drawSeat(canvas);
            drawNumber(canvas);
    
            if (headBitmap == null) {
                headBitmap = drawHeadInfo();
            }
            canvas.drawBitmap(headBitmap, 0, 0, null);
    
            drawScreen(canvas);
    
            if (isDrawOverview) {
                long s = System.currentTimeMillis();
                if (isDrawOverviewBitmap) {
                    drawOverview();
                }
                canvas.drawBitmap(overviewBitmap, 0, 0, null);
                drawOverview(canvas);
                Log.d("drawTime", "OverviewDrawTime:" + (System.currentTimeMillis() - s));
            }
    
            if (DBG) {
                long drawTime = System.currentTimeMillis() - startTime;
                Log.d("drawTime", "totalDrawTime:" + drawTime);
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int y = (int) event.getY();
            int x = (int) event.getX();
            super.onTouchEvent(event);
    
            scaleGestureDetector.onTouchEvent(event);
            gestureDetector.onTouchEvent(event);
            int pointerCount = event.getPointerCount();
            if (pointerCount > 1) {
                pointer = true;
            }
    
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    pointer = false;
                    downX = x;
                    downY = y;
                    isDrawOverview = true;
                    handler.removeCallbacks(hideOverviewRunnable);
                    invalidate();
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (!isScaling && !isOnClick) {
                        int downDX = Math.abs(x - downX);
                        int downDY = Math.abs(y - downY);
                        if ((downDX > 10 || downDY > 10) && !pointer) {
                            int dx = x - lastX;
                            int dy = y - lastY;
                            matrix.postTranslate(dx, dy);
                            invalidate();
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    handler.postDelayed(hideOverviewRunnable, 1500);
    
                    autoScale();
                    int downDX = Math.abs(x - downX);
                    int downDY = Math.abs(y - downY);
                    if ((downDX > 10 || downDY > 10) && !pointer) {
                        autoScroll();
                    }
    
                    break;
            }
            isOnClick = false;
            lastY = y;
            lastX = x;
    
            return true;
        }
    
        private Runnable hideOverviewRunnable = new Runnable() {
            @Override
            public void run() {
                isDrawOverview = false;
                invalidate();
            }
        };
    
        Bitmap drawHeadInfo() {
            String txt = "已售";
            float txtY = getBaseLine(headPaint, 0, headHeight);
            int txtWidth = (int) headPaint.measureText(txt);
            float spacing = dip2Px(10);
            float spacing1 = dip2Px(5);
            float y = (headHeight - seatBitmap.getHeight()) / 2;
    
            float width = seatBitmap.getWidth() + spacing1 + txtWidth + spacing + seatSoldBitmap.getWidth() + txtWidth + spacing1 + spacing + checkedSeatBitmap.getHeight() + spacing1 + txtWidth;
            Bitmap bitmap = Bitmap.createBitmap(getWidth(), (int) headHeight, Bitmap.Config.ARGB_8888);
    
            Canvas canvas = new Canvas(bitmap);
    
            //绘制背景
            canvas.drawRect(0, 0, getWidth(), headHeight, headPaint);
            headPaint.setColor(Color.BLACK);
    
            float startX = (getWidth() - width) / 2;
            tempMatrix.setScale(xScale1,yScale1);
            tempMatrix.postTranslate(startX,(headHeight - seatHeight) / 2);
            canvas.drawBitmap(seatBitmap, tempMatrix, headPaint);
            canvas.drawText("可选", startX + seatWidth + spacing1, txtY, headPaint);
    
            float soldSeatBitmapY = startX + seatBitmap.getWidth() + spacing1 + txtWidth + spacing;
            tempMatrix.setScale(xScale1,yScale1);
            tempMatrix.postTranslate(soldSeatBitmapY,(headHeight - seatHeight) / 2);
            canvas.drawBitmap(seatSoldBitmap, tempMatrix, headPaint);
            canvas.drawText("已售", soldSeatBitmapY + seatWidth + spacing1, txtY, headPaint);
    
            float checkedSeatBitmapX = soldSeatBitmapY + seatSoldBitmap.getWidth() + spacing1 + txtWidth + spacing;
            tempMatrix.setScale(xScale1,yScale1);
            tempMatrix.postTranslate(checkedSeatBitmapX,y);
            canvas.drawBitmap(checkedSeatBitmap, tempMatrix, headPaint);
            canvas.drawText("已选", checkedSeatBitmapX + spacing1 + seatWidth, txtY, headPaint);
    
            //绘制分割线
            headPaint.setStrokeWidth(1);
            headPaint.setColor(Color.GRAY);
            canvas.drawLine(0, headHeight, getWidth(), headHeight, headPaint);
            return bitmap;
    
        }
    
        /**
         * 绘制中间屏幕
         */
        void drawScreen(Canvas canvas) {
            pathPaint.setStyle(Paint.Style.FILL);
            pathPaint.setColor(Color.parseColor("#e2e2e2"));
            float startY = headHeight + borderHeight;
    
            float centerX = seatBitmapWidth * getMatrixScaleX() / 2 + getTranslateX();
            float screenWidth = seatBitmapWidth * screenWidthScale * getMatrixScaleX();
            if (screenWidth < defaultScreenWidth) {
                screenWidth = defaultScreenWidth;
            }
    
            Path path = new Path();
            path.moveTo(centerX, startY);
            path.lineTo(centerX - screenWidth / 2, startY);
            path.lineTo(centerX - screenWidth / 2 + 20, screenHeight * getMatrixScaleY() + startY);
            path.lineTo(centerX + screenWidth / 2 - 20, screenHeight * getMatrixScaleY() + startY);
            path.lineTo(centerX + screenWidth / 2, startY);
    
            canvas.drawPath(path, pathPaint);
    
            pathPaint.setColor(Color.BLACK);
            pathPaint.setTextSize(20 * getMatrixScaleX());
    
            canvas.drawText(screenName, centerX - pathPaint.measureText(screenName) / 2, getBaseLine(pathPaint, startY, startY + screenHeight * getMatrixScaleY()), pathPaint);
        }
    
        Matrix tempMatrix = new Matrix();
    
        void drawSeat(Canvas canvas) {
            zoom = getMatrixScaleX();
            long startTime = System.currentTimeMillis();
            float translateX = getTranslateX();
            float translateY = getTranslateY();
            float scaleX = zoom;
            float scaleY = zoom;
    
            for (int i = 0; i < row; i++) {
                float top = i * seatBitmap.getHeight() * yScale1 * scaleY + i * verSpacing * scaleY + translateY;
    
                float bottom = top + seatBitmap.getHeight() * yScale1 * scaleY;
                if (bottom < 0 || top > getHeight()) {
                    continue;
                }
    
                for (int j = 0; j < column; j++) {
                    float left = j * seatBitmap.getWidth() * xScale1 * scaleX + j * spacing * scaleX + translateX;
    
                    float right = (left + seatBitmap.getWidth() * xScale1 * scaleY);
                    if (right < 0 || left > getWidth()) {
                        continue;
                    }
    
                    int seatType = getSeatType(i, j);
                    tempMatrix.setTranslate(left, top);
                    tempMatrix.postScale(xScale1, yScale1, left, top);
                    tempMatrix.postScale(scaleX, scaleY, left, top);
    
                    switch (seatType) {
                        case SEAT_TYPE_AVAILABLE:
                            canvas.drawBitmap(seatBitmap, tempMatrix, paint);
                            break;
                        case SEAT_TYPE_NOT_AVAILABLE:
                            break;
                        case SEAT_TYPE_SELECTED:
                            canvas.drawBitmap(checkedSeatBitmap, tempMatrix, paint);
                            drawText(canvas, i, j, top, left);
                            break;
                        case SEAT_TYPE_SOLD:
                            canvas.drawBitmap(seatSoldBitmap, tempMatrix, paint);
                            break;
                    }
    
                }
            }
    
            if (DBG) {
                long drawTime = System.currentTimeMillis() - startTime;
                Log.d("drawTime", "seatDrawTime:" + drawTime);
            }
        }
    
        private int getSeatType(int row, int column) {
    
            if (isHave(getID(row, column)) >= 0) {
                return SEAT_TYPE_SELECTED;
            }
    
            if (seatChecker != null) {
                if (!seatChecker.isValidSeat(row, column)) {
                    return SEAT_TYPE_NOT_AVAILABLE;
                } else if (seatChecker.isSold(row, column)) {
                    return SEAT_TYPE_SOLD;
                }
            }
    
            return SEAT_TYPE_AVAILABLE;
        }
    
        private int getID(int row, int column) {
            return row * this.column + (column + 1);
        }
    
        /**
         * 绘制选中座位的行号列号
         *
         * @param row
         * @param column
         */
        private void drawText(Canvas canvas, int row, int column, float top, float left) {
    
            String txt = (row + 1) + "排";
            String txt1 = (column + 1) + "座";
    
            if(seatChecker!=null){
                String[] strings = seatChecker.checkedSeatTxt(row, column);
                if(strings!=null&&strings.length>0){
                    if(strings.length>=2){
                        txt=strings[0];
                        txt1=strings[1];
                    }else {
                        txt=strings[0];
                        txt1=null;
                    }
                }
            }
    
            TextPaint txtPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
            txtPaint.setColor(txt_color);
            txtPaint.setTypeface(Typeface.DEFAULT_BOLD);
            float seatHeight = this.seatHeight * getMatrixScaleX();
            float seatWidth = this.seatWidth * getMatrixScaleX();
            txtPaint.setTextSize(seatHeight / 3);
    
            //获取中间线
            float center = seatHeight / 2;
            float txtWidth = txtPaint.measureText(txt);
            float startX = left + seatWidth / 2 - txtWidth / 2;
    
            //只绘制一行文字
            if(txt1==null){
                canvas.drawText(txt, startX, getBaseLine(txtPaint, top, top + seatHeight), txtPaint);
            }else {
                canvas.drawText(txt, startX, getBaseLine(txtPaint, top, top + center), txtPaint);
                canvas.drawText(txt1, startX, getBaseLine(txtPaint, top + center, top + center + seatHeight / 2), txtPaint);
            }
    
            if (DBG) {
                Log.d("drawTest:", "top:" + top);
            }
        }
    
        int bacColor = Color.parseColor("#7e000000");
    
        /**
         * 绘制行号
         */
        void drawNumber(Canvas canvas) {
            long startTime = System.currentTimeMillis();
            lineNumberPaint.setColor(bacColor);
            int translateY = (int) getTranslateY();
            float scaleY = getMatrixScaleY();
    
            rectF.top = translateY - lineNumberTxtHeight / 2;
            rectF.bottom = translateY + (seatBitmapHeight * scaleY) + lineNumberTxtHeight / 2;
            rectF.left = 0;
            rectF.right = numberWidth;
            canvas.drawRoundRect(rectF, numberWidth / 2, numberWidth / 2, lineNumberPaint);
    
            lineNumberPaint.setColor(Color.WHITE);
    
            for (int i = 0; i < row; i++) {
    
                float top = (i *seatHeight + i * verSpacing) * scaleY + translateY;
                float bottom = (i * seatHeight + i * verSpacing + seatHeight) * scaleY + translateY;
                float baseline = (bottom + top - lineNumberPaintFontMetrics.bottom - lineNumberPaintFontMetrics.top) / 2;
    
                canvas.drawText(lineNumbers.get(i), numberWidth / 2, baseline, lineNumberPaint);
            }
    
            if (DBG) {
                long drawTime = System.currentTimeMillis() - startTime;
                Log.d("drawTime", "drawNumberTime:" + drawTime);
            }
        }
    
        /**
         * 绘制概览图
         */
        void drawOverview(Canvas canvas) {
    
            //绘制红色框
            int left = (int) -getTranslateX();
            if (left < 0) {
                left = 0;
            }
            left /= overviewScale;
            left /= getMatrixScaleX();
    
            int currentWidth = (int) (getTranslateX() + (column * seatWidth + 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 * seatHeight + 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);
        }
    
        Bitmap drawOverview() {
            isDrawOverviewBitmap = false;
    
            int bac = Color.parseColor("#7e000000");
            overviewPaint.setColor(bac);
            overviewPaint.setAntiAlias(true);
            overviewPaint.setStyle(Paint.Style.FILL);
            overviewBitmap.eraseColor(Color.TRANSPARENT);
            Canvas canvas = new Canvas(overviewBitmap);
            //绘制透明灰色背景
            canvas.drawRect(0, 0, rectW, rectH, overviewPaint);
    
            overviewPaint.setColor(Color.WHITE);
            for (int i = 0; i < row; i++) {
                float top = i * rectHeight + i * overviewVerSpacing + overviewVerSpacing;
                for (int j = 0; j < column; j++) {
    
                    int seatType = getSeatType(i, j);
                    switch (seatType) {
                        case SEAT_TYPE_AVAILABLE:
                            overviewPaint.setColor(Color.WHITE);
                            break;
                        case SEAT_TYPE_NOT_AVAILABLE:
                            continue;
                        case SEAT_TYPE_SELECTED:
                            overviewPaint.setColor(overview_checked);
                            break;
                        case SEAT_TYPE_SOLD:
                            overviewPaint.setColor(overview_sold);
                            break;
                    }
    
                    float left;
    
                    left = j * rectWidth + j * overviewSpacing + overviewSpacing;
                    canvas.drawRect(left, top, left + rectWidth, top + rectHeight, overviewPaint);
                }
            }
    
            return overviewBitmap;
        }
    
        /**
         * 自动回弹
         * 整个大小不超过控件大小的时候:
         * 往左边滑动,自动回弹到行号右边
         * 往右边滑动,自动回弹到右边
         * 往上,下滑动,自动回弹到顶部
         * <p>
         * 整个大小超过控件大小的时候:
         * 往左侧滑动,回弹到最右边,往右侧滑回弹到最左边
         * 往上滑动,回弹到底部,往下滑动回弹到顶部
         */
        private void autoScroll() {
            float currentSeatBitmapWidth = seatBitmapWidth * getMatrixScaleX();
            float currentSeatBitmapHeight = seatBitmapHeight * getMatrixScaleY();
            float moveYLength = 0;
            float moveXLength = 0;
    
            //处理左右滑动的情况
            if (currentSeatBitmapWidth < getWidth()) {
                if (getTranslateX() < 0 || getMatrixScaleX() < numberWidth + spacing) {
                    //计算要移动的距离
    
                    if (getTranslateX() < 0) {
                        moveXLength = (-getTranslateX()) + numberWidth + spacing;
                    } else {
                        moveXLength = numberWidth + spacing - getTranslateX();
                    }
    
                }
            } else {
    
                if (getTranslateX() < 0 && getTranslateX() + currentSeatBitmapWidth > getWidth()) {
    
                } else {
                    //往左侧滑动
                    if (getTranslateX() + currentSeatBitmapWidth < getWidth()) {
                        moveXLength = getWidth() - (getTranslateX() + currentSeatBitmapWidth);
                    } else {
                        //右侧滑动
                        moveXLength = -getTranslateX() + numberWidth + spacing;
                    }
                }
    
            }
    
            float startYPosition = screenHeight * getMatrixScaleY() + verSpacing * getMatrixScaleY() + headHeight + borderHeight;
    
            //处理上下滑动
            if (currentSeatBitmapHeight+headHeight < getHeight()) {
    
                if (getTranslateY() < startYPosition) {
                    moveYLength = startYPosition - getTranslateY();
                } else {
                    moveYLength = -(getTranslateY() - (startYPosition));
                }
    
            } else {
    
                if (getTranslateY() < 0 && getTranslateY() + currentSeatBitmapHeight > getHeight()) {
    
                } else {
                    //往上滑动
                    if (getTranslateY() + currentSeatBitmapHeight < getHeight()) {
                        moveYLength = getHeight() - (getTranslateY() + currentSeatBitmapHeight);
                    } else {
                        moveYLength = -(getTranslateY() - (startYPosition));
                    }
                }
            }
    
            Point start = new Point();
            start.x = (int) getTranslateX();
            start.y = (int) getTranslateY();
    
            Point end = new Point();
            end.x = (int) (start.x + moveXLength);
            end.y = (int) (start.y + moveYLength);
    
            moveAnimate(start, end);
    
        }
    
        private void autoScale() {
    
            if (getMatrixScaleX() > 2.2) {
                zoomAnimate(getMatrixScaleX(), 2.0f);
            } else if (getMatrixScaleX() < 0.98) {
                zoomAnimate(getMatrixScaleX(), 1.0f);
            }
        }
    
        Handler handler = new Handler();
    
        ArrayList<Integer> selects = new ArrayList<>();
    
        public ArrayList<String> getSelectedSeat(){
            ArrayList<String> results=new ArrayList<>();
            for(int i=0;i<this.row;i++){
                for(int j=0;j<this.column;j++){
                    if(isHave(getID(i,j))>=0){
                        results.add(i+","+j);
                    }
                }
            }
            return results;
        }
    
        private int isHave(Integer seat) {
            return Collections.binarySearch(selects, seat);
        }
    
        private void remove(int index) {
            selects.remove(index);
        }
    
        float[] m = new float[9];
    
        private float getTranslateX() {
            matrix.getValues(m);
            return m[2];
        }
    
        private float getTranslateY() {
            matrix.getValues(m);
            return m[5];
        }
    
        private float getMatrixScaleY() {
            matrix.getValues(m);
            return m[4];
        }
    
        private float getMatrixScaleX() {
            matrix.getValues(m);
            return m[Matrix.MSCALE_X];
        }
    
        private float dip2Px(float value) {
            return getResources().getDisplayMetrics().density * value;
        }
    
        private float getBaseLine(Paint p, float top, float bottom) {
            Paint.FontMetrics fontMetrics = p.getFontMetrics();
            int baseline = (int) ((bottom + top - fontMetrics.bottom - fontMetrics.top) / 2);
            return baseline;
        }
    
        private void moveAnimate(Point start, Point end) {
            ValueAnimator valueAnimator = ValueAnimator.ofObject(new MoveEvaluator(), start, end);
            valueAnimator.setInterpolator(new DecelerateInterpolator());
            MoveAnimation moveAnimation = new MoveAnimation();
            valueAnimator.addUpdateListener(moveAnimation);
            valueAnimator.setDuration(400);
            valueAnimator.start();
        }
    
        private void zoomAnimate(float cur, float tar) {
            ValueAnimator valueAnimator = ValueAnimator.ofFloat(cur, tar);
            valueAnimator.setInterpolator(new DecelerateInterpolator());
            ZoomAnimation zoomAnim = new ZoomAnimation();
            valueAnimator.addUpdateListener(zoomAnim);
            valueAnimator.addListener(zoomAnim);
            valueAnimator.setDuration(400);
            valueAnimator.start();
        }
    
        private float zoom;
    
        private void zoom(float zoom) {
            float z = zoom / getMatrixScaleX();
            matrix.postScale(z, z, scaleX, scaleY);
            invalidate();
        }
    
        private void move(Point p) {
            float x = p.x - getTranslateX();
            float y = p.y - getTranslateY();
            matrix.postTranslate(x, y);
            invalidate();
        }
    
        class MoveAnimation implements ValueAnimator.AnimatorUpdateListener {
    
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Point p = (Point) animation.getAnimatedValue();
    
                move(p);
            }
        }
    
        class MoveEvaluator implements TypeEvaluator {
    
            @Override
            public Object evaluate(float fraction, Object startValue, Object endValue) {
                Point startPoint = (Point) startValue;
                Point endPoint = (Point) endValue;
                int x = (int) (startPoint.x + fraction * (endPoint.x - startPoint.x));
                int y = (int) (startPoint.y + fraction * (endPoint.y - startPoint.y));
                return new Point(x, y);
            }
        }
    
        class ZoomAnimation implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
    
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                zoom = (Float) animation.getAnimatedValue();
                zoom(zoom);
    
                if (DBG) {
                    Log.d("zoomTest", "zoom:" + zoom);
                }
            }
    
            @Override
            public void onAnimationCancel(Animator animation) {
            }
    
            @Override
            public void onAnimationEnd(Animator animation) {
            }
    
            @Override
            public void onAnimationRepeat(Animator animation) {
            }
    
            @Override
            public void onAnimationStart(Animator animation) {
            }
    
        }
    
        public void setData(int row, int column) {
            this.row = row;
            this.column = column;
            init();
            invalidate();
        }
    
        ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.OnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                isScaling = true;
                float scaleFactor = detector.getScaleFactor();
                if (getMatrixScaleY() * scaleFactor > 3) {
                    scaleFactor = 3 / getMatrixScaleY();
                }
                if (firstScale) {
                    scaleX = detector.getCurrentSpanX();
                    scaleY = detector.getCurrentSpanY();
                    firstScale = false;
                }
    
                if (getMatrixScaleY() * scaleFactor < 0.5) {
                    scaleFactor = 0.5f / getMatrixScaleY();
                }
                matrix.postScale(scaleFactor, scaleFactor, scaleX, scaleY);
                invalidate();
                return true;
            }
    
            @Override
            public boolean onScaleBegin(ScaleGestureDetector detector) {
                return true;
            }
    
            @Override
            public void onScaleEnd(ScaleGestureDetector detector) {
                isScaling = false;
                firstScale = true;
            }
        });
    
        GestureDetector gestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                isOnClick = true;
                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 + (j+1) * spacing) * getMatrixScaleX() + getTranslateX());
                        int maxTemX = (int) (tempX + seatWidth * getMatrixScaleX());
    
                        int tempY = (int) ((i * seatHeight + i * verSpacing) * getMatrixScaleY() + getTranslateY());
                        int maxTempY = (int) (tempY + seatHeight * getMatrixScaleY());
    
                        if (seatChecker != null && seatChecker.isValidSeat(i, j) && !seatChecker.isSold(i, j)) {
                            if (x >= tempX && x <= maxTemX && y >= tempY && y <= maxTempY) {
                                int id = getID(i, j);
                                int index = isHave(id);
                                if (index >= 0) {
                                    remove(index);
                                    if (seatChecker != null) {
                                        seatChecker.unCheck(i, j);
                                    }
                                } else {
                                    if (selects.size() >= maxSelected) {
                                        Toast.makeText(getContext(), "最多只能选择" + maxSelected + "个", Toast.LENGTH_SHORT).show();
                                        return super.onSingleTapConfirmed(e);
                                    } else {
                                        addChooseSeat(i, j);
                                        if (seatChecker != null) {
                                            seatChecker.checked(i, j);
                                        }
                                    }
                                }
                                isNeedDrawSeatBitmap = true;
                                isDrawOverviewBitmap = true;
                                float currentScaleY = getMatrixScaleY();
    
                                if (currentScaleY < 1.7) {
                                    scaleX = x;
                                    scaleY = y;
                                    zoomAnimate(currentScaleY, 1.9f);
                                }
    
                                invalidate();
                                break;
                            }
                        }
                    }
                }
    
                return super.onSingleTapConfirmed(e);
            }
        });
    
        private void addChooseSeat(int row, int column) {
            int id = getID(row, column);
            for (int i = 0; i < selects.size(); i++) {
                int item = selects.get(i);
                if (id < item) {
                    selects.add(i, id);
                    return;
                }
            }
    
            selects.add(id);
        }
    
        public interface SeatChecker {
            /**
             * 是否可用座位
             *
             * @param row
             * @param column
             * @return
             */
            boolean isValidSeat(int row, int column);
    
            /**
             * 是否已售
             *
             * @param row
             * @param column
             * @return
             */
            boolean isSold(int row, int column);
    
            void checked(int row, int column);
    
            void unCheck(int row, int column);
    
            /**
             * 获取选中后座位上显示的文字
             * @param row
             * @param column
             * @return 返回2个元素的数组,第一个元素是第一行的文字,第二个元素是第二行文字,如果只返回一个元素则会绘制到座位图的中间位置
             */
            String[] checkedSeatTxt(int row,int column);
    
        }
    
        public void setScreenName(String screenName) {
            this.screenName = screenName;
        }
    
        public void setMaxSelected(int maxSelected) {
            this.maxSelected = maxSelected;
        }
    
        public void setSeatChecker(SeatChecker seatChecker) {
            this.seatChecker = seatChecker;
            invalidate();
        }
    
        private int getRowNumber(int row){
            int result=row;
            if(seatChecker==null){
                return -1;
            }
    
            for(int i=0;i<row;i++){
                for (int j=0;j<column;j++){
                    if(seatChecker.isValidSeat(i,j)){
                        break;
                    }
    
                    if(j==column-1){
                        if(i==row){
                            return -1;
                        }
                        result--;
                    }
                }
            }
            return result;
        }
    
        private int getColumnNumber(int row,int column){
            int result=column;
            if(seatChecker==null){
                return -1;
            }
    
            for(int i=row;i<=row;i++){
                for (int j=0;j<column;j++){
    
                    if(!seatChecker.isValidSeat(i,j)){
                        if(j==column){
                            return -1;
                        }
                        result--;
                    }
                }
            }
            return result;
        }
    }
    

    values/attrs.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <declare-styleable name="SeatTableView">
            <attr name="seat_checked" format="reference" />
            <attr name="seat_sold" format="reference" />
            <attr name="seat_available" format="reference" />
            <attr name="overview_checked" format="color" />
            <attr name="overview_sold" format="color" />
            <attr name="txt_color" format="color"/>
        </declare-styleable>
    
    </resources>
    

    layout_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="16dp"
        android:paddingTop="16dp"
        android:paddingRight="16dp"
        android:paddingBottom="16dp">
    
        <com.demo2.SeatTable
            android:id="@+id/seatView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#ffffff" />
    
    </RelativeLayout>
    
    

    MainActivity.class

    import androidx.appcompat.app.AppCompatActivity;
    import android.os.Bundle;
    
    public class ShimmerRecyclerViewActivity extends AppCompatActivity {
    
        public SeatTable seatTableView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.layout_main);
            seatTableView = (SeatTable) findViewById(R.id.seatView);
            seatTableView.setScreenName("8号厅荧幕");//设置屏幕名称
            seatTableView.setMaxSelected(3);//设置最多选中
    
            seatTableView.setSeatChecker(new SeatTable.SeatChecker() {
    
                @Override
                public boolean isValidSeat(int row, int column) {
                    if(column==2) {
                        return false;
                    }
                    return true;
                }
    
                @Override
                public boolean isSold(int row, int column) {
                    if(row==6&&column==6){
                        return true;
                    }
                    return false;
                }
    
                @Override
                public void checked(int row, int column) {
    
                }
    
                @Override
                public void unCheck(int row, int column) {
    
                }
    
                @Override
                public String[] checkedSeatTxt(int row, int column) {
                    return null;
                }
    
            });
            seatTableView.setData(10,15);
        }
    }
    
    seat_gray.png seat_green.png seat_sold.png

    相关文章

      网友评论

        本文标题:Android 电影票在线选座

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