Многоградиентные формы

Я хотел бы создать форму, похожую на следующее изображение:

alt text

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

Любые идеи?

Ответ 1

Я не думаю, что вы можете сделать это в 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 позволяет вам выбрать несколько остановок цвета, а следующий массив float определяет, где расположены эти стопы (от 0 до 1). Вы можете, как указано, просто использовать это как стандартный Drawable.

Изменить: вот как вы могли бы использовать это в своем сценарии. Скажем, у вас есть кнопка, определенная в 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 файл layer_list. Я помещал зеленый градиент в нижний слой, затем накладывал половину оверлея как второй слой, смещенный сверху с 50dp. Очевидно, вы хотите, чтобы этот номер всегда был половиной от вашего размера, но не фиксированного 50dp. Я не думаю, что вы можете использовать проценты. Оттуда я просто вставил TextView в мой макет test.xml, используя файл layer_list.xml в качестве фона. Я установил высоту в 100dp (вдвое больше размера смещения наложения), в результате получилось следующее:

alt text

Тада!

Еще одно редактирование. Я понял, что вы можете просто вставлять фигуры в список слоев, доступных как элементы, а это значит, что вам больше не нужны 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);
            }
        });

И я закончил! Уф!:)

Ответ 2

Вы можете формировать слои градиента в 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>

Ответ 3

Вы можете использовать его только с помощью 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>

Ответ 4

Вы пытались наложить один градиент с прозрачной непрозрачностью для подсветки поверх другого изображения с непрозрачной непрозрачностью для зеленого градиента?

Ответ 5

Попробуйте этот метод, тогда вы можете делать все, что захотите.
Это похоже на стек, поэтому будьте осторожны, какой элемент приходит первым или последним.

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