这节课是 Android 开发(入门)课程 的第二部分《多屏幕应用》的第三节课,导师依然是 Jessica Lin 和 Katherine Kuan,这节课完成了 Miwok App 的以下几点内容:
- Add all the images to the app.
- Display correct images for each word in the list.
- Add visual polish.
课程 3 的重点是灵活运用面对新需求时的代码变更策略。
当 App 有新需求时,尤其是重大变更时,要养成逐步验证的习惯,不要等到多项修改,甚至完全改完再验证,如果此时发现 bug 将很难定位。既然要分步骤完成,那么作为开发者就应该知道如何安排代码变更的优先级。
- 策略一:首先做风险最大、难度最高的更改,来验证整体概念的可行性。
(Do the riskiest parts first to demonstrate proof of concept) - 策略二:根据目前拥有的资源或数据来安排,此时需要用到占位符数据。即在设计师给出最终图片、媒体文件、文本等资源(Assets)之前,使用虚拟数据放在 App 中大致了解更改的效果。
(Implement what you can based on the available data. Use palceholder data / dummy data)
针对 Miwok App 要增加图片的需求,分四个步骤来完成。
- 首先修改 list_item.xml,添加一个 ImageView,暂时使用
@mipmap/ic_launcher
作为占位符数据来查看效果。 - 等拿到图片 Assets 后,放到 app/res/drawable 目录下就把资源加入 App 工程中了,这里留意到 drawable 分为带后缀的几个目录,不同文件夹存放不同分辨率的图片,Android App 会针对不同屏幕分辨率的设备显示不同目录下的图片,如下图所示,中等分辨率的设备会对应显示 drawable-mdpi 目录下的图片文件。
在设计各个分辨率的图片时,可以参考以下独立像素 (dp) 与像素 (px) 的换算关系。
- 布局修改好,同时资源准备好后,修改 Word 类将图片相关的 state 和 method 添加进来,注意这里存储图片的 ID 比直接将图片存入 Word 会更节省资源,代码如下:
// 声明一个 private 内部变量(m)用于存储图片的 ID
private int mImageResourceId;
// 添加一个构造函数,名字与类完全相同,无返回值
// 因为 PhrasesActivity 不含图片,所以为其他三个 Activity 添加一个三个输入参数的构造函数
/**
* Create a new Word object.
*
* @param defaultTranslation is the word in a language that the user is already familiar with
* (such as English)
* @param miwokTranslation is the word in the Miwok language
* @param imageResourceId is the drawable resource ID for the image assets
*/
public Word(String defaultTranslation, String miwokTranslation, int imageResourceId) {
mDefaultTranslation = defaultTranslation;
mMiwokTranslation = miwokTranslation;
mImageResourceId = imageResourceId;
}
// 图片 ID 对应的 getter method,访问修饰符为 public
public int getmImageResourceId() {
return mImageResourceId;
}
- 最后修改 WordAdapter 将保存了图片 ID 的 Word 数据传入 WordAdapter 。在
getView
method 内添加以下代码即可,用到了setImageResource
method。
// Find the ImageView in the list_item.xml layout with the ID image
ImageView imageView = listItemView.findViewById(R.id.image);
// Get the image resource ID from the current Word object and
// set this text on the image ImageView
imageView.setImageResource(currentWord.getmImageResourceId());
但是在 PhrasesActivity 中由于没有图片,所以视图会产生错误。修复此问题可以为 PhrasesActivity 新建一个 Adapter,与其他三个 Activity 的分开,但是如此一来两个 Adapter 的逻辑有较多重复。为了精简代码,所以修改 WordAdapter 适配两种 Activity 是更好的方案。通过 if/else 判断 Activity 是否含有图片,并利用 setVisibility 属性来控制图片的显示。
In Word.java
// 首先设置一个常量,默认无图片
// static 表示变量与类相关,而不是与类的对象相关,使用时格式例如 View.VISIBLE
// 修饰符 (modifier) final 表示值不可变,即常量,对此赋值 Java 会报错,在 Java 中大写
private static final int NO_IMAGE_PROVIDED = -1;
// 将 mImageResourceId 初始化为无图片
private int mImageResourceId = NO_IMAGE_PROVIDED;
// 新建一个 method 判断是否有图片
// 当图片 ID 不等于 -1 时,返回值 true,表示有图片
// 当图片 ID 等于 -1 时,返回值 false,表示无图片
/**
* Return whether or not there is an image for this word.
*/
public boolean hasImage() {
return mImageResourceId != NO_IMAGE_PROVIDED;
}
In WordAdapter.java
// 调用 hasImage() 判断是否传入有图片
// 若真,将图片 ID 设置到 ImageView,同时设置 ImageView 为 VISIBLE
// 若假,设置 ImageView 为 GONE,不显示图片且不占位
// Find the ImageView in the list_item.xml layout with the ID image
ImageView imageView = listItemView.findViewById(R.id.image);
if (currentWord.hasImage()) {
// Get the image resource ID from the current Word object and
// set this text on the image ImageView
imageView.setImageResource(currentWord.getmImageResourceId());
// Make sure the view is visible
imageView.setVisibility(View.VISIBLE);
} else {
// Otherwise hide the ImageView to the image resource specified in the current Word.
imageView.setVisibility(View.GONE);
}
至此,Miwok App 增加图片的需求分四个步骤完成了。
接下来介绍如何在应用同一个 Layout 的不同 Activity 实现不同的 Layout Attribute,针对 Miwok App 而言是修改四个 Activity 中 Text Container 的背景颜色。最恰当的方法是将颜色 ID 存在 WordAdapter 中,代码如下。
// 首先声明一个存储颜色 ID 的变量
private int mColorResourceId;
// 修改 WordAdapter 的构造函数,将颜色 ID 输入进来并赋值给相应变量
/**
* This is our own custom constructor (it doesn't mirror a superclass constructor).
* The context is used to inflate the layout file, and the list is the data we want
* to populate into the lists.
*
* @param context The current context. Used to inflate the layout file.
* @param words A List of Word objects to display in a list
* @param colorResourceId The background color ID of the LinearLayout of the two words in a list
*/
public WordAdapter(Context context, ArrayList<Word> words, int colorResourceId) {
// Here, we initialize the ArrayAdapter's internal storage for the context and the list.
// the second argument is used when the ArrayAdapter is populating a single TextView.
// Because this is a custom adapter for two TextViews, the adapter is not
// going to use this second argument, so it can be any value. Here, we used 0.
super(context, 0, words);
mColorResourceId = colorResourceId;
}
// 取得颜色 ID 后在 getView 中将它应用到相应的属性 setBackgroundColor 中
// Set the theme color for the list item
View textContainer = listItemView.findViewById(R.id.text_container);
// Find the color that the resource ID maps to
int color = ContextCompat.getColor(getContext(), mColorResourceId);
// Set the background color of the text container View
textContainer.setBackgroundColor(color);
这节课的内容不多,主要讲了面对新需求时的两种代码变更策略,并通过 Miwok App 实践了第二种策略。后续的课程也会延续这种思路,有越来越开放的趋势,包括有在讲解知识点之前要求学员自行找资料尝试 coding 再回来看视频教程的,也在很多要求学员思考并写下来的小练习。这种主动学习会很有意思。
网友评论