Multi-Gradient Shapes

Multi-gradient shapes

I don't think you can do this in XML (at least not in Android), but I've found a good solution posted here that looks like it'd be a great help!

ShapeDrawable.ShaderFactory sf = new ShapeDrawable.ShaderFactory() {
@Override
public Shader resize(int width, int height) {
LinearGradient lg = new LinearGradient(0, 0, width, height,
new int[]{Color.GREEN, Color.GREEN, Color.WHITE, Color.WHITE},
new float[]{0,0.5f,.55f,1}, Shader.TileMode.REPEAT);
return lg;
}
};

PaintDrawable p=new PaintDrawable();
p.setShape(new RectShape());
p.setShaderFactory(sf);

Basically, the int array allows you to select multiple color stops, and the following float array defines where those stops are positioned (from 0 to 1). You can then, as stated, just use this as a standard Drawable.

Edit: Here's how you could use this in your scenario. Let's say you have a Button defined in XML like so:

<Button
android:id="@+id/thebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Press Me!"
/>

You'd then put something like this in your onCreate() method:

Button theButton = (Button)findViewById(R.id.thebutton);
ShapeDrawable.ShaderFactory sf = new ShapeDrawable.ShaderFactory() {
@Override
public Shader resize(int width, int height) {
LinearGradient lg = new LinearGradient(0, 0, 0, theButton.getHeight(),
new int[] {
Color.LIGHT_GREEN,
Color.WHITE,
Color.MID_GREEN,
Color.DARK_GREEN }, //substitute the correct colors for these
new float[] {
0, 0.45f, 0.55f, 1 },
Shader.TileMode.REPEAT);
return lg;
}
};
PaintDrawable p = new PaintDrawable();
p.setShape(new RectShape());
p.setShaderFactory(sf);
theButton.setBackground((Drawable)p);

I cannot test this at the moment, this is code from my head, but basically just replace, or add stops for the colors that you need. Basically, in my example, you would start with a light green, fade to white slightly before the center (to give a fade, rather than a harsh transition), fade from white to mid green between 45% and 55%, then fade from mid green to dark green from 55% to the end. This may not look exactly like your shape (Right now, I have no way of testing these colors), but you can modify this to replicate your example.

Edit: Also, the 0, 0, 0, theButton.getHeight() refers to the x0, y0, x1, y1 coordinates of the gradient. So basically, it starts at x = 0 (left side), y = 0 (top), and stretches to x = 0 (we're wanting a vertical gradient, so no left to right angle is necessary), y = the height of the button. So the gradient goes at a 90 degree angle from the top of the button to the bottom of the button.

Edit: Okay, so I have one more idea that works, haha. Right now it works in XML, but should be doable for shapes in Java as well. It's kind of complex, and I imagine there's a way to simplify it into a single shape, but this is what I've got for now:

green_horizontal_gradient.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<corners
android:radius="3dp"
/>
<gradient
android:angle="0"
android:startColor="#FF63a34a"
android:endColor="#FF477b36"
android:type="linear"
/>
</shape>

half_overlay.xml

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

layer_list.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android"
>
<item
android:drawable="@drawable/green_horizontal_gradient"
android:id="@+id/green_gradient"
/>
<item
android:drawable="@drawable/half_overlay"
android:id="@+id/half_overlay"
android:top="50dp"
/>
</layer-list>

test.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
>
<TextView
android:id="@+id/image_test"
android:background="@drawable/layer_list"
android:layout_width="fill_parent"
android:layout_height="100dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:gravity="center"
android:text="Layer List Drawable!"
android:textColor="@android:color/white"
android:textStyle="bold"
android:textSize="26sp"
/>
</RelativeLayout>

Okay, so basically I've created a shape gradient in XML for the horizontal green gradient, set at a 0 degree angle, going from the top area's left green color, to the right green color. Next, I made a shape rectangle with a half transparent gray. I'm pretty sure that could be inlined into the layer-list XML, obviating this extra file, but I'm not sure how. But okay, then the kind of hacky part comes in on the layer_list XML file. I put the green gradient as the bottom layer, then put the half overlay as the second layer, offset from the top by 50dp. Obviously you'd want this number to always be half of whatever your view size is, though, and not a fixed 50dp. I don't think you can use percentages, though. From there, I just inserted a TextView into my test.xml layout, using the layer_list.xml file as my background. I set the height to 100dp (twice the size of the offset of the overlay), resulting in the following:

alt text

Tada!

One more edit: I've realized you can just embed the shapes into the layer list drawable as items, meaning you don't need 3 separate XML files any more! You can achieve the same result combining them like so:

layer_list.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android"
>
<item>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<corners
android:radius="3dp"
/>
<gradient
android:angle="0"
android:startColor="#FF63a34a"
android:endColor="#FF477b36"
android:type="linear"
/>
</shape>
</item>
<item
android:top="50dp"
>
<shape
android:shape="rectangle"
>
<solid
android:color="#40000000"
/>
</shape>
</item>
</layer-list>

You can layer as many items as you like this way! I may try to play around and see if I can get a more versatile result through Java.

I think this is the last edit...: Okay, so you can definitely fix the positioning through Java, like the following:

    TextView tv = (TextView)findViewById(R.id.image_test);
LayerDrawable ld = (LayerDrawable)tv.getBackground();
int topInset = tv.getHeight() / 2 ; //does not work!
ld.setLayerInset(1, 0, topInset, 0, 0);
tv.setBackgroundDrawable(ld);

However! This leads to yet another annoying problem in that you cannot measure the TextView until after it has been drawn. I'm not quite sure yet how you can accomplish this...but manually inserting a number for topInset does work.

I lied, one more edit

Okay, found out how to manually update this layer drawable to match the height of the container, full description can be found here. This code should go in your onCreate() method:

final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
LayerDrawable ld = (LayerDrawable)tv.getBackground();
ld.setLayerInset(1, 0, tv.getHeight() / 2, 0, 0);
}
});

