0
点赞
收藏
分享

微信扫一扫

Android资源那些事儿(详)




Android资源那些事儿(详)_数组


Android资源那些事儿


本文将要着重讲解的Android资源大致可以分为三类:
1. values资源
  • string 字符串资源
  • color 颜色资源
  • dimen 尺寸资源
  • array 数组资源
  • style 样式资源
  • theme 主题资源
2. drawable资源
  • 图片资源
  • StateListDrawable资源
  • LayerDrawable资源
  • ShapeDrawable资源
  • ClipDrawable资源
3.ColorStateList资源

--以下:正文部分--
Android的设计哲学为:设计与表现分离。
这样有利于程序的解耦。所以我们才可以在XML文件中定义各种资源类型,并在其他的xml文件或java代码中进行引用。

1.1 String资源:

字符串资源所对应的xml文件位于​​/res/values/​​​目录下。
其默认名为​​​strings.xml​​​对应于R类中的内部类的名称:R.string
文件的根元素为​​resources​​:

定义:

<resources>
<string name="app_name">Hello World</string>
<string name="button_name">Hello World</string>
<string name="text_name">Hi there</string>
</resources>

引用:

一般我们都是在同一个包下的其他xml文件中引用字符串资源:
比如在TextView中引用之前定义的字符串:

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_name" />

​android:text="@string/text_name"​​​所表达的
正是引用同一包下字符串资源文件中名为text_name的字符串资源。
当然,如果是引用不同包下的资源,可则只需在@和string之间加上包名。
事实上,在xml代码中使用资源的通用完整语法格式正是:
​​​@[<package_name>:]<resource_type>/<resource_name>​​其中中括号代表选填,尖括号代表必填。

1.2 Color资源:

与字符串资源类似,我们可以事先在xml文件中定义,并在之后对其进行引用。
颜色资源所对应的xml文件位于​​​/res/values/​​​目录下。
其默认名为​​​colors.xml​​​对应于R类中的内部类的名称:R.color
文件的根元素为​​resources​​:

定义:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorGray">#aaa</color>
<color name="colorWhite">#ffffff</color>
<color name="colorBlack">#454647</color>
</resources>

引用:

<TextView
...
android:textColor="@color/colorWhite"/>

方法与对string资源的引用大同小异,不再赘述。

1.3 dimen资源:

dimen是dimension的缩写,表示尺寸。如果我们的布局中有多个view需要指定相同的尺寸,那么我们可以事先在dimen资源中对该尺寸进行定义,之后便可以很方便地复用。
dimen资源所对应的xml文件位于​​​/res/values/​​​目录下。
其默认名为​​​dimens.xml​​​对应于R类中的内部类的名称:R.dimen
文件的根元素为​​resources​​:

定义:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="text_view_height">60dp</dimen>
<dimen name="text_view_width">60dp</dimen>
</resources>

引用:

<TextView
android:layout_width="@dimen/text_view_height"
android:layout_height="@dimen/text_view_width" />

1.4 array资源:

数组资源所对应的xml文件位于​​/res/values/​​​目录下。
其默认名为​​​arrays.xml​​​对应于R类中的内部类的名称:R.array
文件的根元素为​​resources​​:
不同的是,arrays.xml文件中可以定义三种不同类型的子元素:

  1. 普通类型的数组,比如Drawable数组,用​​<array.../>​​来表示。
  2. 字符串类型的数组,用​​<string-array.../>​​来表示。
  3. 整型数组,用​​<integer-array.../>​​来表示。
1.4.1 typedArray定义:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--数组名-->
<array name="color_array">
<item>@color/colorPrimary</item>
<item>@color/colorAccent</item>
<item>@color/colorPrimaryDark</item>
<item>@color/colorBlack</item>
<item>@color/colorCyan</item>
<item>@color/colorGreen</item>
</array>
</resources>

以上在​​/res/values/arrays.xml​​中定义了一个普通类型的数组。这种类型的数组也叫做TypedArray,其中的数组项可以定义Drawable对象等。

在数组的每一项中都引用了​​/res/values/colors/​​​中定义的颜色资源。
接下来可以在java代码中对该数组中的资源加以运用。比如我们可以在布局文件中定义一个文本框,再定义一个按钮,点击按钮实现文本框背景色的轮播:

