web-dev-qa-db-fra.com

Formes multi-dégradées

Je voudrais créer une forme qui ressemble à l'image suivante:

alt text

Notez les demi-gradients supérieurs de la couleur 1 à la couleur 2, mais une moitié inférieure les gradients de la couleur 3 à la couleur 4. Je sais comment créer une forme avec un seul dégradé, mais je ne suis pas sûr de savoir comment scinder une forme en une deux moitiés et faire 1 forme avec 2 gradients différents.

Des idées?

174
Andrew

Je ne pense pas que vous puissiez le faire en XML (du moins pas en Android), mais j'ai trouvé une bonne solution postée ici qui pourrait être d'une grande aide!

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

Fondamentalement, le tableau int vous permet de sélectionner plusieurs arrêts de couleur, et le tableau flottant suivant définit l'emplacement de ces arrêts (de 0 à 1). Vous pouvez ensuite, comme indiqué, utiliser ceci comme dessin standard.

Edit: Voici comment vous pouvez utiliser cela dans votre scénario. Disons que vous avez un bouton défini en XML comme suit:

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

Vous mettriez alors quelque chose comme ceci dans votre méthode 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);

Je ne peux pas tester cela pour le moment, c'est du code de ma tête, mais fondamentalement, remplacez simplement ou ajoutez des arrêts pour les couleurs dont vous avez besoin. Fondamentalement, dans mon exemple, vous commenceriez par un vert clair, passant au blanc légèrement avant le centre (pour créer un fondu plutôt qu’une transition dure), puis passiez du blanc au vert moyen entre 45% et 55%, puis vert moyen à vert foncé de 55% à la fin. Cela peut ne pas ressembler exactement à votre forme (pour l’instant, je n’ai aucun moyen de tester ces couleurs), mais vous pouvez le modifier pour reproduire votre exemple.

Edit: De plus, la fonction 0, 0, 0, theButton.getHeight() fait référence aux coordonnées x0, y0, x1, y1 du dégradé. Donc, fondamentalement, il commence à x = 0 (côté gauche), y = 0 (haut), et s'étend à x = 0 (nous voulons un dégradé vertical, de sorte qu'aucun angle de gauche à droite n'est nécessaire), y = la hauteur du bouton. Ainsi, le dégradé va à un angle de 90 degrés du haut du bouton au bas du bouton.

Edit: OK, alors j'ai une autre idée qui fonctionne, haha. Pour le moment, cela fonctionne en XML, mais cela devrait être faisable pour les formes dans Java aussi. C'est un peu complexe, et j'imagine qu'il y a un moyen de le simplifier en une seule forme, mais c'est ce que J'ai pour l'instant:

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>

Donc, en gros, j'ai créé un dégradé de forme en XML pour le dégradé vert horizontal, placé à un angle de 0 degré, allant de la couleur verte à gauche de la zone supérieure à la couleur verte à droite. Ensuite, j'ai créé un rectangle de forme avec un gris à moitié transparent. Je suis à peu près sûr que cela pourrait être inséré dans le fichier XML de liste de couches, ce qui éviterait ce fichier supplémentaire, mais je ne sais pas comment. Mais bon, alors le type de partie hacky entre dans le fichier XML layer_list. Je mets le dégradé vert comme calque inférieur, puis le demi-calque comme deuxième calque, décalé du haut par 50dp. Évidemment, vous voudriez que ce nombre soit toujours égal à la moitié de la taille de votre vue, et non à une valeur fixe de 50dp. Je ne pense pas que vous puissiez utiliser des pourcentages, cependant. À partir de là, je viens d'insérer un TextView dans ma mise en page test.xml, en utilisant le fichier layer_list.xml comme arrière-plan. Je règle la hauteur à 100dp (deux fois la taille du décalage de la superposition), ce qui a pour résultat:

alt text

Tada!

Encore une édition : J'ai compris que vous pouvez simplement incorporer les formes dans la liste des couches pouvant être dessinées sous forme d'éléments, ce qui signifie que vous n'avez pas besoin de 3 fichiers XML séparés. pas plus! Vous pouvez obtenir le même résultat en les combinant comme suit:

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>

