shape、selector和layer-list的使用

shape

在Android中,很多时候系统原生的控件的格式并不能满足我们的需求,我们想要更加好看点的样式,像什么圆角矩形啊,颜色渐变啊,阴影效果啊等等的,这个时候就是我们的ShapeDrawable发挥效果的时候了。
首先,我们要在res/drawable/ 路径下创建一个xml文件, 其格式如下:

<?xml version="1.0" encoding="utf-8"?>
<shape  xmlns:android="http://schemas.android.com/apk/res/android" android:shape=["rectangle" | "oval" | "line" | "ring"] >
    <corners  android:radius="integer" android:topLeftRadius="integer" android:topRightRadius="integer" android:bottomLeftRadius="integer" android:bottomRightRadius="integer" />
    <gradient  android:angle="integer" android:centerX="integer" android:centerY="integer" android:centerColor="integer" android:endColor="color" android:gradientRadius="integer" android:startColor="color" android:type=["linear" | "radial" | "sweep"] android:useLevel=["true" | "false"] />
    <padding  android:left="integer" android:top="integer" android:right="integer" android:bottom="integer" />
    <size  android:width="integer" android:height="integer" />
    <solid  android:color="color" />
    <stroke  android:width="integer" android:color="color" android:dashWidth="integer" android:dashGap="integer" />
</shape>
  • shape(形状)
    表示绘制的形状,有四个值供选择

    • rectangle (矩形,不写默认是矩形)
    • oval (椭圆)
    • line (线)
    • ring(环)

      前面几种比较简单,现在就说说最后的ring(环)吧,我们必须把android:useLevel设为false,不然的话,这个环是显示不出来的。
      关于Ring还有几个其特有的属性:

      • innerRadius,设置环内圆的半径。
      • innerRadiusRatio,设置的是内圆半径的比例,默认是3,表明是整个环的宽度的1/3,但是如果我们设置了innerRadius,即具体的宽度的话,这个属性是会被覆盖的。
      • thickness,设置环的厚度。
      • thincknessRatio,设置环的厚度的比例,默认是9,表明是整个环的宽度的1/9,但是如果设置了thickness,同样的,这个属性也会被覆盖的。
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="ring" android:innerRadiusRatio="3" android:thicknessRatio="9" android:useLevel="false">
    <solid android:color="#FF0000" />    
</shape>    

  • corners(角半径)
    表示的是矩形的四个角的半径(xxdp),只能用在android:shape=”rectangle” 的时候,一般情况下就是用来实现圆角矩形的效果,它只有5个子元素,如下:

    • android:radius(radius属性是对四个角都起作用的)
    • android:topLeftRadius(左上角)
    • android:topRightRadius(右上角)
    • android:bottomLeftRadius (左下角)
    • android:bottomRightRadius(右下角)

    注:如果同时定义了radius和topLeftRadius,这时radius对其他角仍有效, 而topLeftRadius会覆盖radius定义左上角。

  • padding(内边距)
    padding支持四个属性,分别是left,right,top 和 bottom。
    当drawable里面有内容时(譬如它作为TextView的background),可以设置内容距离边缘的内边距,其实就跟我们平常认识的padding一样效果。

  • size(大小)
    size支持两个属性,width和height,顾名思义就是设置drawable的大小。

  • solid(填充)
    solid只有一个属性,就是填充的颜色color。

  • stroke(边缘)
    stroke其实就是边缘的意思,当我们在定义画笔的时候,有很多时候会用到 FILL 和 STROKE,前者能够画出一个实心的形状,而后者就画出一个空心的图形,所以在这里,stroke表示同样的意思,就是描边。
    它只有四个属性:

    • android:width,表示的是线的粗细。
    • android:color,表示的是线的颜色。
    • android:dashGap,表示是一条虚线,gap表明的是虚线中线与线间的空隙。
    • android:dashWidth,表示的是虚线中线的长度,只有设置了dashGap来画一条虚线,这个属性值才能起作用。
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
    <stroke  android:dashGap="2dp" android:dashWidth="10dp" android:width="2dp" android:color="#FF0000" />
