在视图的背景中绘制一个半圆形。

19

我正在尝试创建一个背景为半圆的TextView。我使用ShapeDrawable创建了一个椭圆形。我尝试使用ScaleDrawable将椭圆形的垂直大小加倍并裁剪来创建半圆。但是,ScaleDrawable没有效果。为什么?在View的背景中绘制半圆的最佳方式是什么?

res/layout/activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <TextView
        android:id="@+id/main_view"
        android:background="@drawable/semicircle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:gravity="center_horizontal"
    />
    </RelativeLayout>

res/drawable/semicircle.xml

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/circle"
    android:scaleGravity="top|clip_vertical"
    android:scaleHeight="200%"
    android:scaleWidth="100%" >
</scale>

res/drawable/circle.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval"
    <solid
        android:color="#444" />
</shape>

src/.../MainActivity.java

//...
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        findViewById(R.id.main_view).getBackground().setLevel(10000);
    }
//...

1
我认为这个问题的最佳答案在这里:https://dev59.com/KVkS5IYBdhLWcg3wUVIF#45406439 - Naeem Baghi
5个回答

29

要剪裁椭圆形状,只需像这样将其嵌入到ClipDrawable中:

res/drawable/semicircle.xml

<?xml version="1.0" encoding="utf-8"?>
<clip
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:clipOrientation="vertical"
    android:gravity="bottom">
    <shape android:shape="oval">
        <solid android:color="#444"/>
    </shape>
</clip>

ClipDrawable 的常见用途是创建自定义进度条。当其 "level" 属性在 [0; 10000] 范围内增加时(0=隐藏,10000=完全显示),它会剪切其内容的一部分并逐步显示。

  • clipOrientation 是剪裁进度的方向。
  • gravity 是剪裁进度的起始边缘/侧面。

要获得半圆形,请将此 ClipDrawable 设置为您的视图背景,并以编程方式调整其 "level":

//...
findViewById(R.id.my_view).getBackground().setLevel(5000)
//...

适用于所有安卓版本("自API level 1起添加")且不需要自定义视图。

;-)


可以在剪辑周围也加一个边框吗? - Stefan Sprenger
1
可以在子形状中添加描边: <stroke android:color="@android:color/holo_green_light" android:width="4dp"/>(在椭圆形的<shape>标签内) 但这可能不是您想要的,因为此边框也会被裁剪。 - John
谢谢John。所以没有可能用剪辑形状来限制父元素的边界。 - Stefan Sprenger

23
你可以实现自己的Drawable,但无法从XML中填充。你需要使用View.setBackgroundDrawable()从代码中设置drawable。 请查看我的示例实现,以绘制半圆形状。
public class SemiCircleDrawable extends Drawable {

    private Paint paint;
    private RectF rectF;
    private int color;
    private Direction angle;

    public enum Direction
    {
        LEFT,
        RIGHT,
        TOP,
        BOTTOM
    }

    public SemiCircleDrawable() {
        this(Color.BLUE, Direction.LEFT);
    }

    public SemiCircleDrawable(int color, Direction angle) {
        this.color = color;
        this.angle = angle;

        paint = new Paint();
        paint.setColor(color);
        paint.setStyle(Style.FILL);
        paint.setAntiAlias(true);

        rectF = new RectF();
    }

    public int getColor() {
        return color;
    }

    /**
     * A 32bit color not a color resources.
     * @param color
     */
    public void setColor(int color) {
        this.color = color;
        paint.setColor(color);
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.save();

        Rect bounds = getBounds();

        if(angle == Direction.LEFT || angle == Direction.RIGHT)
        {
            canvas.scale(2, 1);
            if(angle == Direction.RIGHT)
            {
                canvas.translate(-(bounds.right / 2), 0);
            }
        }
        else
        {
            canvas.scale(1, 2);
            if(angle == Direction.BOTTOM)
            {
                canvas.translate(0, -(bounds.bottom / 2));
            }
        }


        rectF.set(bounds);

        if(angle == Direction.LEFT)
            canvas.drawArc(rectF, 90, 180, true, paint);
        else if(angle == Direction.TOP)
            canvas.drawArc(rectF, -180, 180, true, paint);
        else if(angle == Direction.RIGHT)
            canvas.drawArc(rectF, 270, 180, true, paint);
        else if(angle == Direction.BOTTOM)
            canvas.drawArc(rectF, 0, 180, true, paint);

        canvas.restore()
    }

    @Override
    public void setAlpha(int alpha) {
        // Has no effect
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
        // Has no effect
    }

    @Override
    public int getOpacity() {
        // Not Implemented
        return PixelFormat.UNKNOWN;
    }

}