Vous pouvez superposer autant d'articles que vous le souhaitez de cette manière! Je peux essayer de jouer et de voir si je peux obtenir un résultat plus polyvalent avec Java.

Je pense que c'est la dernière édition ... : Bon, vous pouvez donc corriger le positionnement via Java, comme suit:

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

Pourtant! Cela pose un autre problème gênant en ce sens que vous ne pouvez pas mesurer le TextView tant qu’il n’a pas été dessiné. Je ne sais pas encore comment vous pouvez y arriver ... mais l'insertion manuelle d'un numéro pour topInset fonctionne.

J'ai menti, encore une édition

Ok, vous avez découvert comment mettre à jour manuellement cette couche, de manière à ce qu’elle puisse correspondre à la hauteur du conteneur, une description complète peut être trouvée ici . Ce code devrait aller dans votre méthode 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);
            }
        });

Et j'ai fini! Ouf! :)

376
kcoppock

Vous pouvez superposer des formes de dégradés dans le XML à l'aide d'une liste de calques. Imaginez un bouton avec l'état par défaut comme ci-dessous, où le deuxième élément est semi-transparent. Cela ajoute une sorte de vignettage. (Veuillez excuser les couleurs personnalisées.)

<!-- Normal state. -->
<item>
    <layer-list>
        <item>  
            <shape>
                <gradient 
                    Android:startColor="@color/grey_light"
                    Android:endColor="@color/grey_dark"
                    Android:type="linear"
                    Android:angle="270"
                    Android:centerColor="@color/grey_mediumtodark" />
                <stroke
                    Android:width="1dp"
                    Android:color="@color/grey_dark" />
                <corners
                    Android:radius="5dp" />
            </shape>
        </item>
        <item>  
            <shape>
                <gradient 
                    Android:startColor="#00666666"
                    Android:endColor="#77666666"
                    Android:type="radial"
                    Android:gradientRadius="200"
                    Android:centerColor="#00666666"
                    Android:centerX="0.5"
                    Android:centerY="0" />
                <stroke
                    Android:width="1dp"
                    Android:color="@color/grey_dark" />
                <corners
                    Android:radius="5dp" />
            </shape>
        </item>
    </layer-list>
</item>
27
LordParsley

Vous POUVEZ le faire en utilisant uniquement des formes xml - utilisez simplement la liste de couches ET le remplissage négatif comme ceci:

    <layer-list>

        <item>
            <shape>
                <solid Android:color="#ffffff" />

                <padding Android:top="20dp" />
            </shape>
        </item>

        <item>
            <shape>
                <gradient Android:endColor="#ffffff" Android:startColor="#efefef" Android:type="linear" Android:angle="90" />

                <padding Android:top="-20dp" />
            </shape>
        </item>

    </layer-list>
4
konmik

Avez-vous essayé de superposer à un dégradé une opacité presque transparente pour le surlignage par-dessus une autre image avec une opacité opaque pour le dégradé vert?

1
Greg D

Essayez cette méthode, alors vous pouvez faire tout ce que vous voulez.
C'est comme un pile alors faites attention à ce qui vient en premier ou en dernier.

<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:Android="http://schemas.Android.com/apk/res/Android">
<item Android:right="50dp" Android:start="10dp" Android:left="10dp">
    <shape
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        Android:shape="rectangle">
        <corners Android:radius="3dp" />
        <solid Android:color="#012d08"/>
    </shape>
</item>
<item Android:top="50dp">
    <shape Android:shape="rectangle">
        <solid Android:color="#7c4b4b" />
    </shape>
</item>
<item Android:top="90dp" Android:end="60dp">
    <shape Android:shape="rectangle">
        <solid Android:color="#e2cc2626" />
    </shape>
</item>
<item Android:start="50dp" Android:bottom="20dp" Android:top="120dp">
    <shape Android:shape="rectangle">
        <solid Android:color="#360e0e" />
    </shape>
</item>
1
Khalid Ali