Android 自定义漏斗图FunnelView(一)

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qingfeng812/article/details/52211110 ...
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qingfeng812/article/details/52211110

  1. 这个版本为固定设置漏斗数据;后面提供动态设置数据;
  2. 具有动画效果;
  3. 主要麻烦的地方是算坐标点;


效果截图:


代码 FunnelView:




import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;

import java.util.ArrayList;
import java.util.List;

/**
 * 漏斗图 v1.0
 */
@SuppressLint("NewApi")
public class FunnelView extends View implements ValueAnimator.AnimatorUpdateListener {
    public static final float ANGLE_SCALE = 3.0f;
    private List<Integer> mMoneys = new ArrayList<>();
    private int maxMoney;

    private float phaseX = 1f;
    private int textAlpha = 255;

    private Paint mPaint1;
    private Paint mPaint2;
    private Paint mPaint3;
    private Paint mPaint4;
    private Paint mPaint5;
    private Paint mPaint6;
    private Paint mPaint7;
    private Paint mPaint8;
    private Paint mPaint9;
    private Paint mPaint10;

    private Paint mPaintLine;
    private Paint mPaintText;


    private Path mPath1;
    private Path mPath2;
    private Path mPath3;
    private Path mPath4;
    private Path mPath5;
    private Path mPath6;
    private Path mPath7;
    private Path mPath8;
    private Path mPath9;
    private Path mPath10;


