如何在 Android 中使用画布旋转文本

问题描述 投票:0回答:6

我在 Android 中使用画布绘制饼图。我正在使用下面的代码。我在该饼图的每个切片上绘制文本(在路径上绘制弧线),现在我想按长度绘制文本,即从每个切片的中心到末端,那么如何使用起始角和扫描角旋转弧线。

p.addArc(mEventsRect, fStartAngle, fSweepAngle);
mBgPaints.setColor(iTextColor);
canvas.drawTextOnPath(sTextValue, p, fHOffSet, fVOffSet, mBgPaints);

enter image description here

android canvas path geometric-arc
6个回答
9
投票

你可以试试这个片段:(来自:http://www.helloandroid.com/tutorials/how-use-canvas-your-android-apps-part-2

int x = 75;
int y = 185;
paint.setColor(Color.GRAY);
paint.setTextSize(25);
String rotatedtext = "Rotated helloandroid :)";

//Draw bounding rect before rotating text:

Rect rect = new Rect();
paint.getTextBounds(rotatedtext, 0, rotatedtext.length(), rect);
canvas.translate(x, y);
paint.setStyle(Paint.Style.FILL);

canvas.drawText(rotatedtext , 0, 0, paint);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(rect, paint);

canvas.translate(-x, -y);


paint.setColor(Color.RED);
canvas.rotate(-45, x + rect.exactCenterX(),y + rect.exactCenterY());
paint.setStyle(Paint.Style.FILL);
canvas.drawText(rotatedtext, x, y, paint);

6
投票

你已经有了文本的 x 和 y,用它们来旋转画布

canvas.rotate(yourDegrees, x, y)
canvas.drawText(yourText, x, y, yourPaint)
canvas.rotate(-yourDegrees, x, y)

负号否定第一次旋转。你可以交换它以相反的方向旋转。

您可以循环执行此操作,但每次坐标更改时都必须执行旋转循环。


1
投票

这可能对你有帮助,, 这里 39.5 是半径,这将在 mdpi 屏幕上完美显示结果

 protected void onDraw(){
        canvas.save();
    PointF pf = PointOnCircle(35f, 45f, new PointF(39.5f, 39.5f));
            canvas.rotate(-45, pf.x, pf.y);
            canvas.drawText("67%", pf.x, pf.y, red);//23.5
            canvas.restore();
            canvas.save();
            PointF pfa = PointOnCircle(35f, 135f, new PointF(39.5f, 39.5f));
            canvas.rotate(45, pfa.x, pfa.y);
            canvas.drawText("33%", pfa.x, pfa.y, red);//23.5
            canvas.restore();
            canvas.save();
            pfa = PointOnCircle(27.5f, 225f, new PointF(39.5f, 39.5f));
            canvas.rotate(-45, pfa.x, pfa.y);
            canvas.drawText("45%", pfa.x, pfa.y, red);//23.5
            canvas.restore();
            canvas.save();
            pfa = PointOnCircle(27.5f, 315f, new PointF(39.5f, 39.5f));
            canvas.rotate(45, pfa.x, pfa.y);
            canvas.drawText("55%", pfa.x, pfa.y, red);//23.5

            canvas.restore();}

    protected static final PointF PointOnCircle(float radius, float angleInDegrees, PointF origin) {
            // Convert from degrees to radians via multiplication by PI/180        
            float x = (float) (radius * Math.cos(angleInDegrees * Math.PI / 180F)) + origin.x;
            float y = (float) (radius * Math.sin(angleInDegrees * Math.PI / 180F)) + origin.y;

            return new PointF(x, y);
        }

1
投票

在这个库的帮助下搜索了两天后,我终于做到了 https://github.com/Ken-Yang/AndroidPieChart 在我的朋友的帮助下和大量搜索中完成的方程式使文本居中

在 MainActivity onCreate 或 oncreateView 上,如果您使用的是片段:

PieChart pie = (PieChart) rootView.findViewById(R.id.pieChart);

            ArrayList<Float> alPercentage = new ArrayList<Float>();
            alPercentage.add(2.0f);
            alPercentage.add(8.0f);
            alPercentage.add(20.0f);
            alPercentage.add(10.0f);
            alPercentage.add(10.0f);
            alPercentage.add(10.0f);
            alPercentage.add(10.0f);
            alPercentage.add(10.0f);
            alPercentage.add(10.85f);
            alPercentage.add(9.15f);
            try {
                // setting data
                pie.setAdapter(alPercentage);

                // setting a listener
                pie.setOnSelectedListener(new OnSelectedLisenter() {
                    @Override
                    public void onSelected(int iSelectedIndex) {
                        Toast.makeText(getActivity(),
                                "Select index:" + iSelectedIndex,
                                Toast.LENGTH_SHORT).show();
                    }
                });
            } catch (Exception e) {
                if (e.getMessage().equals(PieChart.ERROR_NOT_EQUAL_TO_100)) {
                    Log.e("kenyang", "percentage is not equal to 100");
                }
            }



public class PieChart extends View {

    public interface OnSelectedLisenter {
        public abstract void onSelected(int iSelectedIndex);
    }

    private OnSelectedLisenter onSelectedListener = null;

    private static final String TAG = PieChart.class.getName();
    public static final String ERROR_NOT_EQUAL_TO_100 = "NOT_EQUAL_TO_100";
    private static final int DEGREE_360 = 360;
    private static String[] PIE_COLORS = null;
    private static int iColorListSize = 0;
    ArrayList<Float> array;
    private Paint paintPieFill;
    private Paint paintPieBorder;
    private Paint paintCenterCircle;
    private ArrayList<Float> alPercentage = new ArrayList<Float>();
    private int mCenterX = 320;
    private int mCenterY = 320;
    private int iDisplayWidth, iDisplayHeight;
    private int iSelectedIndex = -1;
    private int iCenterWidth = 0;
    private int iShift = 0;
    private int iMargin = 0; // margin to left and right, used for get Radius
    private int iDataSize = 0;
    private Canvas canvas1;
    private RectF r = null;
    private RectF centerCircle = null;
    private float fDensity = 0.0f;
    private float fStartAngle = 0.0f;
    private float fEndAngle = 0.0f;
    float fX;
    float fY;

    public PieChart(Context context, AttributeSet attrs) {
        super(context, attrs);
        PIE_COLORS = getResources().getStringArray(R.array.colors);
        iColorListSize = PIE_COLORS.length;
        array = new ArrayList<Float>();
        fnGetDisplayMetrics(context);
        iShift = (int) fnGetRealPxFromDp(30);
        iMargin = (int) fnGetRealPxFromDp(40);
        centerCircle = new RectF(200, 200, 440, 440);
        // used for paint circle
        paintPieFill = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintPieFill.setStyle(Paint.Style.FILL);
        // used for paint centerCircle
        paintCenterCircle = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintCenterCircle.setStyle(Paint.Style.FILL);
        paintCenterCircle.setColor(Color.WHITE);
        // used for paint border
        paintPieBorder = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintPieBorder.setStyle(Paint.Style.STROKE);
        paintPieBorder.setStrokeWidth(fnGetRealPxFromDp(3));
        paintPieBorder.setColor(Color.WHITE);
        Log.i(TAG, "PieChart init");

    }

    // set listener
    public void setOnSelectedListener(OnSelectedLisenter listener) {
        this.onSelectedListener = listener;
    }

    float temp = 0;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.i(TAG, "onDraw");
        float centerX = (r.left + r.right) / 2;
        float centerY = (r.top + r.bottom) / 2;
        float radius1 = (r.right - r.left) / 2;
        radius1 *= 0.5;
        float startX = mCenterX;
        float startY = mCenterY;
        float radius = mCenterX;
        float medianAngle = 0;
        Path path = new Path();

        for (int i = 0; i < iDataSize; i++) {

            // check whether the data size larger than color list size
            if (i >= iColorListSize) {
                paintPieFill.setColor(Color.parseColor(PIE_COLORS[i
                        % iColorListSize]));
            } else {
                paintPieFill.setColor(Color.parseColor(PIE_COLORS[i]));
            }

            fEndAngle = alPercentage.get(i);

            // convert percentage to angle
            fEndAngle = fEndAngle / 100 * DEGREE_360;

            // if the part of pie was selected then change the coordinate
            if (iSelectedIndex == i) {
                canvas.save(Canvas.MATRIX_SAVE_FLAG);
                float fAngle = fStartAngle + fEndAngle / 2;
                double dxRadius = Math.toRadians((fAngle + DEGREE_360)
                        % DEGREE_360);
                fY = (float) Math.sin(dxRadius);
                fX = (float) Math.cos(dxRadius);
                canvas.translate(fX * iShift, fY * iShift);
            }

            canvas.drawArc(r, fStartAngle, fEndAngle, true, paintPieFill);
            float angle = (float) ((fStartAngle + fEndAngle / 2) * Math.PI / 180);
            float stopX = (float) (startX + (radius/2) * Math.cos(angle));
            float stopY = (float) (startY + (radius/2) * Math.sin(angle));


            // if the part of pie was selected then draw a border
            if (iSelectedIndex == i) {
                canvas.drawArc(r, fStartAngle, fEndAngle, true, paintPieBorder);
                 canvas.drawLine(startX, startY, stopX, stopY, paintPieFill);
                canvas.restore();
            }
            fStartAngle = fStartAngle + fEndAngle;
        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // get screen size
        iDisplayWidth = MeasureSpec.getSize(widthMeasureSpec);
        iDisplayHeight = MeasureSpec.getSize(heightMeasureSpec);

        if (iDisplayWidth > iDisplayHeight) {
            iDisplayWidth = iDisplayHeight;
        }

        /*
         * determine the rectangle size
         */
        iCenterWidth = iDisplayWidth / 2;
        int iR = iCenterWidth - iMargin;
        if (r == null) {
            r = new RectF(iCenterWidth - iR, // top
                    iCenterWidth - iR, // left
                    iCenterWidth + iR, // right
                    iCenterWidth + iR); // bottom
        }
        if (centerCircle == null) {
            // centerCircle=new RectF(left, top, right, bottom);

        }
        setMeasuredDimension(iDisplayWidth, iDisplayWidth);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        // get degree of the touch point
        double dx = Math.atan2(event.getY() - iCenterWidth, event.getX()
                - iCenterWidth);
        float fDegree = (float) (dx / (2 * Math.PI) * DEGREE_360);
        fDegree = (fDegree + DEGREE_360) % DEGREE_360;

        // get the percent of the selected degree
        float fSelectedPercent = fDegree * 100 / DEGREE_360;

        // check which pie was selected
        float fTotalPercent = 0;
        for (int i = 0; i < iDataSize; i++) {
            fTotalPercent += alPercentage.get(i);
            if (fTotalPercent > fSelectedPercent) {
                iSelectedIndex = i;
                break;
            }
        }
        if (onSelectedListener != null) {
            onSelectedListener.onSelected(iSelectedIndex);
        }
        invalidate();
        return super.onTouchEvent(event);
    }

    private void fnGetDisplayMetrics(Context cxt) {
        final DisplayMetrics dm = cxt.getResources().getDisplayMetrics();
        fDensity = dm.density;
    }

    private float fnGetRealPxFromDp(float fDp) {
        return (fDensity != 1.0f) ? fDensity * fDp : fDp;
    }

    public void setAdapter(ArrayList<Float> alPercentage) throws Exception {
        this.alPercentage = alPercentage;
        iDataSize = alPercentage.size();
        float fSum = 0;
        for (int i = 0; i < iDataSize; i++) {
            fSum += alPercentage.get(i);
        }
        if (fSum != 100) {
            Log.e(TAG, ERROR_NOT_EQUAL_TO_100);
            iDataSize = 0;
            throw new Exception(ERROR_NOT_EQUAL_TO_100);
        }

    }

在你的布局中:

<com.example.piecharts.PieChart
        android:id="@+id/pieChart"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </com.example.piecharts.PieChart>

0
投票

This question is pretty old,但我想我会写一个一般的答案。这里我假设你想在画布中间画你的饼图,并且你有一个数组中的起始角和渗出角。

x = canvas.getWidth/2 //Horizontal center of canvas view
y = canvas.getHeight/2 //Vertical center of canvas view
canvas.rotate(fStartAngle[i]+ fSweepAngle[i]/2, x ,y ); //Rotates canvas to a line in the middle 
//of start and end of arc
canvas.translate(50f,0);//Moves the text a little out of the center of the circle (50f is arbitrary)
paintText.setStyle(Paint.Style.FILL);
canvas.drawText(rotatedtext, x, y, paintText);
//Undo the translations and rotations so that next arc can be drawn normally
canvas.translate(-50f,0); 
canvas.rotate(-(temp+ value_degree[i]/2), x ,y );

0
投票

现在是 2023 年,可能还有其他答案,但这是一个肯定有效的答案

    //the path where your text/paint will be drawn across
    Path path = new Path();

    path.addArc(mEventsRect, fStartAngle, fSweepAngle);//add this if you want your path to be drawn across the arc of your sector

    //if you are using a text get the width
    float textWidth = mTextPaint.measureText("text");

    //this is the y co-ordinate your text will start from
    int hOffset = 100;

    //this is the x co-ordinate your text will start from
    int vOffset = 100;

    //we will be using the matrix to rotate the bunds of our current path
    Matrix matrix = new Matrix();

    //we will use this to get the bounds of our current path
    RectF bounds = new RectF();

    path.computeBounds(bounds,true);

    //we are using the matrix to rotate the bound (with is the bound of the path) by 90 degrees
    matrix.setRotate(90,bounds.centerX(),bounds.centerY());

    the we transform the points in the path using the matrix
    path.transform(matrix);

    //you can now draw the text on the path
    canvas.drawTextOnPath("text", path, hOffset, vOffset , mBgPaints);
© www.soinside.com 2019 - 2024. All rights reserved.