Creating a ring shape in Android code

I have the following XML format:

<?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> 

I would like to use the code to create this form, as some things need to be calculated "on the fly" before I do this, so the static predefined layout does not shorten it.

I am new to Android and cannot understand how XML is converted to code, and there is no RingShape class inheriting from Shape.

In addition to answering this question, if there is a guide somewhere that details the relationship between XML and Java code and how XML is processed to finish on the screen, I would appreciate a link. Thanks.

0
source share
5 answers

Ring and other GradientDrawables shapes.

If you look at the source code for GradientDrawable, you will see that some properties (e.g. innerRadius) can only be defined via XML ... they are not exposed through access methods. The corresponding state is also uselessly closed to the class, so subclassing also does not help.

+9
source

Ruben has already pointed out the most useful comments, so I will just focus on the implementation of this story. There are several approaches using reflection that are likely to give you what you are looking for.

First, you need to (ab) use the private GradientDrawable constructor, which accepts a GradientState reference. Unfortunately, the latter is the final subclass with package visibility, so you cannot easily access it. To use it, you will need to dive even further into the use of reflection or imitate its functionality in your own code.

The second approach is to use reflection to get the private member variable mGradientState, which, fortunately, has a getter in the form getConstantState() . This will give you a ConstantState, which at run time is actually a GradientState, and so we can use reflection to access our members and change them at run time.

To support the above statements, here is a somewhat basic implementation for creating a ring shape extracted from code:

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

The above can be used as follows to create a RingDrawable from the code and display it in a standard ImageView.

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

This will show a simple opaque blue ring in ImageView (inner radius of 10 units, 20 units). You will need not to set the width and height of the ImageView in wrap_content unless you add ring.setSize(width, height) to the code above so that it displays.

Hope this helps you anyway.

+10
source

This can be done from the code:

 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 
+2
source

You can do something like this:

 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
source

For me, this works as follows: (also for Android version> lollipop)

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

Source: https://habr.com/ru/post/980561/


All Articles