No creo que pueda hacer esto en XML (al menos no en Android), pero he encontrado una buena solución publicada aquí que parece que sería de gran ayuda.
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);
Básicamente, la matriz int le permite seleccionar múltiples paradas de color, y la siguiente matriz flotante define dónde se ubican esas paradas (de 0 a 1). Luego, como se indicó, puede usar esto como un Drawable estándar.
Editar: así es como podría usar esto en su escenario. Digamos que tiene un botón definido en XML así:
<Button
android:id="@+id/thebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Press Me!"
/>
Luego pondrías algo como esto en tu método onCreate ():
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);
No puedo probar esto en este momento, este es el código de mi cabeza, pero básicamente solo reemplazo o agrego paradas para los colores que necesita. Básicamente, en mi ejemplo, comenzaría con un verde claro, se desvanecería a blanco ligeramente antes del centro (para dar una transición más desvanecida, en lugar de una dura), se desvanecería de blanco a verde medio entre 45% y 55%, luego se desvanecería de verde medio a verde oscuro desde 55% hasta el final. Es posible que esto no se vea exactamente como su forma (en este momento, no tengo forma de probar estos colores), pero puede modificar esto para replicar su ejemplo.
Editar: también, se 0, 0, 0, theButton.getHeight()
refiere a las coordenadas x0, y0, x1, y1 del gradiente. Básicamente, comienza en x = 0 (lado izquierdo), y = 0 (arriba) y se extiende hasta x = 0 (queremos un gradiente vertical, por lo que no es necesario un ángulo de izquierda a derecha), y = la altura del botón Entonces el gradiente va en un ángulo de 90 grados desde la parte superior del botón hasta la parte inferior del botón.
Editar: Bueno, tengo una idea más que funciona, jaja. En este momento funciona en XML, pero también debería ser factible para formas en Java. Es un poco complejo, e imagino que hay una manera de simplificarlo en una sola forma, pero esto es lo que tengo por ahora:
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>
Bien, entonces básicamente he creado un degradado de forma en XML para el degradado verde horizontal, establecido en un ángulo de 0 grados, que va desde el color verde izquierdo del área superior hasta el color verde derecho. A continuación, hice un rectángulo de forma con un gris medio transparente. Estoy bastante seguro de que podría incluirse en el XML de la lista de capas, obviando este archivo adicional, pero no estoy seguro de cómo. Pero bueno, entonces el tipo de parte hacky viene en el archivo XML de layer_list Puse el degradado verde como la capa inferior, luego puse la mitad superpuesta como la segunda capa, compensada desde la parte superior por 50dp. Obviamente, querrás que este número sea siempre la mitad del tamaño de tu vista, y no un 50dp fijo. Sin embargo, no creo que puedas usar porcentajes. A partir de ahí, acabo de insertar un TextView en mi diseño test.xml, usando el archivo layer_list.xml como fondo.
Tada!
Una edición más : me he dado cuenta de que puede incrustar las formas en la lista de capas dibujables como elementos, lo que significa que ya no necesita 3 archivos XML separados. Puedes lograr el mismo resultado combinándolos así:
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>
¡Puedes colocar tantos elementos como quieras de esta manera! Puedo intentar jugar y ver si puedo obtener un resultado más versátil a través de Java.
Creo que esta es la última edición ... : Ok, entonces definitivamente puedes arreglar el posicionamiento a través de Java, como el siguiente:
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);
¡Sin embargo! Esto lleva a otro problema molesto, ya que no puede medir TextView hasta después de que se haya dibujado. Todavía no estoy seguro de cómo puede lograr esto ... pero insertar manualmente un número para topInset funciona.
Mentí, una edición más
Bien, descubrí cómo actualizar manualmente esta capa dibujable para que coincida con la altura del contenedor, la descripción completa se puede encontrar aquí . Este código debe ir en su método onCreate ():
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);
}
});
Y ya he terminado! ¡Uf! :)