【问题标题】:Multi-gradient shapes多渐变形状
【发布时间】:2011-05-21 19:54:36
【问题描述】:

我想创建一个类似于下图的形状:

注意上半部分从颜色 1 渐变到颜色 2,但下半部分从颜色 3 渐变到颜色 4。我知道如何使用单个渐变制作形状,但我不确定如何拆分一个形状分成两半,并用 2 个不同的渐变制作 1 个形状。

有什么想法吗?

【问题讨论】:

标签: android gradient shapes


【解决方案1】:

我认为您不能在 XML 中执行此操作(至少不能在 Android 中),但我找到了一个很好的解决方案,已发布 here,看起来很有帮助!

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)。然后,如上所述,您可以将其用作标准 Drawable。

编辑:这是您在场景中使用它的方法。假设您在 XML 中定义了一个 Button,如下所示:

<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_horizo​​ntal_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 中,从而避免这个额外的文件,但我不确定如何。但是好的,那么 layer_list XML 文件中就出现了这种骇人听闻的部分。我将绿色渐变作为底层,然后将半覆盖层作为第二层,从顶部偏移 50dp。显然,您希望这个数字始终是您的视图大小的一半,而不是固定的 50dp。不过,我认为您不能使用百分比。从那里,我刚刚将 TextView 插入到我的 test.xml 布局中,使用 layer_list.xml 文件作为我的背景。我将高度设置为 100dp(叠加层偏移量的两倍),结果如下:

多田!

再修改一次:我意识到您可以将形状作为项目嵌入到可绘制的图层列表中,这意味着您不再需要 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 的数字确实有效。

我撒谎了,再来一次编辑

好的,找到了如何手动更新这个图层drawable以匹配容器的高度,完整的描述可以找到here。这段代码应该放在你的 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 动态地制作这些(根据 cmets),但这现在可以解决固定大小的问题。
  • 这就是你想给+10的答案。我喜欢你回答中的战斗感觉:你 vs Android API,谁会赢?我们最终会知道么? ;) 另外,它是非常有用的学习材料,因为您保留了您遇到的所有怪癖。
【解决方案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】:

      试试这个方法,然后你就可以做你想做的每一件事。
      它就像一个堆栈,所以要小心哪个项目先出现或最后出现。

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

      【讨论】:

        【解决方案5】:

        您是否尝试过在另一张具有不透明不透明绿色渐变的图像上叠加一个具有近乎透明不透明度的高光渐变?

        【讨论】:

        • 不,我没有,虽然我不太确定如何将一个渐变放在另一个形状上。我使用 android:background="@drawable/myshape" 将形状用作 LinearLayout 的背景
        • 是否可以将具有不同不透明度的两个形状叠加在一起? (我不知道。)
        猜你喜欢
        • 1970-01-01
        • 2012-08-02
        • 1970-01-01
        • 2016-09-16
        • 1970-01-01
        • 2022-11-13
        • 1970-01-01
        • 1970-01-01
        • 2018-09-11
        相关资源
        最近更新 更多