Создание формы кольца в коде Android - PullRequest
1 голос
/ 19 февраля 2012

У меня есть следующая форма XML:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:a="http://schemas.android.com/apk/res/android"
       a:shape="ring"
       a:innerRadiusRatio="3"
       a:thicknessRatio="8"
       a:useLevel="false">

    <!-- some other stuff goes here -->

    </gradient>
</shape>

Я хотел бы использовать вместо этого код для создания этой фигуры, так как некоторые вещи нужно вычислять на лету, прежде чем я это сделаю, поэтому статическая предопределенная компоновка не обрезает ее.

Я новичок в Android и не могу понять, как XML преобразуется в код, и нет класса RingShape, унаследованного от Shape.

В дополнение к ответу только на этот вопрос, если где-то есть руководство, в котором подробно описывается связь между XML и кодом Java и то, как XML обрабатывается для того, чтобы оказаться на экране, я также был бы признателен за ссылку. Спасибо.

Ответы [ 5 ]

11 голосов
/ 25 февраля 2012

Рувим уже указал на большинство самых полезных наблюдений, поэтому я просто сосредоточусь на реализации этой истории.Существует множество подходов с использованием рефлексии, которые, вероятно, дадут вам то, что вы ищете.

Первый - это (ab) использование частного конструктора GradientDrawable, который принимает ссылку GradientState.К сожалению, последний является последним подклассом с видимостью пакета, поэтому вы не можете легко получить к нему доступ.Для того, чтобы использовать его, вам необходимо углубиться в использование отражения или подражать его функциональности в ваш собственный код.

Второй подход - использовать отражение, чтобы получить закрытую переменную-член mGradientState, которая, к счастью, имеет геттер вформа getConstantState().Это даст вам ConstantState, который во время выполнения на самом деле является GradientState, и, следовательно, мы можем использовать отражение для доступа к его членам и изменять их во время выполнения.

Для поддержки приведенных выше утверждений, вот несколько базовая реализациясоздайте рисованный объект в форме кольца из кода:

RingDrawable.java

public class RingDrawable extends GradientDrawable {

    private Class<?> mGradientState;

    public RingDrawable() {
        this(Orientation.TOP_BOTTOM, null);
    }

    public RingDrawable(int innerRadius, int thickness, float innerRadiusRatio, float thicknessRatio) {
        this(Orientation.TOP_BOTTOM, null, innerRadius, thickness, innerRadiusRatio, thicknessRatio);
    }

    public RingDrawable(GradientDrawable.Orientation orientation, int[] colors) {
        super(orientation, colors);
        setShape(RING);
    }

    public RingDrawable(GradientDrawable.Orientation orientation, int[] colors, int innerRadius, int thickness, float innerRadiusRatio, float thicknessRatio) {
        this(orientation, colors);
        try {
            setInnerRadius(innerRadius);
            setThickness(thickness);
            setInnerRadiusRatio(innerRadiusRatio);
            setThicknessRatio(thicknessRatio);
        } catch (Exception e) {
            // fail silently - change to your own liking
            e.printStackTrace();
        }
    }

    public void setInnerRadius(int radius) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
        if (mGradientState == null) mGradientState = resolveGradientState();
        Field innerRadius = resolveField(mGradientState, "mInnerRadius");
        innerRadius.setInt(getConstantState(), radius);
    }       

    public void setThickness(int thicknessValue) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
        if (mGradientState == null) mGradientState = resolveGradientState();
        Field thickness = resolveField(mGradientState, "mThickness");
        thickness.setInt(getConstantState(), thicknessValue);
    }

    public void setInnerRadiusRatio(float ratio) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
        if (mGradientState == null) mGradientState = resolveGradientState();
        Field innerRadiusRatio = resolveField(mGradientState, "mInnerRadiusRatio");
        innerRadiusRatio.setFloat(getConstantState(), ratio);
    }

    public void setThicknessRatio(float ratio) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
        if (mGradientState == null) mGradientState = resolveGradientState();
        Field thicknessRatio = resolveField(mGradientState, "mThicknessRatio");
        thicknessRatio.setFloat(getConstantState(), ratio);
    }

    private Class<?> resolveGradientState() {
        Class<?>[] classes = GradientDrawable.class.getDeclaredClasses();
        for (Class<?> singleClass : classes) {
            if (singleClass.getSimpleName().equals("GradientState")) return singleClass;
        }
        throw new RuntimeException("GradientState could not be found in current GradientDrawable implementation");
    }

    private Field resolveField(Class<?> source, String fieldName) throws SecurityException, NoSuchFieldException {
        Field field = source.getDeclaredField(fieldName);
        field.setAccessible(true);
        return field;
    }

}

Выше можно использовать следующую процедуру для создания RingDrawable из кода и отображения его в видестандартный ImageView.

ImageView target = (ImageView) findViewById(R.id.imageview);
RingDrawable ring = new RingDrawable(10, 20, 0, 0);
ring.setColor(Color.BLUE);
target.setImageDrawable(ring);

Это покажет простое, непрозрачное синее кольцо в ImageView (внутренний радиус 10 единиц, толщина 20 единиц).Вам нужно убедиться, что вы не установили ширину и высоту ImageView на wrap_content, если только вы не добавите ring.setSize(width, height) к коду выше, чтобы он появился.

Надеюсь, это поможет вам в любомспособ.

10 голосов
/ 22 февраля 2012

Кольцо и другие формы: GradientDrawables.

Если вы посмотрите на исходный код для GradientDrawable, вы увидите, что он выглядит как определенные свойства (как innerRadius) можно только быть определено через XML ... они не доступны через методы доступа.Соответствующее состояние также бесполезно приватно для класса, поэтому подклассы также не помогают.

3 голосов
/ 18 июля 2016

Вы можете сделать что-то вроде этого:

private ShapeDrawable newRingShapeDrawable(int color) {
        ShapeDrawable drawable = new ShapeDrawable(new OvalShape());
        drawable.getPaint().setColor(color);
        drawable.getPaint().setStrokeWidth(2);
        drawable.getPaint().setStyle(Paint.Style.STROKE);
        return drawable;
}
2 голосов
/ 22 августа 2014

Это можно сделать из кода:

int r = dipToPixels(DEFAULT_CORNER_RADIUS_DIP); // this can be used to make it circle
float[] outerR = new float[]{r, r, r, r, r, r, r, r};
int border = dipToPixels(2); // border of circle
RectF rect = new RectF(border, border, border, border);
RoundRectShape rr = new RoundRectShape(outerR, rect, outerR);// must checkout this constructor
ShapeDrawable drawable = new ShapeDrawable(rr);
drawable.getPaint().setColor(badgeColor);// change color of border
// use drawble now
0 голосов
/ 30 мая 2017

Для меня это работает следующим образом: (также для версии Android> леденец)

    ImageView target = (ImageView) findViewById(R.id.imageview);

    GradientDrawable shapeRing = new GradientDrawable();
    shapeRing.setShape(GradientDrawable.OVAL);
    shapeRing.setColor(centerColor); // transparent
    shapeRing.setStroke(stroke, strokeColor);
    shapeRing.setSize(width, width);

    target.setImageDrawable(ring);
...