</shape>


- gradient(渐变)
它的元素比较多,我们逐一看一下,如下:
- android:startColor(渐变起始颜色)
- android:endColor(渐变终止颜色)
- android:centerColor(渐变中间颜色)
- android:angle(渐变方向逆时针旋转的角度,必须是45的倍数,否则会报错)
- android:centerX(中间的颜色相对的位置,X就只对左右的渐变有用,0~1.0)
- android:centerY(中间的颜色相对的位置,Y就只对上下的渐变有用,0~1.0)
- android:gradientRadius(raidal渐变半径)
- android:type(渐变类型,有”linear”、”radial”和”sweep”)
下面我们通过几个例子来看一下效果:

1.两种颜色线性渐变,角度分别为0、45、90、180

<?xml version="1.0" encoding="utf-8"?>  
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">  
    <corners android:radius="15dip"/>  
    <gradient android:startColor="#FF0000" android:endColor="#00FF00" android:angle="45" android:type="linear"/>  
</shape>  


可以看到,从0到180,渐变颜色会慢慢随着逆时针的方向旋转。而这个角度的值必须是45的倍数,否则会报错。不过我们仔细看一下,其实方向只有上下左右,45和0其实没有区别。
2.三种颜色线性渐变,centerX分别为0.2、0.4、0.6、0.8

<?xml version="1.0" encoding="utf-8"?>  
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">  
    <corners android:radius="15dip"/>  
    <gradient android:startColor="#FF0000" android:centerColor="#0000FF" android:endColor="#00FF00" android:centerX="0.2" android:type="linear"/>  
</shape> 


可以看到渐变开始部分从水平方向逐渐右移,同理centerY控制竖直方向的渐变点。我们不写这两个属性,默认值都是0.5,从中间开始。
3.两种颜色辐射渐变,渐变半径分别为100、200、300、400

<?xml version="1.0" encoding="utf-8"?>  
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">    
    <gradient android:startColor="#FF0000" android:endColor="#00FF00" android:gradientRadius="100" android:type="radial"/>  
</shape> 


可以看到渐变半径逐渐增大,当android:type=”raidal”的时候,gradientRadius的属性是一定要设置的。
4.三种颜色扫描渐变,centerX分别为0.1、0.5、0.9,同时centerY也为0.1、0.5、0.9

<?xml version="1.0" encoding="utf-8"?>  
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">  
    <corners android:radius="15dip"/>  
    <gradient android:startColor="#FF0000" android:centerColor="#0000FF" android:centerX="0.1" android:centerY="0.1" android:endColor="#00FF00" android:type="sweep"/>  
</shape> 


在type=”sweep”中,angle的属性是一点用没有的,而centerX/Y跟radial一样,都是针对开始颜色的比例来设置的,x是针对开始颜色在X轴上面的比例,而y则是针对在Y轴上面的比例。

selector

Android中的Selector是背景选择器,主要是用来改变ListView和Button控件的默认背景。Selector状态相关属性:
android:state_selected是选中
android:state_focused是获得焦点
android:state_pressed是点击
android:state_enabled是设置是否响应事件,指所有事件

其使用方法可以按以下步骤来设计:

  • 创建xml文件,位置:drawable/my_listview.xml,同时目录下记得要放相关图片
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 没有焦点时的背景图片 -->
    <item android:drawable="@drawable/pic_1" android:state_window_focused="false"/>
    <!-- 非触摸模式下获得焦点并单击时的背景图片 -->
    <item android:drawable="@drawable/pic_2" android:state_focused="true" android:state_pressed="true"/>
    <!-- 触摸模式下单击时的背景图片 -->
    <item android:drawable="@drawable/pic_3" android:state_focused="false" android:state_pressed="true"/>
    <!-- 选中时的图片背景 -->
    <item android:drawable="@drawable/pic_4" android:state_selected="true"/>
    <!-- 获得焦点时的图片背景 -->
    <item android:drawable="@drawable/pic_5" android:state_focused="true"/>
    <!-- 默认时的背景图片 -->
    <item android:drawable="@drawable/pic_1"/>
