• [原创]android使用代码生成LayerDrawable的方法和注意事项


    为了有更好的UI体验,一般我们会把button、textview等控件的背景设置上阴影。传统的做法是美工提供一张具有阴影效果的nine patch图,然后将其在xml文件中添加到background属性。这种做法没有问题,不过缺乏灵活性。

    图1.使用代码生成的具有“阴影”效果的控件

    在android中,每一种在xml文件中定义的图片,均可以使用java代码生成,其中LayerDrawable对应的xml文件的根元素为<layer-list>。

    首先我介绍一下使用xml文件生成“阴影”背景效果图片:

    <?xml version= "1.0" encoding ="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    
        <item >
            <shape android:shape="rectangle" >
                <solid android:color="#ffbbbbbb" />
    
                <corners android:radius="2dp" />
    
            </shape>
        </item >
    
        <item
            android:bottom="1px"
            android:right="1px" >
            <shape android:shape="rectangle" >
                <solid android:color="#ffdddddd" />
    
                <corners android:radius="2dp" />
    
                <padding
                    android:bottom="10dp"
                    android:left="10dp"
                    android:right="10dp"
                    android:top="10dp" />
            </shape>
        </item >
    
    </layer-list>

    解析:

    1.shape元素生成ShapeDrawable对象,不过需要注意的是,xml中虽然指明生成"rectangle"类型的对象,但如果想要在java中生成的rectangle具有圆角,那么java中对应的shape应该是RoundRectShape。
    2.solid元素指明背景颜色,且paint的style为fill。
    3.第二个元素android:bottom等代表的是LayerDrawable中第二个drawable相对于第一个drawable的inset,对应的java代码为:
    layerDrawable.setLayerInset(1, 0, 0, 1, 1);
    源码为:
     1 /** Specify modifiers to the bounds for the drawable[index].
     2         left += l
     3         top += t;
     4         right -= r;
     5         bottom -= b;
     6     */
     7     public void setLayerInset(int index, int l, int t, int r, int b) {
     8         ChildDrawable childDrawable = mLayerState.mChildren[index];
     9         childDrawable.mInsetL = l;
    10         childDrawable.mInsetT = t;
    11         childDrawable.mInsetR = r;
    12         childDrawable.mInsetB = b;
    13     }

    可以看出setLayerInset()函数的作用就是将某层(层数从0开始计数)相对于上一层进行向里偏移。当然如果传入的数值为负数,就是向外偏移了,不过这时上层就遮挡住下层了,失去了使用layer的意义了。

    4.padding的作用同样非常重要:

        (1)当在最上层使用padding时,它指明的是最上层的drawable边缘与内容之间的padding;

        (2)当在非最上层使用padding时,它指明当前层与上层之间的padding。

    下面使用java代码生成LayerDrawable。

     1 private void setLayerBg(View view){
     2         
     3         int radius0 = 10;
     4         float[] outerR = new float[] { radius0, radius0, radius0, radius0, radius0, radius0, radius0, radius0 };
     5         RoundRectShape roundRectShape0 = new RoundRectShape(outerR, null, null);
     6         
     7         int radius1 = 10;
     8         float[] outerR1 = new float[] { radius1, radius1, radius1, radius1, radius1, radius1, radius1, radius1 };
     9         RoundRectShape roundRectShape1 = new RoundRectShape(outerR1, null, null);
    10         
    11         ShapeDrawable shapeDrawableBg = new ShapeDrawable();
    12         
    13         shapeDrawableBg.setPadding(0, 0, 0, 0);
    14         shapeDrawableBg.setShape(roundRectShape0);
    15 
    16         shapeDrawableBg.getPaint().setStyle(Paint.Style.FILL);
    17         shapeDrawableBg.getPaint().setColor(0xffbbbbbb);
    18         
    19         
    20         ShapeDrawable shapeDrawableFg = new ShapeDrawable();
    21         
    22         shapeDrawableFg.setPadding(23, 23, 23, 23);
    23         shapeDrawableFg.setShape(roundRectShape1);
    24 
    25         shapeDrawableFg.getPaint().setStyle(Paint.Style.FILL);
    26         shapeDrawableFg.getPaint().setColor(0xffdddddd);
    27         
    28         Drawable[] layers = {shapeDrawableBg, shapeDrawableFg};
    29         LayerDrawable layerDrawable = new LayerDrawable(layers);
    30         layerDrawable.setLayerInset(1, 0, 0, 1, 1);
    31         
    32         view.setBackgroundDrawable(layerDrawable);
    33         
    34     }

    注释我就不写了,具体的解释见上面的解析。

    LayerDrawable和StateListDrawable相结合使用

    当我们遇到可点击的控件时,需要给此控件自定义几个不同状态的background,比如按下效果、普通状态效果,这时就需要用到StateListDrawable。

    采用LayerDrawable生成的图片具有的只是静态属性,当将不同状态的LayerDrawable添加到一个StateListDrawable中,这样控件不同状态时均选择是否具有阴影效果。

    代码如下:

     1 @Override
     2     protected void onCreate(Bundle savedInstanceState) {
     3         super.onCreate(savedInstanceState);
     4         setContentView(R.layout.activity_main);
     5     
     6         text  = (TextView)this.findViewById(R.id.text);
     7         text.setBackgroundDrawable(getStateListDrawable());
     8         text.setOnClickListener(new View.OnClickListener() {
     9             
    10             @Override
    11             public void onClick(View v) {
    12                 Toast.makeText(getApplicationContext(), "ttt", Toast.LENGTH_SHORT).show();
    13             }
    14         });
    15    }
    16 
    17     private Drawable getStateListDrawable(){
    18 
    19         StateListDrawable stateListDrawable = new StateListDrawable();
    20 
    21         int[] stateHighlighted = new int[]{android.R.attr.state_pressed};
    22         Drawable highlightedDrawable = getLayerDrawable(0xffcccccc);
    23         stateListDrawable.addState(stateHighlighted, highlightedDrawable);
    24 
    25         int[] stateNormal = new int[]{};
    26         Drawable normalDrawable = getLayerDrawable(0xffdddddd);
    27         stateListDrawable.addState(stateNormal, normalDrawable);
    28 
    29         return stateListDrawable;
    30     }
    31     
    32     private Drawable getLayerDrawable(int foregroundColor){
    33         
    34         int radius0 = 10;
    35         float[] outerR = new float[] { radius0, radius0, radius0, radius0, radius0, radius0, radius0, radius0 };
    36         RoundRectShape roundRectShape0 = new RoundRectShape(outerR, null, null);
    37         
    38         int radius1 = 10;
    39         float[] outerR1 = new float[] { radius1, radius1, radius1, radius1, radius1, radius1, radius1, radius1 };
    40         RoundRectShape roundRectShape1 = new RoundRectShape(outerR1, null, null);
    41         
    42         ShapeDrawable shapeDrawableBg = new ShapeDrawable();
    43         shapeDrawableBg.setPadding(0, 0, 0, 0);
    44         shapeDrawableBg.setShape(roundRectShape0);
    45         shapeDrawableBg.getPaint().setStyle(Paint.Style.FILL);
    46         shapeDrawableBg.getPaint().setColor(0xffbbbbbb);
    47         
    48         ShapeDrawable shapeDrawableFg = new ShapeDrawable();
    49         shapeDrawableFg.setPadding(23, 23, 23, 23);
    50         shapeDrawableFg.setShape(roundRectShape1);
    51         shapeDrawableFg.getPaint().setStyle(Paint.Style.FILL);
    52         shapeDrawableFg.getPaint().setColor(foregroundColor);
    53         
    54         Drawable[] layers = {shapeDrawableBg, shapeDrawableFg};
    55         LayerDrawable layerDrawable = new LayerDrawable(layers);
    56         layerDrawable.setLayerInset(1, 0, 0, 1, 1);
    57         
    58         return layerDrawable;
    59     }

    需要注意的是:当给View、TextView、ImageView、ViewGroup等类型的默认没有按下事件的控件添加StateListDrawable时,控件需要设置上click事件,否则按下效果不起作用

    备注:

    这里只是通过两幅颜色单一的drawable错位简单的生成“阴影效果”,后续可以通过shader等效果,生成逐渐淡出的“阴影”效果。

    使用xml定义layer-list的示例:

    使用layer-list定义的xml作为button的背景。其中:

    Button1:
    1.底部drawable没有设置padding
    2.顶部drawable没有设置padding
    3.顶部drawable设置inset为5px

            android:left="5px"
            android:top="5px"
            android:bottom="5px"
            android:right="5px"

    button1背景的完整xml:

    <?xml version= "1.0" encoding ="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    
        <item >
            <shape android:shape="rectangle" >
                <solid android:color="#ff00ff00" />
    
                <corners android:radius="3dp" />
                
            </shape>
        </item >
    
        <item
            android:left="5px"
            android:top="5px"
            android:bottom="5px"
            android:right="5px" >
            <shape android:shape="rectangle" >
                <solid android:color="#ffff0000" />
    
                <corners android:radius="3dp" />
    
            </shape>
        </item >
    
    </layer-list>    

    Button2:
    1.底部drawable没有设置padding
    2.顶部drawable设置inset均为5px

            android:left="5px"
            android:top="5px"
            android:bottom="5px"
            android:right="5px"

    3.顶部drawable设置padding均为50dp

         <padding
                android:bottom="50dp"
                android:left="50dp"
                android:right="50dp"
                android:top="50dp" />

    button2背景的完整xml:

    <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
        <!-- Z-Order 底部drawable -->
        <item >
            <shape android:shape="rectangle" >
                <solid android:color="#ff00ff00" />
    
                <corners android:radius="3dp" />
                
            </shape>
        </item >
    
        <!-- 顶部drawable -->
        <item
            android:left="5px"
            android:top="5px"
            android:bottom="5px"
            android:right="5px" >
            <shape android:shape="rectangle" >
                <solid android:color="#ffff0000" />
    
                <corners android:radius="3dp" />
    
                <padding
                    android:bottom="50dp"
                    android:left="50dp"
                    android:right="50dp"
                    android:top="50dp" />
            </shape>
        </item >
    
    </layer-list>

    Button3:
    1.底部drawable设置padding均为20dp
    2.顶部drawable没有设置inset
    3.顶部drawable设置padding均为50dp

        <padding
                android:bottom="50dp"
                android:left="50dp"
                android:right="50dp"
                android:top="50dp" />

    button3背景的完整xml:

    <?xml version= "1.0" encoding ="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    
        <item>
            <shape android:shape="rectangle" >
                <solid android:color="#ff00ff00" />
    
                <corners android:radius="3dp" />
    
                <padding
                    android:bottom="20dp"
                    android:left="20dp"
                    android:right="20dp"
                    android:top="20dp" />
            </shape>
        </item>
    
        <item>
            <shape android:shape="rectangle" >
                <solid android:color="#ffff0000" />
    
                <corners android:radius="3dp" />
    
                <padding
                    android:bottom="50dp"
                    android:left="50dp"
                    android:right="50dp"
                    android:top="50dp" />
            </shape>
        </item>
    
    </layer-list>    


  • 相关阅读:
    数据库表分区
    将对象序列化成XML字符串
    [邀月博客] SQL Server 2008中SQL增强之二:Top新用途
    多线程:子线程执行过程中调用主线程
    Jquery版文字闪烁
    金马自定义对联
    清除数据
    QQ、微信、QQ浏览器UserAgent
    jump.html域名跳转javascript版
    注册页面位置调整
  • 原文地址:https://www.cnblogs.com/carbs/p/5302908.html
Copyright © 2020-2023  润新知