Android的形状三角形边界角形、边界、形状、Android

2023-09-07 00:31:49 作者:深情不如久伴。

我创建了三角形在下面code:

I've created triangle shape as in code below:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
        <rotate
            android:fromDegrees="45"
            android:toDegrees="45"
            android:pivotX="13%"
            android:pivotY="-40%" >
            <shape
                android:shape="rectangle" >
                <stroke android:color="#000000" android:width="1dp"/>
                <solid
                    android:color="#000000" />
            </shape>
        </rotate>
    </item>
</layer-list>

我怎样才能从形状与众不同的三角形的边框颜色?如果我改变笔触颜色还挺工作,以及我有不同颜色的双方,没有第三境。我应该如何纠正呢?

How can I make border color of the triangle different from the rest of shape? If I change stroke color it kinda works, well I have two sides with different color, without third border. How should I correct it?

推荐答案

现在它没有带我一段时间,要尽量做到一个很好的例子给你用帆布玩。继code仍可线性/径向渐变的提高,也可以让它如果你想更加个性化。也许我会做它在未来,但我今天做的。

Now it did take me some time to try to make a good example for you to play around with canvas. Following code can still be improved with linear/radial gradients, also you can make it more customizable if you wish. Maybe I will do it in the future, but I'm done for today.

首先添加到您的资源值。我用/values​​/attrs.xml如果您没有看到这个文件,创建它。

First add this to your resource values. I use /values/attrs.xml If you do not see this file, create it.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="Triangle">
        <attr name="triangleColor" format="color"/>
        <attr name="triangleStrokeColor" format="color" />
        <attr name="triangleStrokeWidth" format="dimension" />
    </declare-styleable>
</resources>

现在,你让你的自定义视图文件夹中创建pferably一个三角形类,$ P $。修复包名称,在研究类导入。

Now create a Triangle class, preferably in a folder where you keep your custom views. Fix package name and the R class import.

package si.kseneman.views;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;

import si.kseneman.mobile.R;


public class Triangle extends View {

    private int measuredWidth, measuredHeight;
    private float density;
    private Paint mTrianglePaint, mStrokePaint;
    private PointF a, b, c;
    private Path mTrianglePath;
    private float mStrokeWidth;

    // Default values
    private int mTriangleColor = 0xAA4CAF50; //ARGB int
    private int mStrokeColor = Color.BLACK; //ARGB int
    private float defaultPadding = 5; //dp

    public Triangle(Context context) {
        super(context);
        init(context, null, 0);
    }

    public Triangle(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

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

    private void init(Context context, AttributeSet attrs, int style) {

        Resources res = context.getResources();
        density = res.getDisplayMetrics().density;
        defaultPadding *= density;


        // Get the values from XML
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.Triangle, style, 0);

        int tmp;

        mTriangleColor = typedArray.getColor(R.styleable.Triangle_triangleColor, mTriangleColor);
        mStrokeColor = typedArray.getColor(R.styleable.Triangle_triangleStrokeColor, mStrokeColor);

        tmp = typedArray.getDimensionPixelSize(R.styleable.Triangle_triangleStrokeWidth, -1);
        mStrokeWidth = tmp != -1 ? tmp : 2 * density; // Use 2dp as a default value

        typedArray.recycle();

        a = new PointF();
        b = new PointF();
        c = new PointF();

        mTrianglePath = new Path();

        mTrianglePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTrianglePaint.setStyle(Paint.Style.FILL);
        mTrianglePaint.setColor(mTriangleColor);

        mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mStrokePaint.setStyle(Paint.Style.STROKE);
        mStrokePaint.setStrokeWidth(mStrokeWidth);
        mStrokePaint.setColor(mStrokeColor);

    }

    public void setTriangleColorResId(int resId) {
        // Example: setTriangleColorResId(R.color.blue);
        setTriangleColor(getContext().getResources().getColor(resId));
    }

    public void setTriangleColor(String color) {
        setTriangleColor(Color.parseColor(color));
    }

    public void setTriangleColor(int color) {
        mTriangleColor = color;
        mTrianglePaint.setColor(mTriangleColor);
        invalidate();
    }