</selector>

其实我们很少会在一个xml中用到这么多状态,最常见的是在正常状态下显示一个drawable,在按下状态显示另一个drawable:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:drawable="@drawable/alarm_add_btn" android:state_pressed="false" ></item>
    <item android:drawable="@drawable/alarm_add_btn_selected" android:state_pressed="true" ></item>
</selector>

这里的drawable也可以是color,这样就变成:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
        <item android:drawable="@color/blue_bg" android:state_pressed="true"></item>
        <item android:drawable="@color/white_bg" android:state_pressed="false"></item>
</selector>

注:对seletor文件的解析是按照xml中item的先后顺序来的,即第一个item满足条件就显示第一个,不再往下找,不满足再找第二个,以此类推。

  • 使用xml文件

(1)ListView中使用

方法一:在ListView中添加如下属性代码

android:listSelector="@drawable/my_listview"

方法二:在ListView的item界面中添加如下属性代码

android:background="@drawable/my_listview"

方法三:在JAVA代码中直接编写

Drawable drawable = getResources().getDrawable(R.drawable.my_listview); 
listView.setSelector(drawable);

注:但是这样会出现列表有时候为黑的情况,所以需要在ListView中添加以下的属性代码使其透明

android:cacheColorHint="@android:color/transparent";

(2)Button中使用

根据这些状态同样可以设置button的selector效果。还可以设置selector改变button中的文字状态。以下是配置button中的文字颜色效果:drawable/button_font.xml

<?xml version="1.0" encoding="utf-8"?>  
<selector xmlns:android="http://schemas.android.com/apk/res/android">  
    <item android:state_selected="true" android:color="#FFF" />  
    <item android:state_focused="true" android:color="#FFF" />  
    <item android:state_pressed="true" android:color="#FFF" />  
    <item android:color="#000" />  
</selector> 

selector关联到控件:

    <Button
        android:id="@+id/button"
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:focusable="true"
        android:text="Button"
        android:textColor="@drawable/button_font"/>

Button还可以实现更复杂的效果,例如渐变:drawable/button_color.xml

<?xml version="1.0" encoding="utf-8"?>  
<selector xmlns:android="http://schemas.android.com/apk/res/android">  
<item android:state_pressed="true">
     <shape>  
          <gradient android:startColor="#8600ff" />   
          <stroke android:width="2dp" android:color="#000000" />   
          <corners android:radius="5dp" />    
          <padding android:left="10dp" android:top="10dp" android:bottom="10dp" android:right="10dp"/>    
     </shape>   
</item>   
<item android:state_pressed="false">
     <shape>   
           <gradient android:startColor="#eac100"/>   
           <stroke android:width="2dp" android:color="#333333"/>   
           <corners android:radius="8dp" />     
           <padding android:left="10dp" android:top="10dp" android:bottom="10dp" android:right="10dp"/>                     
      </shape>   
  </item>  
</selector>

selector关联到控件:

<Button
        android:id="@+id/button"
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:focusable="true"
        android:text="Button"
        android:background="@drawable/button_color"/>

layer-list

Android layer-list是用来做多个图层(item)堆叠显示的,借这个特性可以做一些特别的效果(比如:阴影、下面的效果等)。
每个item都可以有自己的属性:

  • android:top(距离上方距离)
  • android:bottom(距离下方距离)
  • android:left(距离左边距离)
  • android:right(距离右边距离)

1.先来看一个简单的例子

<?xml version="1.0" encoding="utf-8"?>  
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">  
    <item>  
      <bitmap android:src="@drawable/android_red" android:gravity="center" />  
    </item>  
    <item android:top="10dp" android:left="10dp">  
      <bitmap android:src="@drawable/android_green" android:gravity="center" />  
    </item>  
    <item android:top="20dp" android:left="20dp">  
      <bitmap android:src="@drawable/android_blue" android:gravity="center" />  
    </item>  
