Formen mit mehreren Verläufen

177

Ich möchte eine Form erstellen, die dem folgenden Bild entspricht:

Alt-Text

Beachten Sie die Farbverläufe der oberen Hälfte von Farbe 1 bis Farbe 2, aber es gibt eine untere Hälfte, die Farbverläufe von Farbe 3 bis Farbe 4 aufweist. Ich weiß, wie man eine Form mit einem einzelnen Farbverlauf erstellt, bin mir aber nicht sicher, wie ich eine Form aufteilen soll zwei Hälften und machen 1 Form mit 2 verschiedenen Verläufen.

Irgendwelche Ideen?

Andrew
quelle

Antworten:

383

Ich glaube nicht, dass Sie dies in XML tun können (zumindest nicht in Android), aber ich habe hier eine gute Lösung gefunden , die anscheinend eine große Hilfe wäre!

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

Grundsätzlich können Sie mit dem Int-Array mehrere Farbstopps auswählen. Das folgende Float-Array definiert, wo diese Stopps positioniert sind (von 0 bis 1). Sie können dies dann, wie angegeben, einfach als Standard-Drawable verwenden.

Bearbeiten: So können Sie dies in Ihrem Szenario verwenden. Angenommen, Sie haben eine Schaltfläche in XML wie folgt definiert:

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

Sie würden dann so etwas in Ihre onCreate () -Methode einfügen:

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

Ich kann dies im Moment nicht testen, dies ist Code aus meinem Kopf, aber im Grunde nur ersetzen oder Stopps für die Farben hinzufügen, die Sie benötigen. Grundsätzlich würden Sie in meinem Beispiel mit einem Hellgrün beginnen, kurz vor der Mitte zu Weiß verblassen (um eine Überblendung anstelle eines harten Übergangs zu erzielen), zwischen 45% und 55% von Weiß zu Mittelgrün überblenden und dann abblenden mittelgrün bis dunkelgrün von 55% bis zum Ende. Dies sieht möglicherweise nicht genau wie Ihre Form aus (im Moment habe ich keine Möglichkeit, diese Farben zu testen), aber Sie können dies ändern, um Ihr Beispiel zu replizieren.

Bearbeiten: Das 0, 0, 0, theButton.getHeight()bezieht sich auch auf die x0-, y0-, x1- und y1-Koordinaten des Gradienten. Im Grunde genommen beginnt es bei x = 0 (linke Seite), y = 0 (oben) und erstreckt sich bis x = 0 (wir wollen einen vertikalen Gradienten, daher ist kein Winkel von links nach rechts erforderlich), y = Höhe der Taste. Der Farbverlauf verläuft also in einem Winkel von 90 Grad von der Oberseite der Schaltfläche zur Unterseite der Schaltfläche.

Edit: Okay, also ich habe noch eine Idee, die funktioniert, haha. Im Moment funktioniert es in XML, sollte aber auch für Formen in Java möglich sein. Es ist ziemlich komplex und ich stelle mir vor, dass es eine Möglichkeit gibt, es in eine einzige Form zu vereinfachen, aber das habe ich jetzt:

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, im Grunde habe ich in XML einen Formverlauf für den horizontalen grünen Farbverlauf erstellt, der in einem Winkel von 0 Grad von der linken grünen Farbe des oberen Bereichs zur rechten grünen Farbe festgelegt ist. Als nächstes machte ich ein Formrechteck mit einem halbtransparenten Grau. Ich bin mir ziemlich sicher, dass dies in das XML der Ebenenliste eingefügt werden könnte, wodurch diese zusätzliche Datei vermieden wird, aber ich bin mir nicht sicher, wie. Aber okay, dann kommt die Art von Hacky-Teil in die XML-Datei layer_list. Ich habe den grünen Farbverlauf als untere Ebene und dann die halbe Überlagerung als zweite Ebene eingefügt, die um 50 dp von der Oberseite versetzt ist. Natürlich möchten Sie, dass diese Zahl immer die Hälfte Ihrer Ansichtsgröße beträgt und keine festen 50 dp. Ich glaube jedoch nicht, dass Sie Prozentsätze verwenden können. Von dort aus habe ich gerade eine Textansicht in mein test.xml-Layout eingefügt und dabei die Datei layer_list.xml als Hintergrund verwendet.

Alt-Text

Tada!

Noch eine Bearbeitung : Ich habe festgestellt, dass Sie die Formen einfach in die als Elemente zeichnbare Ebenenliste einbetten können, sodass Sie keine 3 separaten XML-Dateien mehr benötigen! Sie können das gleiche Ergebnis erzielen, indem Sie sie wie folgt kombinieren:

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>