public class MainActivity extends AppCompatActivity {

int counter = 0;
TextView tv;
Button bn;
TypedArray typedArray;
//注意:调用typedArray的getColor()方法时
//如果不加这个@SuppressWarning标签就会报错
@SuppressWarnings("ResourceType")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.text_view);
bn = (Button) findViewById(R.id.bn);
//通过getResources()方法获取到Resources,并将引用赋给res
Resources res = getResources();
//向obtainTypedArray()方法中,传入R.array.color_array
//返回一个TypedArray对象,命名typedArray
//里面存储的是<array name="color_array">数组中的颜色资源
typedArray = res.obtainTypedArray(R.array.color_array);

bn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int color = typedArray.getColor(counter%typedArray.length(), 0);
tv.setBackgroundColor(color);
counter++;
}
});
}
}


public class MainActivity extends AppCompatActivity {

int counter = 0;
TextView tv;
Button bn;
TypedArray typedArray;
//注意:调用typedArray的getColor()方法时
//如果不加这个@SuppressWarning标签就会报错
@SuppressWarnings("ResourceType")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.text_view);
bn = (Button) findViewById(R.id.bn);
//通过getResources()方法获取到Resources,并将引用赋给res
Resources res = getResources();
//向obtainTypedArray()方法中,传入R.array.color_array
//返回一个TypedArray对象,命名typedArray
//里面存储的是<array name="color_array">数组中的颜色资源
typedArray = res.obtainTypedArray(R.array.color_array);

bn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int color = typedArray.getColor(counter%typedArray.length(), 0);
tv.setBackgroundColor(color);
counter++;
}
});
}
}

效果:




Android资源那些事儿(详)_xml_02


应用TypedArray中的颜色资源-效果图


注:TypedArray在自定义view时也有应用,限于篇幅,本文不深入讲解。

1.4.2 string-array定义:

<string-array name="list_items">
<item>Android</item>
<item>Ios</item>
<item>Swift</item>
<item>Java</item>
<item>C##</item>
<item>@string/text_content</item>
</string-array>

方法是类似的。只不过根元素写的是​​string-array​​​。
其中的字符串既可以直接定义值(前5项),也可以引用事先定义好的字符串(最后一项)。
应用:
比如我们可以在布局文件中定义一个ListView,然后在其​​​entries​​属性中引用该数组:

<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:entries="@array/list_items"/>

效果:




Android资源那些事儿(详)_数组_03


在ListView中引用string-array中的资源


1.4.3 integer-array定义:

与string-array的定义类似,只是将string资源变成了integer类型的资源。

<integer-array name="int_array">
<item>2</item>
<item>4</item>
<item>8</item>
<item>16</item>
</integer-array>

应用:
简单起见,我们同样也可以在ListView中对该数组进行引用:

<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:entries="@array/int_array"/>

效果:




Android资源那些事儿(详)_android_04


在ListView中引用integer-array中的资源


1.5 style资源:

style资源指的是Android的样式资源。
同样在​​​/res/values/​​​目录下定义
style资源文件的根元素也是​​​resources​​​。
​​​resources​​​下可以包含多个​​<style.../>​​​子元素,每个​​style​​​子元素可以定义一个样式,​​style​​标签可以指定两个属性:

  • ​name:​​指定样式的名称;
  • ​parent:​​​ 指定该样式所继承的父样式。与java中的继承类似:当继承某个父样式时,该样式将会获得父样式中定义的全部样式。同样地,当前样式也可以覆盖父样式中指定的格式。
    ​​​<style.../>​​​元素内可以包含多个​​<item.../>​​子元素,每个都可以定义一个格式项。

<style name="样式名" parent="@style/事先定义好的样式名">
<!--可以包含多个item子项-->
<item>...</itme>
</style>

举例:

<style name="style1">
<item name="android:text">Button</item>
<item name="android:textAllCaps">false</item>
</style>
<style name="style2" parent="@style/style1">
<item name="android:background">#666</item>
<!--覆盖父样式中指定的属性-->
<item name="android:textAllCaps">true</item>
</style>

我们可以为两个button分别指定定义的style1和style2:

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/style1"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/style2"/>

效果:




Android资源那些事儿(详)_数组_05


为button指定style


可以看到,第二个button所引用的style2的parent属性指定的是style1。

