Багатоградієнтні форми


177

Я хотів би створити форму, схожу на наступне зображення:

alt текст

Помітьте градієнти верхньої половини від кольору 1 до кольору 2, але нижня половина - це градієнти від кольору 3 до кольору 4. Я знаю, як зробити фігуру за допомогою одного градієнта, але я не впевнений, як розділити фігуру на дві половинки і зробіть 1 форму з 2 різними градієнтами.

Будь-які ідеї?


Відповіді:


383

Я не думаю, що ви можете це зробити в XML (принаймні, не в Android), але я знайшов хороше рішення, розміщене тут , схоже, що це буде чудовою допомогою!

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

В основному, int масив дозволяє вибрати декілька кольорових зупинок, а наступний поплавковий масив визначає місце розташування цих зупинок (від 0 до 1). Потім ви можете, як зазначено, просто використовувати це як стандартний Dravable.

Редагувати: Ось як ви могли це використовувати у своєму сценарії. Скажімо, у вас є кнопка, визначена в XML так:

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

Потім ви поставите щось подібне у свій метод 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);

Я не можу перевірити це на даний момент, це код з моєї голови, але в основному просто замінити або додати зупинки на потрібні вам кольори. В основному, у моєму прикладі ви б почали зі світло-зеленого кольору, злегка вицвіли до білого кольору перед центром (щоб отримати блякший, а не різкий перехід), вицвіли від білого до середини зеленого від 45% до 55%, потім зникнуть від середньо-зелений до темно-зелений від 55% до кінця. Це може виглядати не так, як ваша форма (зараз я не можу перевірити ці кольори), але ви можете змінити це, щоб повторити ваш приклад.

Правка: Також 0, 0, 0, theButton.getHeight()посилається на координати градієнта x0, y0, x1, y1. Таким чином, він починається з x = 0 (ліва сторона), y = 0 (вгорі) і тягнеться до x = 0 (ми хочемо вертикальний градієнт, тому жодного лівого правого кута не потрібно), y = висота кнопки. Тож градієнт йде під кутом 90 градусів від верхньої частини кнопки до нижньої частини кнопки.

Редагувати: Гаразд, тому у мене є ще одна ідея, яка працює, ха-ха. Зараз він працює в XML, але він повинен бути виконаний і для фігур на Java. Це щось складне, і я думаю, що є спосіб спростити його в єдину форму, але ось що я зараз маю:

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>

Гаразд, так я в основному створив градієнт фігури в XML для горизонтального зеленого градієнта, встановленого під кутом 0 градусів, переходячи від лівого зеленого кольору верхньої області до правого зеленого кольору. Далі я зробив прямокутник форми з напівпрозорим сірим кольором. Я майже впевнений, що це може бути включено до списку шарів XML, усуваючи цей додатковий файл, але я не знаю як. Але гаразд, тоді така різновид хекійна частина входить у XML-файл шару_логу. Я поклав зелений градієнт як нижній шар, потім поклав половину накладання як другий шар, зміщений зверху на 50dp. Очевидно, ви хочете, щоб це число завжди було наполовину, не залежно від розміру вашого перегляду, а не фіксованим 50dp. Я не думаю, що ви можете використовувати відсотки. Звідти я щойно вставив TextView у мій тест test.xml, використовуючи файл layer_list.xml як фон.

alt текст

Тада!

Ще одна редакція : я зрозумів, що ви можете просто вставити фігури в список шарів, що можна малювати як елементи, тобто більше вам не потрібні 3 окремі XML-файли! Ви можете досягти того ж результату, поєднуючи їх так:

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>

Таким чином ви можете напланувати стільки предметів, скільки вам подобається! Я можу спробувати пограти і побачити, чи зможу я отримати більш універсальний результат через Java.

Я думаю, що це остання редакція ... : Гаразд, тому ви можете точно виправити позиціонування через Java, як-от наступне:

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

Однак! Це призводить до ще однієї дратівливої ​​проблеми в тому, що ви не можете виміряти TextView, поки не буде намальовано. Я ще не зовсім впевнений, як ви можете це досягти ... але вручну вставити номер для topInset справді працює.

Я збрехав, ще одна редакція

Гаразд, дізнайтеся, як вручну оновити цей шар, який можна малювати, щоб він відповідав висоті контейнера, повний опис можна знайти тут . Цей код повинен міститись у вашому методі 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);
            }
        });

І я закінчив! Вау! :)


Проблема в цьому полягає в тому, що градієнт вертикальний. Мені потрібна форма, розділена вертикально (тобто: горизонтальна лінія), але мені потрібен градієнт, який повинен бути горизонтальним (тобто: рухатись вліво-вправо). У цьому прикладі і сепаратор, і градієнт вертикальні.
Андрій

Добре, я розумію, що ти зараз кажеш. (Зображення блокуються на роботі, тому я міг переглядати твої лише зі свого телефону, важко добре розказати деталі). На жаль, у мене це рішення не існує, хоча я б припустив, що ви можете створити форму, що малюється, з двома прямокутниками, один з горизонтальним градієнтом, один з вертикальним градієнтом білого до чорного кольору в половині розміру з половиною непрозорості, вирівняні донизу. Щодо того, як це зробити, на даний момент я не маю конкретних прикладів.
Кевін Коппок

Так, саме це я сподівався зробити, але я цього ще не зрозумів. Я вдячний за вашу допомогу
Андрій

Ласкаво просимо! Я ще раз спробував, і я думаю, що я майже все отримав те, що ви шукаєте. Можливо, вам доведеться робити це динамічно через Java (відповідно до коментарів), але це робить фокус для фіксованих розмірів прямо зараз.
Кевін Коппок

17
Ось такий варіант відповіді ви хочете дати хоча б +10. Мені подобається почуття боротьби у вашій відповіді: ви проти Android API, хто виграє? Ми коли-небудь дізнаємось? ;) Крім того, це дуже корисний навчальний матеріал, оскільки ви зберегли всі примхи, з якими ви стикалися.
andr

28

Ви можете шарувати градієнтні форми в xml, використовуючи список шарів. Уявіть кнопку зі станом за замовчуванням, як показано нижче, де другий елемент напівпрозорий. Це додає свого роду віньєтування. (Вибачте, визначені на замовлення кольори.)

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


4

Ви МОЖЕТЕ це зробити, використовуючи лише форми XML - просто використовуйте список шарів І негативні накладки, як це:

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

1

Спробуйте цей метод, тоді ви можете робити все, що завгодно.
Це як стек, тому будьте уважні, який предмет виходить першим чи останнім.

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


0

Ви намагалися перекрити один градієнт майже прозорою непрозорістю для виділення поверх іншого зображення з непрозорою непрозорістю для зеленого градієнта?


Ні, ні, хоча я не зовсім впевнений, як розташувати один градієнт над іншим у формі. Я використовую фігуру як фон LinearLayout, використовуючи android: background = "@ dravable / myshape"
Андрій

Чи можна нанести дві форми з різною непрозорістю один на одного? (Я не знаю.)
Грег Д
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.