欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android 炫酷表格

程序员文章站 2022-05-26 16:12:14
...

android 自定义view实现炫酷表格,还是先来个效果图


android 炫酷表格android 炫酷表格android 炫酷表格


直接上传代码:

public LineViewDemo(Context context, AttributeSet attrs) {
        super(context, attrs);
        inits(context, attrs);
    }

    public LineViewDemo(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        inits(context, attrs);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public LineViewDemo(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        inits(context, attrs);
    }

    private void inits(Context context, AttributeSet attrs) {
        this.mContext = context;
        mDivderPaint = new Paint();
        mDivderPaint.setAntiAlias(true);
        mDivderPaint.setStyle(Paint.Style.STROKE);
        mDivderPaint.setStrokeWidth(dp2px(0.5f));
        mDivderPaint.setColor(Color.GRAY);

        mPotinPaint = new Paint();
        mPotinPaint.setAntiAlias(true);
        mPotinPaint.setStyle(Paint.Style.FILL);

        mBgPaint = new Paint();
        mBgPaint.setAntiAlias(true);
        mBgPaint.setStyle(Paint.Style.FILL);

        mLinePaint = new Paint();
        mLinePaint.setAntiAlias(true);
        mLinePaint.setStyle(Paint.Style.FILL);
        mLinePaint.setStrokeWidth(dp2px(1f));
        mLinePaint.setColor(Color.parseColor("#42afff"));

        mHintPaint = new TextPaint();
        mHintPaint.setColor(Color.parseColor("#332b2c"));
        mHintPaint.setTextSize(sp2px(10));

        mTextPaint = new Paint();
        mTextPaint.setColor(Color.GRAY);
        mTextPaint.setTextSize(sp2px(12));
        //模拟数据
        getDatas();
    }

    private void getDatas() {

        mSpanY = dp2px(60);
        mSpanX = dp2px(60);
        mSpanTop = dp2px(40);
        mR = dp2px(10);
        mHintSpan = dp2px(10);
        mTextSpan = dp2px(5);
        mBottomR = dp2px(4);


        mValueX = new ArrayList<>();
        mValueY = new ArrayList<>();
        mPoints = new ArrayList<>();

        for (int i = 1; i <= 18; i++) {
            mValueX.add(i + "题");
        }
        for (int i = 0; i <= 5; i++) {
            mValueY.add(i * 2 + "");
        }
    }

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        this.mCanvas = canvas;

        drawLineX(canvas);

        drawLineY(canvas);

        drawLinePath(canvas);

        drawPoint(canvas);

        drawHint();
    }

/**
     * 绘制填充颜色
     */
    private void drawLinePath(Canvas canvas) {
        Path path = new Path();
        path.moveTo(mSpanX, mRow * (1 + 2 * 5) / 2 + mSpanTop);

        for (int i = 0; i < mValueX.size(); i++) {
            PointEntity pointS = mPoints.get(i);
            path.lineTo(pointS.getX(), pointS.getY());
        }
        path.lineTo(mSpanX + mCol * 17.f, mRow * (1 + 2 * 5) / 2 + mSpanTop);
        path.close();
        //绘制填充颜色
        //需完成颜色的渐变
        Shader shader = new LinearGradient(mSpanY, mRow, mSpanY, mRootH - mSpanX, Color.parseColor("#aa42afff"), Color.parseColor("#0042afff"), Shader.TileMode.REPEAT);
        mLinePaint.setShader(shader);
        canvas.drawPath(path, mLinePaint);
        //还原折线画笔  避免下次重新绘制颜色不对
        mLinePaint.reset();
        mLinePaint.setAntiAlias(true);
        mLinePaint.setStyle(Paint.Style.FILL);
        mLinePaint.setStrokeWidth(dp2px(1f));
        mLinePaint.setColor(Color.parseColor("#42afff"));
    }

