美文网首页
课程 3B: 面向对象编程

课程 3B: 面向对象编程

作者: HsuJin | 来源:发表于2017-12-24 17:58 被阅读0次

    这节课是 Android 开发(入门)课程 的第一部分《布局和交互》的最后一节课,导师是 Katherine Kuan 和 Kunal Chawla,主要内容是更多的 XML 布局与 Java 代码,完善 Just Java App。这节课出现了很多第一次应用的 Views,但在掌握了高效的学习方法的情况下,可以体验到快速上手的感觉,下面就带领大家开始。

    关键词:CheckBox、ScrollView、EditText、strings.xml、styles.xml,if-else 流控语句、Intent

    CheckBox
    1. Google 搜索 "checkbox android" 第一个结果是 Android 文档;第二个结果是代码示例,也可以在 Common Android Views Cheat Sheets 中找到。

    2. Just Java App 中的 CheckBox 代码如下:

    <CheckBox
       android:id="@+id/whipped_cream_checkbox"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginBottom="8dp"
       android:paddingEnd="24dp"
       android:paddingStart="24dp"
       android:text="@string/whipped_cream"
       android:textSize="16sp" />
    

    CheckBox 是 TextView 的子 class,所以拥有 TextView 的属性,如 android:text。其中,

       android:paddingEnd="24dp"
       android:paddingStart="24dp"
    

    将勾选框和文字分开 24dp。paddingStart 应用于从左往右的视图,paddingEnd 应用于从右往左的视图。

    1. CheckBox 是一个对象,它有一个布尔 (Boolean) 类型的的变量来保存勾选框的状态,显然只有真 (true) 或假 (false) 两种,也就是说布尔变量只有两个值。Google 搜索 "java data type" 查找 Java 的基本数据类型 (Primitive Data Types) 进一步了解。

    2. Just Java App 中的 CheckBox 对象如下:

    CheckBox whippedCreamCheckBox = (CheckBox) findViewById(R.id.whipped_cream_checkbox);
    boolean hasWhippedCream = whippedCreamCheckBox.isChecked();
    //Log.v("MainActivity", "Has whipped cream:" + hasWhippedCream);
    

    (1)布尔变量名同样遵循变量命名规则,常以 has 或 is 开头,如 hasWhippedCream。
    (2)在调试时将信息打印到 logcat 中,能检查代码的运行情况。格式:

    Log.v(“class名”, “字符串”);
    
    • 注意打印信息的长度,避免刷屏刷掉错误等重要信息。调试结束后可将此行注释掉。
    • 布尔变量与字符串连接会变成字符串 true 或 false,如 "Has whipped cream: " + hasWhippedCream → Has whipped cream: true 或 Has whipped cream: false。
    ScrollView

    Google 搜索 "how to scroll in android app" 找到 stack overflow 论坛的结果:利用 ScrollView 实现屏幕垂直方向滚动。这对设备横屏状态下非常有用。ScrollView 只能有一个子 View,Just Java App 中的 ScrollView 如下:

    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:tools="http://schemas.android.com/tools"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       tools:context="com.example.android.justjava.MainActivity">
       …
    </ScrollView>
    

    fill_parent 是 match_parent 的旧版本写法。

    EditText
    1. Google 搜索 "user input android" 找到 Best Practices for User Input 结果,选择 Keyboard Input 可找到 EditText 的代码示例,也可以在 Common Android Views Cheat Sheets 中找到。

    2. EditText 允许用户在 App 中输入文字,Just Java App 中的 EditText 如下:

    <EditText
       android:id="@+id/name_field"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:hint="@string/name"
       android:inputType="textCapWords" />
    

    在 Android 文档中查找 EditText 的属性含义。

    • android:hint 设置在 EditText 无输入时显示的提示文字。
    • android:inputType 设置文字在 EditText 中的显示方式,textCapWords 表示每个单词首字母自动大写,但对 Google 拼音输入法无效。
    1. Google 搜索 "how do i get text from edittext field android" 找到 stack overflow 论坛的结果:通过链式调用 EditText 的 methods 来获取 EditText 中的文字。

    2. Just Java App 中的 EditText 对象如下:

    EditText nameField = (EditText) findViewById(R.id.name_field);
    String name = nameField.getText().toString();
    //Log.v("MainActivity", "Name: " + name);
    

    getText() 的返回值类型为 Editable 对象,进一步调用 toString() 确定返回值为字符串。

    strings.xml
    1. 在 Android Studio 中左侧 Project 标签 Android 视图下 app→res→values→strings.xml 文件应保存 App 的所有字符串资源,所以应将先前硬编码的字符串取到 strings.xml 中。
    2. Just Java App 中的 strings.xml 代码如下:
    <?xml version="1.0" encoding="utf-8"?>
    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
       <!-- Title for the application. [CHAR LIMIT=12] -->
       <string name="app_name">Just Java</string>
    
       <!-- Hint text display in the empty field for the user's name [CHAR LIMIT=20] -->
       <string name="name">Name</string>
       <string name="toppings">Toppings</string>
       <string name="whipped_cream">Whipped Cream</string>
       <string name="chocolate">Chocolate</string>
       <string name="quantity">Quantity</string>
       <string name="decrement">-</string>
       <string name="initial_quantity_value">1</string>
       <string name="increment">+</string>
       <string name="order">Order</string>
    
       <!--
         Name for the order summary. It will be shown in the format of "Name: Amy" where Amy is the
         user's name. [CHAR LIMIT=NONE]
       -->
       <string name="order_summary_name">Name: <xliff:g id="name" example="Amy">%s</xliff:g></string>
    
       <!--
         Whipped cream topping for the order summary. It will be shown in the format of
         "Add whipped cream? true" or "Add whipped cream? false". [CHAR LIMIT=NONE]
       -->
       <string name="order_summary_whipped_cream">Add Whipped Cream? <xliff:g id="addWhippedCream" example="true">%b</xliff:g></string>
    
       <!--
         Chocolate topping for the order summary. It will be shown in the format of
         "Add chocolate? true" or "Add chocolate? false". [CHAR LIMIT=NONE]
       -->
       <string name="order_summary_chocolate">Add chocolate? <xliff:g id="addChocolate" example="true">%b</xliff:g></string>
    
       <!--
         Quantity of coffee cups for the order summary. It will be shown in the format of
         "Quantity: 2", where 2 is the number of cups ordered. [CHAR LIMIT=NONE]
       -->
       <string name="order_summary_quantity">Quantity: <xliff:g id="quantity" example="2">%d</xliff:g></string>
    
       <!--
         Total price for the order summary. It will be shown in the format of
         "Total: $10" where $10 is the price. [CHAR LIMIT=NONE]
       -->
       <string name="order_summary_price">Total: <xliff:g id="price" example="$10">%s</xliff:g></string>
    
       <!-- Thank you message for the order summary. [CHAR LIMIT=NONE] -->
       <string name="thank_you">Thank you!</string>
    
       <!--
         Subject line for the order summary email. It will be in the format of
         "Just Java order for Amy" where Amy is the user's name. [CHAR LIMIT=NONE]
       -->
       <string name="order_summary_email_subject">Just Java order for <xliff:g id="name" example="Amy">%s</xliff:g></string>
    </resources>
    

    (1)所有内容在 <resources …>... </resources> 内。
    (2)字符串定义格式:

        <string name=“字符串名”>字符串</string>
    

    (3)这里使用了 xliff 语句在字符串中添加变量,同时作为占位符表示这部分内容无需翻译,如

    <!-- 指定 xliff 命名空间的 URL -->
    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"
    <!-- 在字符串中添加“姓名”字符串变量 -->
    <string name="order_summary_name">Name: <xliff:g id="name" example="Amy">%s</xliff:g></string>
    <!-- 在字符串中添加布尔类型变量 -->
    <string name="order_summary_chocolate">Add chocolate? <xliff:g id="addChocolate" example="true">%b</xliff:g></string>
    

    从上面的例子可以看出,xliff 语句起止于 <xliff:g ...>...</xliff:g>,中间内容依次为

    • 变量 ID,如 id="name"
    • 变量内容示例,如 example="Amy"
    • 变量格式说明符 (Format Specifiers),如 %s 指字符串,%b 指布尔类型,这篇文章列出了更多 Java 格式说明符。

    (4)XML注释格式:

        <!-- … -->
    

    写下具体的注释使代码易理解,对于字符串复用和翻译很有帮助。

    1. 在 strings.xml 定义所有字符串资源后,可在 XML 和 Java 中调用。

    (1)XML 调用字符串,格式:@string/字符串名

    <TextView
       android:id="@+id/quantity_text_view"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:paddingLeft="8dp"
       android:paddingRight="8dp"
       android:text="@string/initial_quantity_value"
       android:textColor="#000000"
       android:textSize="16sp" />
    

    (2)Java 调用字符串,格式:R.string.字符串名

    /**
    * Create summary of the order..
    *
    * @param name            of the Customer
    * @param price           of the order
    * @param addWhippedCream is whether or not the user wants whipped cream topping
    * @param addChocolate    is whether or not the user wants chocolate topping
    * @return text summary
    */
    private String createOrderSummary(String name, int price, boolean addWhippedCream, boolean addChocolate) {
       return getString(R.string.order_summary_name, name) + "\n" +
              getString(R.string.order_summary_whipped_cream, addWhippedCream) + "\n" +
              getString(R.string.order_summary_chocolate, addChocolate) + "\n" +
              getString(R.string.order_summary_quantity, quantity) + "\n" +
              getString(R.string.order_summary_price, NumberFormat.getCurrencyInstance().format(price)) + "\n" +
              getString(R.string.thank_you);
    }
    
    1. Android App 有默认 (Default) 资源和替代 (Alternate) 资源,使 App 可面对不同的运行环境加载不同的资源,Google 搜索 "localization checklist" 进一步了解。支持多语言是本地化的其中一项工作,Google 搜索 "iso 639-1" 找到 Wikipedia 结果,了解全球语言缩写,如中文为 zh。
    2. 在 app→res 路径下新建 values-zh 目录并新建 strings.xml,翻译字符串为中文:
    <?xml version="1.0" encoding="utf-8"?>
    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
       <!-- Title for the application. [CHAR LIMIT=12] -->
       <string name="app_name">Just Java</string>
    
       <!-- Hint text display in the empty field for the user's name [CHAR LIMIT=20] -->
       <string name="name">姓名</string>
       <string name="toppings">配料</string>
       <string name="whipped_cream">奶盖</string>
       <string name="chocolate">巧克力</string>
       <string name="quantity">数量</string>
       <string name="decrement">-</string>
       <string name="initial_quantity_value">1</string>
       <string name="increment">+</string>
       <string name="order">下单</string>
    
       <!--
         Name for the order summary. It will be shown in the format of "Name: Amy" where Amy is the
         user's name. [CHAR LIMIT=NONE]
       -->
       <string name="order_summary_name">姓名: <xliff:g id="name" example="Amy">%s</xliff:g></string>
    
       <!--
         Whipped cream topping for the order summary. It will be shown in the format of
         "Add whipped cream? true" or "Add whipped cream? false". [CHAR LIMIT=NONE]
       -->
       <string name="order_summary_whipped_cream">需要添加奶盖? <xliff:g id="addWhippedCream" example="true">%b</xliff:g></string>
    
       <!--
         Chocolate topping for the order summary. It will be shown in the format of
         "Add chocolate? true" or "Add chocolate? false". [CHAR LIMIT=NONE]
       -->
       <string name="order_summary_chocolate">需要添加巧克力? <xliff:g id="addChocolate" example="true">%b</xliff:g></string>
    
       <!--
         Quantity of coffee cups for the order summary. It will be shown in the format of
         "Quantity: 2", where 2 is the number of cups ordered. [CHAR LIMIT=NONE]
       -->
       <string name="order_summary_quantity">数量:<xliff:g id="quantity" example="2">%d</xliff:g></string>
    
       <!--
         Total price for the order summary. It will be shown in the format of
         "Total: $10" where $10 is the price. [CHAR LIMIT=NONE]
       -->
       <string name="order_summary_price">价格:<xliff:g id="price" example="$10">%s</xliff:g></string>
    
       <!-- Thank you message for the order summary. [CHAR LIMIT=NONE] -->
       <string name="thank_you">谢谢!</string>
    
       <!--
         Subject line for the order summary email. It will be in the format of
         "Just Java order for Amy" where Amy is the user's name. [CHAR LIMIT=NONE]
       -->
       <string name="order_summary_email_subject">Just Java 订单 <xliff:g id="name" example="Amy">%s</xliff:g></string>
    </resources>
    

    在 Android Studio 中左侧 Project 标签 Android 视图下找到 app→res→values→strings.xml 文件,右键菜单中找到并点击 "Open Editor",可打开对照翻译界面,如下图所示。

    更多资源本地化的信息可到 Localizing with Resources | Android Developers 网站查看。

    styles.xml
    1. 在 Android Studio 中左侧 Project 标签 Android 视图下 app→res→values→styles.xml 文件可统一定义 App 的主题以及 Views 的样式,减少代码冗余。
    2. Just Java App 中的 styles.xml 代码如下:
    <resources>
       <!-- Base application theme. -->
       <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
           <!-- Customize your theme here. -->
           <item name="colorPrimary">@color/colorPrimary</item>
           <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
           <item name="colorAccent">@color/colorAccent</item>
       </style>
    
       <!-- Style for a header TextView. -->
       <style name="HeaderTextStyle" parent="Theme.AppCompat.Light.DarkActionBar">
           <!-- Customize your theme here. -->
           <item name="android:layout_width">wrap_content</item>
           <item name="android:layout_height">48dp</item>
           <item name="android:gravity">center_vertical</item>
           <item name="android:textAllCaps">true</item>
           <item name="android:textSize">15sp</item>
       </style>
    </resources>
    

    (1)所有内容在 <resources …>... </resources> 内。
    (2)同一样式在 <style …>... </style> 内。
    (3)属性定义格式:<item name=“属性名”>真实值</item>

    1. 在 styles.xml 定义样式资源后,可在 XML 中调用:
    <TextView
       style="@style/HeaderTextStyle"
       android:text="@string/toppings" />
    
    <TextView
       style="@style/HeaderTextStyle"
       android:text="@string/quantity" />
    

    两个 TextView 相同处应用同一样式,不同处自行定义属性,减少代码冗余。

    1. 样式可应用于单个的 View,而主题的 style 是应用于某类活动或整个应用的,例如所有文字的颜色,应用栏或状态栏的颜色。如
       <!-- Base application theme. -->
       <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
           <!-- Customize your theme here. -->
           <item name="colorPrimary">@color/colorPrimary</item>
           <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
           <item name="colorAccent">@color/colorAccent</item>
       </style>
    

    其中 parent="Theme.AppCompat.Light.DarkActionBar" 表示 App 主题通过 Appcompat 兼容使用了 Light.DarkActionBar 的主题。同时 AppTheme 在 AndroidManifest.xml 内引用,对于多屏幕应用,不同主题的 Activity 就在这个文件内指定。

    if-else 语句
    1. 控制代码执行顺序称为控制流 (Control Flow),利用 if-else 语句可以使程序跳过部分代码。
    2. Just Java App 中的 if-else 语句如下:
    /**
    * This method is called when the + button is clicked.
    */
    public void increment(View view) {
       if (quantity == 100) {
           // Show an error message as a toast
           Toast.makeText(this,
                   "You cannot have more than 100 coffees!", Toast.LENGTH_SHORT).show();
           // Exit this method early because there's nothing left to do
           return;
       }
       quantity += 1;
       displayQuantity(quantity);
    }
    

    (1)当满足 if 小括号内的条件时,执行大括号内的代码,否则执行 else 大括号内的代码。
    (2)当 else 无需任何操作时可删去。
    (3)利用 return; 提前结束 method。
    (4)运算符 += 可精简代码。
    (5)用 toast 告知用户错误信息或提示,以防用户误以为 App 故障或停止运行。Google 搜索 "toast android" 进一步了解。

    Intent
    1. 利用 Android 框架 Intent 请求其它 App 组件完成动作,使 App 能使用其他 App 的功能,避免重复开发,如相机、地图、浏览器、邮件等。Google 搜索 "common intents" 进一步了解。
    2. Intent 就像传球,无需指定某一个 App 接球,只要设备中有 App 响应,把球接住即可。
    3. Intent 的内容通常有常规动作 (Action)、动作的数据 (Data),还有 Category、Component、Extra。
    4. Just Java App 中的 Intent 代码如下:
    Intent intent = new Intent(Intent.ACTION_SENDTO);
    // only email apps should handle this
    intent.setData(Uri.parse("mailto:"));
    intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.order_summary_email_subject, name));
    intent.putExtra(Intent.EXTRA_TEXT, createOrderSummary(name, price, hasWhippedCream, hasChocolate));
    if (intent.resolveActivity(getPackageManager()) != null) {
       startActivity(intent);
    }
    

    (1)Intent的常规动作 (Aciton): ACTION_SENDTO,常量(全大写、值不变)。
    (2)Intent 的动作数据 (Data): Uri.parse(“mailto:”),有标准格式,Uniform Resource Identifier。
    (3)Intent 的额外数据 (Extra): EXTRA_SUBJECT、EXTRA_TEXT 分别设置邮件主题和内容的文本。
    (4)if 语句判断设备中是否有 App 能响应 Intent,防止 App 崩溃。

    Intent 的内容会在接下来课程中详细介绍。至此 Just Java App 的开发就告一段落了,现阶段这个咖啡订购 App 实现的功能有

    1. 输入订购人姓名;
    2. 选择订购数量,范围为 1~100;
    3. 选择是否添加奶盖和巧克力;
    4. 将订购信息(包含价格)发送到邮箱。

    第一部分《布局和交互》的课程完成了,同时也迎来了入门课程的第三个实战项目:AUdacityQuiz 小测验应用,它应用了除 Intent 外已学的所有知识点,

    以下是在 coding 过程中发现的几个关键点:

    1. 在 AndroidManifest.xml 内的 MainActivity 添加 android:windowSoftInputMode="stateHidden" 使 App 启动时输入法不会自动弹出。
    2. 为了防止手机在旋转后 RadioButton 的状态丢失,override 了 onSaveInstanceState 和 onRestoreInstanceState 用于保存 RadioButton 的状态。
    // Save the isQuestionTwoRight and isQuestionTwoRight variable state
    // When phone rotate to the landscape mode or portrait mode
    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        // Save UI state changes to the savedInstanceState.
        // This bundle will be passed to onCreate if the process is killed and restarted.
        savedInstanceState.putBoolean("isQuestionTwoRight", isQuestionTwoRight);
        savedInstanceState.putBoolean("isQuestionThreeRight", isQuestionThreeRight);
    }
    
    @Override
    public void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        // Restore UI state from the savedInstanceState.
        // This bundle has also been passed to onCreate.
        isQuestionTwoRight = savedInstanceState.getBoolean("isQuestionTwoRight");
        isQuestionThreeRight = savedInstanceState.getBoolean("isQuestionThreeRight");
    }
    
    1. 利用 TextView 属性使两行内容的 Toast 文字居中显示。
    Toast toast = Toast.makeText(this, getString(R.string.toast_line_one, result) + "\n" + getString(R.string.toast_line_two, score), Toast.LENGTH_SHORT);
    // Center each of the two lines Toast texts
    TextView v = toast.getView().findViewById(android.R.id.message);
    if (v != null) {
        v.setGravity(Gravity.CENTER);
    }
    toast.show();
    
    1. 在 ImageView 中通过两条指令使图片完整显示。
    android:adjustViewBounds="true"
    android:scaleType="centerInside"
    
    1. 通过 if/else 语句添加了一条错误提示的 Toast。

    这次项目导师还给出了一些很棒的建议,例如定义 style 时应该注意的一些问题:

    1. 所有 style 都应该有一个 parent;
    2. 给 style 命名时应该遵循 parent 的命名惯性;
    3. 最好让自定义 style 继承自 AppCompat 而不是系统的 style,以确保能向后兼容性。

    相关文章

      网友评论

          本文标题:课程 3B: 面向对象编程

          本文链接:https://www.haomeiwen.com/subject/cencgxtx.html