</layer-list>
<ImageView android:layout_height="wrap_content" android:layout_width="wrap_content" android:src="@drawable/layers" /> 

2.通过layer-list多图层叠加效果实现圆角功能, 效果如下图所示

四个角都是圆角的效果。如果让UI设计人员直接出图,可能会更简单一些。但是我们使用android中layer-list多图层叠加效果同样可以实现。
我们把它拆分为三个部分,第一个部分是最顶端的那一行(我这里称为顶部),第二部分是中间部分(中间部分不需要圆角效果),第三部分是底部。

  • 顶部是一个有灰色边框但无下边框,带圆角,白色背景的长方体,实现效果如下:
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">  
    <item >  
        <shape>  
            <solid android:color="#FFFFFF" />  
            <corners android:topLeftRadius="10dp" android:topRightRadius="10dp" android:bottomRightRadius="0.1dp" android:bottomLeftRadius="0.1dp" />  
            <stroke android:width="1dp" android:color="#ffa8abad" />  
        </shape>  
    </item>  
    <item android:top="1dp" android:left="1dp" android:right="1dp">  
        <shape>  
            <solid android:color="#FFFFFF" />  
            <corners android:topLeftRadius="10dp" android:topRightRadius="10dp" android:bottomRightRadius="0.1dp" android:bottomLeftRadius="0.1dp" /> 
        </shape>  
    </item> 
</layer-list>
  • 中间部分是一个不带圆角白色背景灰色边框无下边框长方体.实现效果如下:
<?xml version="1.0" encoding="UTF-8"?>  
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >  
    <item>  
        <shape>  
            <solid android:color="#FFFFFF" />  
            <stroke android:width="1dp" android:color="#ffa8abad" />  
        </shape>  
    </item> 
    <item android:left="1dp" android:right="1dp" android:top="1dp">  
        <shape>  
            <solid android:color="#FFFFFF" />
        </shape>  
    </item> 
</layer-list>
  • 底部是一个具有底部圆角,白色背景,灰色边框的长方体,实现效果如下:
<?xml version="1.0" encoding="UTF-8"?>  
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">  
    <item>  
        <shape>  
            <solid android:color="#FFFFFF" />  
            <corners android:topLeftRadius="0.1dp" android:topRightRadius="0.1dp" android:bottomRightRadius="10dp" android:bottomLeftRadius="10dp" />  
            <stroke android:width="1dp" android:color="#ffa8abad" />  
        </shape>  
    </item>
</layer-list>

使用layer-list图片

...
<LinearLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:layout_marginTop="20dp"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/text1"
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:background="@drawable/subscribe_bg"
            android:gravity="center"
            android:text="Text1" />

        <TextView
            android:id="@+id/text2"
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:background="@drawable/subscribe_bg2"
            android:gravity="center"
            android:text="Text2" />

        <TextView
            android:id="@+id/text2"
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:background="@drawable/subscribe_bg3"
            android:gravity="center"
            android:text="Text3" />
    </LinearLayout>

我们将上中下部分分别拆分开来和并在一起,显示效果如下:

在本文最后将结合上面学习过的三个内容, 做水平一个导航栏,点击后会切换不同选项,大概是下面这种效果:

我们将它拆分:

  • 左侧:
<?xml version="1.0" encoding="utf-8"?>  
<selector xmlns:android="http://schemas.android.com/apk/res/android">  
   <item android:state_selected="true">
     <shape android:shape="rectangle">
         <solid android:color="#FF545A" />    
         <corners  android:bottomLeftRadius="20dp" android:bottomRightRadius="0.1dp" android:topLeftRadius="20dp" android:topRightRadius="0.1dp" />
     </shape>   
   </item>   
   <item android:state_selected="false">
       <layer-list>  
          <item>  
               <shape android:shape="rectangle" >
                    <solid android:color="#ECECEC" />
                    <stroke  android:width="1dp" android:color="#FF545A" />
                    <corners  android:bottomLeftRadius="20dp" android:bottomRightRadius="0.1dp" android:topLeftRadius="20dp" android:topRightRadius="0.1dp" />
               </shape>  
          </item>
          <item android:left="1dp" android:bottom="1dp" android:top="1dp">  
               <shape android:shape="rectangle" >
                    <solid android:color="#ECECEC" />
                    <corners  android:bottomLeftRadius="20dp" android:bottomRightRadius="0.1dp" android:topLeftRadius="20dp" android:topRightRadius="0.1dp" />
               </shape>  
          </item>
       </layer-list>
   </item>  