1
谢谢Vivek!我会采用这种方法。你知道为什么不能使用XML可绘制对象吗? - joshuanapoli
1
检查此讨论 https://groups.google.com/forum/?fromgroups=#!topic/android-developers/glpdi0AdMzI 它有一个解决方案,但是它有一些hacky的部分。 - Vivek Khandelwal
谢谢,Vivek,你知道如何使用GradientDrawable吗? - Real KEK

2

我创建了这个类,希望它能对您有所帮助。所有变量都是用西班牙语表示的,但非常简单。

SemiCirculo构造函数使用rgb作为半圆的参数和分辨率(您半圆的三角形数量)。

CalcularPuntosPorcentaje方法使用圆心、起始角度、角度宽度和半径作为参数。

ImprimeCirculo方法使用画布作为参数,用于在已经使用上述方法计算出半圆点后绘制半圆。

CalcularPuntosPorcentaje方法类似于CalcularPuntosPorcentaje,但它使用0到100的百分比代替起始角度和宽度角度参数。

最后,SetOffset和SetColor用于更改角度的默认起始点或参考以及半圆的颜色。

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;


public class SemiCirculo {

    private Path circulo;
    private Paint color;
    private float px, py, radio, anguloI, anchoa,offset;
    private int r, g, b;
    private int resolucion;
    private float puntox[],puntoy[];


    public SemiCirculo(int r1, int g1, int b1, int resolucion1) {

        this.offset = 0;
        this.color = new Paint();
        this.r = r1;
        this.g = g1;
        this.b = b1;
        this.color.setColor(Color.rgb(r, g, b));
        color.setAntiAlias(true);
        circulo = new Path();
        this.resolucion = resolucion1;
        this.puntox = new float[this.resolucion];
        this.puntoy = new float[this.resolucion];
        this.anguloI = 0;
        this.anchoa = 1;

    }

    public void SetOffset(float off) {
        this.offset = off;
    }

    public void SetColor(int r1,int g1, int b1){        
        this.r=r1;
        this.g=g1;
        this.b=b1;
        this.color.setColor(Color.rgb(r, g, b));
    }

    public void CalcularPuntosPorcentaje(float px1, float py1,
            float porcentaje, float radio1) {
        this.anguloI = 0 + this.offset;
        this.px = px1;
        this.py = py1;
        this.radio = radio1;
        this.anguloI = 0;
        this.anchoa = porcentaje / 100 * 360;

        this.CalcularPuntos(px, py, anguloI, anchoa, radio);
    }

    public void CalcularPuntos(float px1, float py1, float anguloI1,
            float anchoangulo, float radio1) {
        this.anguloI = anguloI1 + this.offset;

        this.anchoa = anchoangulo;
        this.px = px1;
        this.py = py1;
        this.radio = radio1 ;

        float angulo = 360 - this.anguloI - this.anchoa;

        for (int i = 0; i < resolucion; i++) {
            this.puntox[i] = this.px - (float) Math.sin(Math.toRadians(angulo))
                    * this.radio;
            this.puntoy[i] = this.py - (float) Math.cos(Math.toRadians(angulo))
                    * this.radio;
            angulo = (360 - this.anguloI - this.anchoa)
                    + ((this.anchoa / (float) (this.resolucion)) * (i + 2));
        }

        this.circulo.reset();

        this.circulo.moveTo(this.px, this.py);
        for (int i = 0; i < resolucion; i++) {
            this.circulo.lineTo(this.puntox[i], this.puntoy[i]);
        }

    }

    public void ImprimeCirculo(Canvas canvas) {

        canvas.drawPath(this.circulo, this.color);

    }

}

2
Java中的方法名不应该大写。而且它是用西班牙语编写的,这使得这里的人们很难理解。 - afollestad

1

相反,您可以使用图像作为背景...

1>使用绘图工具设计任何图像,并将其保存为任何支持的格式,例如.jpg或.png,即这将是您想要的带有半圆形的图像。

2>将图像保存在res/drawable文件夹中

3>使用android:background="@drawable/yourimage.jpg"将您的文本视图背景设置为该图像

希望这能帮到您...


0

您可以使用矢量图形。我已经在Affinity设计中创建了它。

enter image description here enter image description here

将其放入drawable文件夹中ic_cemicircle.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="48dp"
    android:height="48dp"
    android:viewportWidth="48"
    android:viewportHeight="48">

    <path
        android:fillColor="#FF000000"
        android:pathData="M-0.0005,0.0004C13.2462,0.0004 23.9991,10.7533 23.9991,24C23.9991,37.2467 13.2462,47.9996 -0.0005,47.9996L-0.0005,0.0004Z" />

</vector>

同时在布局的XML文件中添加下列属性

android:adjustViewBounds="true"
android:scaleType="fitXY"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接