目录
7.资源访问(三)
7.样式资源(style)
有时我们需要为某个类型的组件设置相似的格式,比如字体、颜色、背景色等。若每次都要为该组件指定这些属性,不仅会增加工作量,还不利于项目的后期维护。
在编写Word文档时,如果为某段文本设置了样式,那么该样式下的所有格式都会应用于这段文本中。Android的样式与此类似,每种样式都会包含一组格式,一旦为某个组件设置了样式,该样式下的所有格式都会应用于该组件中。
样式资源主要用于对组件的显示样式进行控制,如改变文本框显示文字的大小和颜色等。样式资源文件位于res\values 目录下,它的根元素是<resources></resources>标记,在该元素中,使用<style>标记定义样式。其中,name属性用于指定样式的名称;在<style> 和</style> 标记中间添加<item></item> 标记来定义格式项,在一个<style></style> 标记中,可以包括多个<item></item>标记。
例如,在默认创建的styles.xml中定义一个名称为“title” 的样式,在该样式中定义两个样式,一个是设置文字大小的样式,另一个是设置文字颜色的样式,关键代码如下:
<style name="title">
<item name="android:textSize">30sp</item>
<item name="android:textColor">#f60</item>
</style>
在Android中,还支持继承样式的功能,只需要在<style></style>标记中使用parent属性进行设置即可。例如,定义一个名称为“basic”的样式,再定义一个名称为“title”的样式,并让该样式继承basic样式。关键代码如下:
<style name="basic">
<item name="android:textSize">30sp</item>
<item name="android:textColor">#f60</item>
</style>
<style name="title" parent="basic">
<item name="android:padding">10dp</item>
<item name="android:gravity">center</item>
</style>
注:当一个样式(子样式)继承另一个样式(父样式)后,如果在该子样式中出现了与父样式相同的属性,将使用子样式中定义的属性值。
例:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/background"
android:orientation="vertical">
<TextView
android:id="@+id/main_title"
style="@style/black"
android:layout_gravity="center_horizontal"
android:layout_marginTop="80dp"
android:text="@string/title"
android:textSize="24sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/main_content"
android:text="@string/content"
android:layout_marginTop="20dp"
android:textSize="16sp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ImageView
android:src="@drawable/image"
android:layout_width="match_parent"
android:layout_height="260dp"/>
<TextView
android:id="@+id/main_title_down"
android:text="@string/title_down"
style="@style/text_down"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
res\values\styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="black">
<item name="android:textStyle">bold</item>
<item name="android:textColor">@color/black</item>
</style>
<style name="text_down" parent="black">
<item name="android:layout_gravity">center_horizontal</item>
</style>
</resources>
8.菜单资源(menu)
在桌面应用程序中,菜单的使用十分广泛。但是在Android应用中,菜单大幅减少。不过Android中提供了两种实现菜单的方法,分别是通过Java代码创建菜单和使用菜单资源文件创建菜单,Android推荐使用菜单资源来定义菜单。
8-1.定义菜单资源文件
菜单资源文件通常放置在res\menu目录下,在Android Studio中创建项目时,默认是不自动创建menu目录的,所以需要手动创建。菜单资源的根元素通常使用<menu></menu>标记,在该标记中可以包含多个<item></item>标记,用于定义菜单项,可以通过表7.4所示的属性来为菜单项设置标题等内容。
表7.4 <item></item> 标记的常用属性
属性 | 描述 |
---|---|
android:id | 为菜单项设置ID,也就是唯一标识 |
android:title | 为菜单项设置标题 |
android:alphabeticShortcut | 为菜单项指定字符快捷键 |
android:numericShortcut | 为菜单项指定数字快捷键 |
android:icon | 为菜单项指定图标 |
android:enabled | 指定该菜单项是否可用 |
android:checkable | 指定该菜单项是否可选 |
android:checked | 指定该菜单项是否已选中 |
android:visible | 指定该菜单项是否可见 |
8-2.使用菜单资源
在Android中,定义的菜单资源可以用来创建选项菜单(Option Menu)和上下文菜单(Content Menu)。使用菜单资源创建这两种菜单的方法是不同的。
1.选项菜单
当用户单击菜单按钮时,弹出的菜单就是选项菜单。使用菜单资源创建选项菜单的具体步骤如下:
(1)重写Activity中的onCreateOptionsMenu()方法。在该方法中,首先创建一个用于解析菜单资源文件的MenuInflater对象,然后调用该对象的inflate()方法解析一个菜单资源文件,并把解析后的菜单保存在menu中,关键代码如下:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater=new MenuInflater(this);//实例化一个MenuInflater对象
inflater.inflate(R.menu.optionmenu,menu);//解析菜单文件
return super.onCreateOptionsMenu(menu);
}
(2)重写onOptionsItemSelected()方法,用于当菜单项被选择时,做出相应的处理。例如,当菜单项被选择时,弹出一个消息提示框显示被选中菜单项的标题,可以使用下面的代码:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Toast.makeText(MainActivity.this,item.getTitle(),Toast.LENGTH_SHORT).show();
return super.onOptionsItemSelected(item);
}
例:
res\menu\menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/settings"
android:title="设置"></item>
<item
android:id="@+id/regard"
android:title="关于"></item>
</menu>
MainActivity.java
package com.example.menuresource;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu){
//实例化一个MenuInflater对象
MenuInflater menuInflater=new MenuInflater(this);
//解析菜单文件
menuInflater.inflate(R.menu.menu,menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()){//获取选中的菜单id
//跳转到设置页面
case R.id.settings:
Intent intent = new Intent(MainActivity.this,Settings
.class);
startActivity(intent);
break;
//跳转到关于页面
case R.id.regard:
Intent intent1 = new Intent(MainActivity.this,Regard
.class);
startActivity(intent1);
break;
}
return super.onOptionsItemSelected(item);
}
}
2.上下文菜单
当用户长按组件时,弹出的菜单就是上下文菜单。使用菜单资源创建上下文菜单的具体步骤如下:
(1)在Activity的onCreate()方法中注册上下文菜单。例如,为文本框组件注册上下文菜单,可以使用下面的代码。也就是在长按该文本框时,才显示上下文菜单。
TextView tv=(TextView)findViewById(R.id.show);
registerForContextMenu(tv);//为文本框注册上下文菜单
(2)重写Activity中的onCreateContextMenu()方法。在该方法中,首先创建一个用于解析菜单资源文件的MenuInflater对象,然后调用该对象的inflate() 方法解析一个 菜单资源文件,并把解析后的菜单保存在menu中,最后为菜单头设置图标和标题,关键代码如下:
@override
public void onCreateContextMenu(ContextMenu menu,View v,ContextMenu.ContextMenuInfo menuInfo) {
MenuInflater inflator=new MenuInflater(this);//实例化一个MenuInflater对象
inflator.inflate(R.menu.menus,menu);//解析菜单文件
menu.setHeaderIcon(R.mipmap.ic_launcher);//为菜单头设置图标
menu.setHeaderTitle("请选择");//为菜单头设置标题
}
(3) 重写onContextItemSelected()方法,用于当菜单项被选择时做出相应的处理。例如,当菜单项被选择时,弹出一个消息提示框显示被选中菜单项的标题,可以使用下面的代码:
@Override
public boolean onContextItemSelected(MenuItem item){
Toast.makeText(MainActivity.this,item.getTitle(),Toast.LENGTH_SHORT).show();
return super.onContextItemSelected(item);
}
例:
res\menu\introduce_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_copy"
android:title="@string/introduce_copy"></item>
<item
android:id="@+id/menu_collect"
android:title="@string/introduce_collect"></item>
<item
android:id="@+id/menu_translate"
android:title="@string/introduce_translate"></item>
<item
android:id="@+id/menu_report"
android:title="@string/introduce_report"></item>
</menu>
MainActivity.java
package com.example.contextmenu;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
TextView introduce;//声明TextView组件
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionBar actionBar=getSupportActionBar();
actionBar.hide();
introduce=(TextView) findViewById(R.id.main_introduce);//获取介绍TextView组件
registerForContextMenu(introduce);//为文本框注册上下文菜单
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenu.ContextMenuInfo menuInfo){
MenuInflater inflater=new MenuInflater(this);//实例化一个MenuInflater对象
inflater.inflate(R.menu.introduce_menu,menu);
}
@Override
public boolean onContextItemSelected(MenuItem item){
switch (item.getItemId()){
case R.id.menu_copy:
Toast.makeText(MainActivity.this,"已复制",Toast.LENGTH_SHORT).show();
break;
case R.id.menu_collect:
Toast.makeText(MainActivity.this,"已收藏",Toast.LENGTH_SHORT).show();
break;
}
return true;
}
}
9.Android程序国际化
国际化的英文单词是Internationalization,因为该单词较长,有时简称为I18N,其中,I是国际化的英文单词的首字母;18表示中间省略的字母个数;N是该单词的最后一个字母。Android程序国际化,是指程序可以根据系统所使用的语言,将界面中的文字翻译成与之对应的语言。这样,可以让程序更加通用。Android可以通过资源文件非常方便地实现程序的国际化。
在编写Android项目时,通常都是将程序中要使用的字符串资源放在res\values目录下的stings.xml文件中,为了实现这些字符串资源的国际化,可以在Android项目的res目录下创建对应于各个语言的资源文件夹(例如,为了让程序兼容简体中文、繁体中文和美式英文,可以分别创建名称为“values-zh-rCN” “values-zh-rTW”和“values-en-rUS”的文件夹,如图9.32所示),然后在每个文件夹中创建一个对应的strings.xml文件, 并在该文件中定义对应语言的字符串即可。这样,当程序运行时, 就会自动根据操作系统所使用的语言来显示对应的字符串信息。
例:
res\values-zh-rTW.xml
<resources>
<string name="app_name">Windows Phone</string>
<string name="tv1">微信</string>
<string name="tv2">通訊錄</string>
<string name="tv3">QQ</string>
<string name="tv4">相機</string>
<string name="tv5">時鐘</string>
<string name="tv6">備忘錄</string>
<string name="tv7">音樂</string>
<string name="tv8">互聯網</string>
<string name="tv9">郵件</string>
</resources>
res\values-en-rUS.xml
<resources>
<string name="app_name">Windows Phone</string>
<string name="tv1">WeChat</string>
<string name="tv2">Contacts</string>
<string name="tv3">QQ</string>
<string name="tv4">Camera</string>
<string name="tv5">Clock</string>
<string name="tv6">Notes</string>
<string name="tv7">Music</string>
<string name="tv8">Browser</string>
<string name="tv9">E-mail</string>
</resources>
注:其他代码见3-2小结。
10.难点解答
10-1.dp与px的换算
由于每个厂商的Android手机使用不同尺寸和像素密度的屏幕,所以如果想让App界面能够适应不同密度的手机,那么需要根据各种通用密度级别(包括ldpi、mdpi、hdpi、 xhdpi、xxhdpi)定制相应的位图资源。而要定制位图时,需要知道不同屏幕密度下px与dp的换算关系。表7.5列出了常用屏幕密度下dp与px的换算关系。
表7.5 不同屏幕密度下dp与px的换算
密度 | 密度值 | 分辨率 | 换算 |
---|---|---|---|
ldpi | 120 | 240 X 320 | 1dp=0.75px |
mdpi | 160 | 320 X 480 | 1dp=1px |
hdpi | 240 | 480 X 720 | 1dp=1.5px |
xhdpi | 320 | 720 X 1280 | 1dp=2px |
xxhdpi | 480 | 1080 X 1920 | 1dp=3px |
10-2. Drawable 资源与mipmap资源的区别
从Android Studio 1.1预览版开始,创建项目后就又多出一个mipmap文件夹,用于存储mipmap图片。
mipmap 文件夹仅仅用于存储应用程序启动图标或经常需要缩放而图片质量又要求很高的图片,放置在mipmap文件夹的图片可以根据不同分辨率进行优化,渲染速度快。其他的图像资源,如位图文件(包括PNG、JPG或者GIF)、9-Patch图片或者Shape资源文件等,仍然需要存储在drawable文件夹中。