    public void setStrokeColorResId(int resId) {
        // Example: setTriangleColorResId(R.color.blue);
        setStrokeColor(getContext().getResources().getColor(resId));
    }

    public void setStrokeColor(String color) {
        setTriangleColor(Color.parseColor(color));
    }

    public void setStrokeColor(int color) {
        mStrokeColor = color;
        mStrokePaint.setColor(mStrokeColor);
        invalidate();
    }

    public void setStrokeWidth(float strokeWidth) {
        if (strokeWidth < 0)
            throw new IllegalArgumentException("Stroke width cannot be < 0");
        //NOTE: input parameter is meant to be in pixels, you need to convert dp, sp or anything else
        // when calling this method
        mStrokeWidth = strokeWidth;
        mStrokePaint.setStrokeWidth(mStrokeWidth);
        invalidate();
    }

    public int getStrokeColor() {
        return mStrokeColor;
    }

    public float getStrokeWidth() {
        return mStrokeWidth;
    }

    public int getTriangleColor() {
        return mTriangleColor;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        measuredHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        measuredWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);

        setMeasuredDimension(measuredWidth, measuredHeight);
        //Log.d(TAG, "Height: " + measuredHeight + " Width: " + measuredWidth);
    }

    private float getPaddingOrDefault(int padding, float defaultValue) {
        return padding != 0 ? padding : defaultValue;
    }

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

        if (measuredHeight <= 0 || measuredWidth <= 0) {
            // Not much we can draw...  :/
            return;
        }

        // Define the points of the triangle... make this so that it suits your needs

        // Top point
        a.x = measuredWidth / 2f;
        a.y = getPaddingOrDefault(getPaddingTop(), defaultPadding);

        // Bottom left point
        b.x = getPaddingOrDefault(getPaddingLeft(), defaultPadding);
        b.y = measuredHeight - getPaddingOrDefault(getPaddingBottom(), defaultPadding);

        // Bottom right point
        c.x = measuredWidth - getPaddingOrDefault(getPaddingRight(), defaultPadding);
        c.y = b.y;

        // Clear the path from previous onDraw
        mTrianglePath.reset();

        // Make a path of triangle
        mTrianglePath.moveTo(a.x, a.y);
        mTrianglePath.lineTo(b.x, b.y);
        mTrianglePath.lineTo(c.x, c.y);
        mTrianglePath.lineTo(a.x, a.y);
        mTrianglePath.close();

        // Draw the triangle and the stroke
        canvas.drawPath(mTrianglePath, mTrianglePaint);
        canvas.drawPath(mTrianglePath, mStrokePaint);

    }
}

和现在的箱子一个新的类(这个不neeeded,但我这样做,你可能会得到的想法如何使用帆布)

And now crate a new class (this is not neeeded, but I did so that you might get the idea how to use canvas)