    private float mTotalHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 250, getResources().getDisplayMetrics());

    private float mPath1Height;
    private float mPath2Height;
    private float mPath3Height;
    private float mPath4Height;
    private float mPath5Height;
    private float mPath6Height;
    private float mPath7Height;
    private float mPath8Height;
    private float mPath9Height;
    private float mPath10Height;

    private float mPath1AngleWidth;
    private float mPath2AngleWidth;
    private float mPath3AngleWidth;
    private float mPath4AngleWidth;
    private float mPath5AngleWidth;
    private float mPath6AngleWidth;
    private float mPath7AngleWidth;
    private float mPath8AngleWidth;
    private float mPath9AngleWidth;
    private float mPath10AngleWidth;


    private float mPath3LineStartX;
    private float mPath3LineStartY;

    private float mPath4LineStartX;
    private float mPath4LineStartY;

    private float mPath5LineStartX;
    private float mPath5LineStartY;

    private float maxWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 330, getResources().getDisplayMetrics());
    private float maxLineH = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 90, getResources().getDisplayMetrics());
    private float minLineH = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());

    private float startOffsetX = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());
    private float startOffsetY = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());

    private float lineStartOffsetX = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics());
    private float textStartOffsetX = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 7, getResources().getDisplayMetrics());

    private float mLastX;
    private float mLastY;
    private float mLastWidth;

    public FunnelView(Context context) {
        this(context, null);
    }

    public FunnelView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FunnelView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    /**
     * @desc:设置数据
     * @author:Arison on 2016/8/15
     */
    public void setData(List<Integer> moneys, int maxMoney) {
        this.mMoneys = moneys;
        this.maxMoney = maxMoney;
        calculate();
        //int averageHeight = (int) (mTotalHeight / 5);
        invalidate();
    }

    private void calculate() {
        for (int i = 0; i < mMoneys.size(); i++) {
            int money = mMoneys.get(i);
            float scale = (float) money / maxMoney;
            switch (i) {
                case 0:
                    mPath1Height = mTotalHeight * scale * phaseX;
                    if (mPath1Height < minLineH * phaseX) {
                        mPath1Height = minLineH * phaseX;
                    } else if (mPath1Height > maxLineH * phaseX) {
                        mPath1Height = maxLineH * phaseX;
                    }
                    mPath1AngleWidth = mPath1Height / ANGLE_SCALE;
                    //System.out.println("mPath1Height=" + mPath1Height + " ,phaseX=" + phaseX);
                    break;
                case 1:
                    mPath2Height = mTotalHeight * scale * phaseX;
                    if (mPath2Height < minLineH * phaseX) {
                        mPath2Height = minLineH * phaseX;
                    } else if (mPath2Height > maxLineH * phaseX) {
                        mPath2Height = maxLineH * phaseX;
                    }

                    mPath2AngleWidth = mPath2Height / ANGLE_SCALE;

                    //System.out.println("mPath2Height=" + mPath2Height);
                    break;
                case 2:
                    mPath3Height = mTotalHeight * scale * phaseX;
                    if (mPath3Height < minLineH * phaseX) {
                        mPath3Height = minLineH * phaseX;
                    } else if (mPath3Height > maxLineH * phaseX) {
                        mPath3Height = maxLineH * phaseX;
                    }

                    mPath3AngleWidth = mPath3Height / ANGLE_SCALE;

                    //System.out.println("mPath3Height=" + mPath3Height);
                    break;
                case 3:
                    mPath4Height = mTotalHeight * scale * phaseX;
                    if (mPath4Height < minLineH * phaseX) {
                        mPath4Height = minLineH * phaseX;
                    } else if (mPath4Height > maxLineH * phaseX) {
                        mPath4Height = maxLineH * phaseX;
                    }

                    mPath4AngleWidth = mPath4Height / ANGLE_SCALE;

                    //System.out.println("mPath4Height=" + mPath4Height);
                    break;
                case 4:
                    mPath5Height = mTotalHeight * scale * phaseX;
                    if (mPath5Height < minLineH * phaseX) {
                        mPath5Height = minLineH * phaseX;
                    } else if (mPath5Height > maxLineH * phaseX) {
                        mPath5Height = maxLineH * phaseX;
                    }

                    mPath5AngleWidth = mPath5Height / ANGLE_SCALE;

                    //System.out.println("mPath5Height=" + mPath5Height);
                    break;
                case 5:
                    mPath6Height = mTotalHeight * scale * phaseX;
                    if (mPath6Height < minLineH * phaseX) {
                        mPath6Height = minLineH * phaseX;
                    } else if (mPath6Height > maxLineH * phaseX) {
                        mPath6Height = maxLineH * phaseX;
                    }

                    mPath6AngleWidth = mPath6Height / ANGLE_SCALE;
                    break;
                case 6:
                    mPath7Height = mTotalHeight * scale * phaseX;
                    if (mPath7Height < minLineH * phaseX) {
                        mPath7Height = minLineH * phaseX;
                    } else if (mPath7Height > maxLineH * phaseX) {
                        mPath7Height = maxLineH * phaseX;
                    }

                    mPath7AngleWidth = mPath7Height / ANGLE_SCALE;
                    break;
                case 7:
                    mPath8Height = mTotalHeight * scale * phaseX;
                    if (mPath8Height < minLineH * phaseX) {
                        mPath8Height = minLineH * phaseX;
                    } else if (mPath8Height > maxLineH * phaseX) {
                        mPath8Height = maxLineH * phaseX;
                    }

                    mPath8AngleWidth = mPath8Height / ANGLE_SCALE;
                    break;
                case 8:
                    mPath9Height = mTotalHeight * scale * phaseX;
                    if (mPath9Height < minLineH * phaseX) {
                        mPath9Height = minLineH * phaseX;
                    } else if (mPath9Height > maxLineH * phaseX) {
                        mPath9Height = maxLineH * phaseX;
                    }

                    mPath9AngleWidth = mPath9Height / ANGLE_SCALE;
                    break;
                case 9:
                    mPath10Height = mTotalHeight * scale * phaseX;
                    if (mPath10Height < minLineH * phaseX) {
                        mPath10Height = minLineH * phaseX;
                    } else if (mPath10Height > maxLineH * phaseX) {
                        mPath10Height = maxLineH * phaseX;
                    }

                    mPath10AngleWidth = mPath10Height / ANGLE_SCALE;
                    break;
            }

        }
    }

    private void init() {
        mPaint1 = new Paint();
        mPaint2 = new Paint();
        mPaint3 = new Paint();
        mPaint4 = new Paint();
        mPaint5 = new Paint();
        mPaint6 = new Paint();
        mPaint7 = new Paint();
        mPaint8 = new Paint();
        mPaint9 = new Paint();
        mPaint10 = new Paint();
        //FF0000   00CCFF   FFFF00  00FF00    FF00FF   FF9900  993366  C0C0C0  FFCC99
        mPaint1.setColor(Color.parseColor("#FF0000"));
        mPaint1.setStyle(Paint.Style.FILL);
        mPaint1.setDither(true);
        mPaint1.setAntiAlias(true);

        mPaint2.setColor(Color.parseColor("#00CCFF"));
        mPaint2.setStyle(Paint.Style.FILL);
        mPaint2.setDither(true);
        mPaint2.setAntiAlias(true);

        mPaint3.setColor(Color.parseColor("#FFFF00"));
        mPaint3.setStyle(Paint.Style.FILL);
        mPaint3.setDither(true);
        mPaint3.setAntiAlias(true);

        mPaint4.setColor(Color.parseColor("#00FF00"));
        mPaint4.setStyle(Paint.Style.FILL);
        mPaint4.setDither(true);
        mPaint4.setAntiAlias(true);

        mPaint5.setColor(Color.parseColor("#FF00FF"));
        mPaint5.setStyle(Paint.Style.FILL);
        mPaint5.setDither(true);
        mPaint5.setAntiAlias(true);


        mPaint6.setColor(Color.parseColor("#FF9900"));
        mPaint6.setStyle(Paint.Style.FILL);
        mPaint6.setDither(true);
        mPaint6.setAntiAlias(true);


        mPaint7.setColor(Color.parseColor("#993366"));
        mPaint7.setStyle(Paint.Style.FILL);
        mPaint7.setDither(true);
        mPaint7.setAntiAlias(true);


        mPaint8.setColor(Color.parseColor("#C0C0C0"));
        mPaint8.setStyle(Paint.Style.FILL);
        mPaint8.setDither(true);
        mPaint8.setAntiAlias(true);


        mPaint9.setColor(Color.parseColor("#FFCC99"));
        mPaint9.setStyle(Paint.Style.FILL);
        mPaint9.setDither(true);
        mPaint9.setAntiAlias(true);


        mPaint10.setColor(Color.parseColor("#FFCC99"));
        mPaint10.setStyle(Paint.Style.FILL);
        mPaint10.setDither(true);
        mPaint10.setAntiAlias(true);

        mPaintLine = new Paint();
        mPaintText = new Paint();
        mPaintLine.setColor(Color.parseColor("#A8ADB2"));
        mPaintLine.setStyle(Paint.Style.FILL);
        mPaintLine.setStrokeWidth(2);

        mPaintText.setColor(Color.parseColor("#A8ADB2"));
        mPaintText.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()));
        mPaintText.setAntiAlias(true);
        mPaintText.setTextAlign(Paint.Align.LEFT);
    }

    private void draw1(Canvas canvas) {
        mLastX = startOffsetX;
        mLastY = startOffsetY;
        mLastWidth = maxWidth - startOffsetX;

        mPath1 = new Path();
        mPath1.moveTo(mLastX, startOffsetY);
        mPath1.lineTo(mLastX + mLastWidth, startOffsetY);
        mPath1.lineTo(mLastX + mLastWidth - mPath1AngleWidth, mLastY + mPath1Height);
        mPath1.lineTo(mLastX + mPath1AngleWidth, mLastY + mPath1Height);

        mPath1.close();
        canvas.drawPath(mPath1, mPaint1);
        mLastWidth = mLastWidth - 2 * mPath1AngleWidth;
        mLastX = mLastX + mPath1AngleWidth;
        mLastY = mLastY + mPath1Height;
    }


    private void draw2(Canvas canvas) {

        mPath2 = new Path();

        mPath2.moveTo(mLastX, mLastY);
        mPath2.lineTo(mLastX + mLastWidth, mLastY);
        mPath2.lineTo(mLastX + mLastWidth - mPath2AngleWidth, mLastY + mPath2Height);
        mPath2.lineTo(mLastX + mPath2AngleWidth, mLastY + mPath2Height);
        mPath2.close();
        canvas.drawPath(mPath2, mPaint2);

        mLastWidth = mLastWidth - mPath2AngleWidth - mPath2AngleWidth;
        mLastX = mLastX + mPath2AngleWidth;
        mLastY = mLastY + mPath2Height;
    }


    private void draw3(Canvas canvas) {

        mPath3 = new Path();

        mPath3.moveTo(mLastX, mLastY);
        mPath3.lineTo(mLastX + mLastWidth, mLastY);
        mPath3.lineTo(mLastX + mLastWidth - mPath3AngleWidth, mLastY + mPath3Height);
        mPath3.lineTo(mLastX + mPath3AngleWidth, mLastY + mPath3Height);
        mPath3.close();
        canvas.drawPath(mPath3, mPaint3);

        mLastWidth = mLastWidth - mPath3AngleWidth - mPath3AngleWidth;
        mLastX = mLastX + mPath3AngleWidth;
        mLastY = mLastY + mPath3Height;
        mPath3LineStartX = mLastX + mLastWidth + mPath3AngleWidth / 2;
        mPath3LineStartY = mLastY - mPath3Height / 2;
    }

    private void draw4(Canvas canvas) {

        mPath4 = new Path();

        mPath4.moveTo(mLastX, mLastY);
        mPath4.lineTo(mLastX + mLastWidth, mLastY);
        mPath4.lineTo(mLastX + mLastWidth - mPath4AngleWidth, mLastY + mPath4Height);
        mPath4.lineTo(mLastX + mPath4AngleWidth, mLastY + mPath4Height);
        mPath4.close();
        canvas.drawPath(mPath4, mPaint4);

        mLastWidth = mLastWidth - 2 * mPath4AngleWidth;//最新长度
        mLastX = mLastX + mPath4AngleWidth;//第四个点的x坐标
        mLastY = mLastY + mPath4Height;//第四个点的y坐标
        mPath4LineStartX = mLastX + mLastWidth + mPath4AngleWidth / 2;
        mPath4LineStartY = mLastY - mPath4Height / 2;
    }

    private void draw5(Canvas canvas) {
        mPath5 = new Path();

        mPath5.moveTo(mLastX, mLastY);
        mPath5.lineTo(mLastX + mLastWidth, mLastY);
        mPath5.lineTo(mLastX + mLastWidth - mPath5AngleWidth, mLastY + mPath5Height);
        mPath5.lineTo(mLastX + mPath5AngleWidth, mLastY + mPath5Height);
        mPath5.close();
        canvas.drawPath(mPath5, mPaint5);


        //mLastWidth = maxWidth - startOffsetX;
        //mLastX = mLastX;
//        mLastY = mLastY + mPath5Height;
//        mPath5LineStartX = mLastX + mLastWidth;
//        mPath5LineStartY = mLastY - mPath5Height / 2;

        mLastWidth = mLastWidth - 2 * mPath5AngleWidth;//最新长度
        mLastX = mLastX + mPath5AngleWidth;//第四个点的x坐标
        mLastY = mLastY + mPath5Height;//第四个点的y坐标
    }


    private void draw6(Canvas canvas) {
        mPath6 = new Path();
        mPath6.moveTo(mLastX, mLastY);
        mPath6.lineTo(mLastX + mLastWidth, mLastY);
        mPath6.lineTo(mLastX + mLastWidth - mPath6AngleWidth, mLastY + mPath6Height);
        mPath6.lineTo(mLastX + mPath6AngleWidth, mLastY + mPath6Height);
        mPath6.close();
        canvas.drawPath(mPath6, mPaint6);
        mLastWidth = mLastWidth - 2 * mPath6AngleWidth;//最新长度
        mLastX = mLastX + mPath6AngleWidth;//第四个点的x坐标
        mLastY = mLastY + mPath6Height;//第四个点的y坐标
    }


    private void draw7(Canvas canvas) {
        mPath7 = new Path();
        mPath7.moveTo(mLastX, mLastY);
        mPath7.lineTo(mLastX + mLastWidth, mLastY);
        mPath7.lineTo(mLastX + mLastWidth - mPath7AngleWidth, mLastY + mPath7Height);
        mPath7.lineTo(mLastX + mPath7AngleWidth, mLastY + mPath7Height);
        mPath7.close();
        canvas.drawPath(mPath7, mPaint7);
        mLastWidth = mLastWidth - 2 * mPath7AngleWidth;//最新长度
        mLastX = mLastX + mPath7AngleWidth;//第四个点的x坐标
        mLastY = mLastY + mPath7Height;//第四个点的y坐标
    }


    private void draw8(Canvas canvas) {
        mPath8 = new Path();
        mPath8.moveTo(mLastX, mLastY);
        mPath8.lineTo(mLastX + mLastWidth, mLastY);
        mPath8.lineTo(mLastX + mLastWidth - mPath8AngleWidth, mLastY + mPath8Height);
        mPath8.lineTo(mLastX + mPath8AngleWidth, mLastY + mPath8Height);
        mPath8.close();
        canvas.drawPath(mPath8, mPaint8);
        mLastWidth = mLastWidth - 2 * mPath8AngleWidth;//最新长度
        mLastX = mLastX + mPath8AngleWidth;//第四个点的x坐标
        mLastY = mLastY + mPath8Height;//第四个点的y坐标
    }


    private void draw9(Canvas canvas) {
        mPath9 = new Path();
        mPath9.moveTo(mLastX, mLastY);
        mPath9.lineTo(mLastX + mLastWidth, mLastY);
        mPath9.lineTo(mLastX + mLastWidth - mPath9AngleWidth, mLastY + mPath9Height);
        mPath9.lineTo(mLastX + mPath9AngleWidth, mLastY + mPath9Height);
        mPath9.close();
        canvas.drawPath(mPath9, mPaint9);
//        mLastWidth = mLastWidth - 2 * mPath9AngleWidth;//最新长度
//        mLastX = mLastX + mPath9AngleWidth;//第四个点的x坐标
        mLastY = mLastY + mPath9Height;//第四个点的y坐标
    }


    private void draw10(Canvas canvas) {
        mPath10 = new Path();
        mPath10.moveTo(mLastX, mLastY);
        mPath10.lineTo(mLastX + mLastWidth, mLastY);
        mPath10.lineTo(mLastX + mLastWidth - mPath10AngleWidth, mLastY + mPath10Height);
        mPath10.lineTo(mLastX + mPath10AngleWidth, mLastY + mPath10Height);
        mPath10.close();
        canvas.drawPath(mPath10, mPaint10);
        mLastWidth = mLastWidth - 2 * mPath10AngleWidth;//最新长度
        mLastX = mLastX + mPath10AngleWidth;//第四个点的x坐标
        mLastY = mLastY + mPath10Height;//第四个点的y坐标
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        draw1(canvas);
        draw2(canvas);
        draw3(canvas);
        draw4(canvas);
        draw5(canvas);
        draw6(canvas);
        draw7(canvas);
        draw8(canvas);
        draw9(canvas);
        drawText9(canvas);
    }

    public void animateY() {
        ObjectAnimator xAnimator = ObjectAnimator.ofFloat(this, "phaseX", 0, 1);
        xAnimator.setDuration(2000);
        xAnimator.addUpdateListener(this);
        xAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        xAnimator.start();

        ObjectAnimator alphaAnimator = ObjectAnimator.ofInt(this, "textAlpha", 0, 255);
        alphaAnimator.setDuration(2000);
        //alphaAnimator.addUpdateListener(this);
        alphaAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        alphaAnimator.start();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        /*int specMode = MeasureSpec.getMode(heightMeasureSpec);
        int specSize = MeasureSpec.getSize(heightMeasureSpec);

        int height = 0;
        if (specMode == MeasureSpec.EXACTLY) {
            height = specSize;
        } else {
            height = (int) mLastY;
        }
        System.out.println("onMeasure mLastY=" + mLastY);

        setMeasuredDimension(MeasureSpec.getMode(widthMeasureSpec), height);*/
    }

    public float getPhaseX() {
        return phaseX;
    }

    public void setPhaseX(float phaseX) {
        this.phaseX = phaseX;
    }

    public int getTextAlpha() {
        return textAlpha;
    }

    public void setTextAlpha(int textAlpha) {
        this.textAlpha = textAlpha;
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        calculate();
        postInvalidate();
    }


    private void drawText1(Canvas canvas) {
        Paint.FontMetrics fontMetrics = mPaintText.getFontMetrics();

        float fontTotalHeight = fontMetrics.bottom - fontMetrics.top;

        float offY = fontTotalHeight / 2 - fontMetrics.bottom;
        //System.out.println("offY=" +offY);
        //float newY = baseY + offY;

        mPaintText.setAlpha(textAlpha);
        canvas.drawText("初期沟通(10%)", maxWidth + textStartOffsetX, mLastY - mPath1Height / 2 + offY, mPaintText);
    }

    private void drawText2(Canvas canvas) {
        Paint.FontMetrics fontMetrics = mPaintText.getFontMetrics();

        float fontTotalHeight = fontMetrics.bottom - fontMetrics.top;

        float offY = fontTotalHeight / 2 - fontMetrics.bottom;
        //float newY = baseY + offY;

        canvas.drawText("立项跟踪(10%)", maxWidth + textStartOffsetX, mLastY - mPath2Height / 2 + offY, mPaintText);
    }

    private void drawLine3(Canvas canvas) {
        canvas.drawLine(mPath3LineStartX + lineStartOffsetX, mPath3LineStartY, maxWidth, mPath3LineStartY, mPaintLine);
    }

    private void drawText3(Canvas canvas) {
        Paint.FontMetrics fontMetrics = mPaintText.getFontMetrics();

        float fontTotalHeight = fontMetrics.bottom - fontMetrics.top;

        float offY = fontTotalHeight / 2 - fontMetrics.bottom;
        //float newY = baseY + offY;

        canvas.drawText("呈报方案(10%)", maxWidth + textStartOffsetX, mLastY - mPath3Height / 2 + offY, mPaintText);
    }

    private void drawLine4(Canvas canvas) {
        canvas.drawLine(mPath4LineStartX + lineStartOffsetX, mPath4LineStartY, maxWidth, mPath4LineStartY, mPaintLine);
    }

    private void drawText4(Canvas canvas) {
        Paint.FontMetrics fontMetrics = mPaintText.getFontMetrics();

        float fontTotalHeight = fontMetrics.bottom - fontMetrics.top;

        float offY = fontTotalHeight / 2 - fontMetrics.bottom;
        //float newY = baseY + offY;

        canvas.drawText("商务谈判(10%)", maxWidth + textStartOffsetX, mLastY - mPath4Height / 2 + offY, mPaintText);
    }


    private void drawText9(Canvas canvas) {
        Paint.FontMetrics fontMetrics = mPaintText.getFontMetrics();

        float fontTotalHeight = fontMetrics.bottom - fontMetrics.top;

        float offY = fontTotalHeight / 2 - fontMetrics.bottom;
        //float newY = baseY + offY;

        canvas.drawText("销售漏斗", mLastX + mLastWidth - mPath9AngleWidth + 3 * textStartOffsetX, mLastY - mPath9Height / 2 + offY, mPaintText);
    }


    private void drawLine5(Canvas canvas) {
        canvas.drawLine(mPath5LineStartX + lineStartOffsetX, mPath5LineStartY, maxWidth, mPath5LineStartY, mPaintLine);
    }

    private void drawText5(Canvas canvas) {
        Paint.FontMetrics fontMetrics = mPaintText.getFontMetrics();

        float fontTotalHeight = fontMetrics.bottom - fontMetrics.top;

        float offY = fontTotalHeight / 2 - fontMetrics.bottom;
        //float newY = baseY + offY;

        canvas.drawText("赢单(10%)", maxWidth + textStartOffsetX, mLastY - mPath5Height / 2 + offY, mPaintText);
    }

}