所以style2继承了style1属性。但是style2中也重写了style1的“textAllCaps”属性,所以第二个button所显示的text默认为大写。

当然,style2继承了style1后,也可以定义自己属性,如以上的

​<item name="android:background">#666</item>​​。

如此一来,就可以事先定义好一组样式的集合,然后将该style一次性应用给某个组件。

1.6 theme资源:

theme资源与style资源类似。
同样在​​​/res/values/​​​目录下定义,根元素同样是​​resource​​​,同样用​​<style.../>​​​来定义。
区别在于:主题应该作用于整个应用中的所有Activity或者作用于某个指定的Activity。且主题影响的应该是窗口的标题、边框等属性:

<style name="my_theme">
<item name="android:windowFullscreen">true</item>
</style>

使用该主题:

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.my_theme);
...

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.my_theme);
...

默认情况下,活动的顶部:




Android资源那些事儿(详)_数组_06


默认情况下活动的顶部


设置

<item name="android:windowFullscreen">true</item>

之后,活动的顶部:




Android资源那些事儿(详)_android_07


设置windowFullScreen主题后活动顶部


如果想要让应用中的所有窗口都应用刚才定义的​​my_theme​​​主题,则只需要在清单文件中的​​<application.../>​​​元素下添加​​android:theme="@style/my_theme">​​即可。

--以上:第一部分--

2.1 图片资源:

图片资源可谓是最简单的drawable资源。只需要把Android认可的图片资源(.png,.jpg,*.gif)放在​​/res/drawable-xxx​​​目录下即可。Android SDK在编译应用时会自动加载图片资源,并在R类中生成对该资源的索引。如此,图片资源就和values资源一样,可以通过
​​​@[<package_name>:]drawable/文件名​​​的方式在xml代码中被访问了。
如果想要在java代码中访问到实际的图片Drawable对象,而不是R类中int类型的索引,可以利用Resources类提供的```Drawable getDrawable(int id)方法。该方法可以根据R类中的id获取到实际的Drawable对象。

2.2 StateListDrawable资源
顾名思义,StateList就是一个state(状态)的集合。它可以用来组织多个Drawable对象,并让使用了该StateListDrawable的组件根据自身不同的状态来自动切换至相应的Drawable。
定义:

  • 在Drawable文件夹下,右键new一个新的drawable resource file
  • 根元素为​​selector​​,可以理解为状态选择器
  • 根元素下可以包含多个​​<item.../>​​ 并可以为其指定如下属性:
  1. android:color 或 android:drawable: 指定颜色或者drawable对象
  2. android:state_xxx: 指定一个特定的状态

举例:
比如我们想让一个button在按下时候和未被按下的时候的背景颜色不同,可以这样写:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--true说明按下了-->
<item android:state_pressed="true" android:drawable="@color/colorCyan"/>
<item android:state_pressed="false" android:drawable="@color/colorPrimary"/>
</selector>

在button中引用这个StateListDrawable:

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="@drawable/bn_state_list"
android:textColor="@color/colorWhite"
android:text="按下会变换背景色"
/>

效果:

属性值

含义

android:state_active

表示是否处于激活状态

android:state_checkable

表示是否处于可勾选状态

android:state_checked

表示是否处于已勾选状态

android:state_enabled

表示是否处于可用状态

android:state_first

表示是否处于开始状态

android:state_focused

表示是否处于已得到焦点状态

android:state_last

表示是否处于结束状态

android:state_middle

表示是否处于中间状态

android:state_pressed

表示是否处于被按下状态

android:state_selected

表示是否处于被选中状态

android:state_window_focused

表示窗口是否处于已得到焦点状态



Android资源那些事儿(详)_xml_08


StateListDrawable


当然,以上只是StateListDrawable所支持的其中两个状态。
完整的状态列表如下:

属性值

含义

android:state_active

表示是否处于激活状态

android:state_checkable

表示是否处于可勾选状态

android:state_checked

表示是否处于已勾选状态

android:state_enabled

表示是否处于可用状态

android:state_first

表示是否处于开始状态

android:state_focused

表示是否处于已得到焦点状态

android:state_last

表示是否处于结束状态

android:state_middle

表示是否处于中间状态

android:state_pressed

表示是否处于被按下状态

android:state_selected

表示是否处于被选中状态

android:state_window_focused

表示窗口是否处于已得到焦点状态

2.3 LayerDrawable资源

LayerDrawable顾名思义,就表现得和图层差不多。可以在根元素​​layer-list​​​中定义多个drawable对象,并且像帧布局那样将各个对象堆叠起来。最后定义的对象处于最上面。
相同的时,根元素下同样可以包含多个​​​<item.../>​​​子项,并可以在其中定义drawable对象的引用。同时还可以设置​​top​​​,​​bottom​​​,​​right​​​以及​​left​​​属性来设置堆叠时,drawable对象向各个方向的偏移量(offset)。
不同的是,​​​<item.../>​​中的各个子项除了指定偏移量之外,还可以指定id属性。另外,根据官方说法:

默认情况下,所有可绘制项都会缩放以适应包含视图的大小。因此,将图像放在图层列表中的不同位置可能会增大视图的大小,并且有些图像会相应地缩放。为避免缩放列表中的项目,请在 <item> 元素内使用 <bitmap> 元素指定可绘制对象,并且对某些不缩放的项目(例如 "center")定义重力。例如,以下 <item> 定义缩放以适应其容器视图的项目:
​​​<item android:drawable="@drawable/image" />​

比如我们要让两个图标堆叠在一起并且在ImageView中显示,可以这样写:
先定义一个​​​layer_drawable.xml​​文件:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!--设置偏移量-->
<item
android:right="20dp"
android:top="20dp">
<bitmap android:src="@drawable/ic_launcher"
android:gravity="center"/>
</item>
<item>
<bitmap android:src="@drawable/ic_launcher_round"
android:gravity="center"/>
</item>
</layer-list>

然后在ImageView中引用:

<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/layer_drawable"/>

效果:




Android资源那些事儿(详)_xml_09


LayerDrawable的堆叠效果


2.4 ShapeDrawable资源

简单来说,Android的ShapeDrawable让我们可以不用做图就能实现各种简单的几何图形,并能控制圆角、填充颜色、边框、内边距、半径等各种属性。这样我们在为某个组件(比如TextView)指定背景时,就方便多了。
定义:
ShapeDrawable的根元素是​​​<shape.../>​​​。 其中​​android:shape="​​​属性有4中值可以选:line, rectangle, oval, ring。
举例:
下面分别定义了两个ShapeDrawable:
​​​shape1.xml​

<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!--设置内边距-->
<padding
android:bottom="4dp"
android:left="4dp"
android:right="4dp"
android:top="4dp" />
<!--设置填充颜色-->
<solid android:color="@color/colorBlack" />
<!--设置边框-->
<stroke
android:width="2dp"
android:color="@color/colorAccent" />
<!--设置圆角矩形-->
<corners android:radius="8dp" />
</shape>

和​​shape2.xml​​:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!--设置填充渐变-->
<gradient
android:angle="45"
android:endColor="@color/colorGreen"
android:startColor="@color/colorCyan"
android:type="linear" />

<stroke
android:width="2dp"
android:color="@color/colorPrimaryDark" />
<size
android:width="200dp"
android:height="100dp" />

</shape>

然后将他们分别设置为两个TextView组件的背景,效果如下:




Android资源那些事儿(详)_数组_10


ShapeDrawable用作背景


当然,不止是TextView可以用ShapeDrawable作为背景,支持将drawable对象作为背景的所有组件都可以。其中各项属性的名称可谓见名知意,不再赘述。

2.5 ClipDrawable资源:

ClipDrawable表示从其他位图(注意是位图)上clip(截取)的一个图片片段。
定义时的根元素是​​​<clip.../>​​​。
总共可以指定三个属性:

  • ​android:drawable​​: 指定截取的源位图文件;
  • ​android:clipOrientation​​: 指定截取方向,可以指定水平(horizontal)截取或者垂直(vertical)截取;
  • ​android:gravity​​​: 指定截取时的对齐方式;可选的值为:
    top, bottom, left, right, center_vertical, fill_vertical, center_horizontal, fill_horizontal, center, fill, clip_vertical, clip_horizontal。
    调用ClipDrawable对象的setLevel(int level)方法可以设置截取区域的大小。level的范围在[0,10000]。也就是说,当level=0时,一点都不截取;当level=10000时,截取整张图片。
    举例:
    比如我们可以借助ClipDrawable和Timer类打造一个简单的进度显示圆:
    先定义my_clip.xml:

<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/circle"
android:clipOrientation="vertical"
android:gravity="bottom">
</clip>

**再在ImageView中引用​​my_clip​​:

<ImageView
android:id="@+id/show_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/my_clip"/>

<Button
android:id="@+id/bn_show_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点按以显示进度"/>

最后再java代码中进行设置:

public class MainActivity extends AppCompatActivity {

ImageView showImage;
Button bn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

showImage = (ImageView) findViewById(R.id.show_image);
bn = (Button) findViewById(R.id.bn_show_progress);
//获取图片所显示的ClipDrawable对象
final ClipDrawable circle = (ClipDrawable) showImage.getDrawable();

final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//如果消息是本程序发送的:
if (msg.what == 0x123) {
//修改ClipDrawable的level值
circle.setLevel(circle.getLevel() + 200);
int currentLevel = circle.getLevel();
}
}
};
//设置button的监听器
bn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//设置bn不可被点击
bn.setEnabled(false);
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
Message msg = new Message();
msg.what = 0x123;
//发送消息,通知应用修改ClipDrawable对象的level值
handler.sendMessage(msg);
//取消定时器
if (circle.getLevel() >= 10000)
timer.cancel();
}
}, 0, 200);
}
});
}
}

public class MainActivity extends AppCompatActivity {

ImageView showImage;
Button bn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

showImage = (ImageView) findViewById(R.id.show_image);
bn = (Button) findViewById(R.id.bn_show_progress);
//获取图片所显示的ClipDrawable对象
final ClipDrawable circle = (ClipDrawable) showImage.getDrawable();

final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//如果消息是本程序发送的:
if (msg.what == 0x123) {
//修改ClipDrawable的level值
circle.setLevel(circle.getLevel() + 200);
int currentLevel = circle.getLevel();
}
}
};
//设置button的监听器
bn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//设置bn不可被点击
bn.setEnabled(false);
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
Message msg = new Message();
msg.what = 0x123;
//发送消息,通知应用修改ClipDrawable对象的level值
handler.sendMessage(msg);
//取消定时器
if (circle.getLevel() >= 10000)
timer.cancel();
}
}, 0, 200);
}
});
}
}

效果:




Android资源那些事儿(详)_xml_11


ClipDrawable 演示


--以上:第二部分--


3.1 ColorStateList资源

ColorStateList在好多书上都没提到,但是却是十分有用。
前面有提到StateListDrawable,它会根据不同的状态来引用不同的drawable对象。但是改变的往往是背景色,对于文字颜色就爱莫能助了。
比如,我们想要让一个button在被设置成​​​enabled="false"​​之后,背景色变为黑色,这很简单:

<Button
android:id="@+id/bn_left"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="bn_left"
android:textAllCaps="false" />

<Button
android:id="@+id/bn_right"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="bn_right"
android:textAllCaps="false" />

并且我们定义一个StaleListDrawable命名为​​bn_state_list​​,使引用它的按钮在不可使用时背景色变黑:

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

接下来在java代码中设置bn_right的监听器,让它被按下时,bn_left的enabled的属性被设置为"false",也就是不可使用的状态。
此时,我们会发现,非常尴尬的一幕发生了:




Android资源那些事儿(详)_android_12


很尴尬


当左边按钮的背景色变黑之后,它上面文字的颜色却没有随之改变,用户体验肯定会大打折扣。
这个时候ColorStateList就能派上用场了:
不同的是,这次我们不再在drawable文件夹上右击新建了,而是再创建一个color文件夹,并在里面新建名为​​​button_text_color.xml​​的文件:




Android资源那些事儿(详)_android_13


image.png


剩下的内容就大同小异了:

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

可以看到我们的根元素同样是和StateListDrawable一样的selector(选择器),并且我们为按钮的不同状态指定了不同的文字颜色。接下来只需要引用这个文件了:

<Button
...
android:background="@drawable/bn_state_list"
android:textColor="@color/button_text_color"
.../>

可以看到,background和textColor引用的是不同的文件。而使我们能随状态改变按钮文字颜色的正是​​android:textColor="@color/button_text_color"​​​。
效果:




Android资源那些事儿(详)_android_14


ColorStateList的效果

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

举报

相关推荐

0 条评论