</selector>
  • 右侧:
<?xml version="1.0" encoding="utf-8"?>  
<selector xmlns:android="http://schemas.android.com/apk/res/android">  
   <item android:state_selected="true">
     <shape android:shape="rectangle">
         <solid android:color="#FF545A" />
         <corners  android:bottomLeftRadius="0.1dp" android:bottomRightRadius="20dp" android:topLeftRadius="0.1dp" android:topRightRadius="20dp" />
     </shape>   
   </item>   
   <item android:state_selected="false">
       <layer-list>  
          <item>  
               <shape android:shape="rectangle" >
                    <solid android:color="#ECECEC" />
                    <stroke  android:width="1dp" android:color="#FF545A" />
                    <corners  android:bottomLeftRadius="0.1dp" android:bottomRightRadius="20dp" android:topLeftRadius="0.1dp" android:topRightRadius="20dp" />
               </shape>  
          </item>
          <item android:right="1dp" android:bottom="1dp" android:top="1dp">  
               <shape android:shape="rectangle" >
                    <solid android:color="#ECECEC" />
                    <corners  android:bottomLeftRadius="0.1dp" android:bottomRightRadius="20dp" android:topLeftRadius="0.1dp" android:topRightRadius="20dp" />
               </shape>  
          </item>
       </layer-list> 
   </item>  
</selector>

把图片关联到TextView背景

    <LinearLayout  android:layout_width="200dp" android:layout_height="40dp" android:layout_gravity="center_horizontal" android:layout_marginTop="20dp" android:orientation="horizontal" >

        <TextView  android:id="@+id/news_blog" android:layout_width="0dp" android:layout_height="35dp" android:layout_weight="1" android:background="@drawable/left_filter_bg" android:gravity="center" android:text="最新博客" android:textColor="#ffffff" android:textSize="18sp" />

        <TextView  android:id="@+id/hots_blog" android:layout_width="0dp" android:layout_height="35dp" android:layout_weight="1" android:layout_marginLeft="10dp" android:background="@drawable/right_filter_bg" android:gravity="center" android:text="热门博客" android:textColor="#FF545A" android:textSize="18sp" />
    </LinearLayout>

当我们点击TextView时还要控制TextView的字体颜色以及它的selected状态:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        newblog = (TextView) findViewById(R.id.news_blog);
        hotblog = (TextView) findViewById(R.id.hots_blog);
        seletedNewBlog();
        newblog.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View arg0) {
                seletedNewBlog();
            }       
        });
        hotblog.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View arg0) {
                seletedHotBlog();
            }       
        });
    }

    private void seletedNewBlog (){
        newblog.setSelected(true);
        newblog.setTextColor(Color.parseColor("#ffffff"));
        hotblog.setSelected(false);
        hotblog.setTextColor(Color.parseColor("#FF545A"));
    }

    private void seletedHotBlog (){
        hotblog.setSelected(true);
        hotblog.setTextColor(Color.parseColor("#ffffff"));
        newblog.setSelected(false);
        newblog.setTextColor(Color.parseColor("#FF545A"));
    }

注:Java代码中设置颜色几种方式:

setTextColor(0xffffffff);   //0xFF0000FF是int类型的数据,必须是8个的颜色表示,不接受0000FF这种6个的颜色表示
setTextColor(Color.parseColor("#ffffff"));
setTextColor(this.getResources().getColor(R.color.blue));//通过获得资源文件进行设置
setTextColor(Android.graphics.Color.BLUE); //使用系统自带的颜色类

看一下运行效果吧:

Demo下载地址

你可能感兴趣的:(drawable)