Android设备有着各种各样的形状和大小. 由于设备种类的多样,你有机会让你的app有大量的使用者. 为了让你的app尽量可用, 你的app需要适配各种设备. 一些你需要考虑的重要变化: 不同的语言、屏幕大小和Android版本. 本课程教你使用基础的平台特性来让你的app能为不同设备提供一个最佳化的用户体验, 仅使用一个APK.

1.支持不同语言

从你的app代码中提取出UI strings,并保存在一个永久的文件中, 这个做法很好. Android通过每个Android项目的资源目录来让这做法变得简单. 如果你使用Android SDK 工具来创建你的项目, 那么该工具创建一个 res/目录在该项目的顶层. 在该res/目录内的是不同资源类型的子目录. 其中有一些默认的文件例如res/values/strings.xml, 保存你的string值.

创建区域(Locale)目录和string文件


为了增加对更多语言的支持, 在res/中创建额外的目录,在目录名的后面加上连字符和ISO国家代码. 例如, values-es/是包含语言代码是”es”的区域的简单资源. Android载入这恰当的资源, 按照设备运行时设置的区域. 你一旦决定你要支持的语言, 创建资源子目录和string资源文件,例:

MyProject/ res/ values/ strings.xml values-es/ strings.xml values-fr/ strings.xml

为每个区域增加string值. 运行时, Android系统会正确根据区域选择string资源. 例如, 一下是一些不同语言的string资源文件. English (default locale), /values/strings.xml:

<?xml version=“1.0” encoding=“utf-8”?>     My Application     Hello World!

Spanish, /values-es/strings.xml:

<?xml version=“1.0” encoding=“utf-8”?>     Mi Aplicación     Hola Mundo!

French, /values-fr/strings.xml:

<?xml version=“1.0” encoding=“utf-8”?>     Mon Application     Bonjour le monde !

注意:你可以在任何资源类型下使用区域限定词, 比如你想提供位图绘制的本地化版本. 更多信息,参见 Localization.

使用string资源


在你的原代码和其他XML文件里, 你可以使用资源名字引用你的string资源, 资源名字由<string>元素的name属性定义. 在你的原代码里, 你可以通过语法R.string.<string_name>参考一个string资源. 有多种方法接受一个string资源. 例如

// Get a string resource from your app’s [Resources](https://developer.android.com/reference/android/content/res/Resources.html) String hello = [getResources()](https://developer.android.com/reference/android/content/Context.html#getResources()).getString(R.string.hello_world);

// Or supply a string resource to a method that requires a string TextView textView = new TextView(this); textView.setText(R.string.hello_world); 在其他XML文件中, 每当XML属性接受一个string值, 你可以通过语法@string/<string_name>指向一个string资源. 例如:

2.支持不同屏幕

Android利用两个一般属性:大小和密度(density). 你希望你的app能在不同大小和密度的屏幕上使用. 那么, 你需要包含一些可替代资源, 这些资源能优化你的app在不同屏幕大小和密度下的外观.

  • 有四个通用的尺寸:小(small), 普通(normal), 大(large), 超大(xlarge)
  • 4个不同的密度: 低 low (ldpi), 中等 medium (mdpi), 高 high (hdpi), 超高 extra high (xhdpi)

为了声明你想使用在不同屏幕上的不同的布局和位图, 你需要在不同的目录里放置这些可替代资源, 跟不同语言的做法类似. 同时注意不同屏幕的方向(横屏或竖屏)是被认为是屏幕大小的一个变体, 故许多app应针对不同的方向,来修订布局以优化用户体验

创建不同的布局

首先, 你应为每个屏幕尺寸创建一个独立的布局XML文件来支持不同屏幕. 每个布局应被保存到相应的资源目录, 名称带有-<screen_size>后缀. 例如, 一个针对大屏的布局应以 res/layout-large/保存. 注意:为了能合适地适应屏幕,Android会自动缩放你的布局. 因此, 不同屏幕大小的布局文件不需要担心UI元素的绝对尺寸, 但是要注意布局结构会影响用户体验(例如关键视图与兄弟视图的相对大小和位置) 例如, 这个项目包含一个一个默认的布局和一个为大屏幕设置的布局

MyProject/ res/ layout/ main.xml layout-large/ main.xml

文件名必须完全一致, 但是他们的内容不同以适配不同屏幕大小 照例引用布局文件

@Override  protected void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      setContentView(R.layout.main); }

系统根据屏幕大小载入合适的布局目录. 更多关于Android如何选择合适的资源, 参加Providing Resources 另一个例子, 针对不同屏幕方向