Auf diese Weise können Sie so viele Elemente überlagern, wie Sie möchten! Ich kann versuchen, herumzuspielen und zu sehen, ob ich mit Java ein vielseitigeres Ergebnis erzielen kann.

Ich denke, dies ist die letzte Bearbeitung ... : Okay, Sie können die Positionierung also definitiv über Java korrigieren, wie folgt:

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

Jedoch! Dies führt zu einem weiteren ärgerlichen Problem, da Sie die Textansicht erst nach dem Zeichnen messen können. Ich bin mir noch nicht ganz sicher, wie Sie dies erreichen können ... aber das manuelle Einfügen einer Nummer für topInset funktioniert.

Ich habe gelogen, noch eine Bearbeitung

Okay, Sie haben herausgefunden, wie Sie diese Ebene manuell aktualisieren können, um sie an die Höhe des Containers anzupassen. Eine vollständige Beschreibung finden Sie hier . Dieser Code sollte in Ihrer onCreate () -Methode enthalten sein:

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

Und ich bin fertig! Wütend! :) :)

Kevin Coppock
quelle
Das Problem dabei ist, dass der Gradient vertikal ist. Ich muss die Form vertikal trennen (dh eine horizontale Linie), aber der Farbverlauf muss horizontal sein (dh von links nach rechts bewegen). In diesem Beispiel sind sowohl der Separator als auch der Gradient vertikal.
Andrew
Oh okay, ich verstehe, was du jetzt sagst. (Bilder sind bei der Arbeit blockiert, daher konnte ich Ihre nur von meinem Telefon aus anzeigen, da Details nur schwer zu erkennen sind.) Leider habe ich keine Lösung für dieses Problem, obwohl ich davon ausgehen würde, dass Sie eine zeichnbare Form mit zwei Rechtecken erstellen könnten, eines mit einem horizontalen Farbverlauf und eines mit einem vertikalen Farbverlauf von Weiß zu Schwarz bei halber Größe und halber Deckkraft. nach unten ausgerichtet. Soweit das geht, liegen mir derzeit keine konkreten Beispiele vor.
Kevin Coppock
Ja, das hatte ich mir erhofft, aber ich habe es noch nicht herausgefunden. Ich schätze Ihre Hilfe
Andrew
Bitte! Ich habe es noch einmal versucht und ich glaube, ich habe so ziemlich das, wonach Sie suchen. Möglicherweise müssen Sie diese dynamisch über Java erstellen (gemäß den Kommentaren), aber dies ist derzeit der Trick für feste Größen.
Kevin Coppock
17
Das ist die Art von Antwort, die Sie mindestens +10 geben möchten. Ich liebe das Gefühl des Kampfes in Ihrer Antwort: Sie gegen Android API, wer wird gewinnen? Werden wir je wissen? ;) Außerdem ist es ein sehr nützliches Lernmaterial, da Sie alle Macken beibehalten haben, auf die Sie gestoßen sind.
andr
28

Sie können Verlaufsformen in der XML mithilfe einer Ebenenliste überlagern. Stellen Sie sich eine Schaltfläche mit dem folgenden Standardstatus vor, bei der das zweite Element halbtransparent ist. Es fügt eine Art Vignettierung hinzu. (Bitte entschuldigen Sie die benutzerdefinierten Farben.)

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

LordParsley
quelle
4

Sie können dies nur mit XML-Formen tun - verwenden Sie einfach die Ebenenliste UND das negative Auffüllen wie folgt:

    <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>
konmik
quelle
1

Versuchen Sie diese Methode, dann können Sie alles tun, was Sie wollen.
Es ist wie ein Stapel, also sei vorsichtig, welcher Gegenstand zuerst oder zuletzt kommt.

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

Khalid Ali
quelle
0

Haben Sie versucht, einen Farbverlauf mit einer nahezu transparenten Deckkraft für die Hervorhebung über einem anderen Bild mit einer undurchsichtigen Deckkraft für den grünen Farbverlauf zu überlagern?

Greg D.
quelle
Nein, habe ich nicht, obwohl ich nicht ganz sicher bin, wie ich einen Farbverlauf in einer Form über einen anderen legen soll. Ich benutze die Form als Hintergrund für ein LinearLayout mit android: background = "@ drawable / myshape"
Andrew
Ist es möglich, zwei Formen mit unterschiedlichen Opazitäten übereinander zu legen? (Ich weiß nicht.)
Greg D