相关文章
|
2月前
|
Android开发 开发者
安卓应用开发中的自定义视图
【9月更文挑战第37天】在安卓开发的海洋中,自定义视图犹如一座座小岛,等待着勇敢的探索者去发现其独特之处。本文将带领你踏上这段旅程,从浅滩走向深海,逐步揭开自定义视图的神秘面纱。
41 3
|
2月前
|
数据可视化 Android开发 开发者
安卓应用开发中的自定义View组件
【10月更文挑战第5天】在安卓应用开发中,自定义View组件是提升用户交互体验的利器。本篇将深入探讨如何从零开始创建自定义View,包括设计理念、实现步骤以及性能优化技巧,帮助开发者打造流畅且富有创意的用户界面。
86 0
|
4月前
|
存储 Shell Android开发
基于Android P,自定义Android开机动画的方法
本文详细介绍了基于Android P系统自定义开机动画的步骤,包括动画文件结构、脚本编写、ZIP打包方法以及如何将自定义动画集成到AOSP源码中。
79 2
基于Android P,自定义Android开机动画的方法
|
4月前
|
供应链 物联网 区块链
未来触手可及:探索新兴技术的趋势与应用安卓开发中的自定义视图:从基础到进阶
【8月更文挑战第30天】随着科技的飞速发展,新兴技术如区块链、物联网和虚拟现实正在重塑我们的世界。本文将深入探讨这些技术的发展趋势和应用场景,带你领略未来的可能性。
|
4月前
|
测试技术 Android开发 Python
探索软件测试的艺术:从基础到高级安卓应用开发中的自定义视图
【8月更文挑战第29天】在软件开发的世界中,测试是不可或缺的一环。它如同艺术一般,需要精细的技巧和深厚的知识。本文旨在通过浅显易懂的语言,引领读者从软件测试的基础出发,逐步深入到更复杂的测试策略和工具的使用,最终达到能够独立进行高效测试的水平。我们将一起探索如何通过不同的测试方法来确保软件的质量和性能,就像艺术家通过不同的色彩和笔触来完成一幅画作一样。
|
21天前
|
搜索推荐 前端开发 Android开发
安卓应用开发中的自定义视图实现
【10月更文挑战第30天】在安卓开发的海洋中,自定义视图是那抹不可或缺的亮色,它为应用界面的个性化和交互体验的提升提供了无限可能。本文将深入探讨如何在安卓平台创建自定义视图,并展示如何通过代码实现这一过程。我们将从基础出发,逐步引导你理解自定义视图的核心概念,然后通过一个实际的代码示例,详细讲解如何将理论应用于实践,最终实现一个美观且具有良好用户体验的自定义控件。无论你是想提高自己的开发技能,还是仅仅出于对安卓开发的兴趣,这篇文章都将为你提供价值。
|
23天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
30 5
|
2月前
|
XML 前端开发 Java
安卓应用开发中的自定义View组件
【10月更文挑战第5天】自定义View是安卓应用开发的一块基石,它为开发者提供了无限的可能。通过掌握其原理和实现方法,可以创造出既美观又实用的用户界面。本文将引导你了解自定义View的创建过程,包括绘制技巧、事件处理以及性能优化等关键步骤。
|
3月前
|
Android开发 开发者
安卓开发中的自定义视图:从入门到精通
【9月更文挑战第19天】在安卓开发的广阔天地中,自定义视图是一块充满魔力的土地。它不仅仅是代码的堆砌,更是艺术与科技的完美结合。通过掌握自定义视图,开发者能够打破常规,创造出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战应用,一步步展示如何用代码绘出心中的蓝图。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往创意和效率的大门。让我们一起探索自定义视图的秘密,将你的应用打造成一件艺术品吧!
62 10
|
3月前
|
XML 编解码 Android开发
安卓开发中的自定义视图控件
【9月更文挑战第14天】在安卓开发中,自定义视图控件是一种高级技巧,它可以让开发者根据项目需求创建出独特的用户界面元素。本文将通过一个简单示例,引导你了解如何在安卓项目中实现自定义视图控件,包括创建自定义控件类、处理绘制逻辑以及响应用户交互。无论你是初学者还是有经验的开发者,这篇文章都会为你提供有价值的见解和技巧。
49 3