如何在Android中使用canvas旋转文本

时间:2011-04-21 10:46:23

标签: android canvas path geometric-arc

我在android中使用画布绘制一个饼图,并使用下面的代码我在该饼图的每个切片上绘制文本(在路径上绘制弧),现在我想要明智地绘制文本,即从中心到结尾每个切片,如何使用开始和扫掠角度旋转圆弧。

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

enter image description here

4 个答案:

答案 0 :(得分:6)

您可以尝试以下代码段:(来自: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);

答案 1 :(得分: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);
        }

答案 2 :(得分:0)

这是我在这个图书馆的帮助下进行了两天的搜索之后我最终做到了这一点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>

答案 3 :(得分:0)

这个问题很老了,但我想我会写一个一般的答案。我假设你想在画布中间绘制你的饼图,并且你有一个数组的开始和渗透角度。

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