MyProject/ res/ layout/ main.xml layout-land/ main.xml

默认下, layout/main.xml 在竖屏下使用. 如果你想提供包括大屏幕的横屏, 同时使用largeland限定词

MyProject/ res/ layout/ # default (portrait) main.xml layout-land/ # landscape main.xml layout-large/ # large (portrait) main.xml layout-large-land/ # large landscape main.xml

注意:Android 3.2 或以上版本支持一种定义屏幕大小的高级方法, 该方法允许你依照密度无关的像素基于为最小宽度和高度的不同屏幕大小制定资源

创建不同的位图

你需要提供位图资源,该资源能够被恰当地缩放到每个广义密度桶(generalized density buckets):低, 中, 高, 超高 密度. 这帮你在不同屏幕密度达到高图形质量和性能. 为了生成这些图片, 你需要从你的原始资源 矢量格式, 并为每个密度生成图片, 使用以下尺寸大小:

  • xhdpi: 2.0
  • hdpi: 1.5
  • mdpi: 1.0 (baseline)
  • ldpi: 0.75

这意味着如果你为xhdpi设备生成一个200x200的图片, 你应该在150x150 for hdpi, 100x100 for mdpi, and 75x75 for ldpi 设备中生成同样的资源 然后放置这些文件在相应的可绘制资源目录下:

MyProject/ res/ drawable-xhdpi/ awesomeimage.png drawable-hdpi/ awesomeimage.png drawable-mdpi/ awesomeimage.png drawable-ldpi/ awesomeimage.png

任何时候你引用@drawable/awesomeimage, 系统会根据屏幕密度选择相应的位图. 注意:低密度(ldpi)资源不再是必要的. 当你提供hdpi, 系统会缩放它们一半来适配ldpi屏幕. 更多关于创建icon资产的提示和指导, 参见Iconography design guide

3.支持不同的平台版本

虽然最新版本的Android总是提供很好的API, 但是你还是应该支持旧版本的Android, 直到更多设备获得更新. 本课程教你如何利用好最新API的同时,继续支持旧版本. 平台版本的仪表板定期的显示各个Android版本的分布, 基于访问google play商店的设备数量. 通常地, 在你的app以最新版本为目标时,支持大约90%的活动设备为宜. 提示:为了在几个Android版本上提供更好的特性和功能, 你应该使用Android Support Library, 这让你在旧版本上使用一些最近的平台API.

指定最低和目标API级别

AndroidManifest.xml文件描述关于你app的细节,和识别它支持的Android是哪个版本. 特别的,<uses-sdk中的 minSdkVersiontargetSdkVersion属性定义最低API级别和最高API级别对照(against)你设计和测试过的app. 例如:

        …

当Android的新版本发布, 一些风格和行为可能改变. 为了让你的app利用好这些改变, 并确保你的app符合每个用户设备的风格, 你应该设置targetSdkVersion值匹配最新Android版本.

在运行时检查系统版本

Android为每个平台版本在[Build](https://developer.android.com/reference/android/os/Build.html)常数类中提供一个唯一的代码. 在你的app中使用这些代码构造初始条件, 保证独立于更高API级别的代码能被执行,仅当那些API在系统上可用.

private void setUpActionBar() {     // Make sure we’re running on Honeycomb or higher to use ActionBar APIs     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {         ActionBar actionBar = getActionBar();         actionBar.setDisplayHomeAsUpEnabled(true);     } }

注意: 当解析XML资源时,Android无视那些不被当前设备支持的XML属性. 所以你能安全使用那些仅被更新版本支持的XML属性,而无需当心旧版本遇上这个代码时出错. 例如, 你你设置targetSdkVersion="11", 你的app在Android 3.0或以上版本会默认包含[ActionBar](https://developer.android.com/reference/android/app/ActionBar.html). 然后要给actionbar增加菜单项, 你需要在你的菜单资源XML中设置android:showAsAction="ifRoom". 在交叉版本的XML文件中这是安全的做法, 因为旧版的Android无非就是无视了showAsAction属性(就是说, 你无需在res/menu-v11/中, 设置一个另外的版本

使用平台的风格和主题

Android提供用户体验主题让app有着操作系统般的外观和感受. 在manifest文件内,这些主题能应用到你的app. 通过使用这些风格和主题构建你的app, 你的app会自然遵循最新的Android界面外观. 让你的activity看起来像对话框:

让你的activity拥有透明背景:

使用你的自定义主题, 主题在/res/values/styles.xml中定义:

应用主题到你整个app(所有的activity), 添加android:theme属性到<application>元素

更多关于创建和使用主题的内容, 参见Styles and Themes指引.