开发者

how to rotate text using canvas in Android

i was draw a pie chart using canvas in android and using the below code i draw a text on each slice of that pie chart (draw arc on path), now i want to draw the text length wise i.e. from center to end of the each slice,so how to rotate the arc using start and sweep angle.

p.addArc(mEventsRect, fStartAngle, fSweepAngle);
mBgPaints.setColor(iTextColor);
canvas.drawTextOnPath(sTextValue,开发者_如何学Go p, fHOffSet, fVOffSet, mBgPaints);

how to rotate text using canvas in Android


You can try this snippet: (from: 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);


A bit late to the party but I had to figure this one out and it's a bit simpler than what I found around. You'll already have the x and y for your text, use these to rotate the canvas

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

The negative sign negates the first rotation. You could swap it around to rotate in the opposite direction.

You could do this in a loop but the rotation cycle must be done each time either coordinate changes.


may be this will help you,, here 39.5 is radius,, this will perfectly show result on mdpi screen

 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);
        }


Here's how i finally did it after two days of search with help of this library https://github.com/Ken-Yang/AndroidPieChart And equations to center text done with help of my friends and alot of search

on MainActivity onCreate or oncreateView if you are using fragments:

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);
        }

    }

in your Layout:

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


This question is pretty old, but I figured I would write a general answer.Here I assume you want to draw your pie chart in the middle of the canvas and that you have your start and seep angles in an array.

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 );


it's 2023 there might be other answers out there but here is one that is sure to work

    //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);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