And I'm done! Whew! :)

VBA getting multiple gradient Color from another shape color


Function Get_Color(ShapeName As Variant) As Long
Get_Color = ActiveSheet.Shapes(ShapeName).Fill.ForeColor
End Function

Sub gradients()
Dim Point_Index As Variant ' may be name of shape or index of shape

Point_Index = "OtherShape" ' or 2 if index of "OtherShape" is 2

With ActiveSheet.Shapes("Sauqre1").Fill
.ForeColor.RGB = RGB(0, 128, 128)
.OneColorGradient msoGradientHorizontal, 1, 1
.GradientStops.Insert Get_Color(Point_Index), 0.25
End With
End Sub

Multiple grandients and radial gradient in background

To achieve your desired result you should use coordinates instead of, for example top right

It would look like this radial-gradient(circle at 0% 0%, #2296e5, transparent 30%)
The first 0% is the horizontal position and the second one the vertical. So 0% 0% is the top left and 100% 100% would be bottom right.

Then it's just a matter of randomizing the values and adding enough gradients till you're satisfied.

I would recommend using an SCSS loop for that.

Here is what that looks like

@function noise($color) {
$val: radial-gradient(circle at 0% 0%, $color, transparent 30%);
@for $i from 1 through 50 {
$color2: darken($color, $i*0.9);
$val: #{$val}, radial-gradient(circle at random(100) + #{'%'} random(100) + #{'%'}, #{$color2}, transparent 30%);
}
@return $val;
}

@mixin addnoise($color) {
background: noise($color);
}

div {
width: 100vw;
height: 100vh;
@include addnoise(#2296e5);
}

Which compiles to:





div {
width: 100vw;
height: 100vh;
background: radial-gradient(circle at 0% 0%, #2296e5, transparent 30%), radial-gradient(circle at 19% 51%, #1e94e5, transparent 30%), radial-gradient(circle at 59% 93%, #1b92e3, transparent 30%), radial-gradient(circle at 56% 94%, #1a8fdf, transparent 30%), radial-gradient(circle at 26% 90%, #1a8ddb, transparent 30%), radial-gradient(circle at 6% 65%, #198ad7, transparent 30%), radial-gradient(circle at 5% 100%, #1987d3, transparent 30%), radial-gradient(circle at 93% 21%, #1885cf, transparent 30%), radial-gradient(circle at 6% 68%, #1882ca, transparent 30%), radial-gradient(circle at 97% 33%, #177fc6, transparent 30%), radial-gradient(circle at 43% 53%, #177dc2, transparent 30%), radial-gradient(circle at 30% 60%, #167abe, transparent 30%), radial-gradient(circle at 68% 43%, #1678ba, transparent 30%), radial-gradient(circle at 5% 49%, #1575b6, transparent 30%), radial-gradient(circle at 99% 21%, #1572b2, transparent 30%), radial-gradient(circle at 92% 60%, #1470ae, transparent 30%), radial-gradient(circle at 85% 30%, #146daa, transparent 30%), radial-gradient(circle at 91% 6%, #136aa5, transparent 30%), radial-gradient(circle at 37% 9%, #1368a1, transparent 30%), radial-gradient(circle at 48% 66%, #13659d, transparent 30%), radial-gradient(circle at 82% 62%, #126299, transparent 30%), radial-gradient(circle at 15% 80%, #126095, transparent 30%), radial-gradient(circle at 20% 92%, #115d91, transparent 30%), radial-gradient(circle at 49% 79%, #115b8d, transparent 30%), radial-gradient(circle at 81% 70%, #105889, transparent 30%), radial-gradient(circle at 16% 63%, #105585, transparent 30%), radial-gradient(circle at 32% 28%, #0f5381, transparent 30%), radial-gradient(circle at 8% 6%, #0f507c, transparent 30%), radial-gradient(circle at 77% 39%, #0e4d78, transparent 30%), radial-gradient(circle at 81% 20%, #0e4b74, transparent 30%), radial-gradient(circle at 87% 40%, #0d4870, transparent 30%), radial-gradient(circle at 18% 76%, #0d456c, transparent 30%), radial-gradient(circle at 61% 45%, #0c4368, transparent 30%), radial-gradient(circle at 20% 22%, #0c4064, transparent 30%), radial-gradient(circle at 39% 52%, #0b3d60, transparent 30%), radial-gradient(circle at 44% 37%, #0b3b5c, transparent 30%), radial-gradient(circle at 3% 59%, #0a3857, transparent 30%), radial-gradient(circle at 58% 20%, #0a3653, transparent 30%), radial-gradient(circle at 64% 37%, #09334f, transparent 30%), radial-gradient(circle at 55% 12%, #09304b, transparent 30%), radial-gradient(circle at 52% 36%, #082e47, transparent 30%), radial-gradient(circle at 3% 75%, #082b43, transparent 30%), radial-gradient(circle at 92% 94%, #07283f, transparent 30%), radial-gradient(circle at 37% 96%, #07263b, transparent 30%), radial-gradient(circle at 66% 92%, #062337, transparent 30%), radial-gradient(circle at 43% 2%, #062033, transparent 30%), radial-gradient(circle at 69% 81%, #051e2e, transparent 30%), radial-gradient(circle at 51% 68%, #051b2a, transparent 30%), radial-gradient(circle at 99% 15%, #041926, transparent 30%), radial-gradient(circle at 17% 85%, #041622, transparent 30%), radial-gradient(circle at 72% 69%, #04131e, transparent 30%);
}
<div></div>

Android: drawable ring shape multi-gradient

I couldn't achieve it with a ring shape, so what I ended up doing is:

<FrameLayout
android:id="@+id/gold_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/gold_photo"
android:layout_width="@dimen/all_time_best_photo_size"
android:layout_height="@dimen/all_time_best_photo_size"
android:layout_margin="@dimen/all_time_best_frame_thickness"
app:shapeAppearanceOverlay="@style/CircleImageView" />
</FrameLayout>

and

val medal = GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, intArrayOf(
ContextCompat.getColor(requireContext(), R.color.gold_gradient_1),
ContextCompat.getColor(requireContext(), R.color.gold_gradient_2),
ContextCompat.getColor(requireContext(), R.color.gold_gradient_3),
ContextCompat.getColor(requireContext(), R.color.gold_gradient_4),
ContextCompat.getColor(requireContext(), R.color.gold_gradient_5)
)
)

medal.shape = GradientDrawable.OVAL
binding.goldFrame.background = medal

This way I create a circle with gradient inside and then set it as background as the ImageView's container. Is not perfect as it doesn't work if you have transparency in the images you want, but works in my case.

Also, it can be improved via removing the container and doing:

<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/gold_photo"
android:layout_width="@dimen/all_time_best_photo_size"
android:layout_height="@dimen/all_time_best_photo_size"
android:padding="@dimen/all_time_best_frame_thickness"
app:shapeAppearanceOverlay="@style/CircleImageView" />

and setting the background to the ImageView itself, unfortunately ShapeableImageView scales the background according to padding (standard ImageView doesn't do it).

A user created the issue on github and solved the problem via PR, which has been merged but not yet released, check out issue and pull request.

I'll post an update if I ever manage to do it using Ring shape.



Related Topics



Leave a reply



Submit