//画点及折线
    private void drawPoint(Canvas canvas) {

        for (int i = 0; i < mValueX.size(); i++) {
            PointEntity pointS = mPoints.get(i);
            //先画线
            if (i + 1 < mValueX.size()) {
                PointEntity pointE = mPoints.get(i + 1);
                canvas.drawLine(pointS.getX(), pointS.getY(), pointE.getX(), pointE.getY(), mLinePaint);
            }
            //在画点
            Shader shader;
            if (i == mSelectInde) {
//                int[] colors:表示所需要的渐变颜色数组 float[] stops:表示每个渐变颜色所在的位置百分点,取值0-1,数量必须与colors数组保持一致,不然直接crash,一般第一个数值取0,最后一个数值取1;如果第一个数值和最后一个数值并没有取0和1,比如我们这里取一个位置数组:{0.2,0.5,0.8},起始点是0.2百分比位置,结束点是0.8百分比位置,而0-0.2百分比位置和0.8-1.0百分比的位置都是没有指定颜色的。而这些位置的颜色就是根据我们指定的TileMode空白区域填充模式来自行填充!!!
                shader = new RadialGradient(pointS.getX(), pointS.getY(), dp2px(6),
                        new int[]{Color.parseColor("#42afff"), Color.parseColor("#ffffff"), Color.parseColor("#42afff")},
                        new float[]{0, 0.8f, 1},
                        Shader.TileMode.CLAMP);
                mPotinPaint.setShader(shader);
                canvas.drawCircle(pointS.getX(), pointS.getY(), dp2px(6), mPotinPaint);
            } else {
                shader = new RadialGradient(pointS.getX(), pointS.getY(), dp2px(4),
                        new int[]{Color.parseColor("#42afff"), Color.parseColor("#42afff"), Color.parseColor("#ffffff")},
                        new float[]{0, 0.7f, 1},
                        Shader.TileMode.CLAMP);
                mPotinPaint.setShader(shader);
                canvas.drawCircle(pointS.getX(), pointS.getY(), dp2px(4), mPotinPaint);
            }
        }
    }

  /**
     * 绘制提示文本
     */
    private void drawHint() {
        int marBottom = dp2px(6);
        int radio = dp2px(2);

        boolean isEnable = mSelectInde>=0&&mSelectInde<mPoints.size();
        if (!isEnable){
            return;
        }
        PointEntity p = mPoints.get(mSelectInde);
        int x = p.getX();
        int y = p.getY();
        String msg = "而这些位置的颜色就是根据我们指定的TileMode空白区域填充模式来自行填充!!!有时效果我们是不可控的。所以为了方便起见,建议大家st";
        int textW = getTextW(msg);
        int textH = getTextH(msg);
        Log.e(TAG, "drawHint: textw="+textW +"\ntextH="+textH+"\nx="+x+"\ny="+y);

        if (textW>=mMaxHintW){
            int n = textW/mMaxHintW;
            n = n>3? 3:n;
            textW = mMaxHintW;
            textH = textH*n;
        }
        Log.e(TAG, "drawHint: textw="+textW +"\ntextH="+textH);
        int left ;
        int top = y-textH-dp2px(20);
        if (textW/2>=x){
            //左侧显示不下 ,从最左边开始
            left = mHintSpan;
        }else if (x+textW/2>=mRootW){
            //右侧显示不下 ,从最右边开始
            left = mRootW-mHintSpan-textW;
        }else {
            left = x-textW/2;
        }
        //画白色填充
        Path path = new Path();
        path.moveTo(left-mTextSpan,top-mTextSpan);
        path.lineTo(left+textW+mTextSpan,top-mTextSpan);
        path.lineTo(left+textW+mTextSpan,top+textH+mTextSpan);
        path.lineTo(x-mBottomR,top+textH+mTextSpan);
        path.lineTo(x,y-marBottom);
        path.lineTo(x+mBottomR,top+textH+mTextSpan);
        path.lineTo(left-mTextSpan,top+textH+mTextSpan);
        path.close();
        mBgPaint.setColor(Color.WHITE);
        mCanvas.drawPath(path,mBgPaint);
        //画红色边框
        float[] pts = new float[]{
                left-mTextSpan,top-mTextSpan,left+textW+mTextSpan,top-mTextSpan,
                left+textW+mTextSpan,top-mTextSpan,left+textW+mTextSpan,top+textH+mTextSpan,
                left+textW+mTextSpan,top+textH+mTextSpan,x-mBottomR,top+textH+mTextSpan,
                x-mBottomR,top+textH+mTextSpan,x,y-marBottom,
                x,y-marBottom,x+mBottomR,top+textH+mTextSpan,
                x+mBottomR,top+textH+mTextSpan,left-mTextSpan,top+textH+mTextSpan,
                left-mTextSpan,top+textH+mTextSpan,left-mTextSpan,top-mTextSpan
        };
        mBgPaint.setColor(Color.RED);
        mCanvas.drawLines(pts, mBgPaint);
        mBgPaint.setColor(Color.WHITE);
        mCanvas.drawLine(x-mBottomR,top+textH+mTextSpan,x+mBottomR,top+textH+mTextSpan,mBgPaint);
        StaticLayout layout = new StaticLayout(msg,mHintPaint,textW, Layout.Alignment.ALIGN_NORMAL,1f,0f,true);
        Log.e(TAG, "drawHint: left="+left +"\ntop="+top);
        mCanvas.translate(left,top);
        layout.draw(mCanvas);
        mCanvas.restore();
    }

    //画横线及y轴刻度
    private void drawLineY(Canvas canvas) {
        int width = getTextH(mValueY.get(0));
        int heigt = getTextH(mValueY.get(0));
        for (int i = 0; i < mValueY.size(); i++) {
            //横线
            canvas.drawLine(mSpanY - dp2px(10), mRow * (1 + 2 * i) / 2 + mSpanTop, mRootW, mRow * (1 + 2 * i) / 2 + mSpanTop, mDivderPaint);
            //画文字
            String text = mValueY.get(mValueY.size() - i - 1);
            int x = mSpanY - width - dp2px(20);
            if (i == 0) {
                x -= dp2px(3);
            }
            canvas.drawText(text, x, mRow * (1 + 2 * i) / 2 + heigt / 2 + mSpanTop, mTextPaint);
        }
        width = getTextH("正确数");
        heigt = getTextH("正确数");
        canvas.drawText("正确数", mSpanX - width - dp2px(4), mSpanTop / 2 + heigt, mTextPaint);
    }

 //画竖线及x轴刻度
    private void drawLineX(Canvas canvas) {
        int width = getTextH(mValueX.get(0));
        int heigt = getTextH(mValueX.get(0));
        for (int i = 0; i < mValueX.size(); i++) {
            //竖线
            canvas.drawLine(mSpanX + mCol * i, mSpanTop, mSpanX + mCol * i, mRootH - dp2px(10) - mSpanX, mDivderPaint);
            //画文字
            canvas.drawText(mValueX.get(i), mSpanX + mCol * i - width, mRootH - mSpanX + heigt, mTextPaint);
        }
    }

    //第一次才加载模拟数据
    private boolean isFirst = true;

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mRootH = getMeasuredHeight();
        mRootW = getMeasuredWidth();

        mCol = (int) ((mRootW - mSpanY) * 1.0f / mValueX.size() + 0.5f);
        mRow = (int) ((mRootH - mSpanX - mSpanTop) * 1.0f / mValueY.size() + 0.5f);

        mMaxHintW = mRootW/3;

        Log.e(TAG, "mRootH=" + mRootH +
                "\nmRootW=" + mRootW +
                "\nmCol=" + mCol +
                "\nmRow=" + mRow+
                "\nmMaxHintW=" + mMaxHintW);

        //第一次才加载模拟数据
        if (!isFirst){
            return;
        }else {
            isFirst = false;
        }
        /**
         * 模拟数据
         */
        Random random = new Random();
        mPoints.clear();
        for (int i = 0; i < 18; i++) {
            PointEntity p = new PointEntity(mSpanX + mCol * i, (8 - random.nextInt(8)) * mRow / 2 + mRow / 2 + mSpanTop);
            Log.e(TAG, "index=" + i + "\np.x=" + p.getX() + "\np.y=" + p.getY());
            mPoints.add(p);
        }

    }

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                actionDown(event);
                break;
            case MotionEvent.ACTION_UP:
                actionUp(event);
                break;
            case MotionEvent.ACTION_MOVE:
                actionMove(event);
                break;
        }
        return super.onTouchEvent(event);
    }

    //按下
    private void actionDown(MotionEvent event) {
            float x = event.getX();
            float y = event.getY();

            for (int i = 0; i < mPoints.size(); i++) {
                PointEntity p = mPoints.get(i);
                int left = p.getX() - mR;
                int right = p.getX() + mR;
                int top = p.getY() - mR;
                int bottom = p.getY() + mR;

                boolean isInner = left <= x && right >= x && top <= y && bottom >= y;

                if (isInner) {
                    if (mClickListener != null) {
                        mClickListener.onClickPoint(i,(int)x,(int)y);
                    }
                    upDataView(i);
                }
            }

    }

    private void upDataView(int position) {
        mSelectInde = position;
        invalidate();
        Log.e(TAG, "upDataView: -----------------------------------------"+position );
    }

    private int dp2px(float d) {
        float scale = mContext.getResources().getDisplayMetrics().density;
        return (int) (d * scale + 0.5f);
    }

    private int sp2px(float s) {
        float fontScale = mContext.getResources().getDisplayMetrics().scaledDensity;
        return (int) (s * fontScale + 0.5f);
    }

    private int getTextW(String text) {
        Rect textBounds = new Rect();
        mTextPaint.getTextBounds(text, 0, text.length(), textBounds);
        return textBounds.width();
    }

    private int getTextH(String text) {
        Rect textBounds = new Rect();
        mTextPaint.getTextBounds(text, 0, text.length(), textBounds);
        mTextPaint.measureText(text);
        return textBounds.height();
    }


说明:再次并没有对数据源进行封装,应为需求不一样,进行统一封装比较麻烦,在实际应用中使用频率一般,用的时候只需要根据自己需求修改即可;