package si.kseneman.views;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.View;

    public class TriangleAndArc extends View {

        private int measuredWidth, measuredHeight;
        private float density;
        private float a1Degrees;
        private float textHeight;
        private double a1Radians;

        private Paint mLinePaint, mPointPaint, mTextPaint, mCirclePaint;
        private DashPathEffect dashedEffect;
        private PointF p1, p2, m, c;


        public TriangleAndArc(Context context) {
            super(context);
            init(context);
        }

        public TriangleAndArc(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }

        public TriangleAndArc(Context context, AttributeSet attrs, int style) {
            super(context, attrs, style);
            init(context);
        }

        private void init(Context ctx) {

            p1 = new PointF();
            p2 = new PointF();
            c = new PointF();
            m = new PointF();

            Resources res = ctx.getResources();
            density = res.getDisplayMetrics().density;

            a1Degrees = 36.0f;
            a1Radians = Math.toRadians(a1Degrees);

            dashedEffect = new DashPathEffect(new float[]{5, 5}, 1.0f);
            mLinePaint = new Paint();
            mLinePaint.setAntiAlias(true);
            mLinePaint.setStyle(Paint.Style.STROKE);
            mLinePaint.setStrokeJoin(Paint.Join.ROUND);
            mLinePaint.setStrokeWidth(2 * density);
            mLinePaint.setColor(Color.BLACK);
            //mPaint.setPathEffect(dashedEffect);

            mPointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPointPaint.setStyle(Paint.Style.FILL);
            mPointPaint.setColor(Color.RED);

            mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mTextPaint.setStyle(Paint.Style.FILL);
            mTextPaint.setTextSize(10 * density);
            mTextPaint.setTypeface(Typeface.create("Roboto-Thin", Typeface.BOLD));
            mTextPaint.setColor(Color.RED);

            if(!isInEditMode()) {
                // Shadow layer is not supported in preview mode and android studio makes an ugly warning about it!
                mTextPaint.setShadowLayer(0.1f, 0, 1, Color.GRAY);
            }

            textHeight = Math.abs(mTextPaint.descent() + mTextPaint.ascent());

            mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mCirclePaint.setColor(Color.BLUE);
            mCirclePaint.setStyle(Paint.Style.STROKE);
            mCirclePaint.setStrokeWidth(density);
        }

        // Square a number
        private double sq(double a) {
            return a * a;
        }

        private void drawIncenterAndExcenter(PointF A, PointF B, PointF C, Canvas canvas) {
            double a = Math.sqrt((B.x - C.x) * (B.x - C.x) + (B.y - C.y) * (B.y - C.y));
            double b = Math.sqrt((A.x - C.x) * (A.x - C.x) + (A.y - C.y) * (A.y - C.y));
            double c = Math.sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));

            double perimeter = a + b + c;
            double s = 0.5 * perimeter;
            double area = Math.sqrt(s * (s - a) * (s - b) * (s - c));

            // Inscribed Circle
            double iRadius = area / s;
            PointF iCenter = new PointF();
            iCenter.x = (float) ((a * A.x + b * B.x + c * C.x) / (perimeter));
            iCenter.y = (float) ((a * A.y + b * B.y + c * C.y) / (perimeter));

            // Circumscribed Circle
            PointF cCenter = new PointF();
            double cRadius = (a * b * c) / (4.0 * area);
            double D = 2 * (A.x * (B.y - C.y) + B.x * (C.y - A.y) + C.x * (A.y - B.y));
            double sqA = sq(A.x) + sq(A.y);
            double sqB = sq(B.x) + sq(B.y);
            double sqC = sq(C.x) + sq(C.y);
            cCenter.x = (float) ((sqA * (B.y - C.y) + sqB * (C.y - A.y) + sqC * (A.y - B.y)) / D);
            cCenter.y = (float) ((sqA * (C.x - B.x) + sqB * (A.x - C.x) + sqC * (B.x - A.x)) / D);

            // Draw
            canvas.drawCircle(iCenter.x, iCenter.y, density * 5, mPointPaint);
            canvas.drawCircle(iCenter.x, iCenter.y, (float) iRadius, mCirclePaint);
            canvas.drawText("I", iCenter.x - mTextPaint.measureText("I") * 0.5f, iCenter.y - textHeight - 2 * density, mTextPaint);


            canvas.drawCircle(cCenter.x, cCenter.y, density * 5, mPointPaint);
            canvas.drawCircle(cCenter.x, cCenter.y, (float) cRadius, mCirclePaint);
            canvas.drawText("E", cCenter.x - mTextPaint.measureText("E") * 0.5f, cCenter.y - textHeight - 2 * density, mTextPaint);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            measuredHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
            measuredWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);

            setMeasuredDimension(measuredWidth, measuredHeight);
            //Log.d(TAG, "Height: " + measuredHeight + " Width: " + measuredWidth);
        }

        @Override
        protected void onDraw(Canvas canvas) {

            if (measuredHeight <= 0 || measuredWidth <= 0) {
                // Not much we can draw...  :/
                return;
            }

            // Orientation independent drawing
            int width = (measuredWidth > measuredHeight) ? measuredWidth : measuredHeight;
            int height = (width == measuredWidth) ? measuredHeight : measuredWidth;

            // Define points
            p1.x = width * 0.7f;
            p1.y = height * 0.2f - 20;

            p2.x = width * 0.85f;
            p2.y = 2 * height / 3.0f - 20;

            float dx = p2.x - p1.x;
            float dy = p2.y - p1.y;

            // l1 is half the length of the line from p1 to p2
            double l = Math.sqrt(dx * dx + dy * dy);
            double l1 = l / 2.0;

            // Center of the circle
            double h = l1 / (Math.tan(a1Radians / 2.0));

            // Radius of the circle
            double r = l1 / (Math.sin(a1Radians / 2.0));

            // a2 is the angle at which L intersects the x axis
            double a2 = Math.atan2(dy, dx);

            // a3 is the angle at which H intersects the x axis
            double a3 = (Math.PI / 2.0) - a2;

            // m is the midpoint of the line from e1 to e2
            m.x = (p1.x + p2.x) / 2.0f;
            m.y = (p1.y + p2.y) / 2.0f;

            // c is the the center of the circle
            c.x = (float) (m.x - (h * Math.cos(a3)));
            c.y = (float) (m.y + (h * Math.sin(a3)));

            // rect is the square RectF that bounds the "oval"
            RectF oval = new RectF((float) (c.x - r), (float) (c.y - r), (float) (c.x + r), (float) (c.y + r));

            // a4 is the starting sweep angle
            double rawA4 = Math.atan2(p1.y - c.y, p1.x - c.x);
            float a4 = (float) Math.toDegrees(rawA4);

            // Draw lines
            canvas.drawLine(p1.x, p1.y, p2.x, p2.y, mLinePaint);
            canvas.drawLine(c.x, c.y, p1.x, p1.y, mLinePaint);
            canvas.drawLine(c.x, c.y, p2.x, p2.y, mLinePaint);
            canvas.drawLine(c.x, c.y, m.x, m.y, mLinePaint);

            // Draw arc
            mLinePaint.setPathEffect(dashedEffect);
            canvas.drawArc(oval, a4, a1Degrees, false, mLinePaint);

            // Draw dots
            canvas.drawCircle(p1.x, p1.y, density * 5, mPointPaint);
            canvas.drawCircle(p2.x, p2.y, density * 5, mPointPaint);
            canvas.drawCircle(m.x, m.y, density * 5, mPointPaint);
            canvas.drawCircle(c.x, c.y, density * 5, mPointPaint);

            // Draw text
            float halfOfTextHeight = textHeight * 0.5f; // We need an offset of a half
            canvas.drawText("p1", p1.x + density * 7, p1.y + halfOfTextHeight, mTextPaint);
            canvas.drawText("p2", p2.x + density * 7, p2.y + halfOfTextHeight, mTextPaint);
            canvas.drawText("m", m.x + density * 7, m.y + halfOfTextHeight, mTextPaint);
            canvas.drawText("c", c.x - mTextPaint.measureText("c") - density * 7, c.y + halfOfTextHeight, mTextPaint);

            drawIncenterAndExcenter(p1, p2, c, canvas);

        }
    }

现在终于布局称为canvas_demo.xml

And now finally the layout called canvas_demo.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:triangle="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="2">

    <si.kseneman.views.Triangle
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:padding="10dp"
        android:rotation="0"
        triangle:triangleStrokeColor="@android:color/black"
        triangle:triangleColor="#FF33B5E5"
        triangle:triangleStrokeWidth="3dp"/>

    <si.kseneman.views.TriangleAndArc
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1"/>

</LinearLayout>

最后的结果会是这个样子:

The final result will look something like this:

您可以使用 Android的旋转:旋转属性,例如,通过使用180将是这个样子

You can rotate it by using android:rotation attribute, for example by using 180 it would look something like this

当然,三角形可以是尺寸小,它是可伸缩的反正。你应该设置它的点a,b,c如果这种设置不适合你。

The triangle can of course be smaller in size, it is scalable anyway. You should set it's points a,b,c if this setup doesn't suit you.

注:我没有做的任何参数检查,因此,例如,如果你设定strokeWidth值比视图本身更大,你不会得到任何错误。这是你的responisibilty要小心:)

编码快乐!

 
精彩推荐
图片推荐