【Android入门】提高篇:重构BMI应用程序

一、重构程序

  • 重构BMI应用程序

  我们将 “MVC模式” 应用在 “MainActivity.java” 程序上,把 声明与查找界面组件 和 为特定界面组件添加控制流程 的两段代码,分别整理成两个函数 findViews() 和 setListensers()

MainActivity.java

package com.example.myapp;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import java.text.DecimalFormat;

public class MainActivity extends Activity {

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

    private Button button_calc;
    private EditText field_height;
    private EditText field_weight;
    private TextView view_result;
    private TextView view_suggest;

    private void findViews() {
        button_calc = (Button) findViewById(R.id.submit);
        field_height = (EditText) findViewById(R.id.height);
        field_weight = (EditText) findViewById(R.id.weight);
        view_result = (TextView) findViewById(R.id.result);
        view_suggest = (TextView) findViewById(R.id.suggest);
    }
    //Listen for button clicks
    private void setListensers() {
        button_calc.setOnClickListener(calcBMI);
    }
   //改成“Button.OnClickListener”是为了让“Button”界面组件和“OnClickListener”界面组件方法之间的关系清晰
    private Button.OnClickListener calcBMI = new Button.OnClickListener() {
      public void onClick(View v) {
          DecimalFormat nf = new DecimalFormat("0.00");
          double height = Double.parseDouble(field_height.getText().toString())/100;
          double weight = Double.parseDouble(field_weight.getText().toString());
          double BMI = weight / (height * height);
          //Present result
          view_result.setText(getText(R.string.bmi_result) + nf.format(BMI));
          //Give health advice
          if(BMI > 25) {
              view_suggest.setText(R.string.advice_heavy);
          } else if(BMI < 20) {
              view_suggest.setText(R.string.advice_light);
          } else {
              view_suggest.setText(R.string.advice_average);
          }
      }
    };
}
  •  添加对话框(Dialog)

  本节学习如何显示对话框。在本节中,我们要产生一个应用程序中常见的“关于”页面,我们的“关于”页面将以弹出对话框的方式表现。所需要做的是编写负责处理对话框的“openOptionsDialog”函数,并将之附加在原本应用程序中“calcBMI”这个按钮组件的“OnClickListener”方法上。当我们按下“计算BMI值”按钮时,即弹出对话框。

修改 MainActivity.java

private Button.OnClickListener calcBMI = new Button.OnClickListener() {
        public void onClick(View v) {
            ...
            } else {
                view_suggest.setText(R.string.advice_average);
            }
       //调用对话框 openOptionsDialog(); } };
private void openOptionsDialog() {
     //创建一个"AlertDialog"对话框实体,调用"Builder"方法来预备对应的界面组件
new AlertDialog.Builder(MainActivity.this)
          //设置对话框的标题 .setTitle(R.string.about_title)
          //设置对话框的内容 .setMessage(R.string.about_msg)
          //给对话框添加按钮 .setPositiveButton(R.string.ok_label,
              //包含一个没有作用的对话框接口(DialogInterface),表示当我们按下按钮时,不做任何事情直接退出对话框
new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } } ).show(); }

新增 res/layout/string.xml

<resources>
    ...
    <string name="about_title">关于 Android BMI</string>
    <string name="about_msg">Android BMI Calc</string>
    <string name="ok_label">确认</string>
</resources>
  •  Toast界面组件

“Toast”界面组件的作业是弹出一个消息框,快速在屏幕上显示一小段信息。

import android.widget.Toast;
...
//重写函数"openOptionsDialog"
private void openOptionsDialog() {
  //LENGTH_SHORT表示显示时间的长短   Toast.makeText(MainActivity.
this, "BMI计算器", Toast.LENGTH_SHORT).show(); }
  • 错误处理

  前面使用XML说明文件定义界面的时候,在字段的属性中添加了只能输入数字的限制。现在我们将体重(weight)输入的限制移除,允许输入除了整数之外的其他符号(为了可以输入小数点位数的体重)。

<EditText
  android:id="@+id/weight"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:inputType="numberDecimal" //删除,解除了必须输整数的限制 
  android:singleLine="true"         //增加,避免用户因为按到Enter键而多输入几行内容,所以增加了单行输入
  android:text=""/>        

  上述的修改可能会造成,在“体重”字段中输入非整数也非浮点数的值时,整个程序会报错。因此在下面的程序中加入“try...catch”语句处理错误,利用“Toast”组件来通知用户输入错误。

  DecimalFormat nf = new DecimalFormat("0.00");
  try {
    double height = Double.parseDouble(field_height.getText().toString()) / 100;
    double weight = Double.parseDouble(field_weight.getText().toString());
    double BMI = weight / (height * height);
    //Present result
    view_result.setText(getText(R.string.bmi_result) + nf.format(BMI));
    //Give health advice
    if (BMI > 25) {
      view_suggest.setText(R.string.advice_heavy);
    } else if (BMI < 20) {
      view_suggest.setText(R.string.advice_light);
    } else {
      view_suggest.setText(R.string.advice_average);
    }
      openOptionsDialog();
    } catch (Exception e) {
      Toast.makeText(MainActivity.this, "打错了吗?只能输入数字喔", Toast.LENGTH_SHORT).show();
    }

 二、查看线上内容

打开网页

在“openOptionsDialog”对话框函数中,新添加一个“链接到首页”的按钮

private void openOptionsDialog() {
  new AlertDialog.Builder(this)
    .setTitle(R.string.about_title)
    .setMessage(R.string.about_msg)
    .setPositiveButton(R.string.ok_label,
      new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
        }
      })
    //使用“setNegativeButton”方法提供“NegativeButton”按钮
    .setNegativeButton("首页",
      new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
          //为这个按钮添加了链接到指定网站的动作
          Uri uri = Uri.parse("http://sites.google.com/site/gasodroid");
          Intent intent = new Intent(Intent.ACTION_VIEW, uri);
          startActivity(intent);
        }
      })
    .show();
} 

 通过“startActivity”函数,Android系统根据收到不同的“意图”(Intent)的动作和内容,打开对应的新页面或新程序。Intent及Intent和Activity之间的关系还需要后期系统的学习

 

后期为了方便字符串的管理,需要提取到strings.xml中,然后发现“Uri.parse()”函数不接受资源标识符类型的输入。我们可以使用“android.content.Context”类中的“getString”函数(或是getText),来取得资源标识符对应的文字。

Uri uri = Uri.parse(getString(R.string.homepage_uri));

另外网址输错也是常有的事,为了避免出错影响程序使用,我们可以使用“try...catch”语句来包住它(下面的异常类型有问题,需要查证

try {
  //为这个按钮添加了链接到指定网站的动作
  Uri uri = Uri.parse(getString(R.string.homepage_uri));
  Intent intent = new Intent(Intent.ACTION_VIEW, uri);
  startActivity(intent);
} catch (URISyntaxException e) {
                                    
}

 三、添加菜单

我们把“openOptionsDialog”移出“OnClickListener”方法,将运行流程改为按下“Menu”键后,弹出一个菜单栏(Menu Bar)。当我们点击菜单栏中的选项后,才弹出“openOptionsDialog”的对话框

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
     ...
    }protected static final int MENU_ABOUT = Menu.FIRST;
    protected static final int MENU_Quit = Menu.FIRST + 1;
/** * 创建菜单 * 此方法用于初始化菜单,其中menu参数就是即将要显示的Menu实例。返回true则显示该menu,false则不显示 */ public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); //增加菜单栏中选项,“.setIcon”方法来添加选项图标 //menu.add(0, 标识符(identifer), 0, 字符串或资源标识符) menu.add(0, MENU_ABOUT, 0, "关于...").setIcon(android.R.drawable.ic_menu_help); menu.add(0, MENU_Quit, 0, "结束"); return true; } /** * 处理选项动作 * 菜单项被点击时调用,也就是菜单项的监听方法 */ public boolean onOptionsItemSelected(MenuItem item) { //“item.getItemId()”函数用来获取所选项对应的标识符(identifer) switch (item.getItemId()) { case MENU_ABOUT: openOptionsDialog(); break; case MENU_Quit: finish(); break; } return super.onOptionsItemSelected(item); } }

 四、定义Android列表(Manifest)

AndroidManifest.xml清单文件通常包括如下内容:

  • 应用程序的包名,该包名作为该应用的唯一标识。
  • 应用包含的组件,如Activity,Service,Broadcastreceiver和ContentProvider.
  • 应用程序使用系统所需的权限声明。
  • 其他程序访问该程序所需的权限声明。

 五、添加新活动(Activity)

   本章重点讲Activity的切换。Activity分为独立的Activity和相依赖的Activity,独立的Activity不涉及数据的交换,只是单纯地从一个屏幕跳到下一个屏幕;而相依赖的Activity需要与其他Activity交换数据,相依赖的Activity又可分为单向与双向。

//Activity 切换
Intent intent = new Intent();
intent.setClass(MainActivity.this, Report.class);
startActivity(intent);

将定义好的intent传入“startActivity”函数中。“startActivity”函数会将intent传入Android框架,Android框架会根据各应用程序在系统中注册的信息,找到“要跳转的”Activity,并调用它。

六、传送数据到新意图(Intent)

  Android使用Intent来完成在屏幕间切换的动作。Intent包含Activity间切换所需的动作、分类、传送数据等信息。Intent可以分为“默认的Intent”和“自定义的Intent”

  本章中我们会完成将“BMI应用程序”从一个页面改成两个页面:“输入页面”(原本的Main Activity)与“结果页面”(Report Activity)的应用程序。“输入页面”从界面上取得身高、体重值,通过传送Intent,将值携带到“结果页面”。“结果页面”从Intent中取出其携带的身高、体重值,用这两个参数来产生“BMI”报告结果。

  • 使用Intent传递数据

修改 MainActivity.java  “Button.OnClickListener”函数

private Button.OnClickListener calcBMI = new Button.OnClickListener() {
  public void onClick(View v) {
    //Switch to report page
    Intent intent = new Intent();
    intent.setClass(MainActivity.this, Report.class);
    //携带数据     Bundle bundle
= new Bundle();     bundle.putString("KEY_HEIGHT", field_height.getText().toString());     bundle.putString("KEY_WEIGHT", field_weight.getText().toString());     intent.putExtras(bundle);
    startActivity(intent);
  } };

相依赖的Activity需要靠Intent对象携带数据传送到新的Activity。附加在Intent上的消息都存储在“bundle”对象实体中,通过“intent.putExtras(bundle)”语句,我们将“bundle”对象实体附加在“intent”对象实体上,随着Intent送出而送出

“Bundle”类其实是一种特别定义的映射(map)类型,类似于键值对

  • 使用Intent接收信息

新建“res/values/report.xml”文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="report_title">BMI 报告</string>
    <string name="report_back">前一页</string>
</resources>

修改“res/layout/activity_report.xml”文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context=".Report">

    <TextView android:id="@+id/result"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="" />

    <TextView android:id="@+id/suggest"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="" />

    <Button android:id="@+id/report_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/report_back"/>

</LinearLayout>

 新建Report.java 完整程序如下:

package com.example.myapp;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.text.DecimalFormat;

public class Report extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_report);
        findViews();
        showResults();
        setListensers();
    }

    private Button button_back;
    private TextView view_result;
    private TextView view_suggest;

    private void findViews() {
        button_back = (Button) findViewById(R.id.report_back);
        view_result = (TextView) findViewById(R.id.result);
        view_suggest = (TextView) findViewById(R.id.suggest);
    }

    private void setListensers() {
        button_back.setOnClickListener(backMain);
    }

    private Button.OnClickListener backMain = new Button.OnClickListener() {
        @Override
        public void onClick(View v) {
            //Close this Activity,显示出之前的Activity
            Report.this.finish();
        }
    };

    private void showResults() {
        DecimalFormat nf = new DecimalFormat("0.00");
     //接收传来的“Intent”对象实体
        Bundle bundle = this.getIntent().getExtras();
        double height = Double.parseDouble(bundle.getString("KEY_HEIGHT"))/100;
        double weight = Double.parseDouble(bundle.getString("KEY_WEIGHT"));
        double BMI = weight / (height * height);
        view_result.setText(getString(R.string.bmi_result) + nf.format(BMI));

        //Give health advice
        if(BMI>25) {
            view_suggest.setText(R.string.advice_heavy);
        } else if(BMI<20) {
            view_suggest.setText(R.string.advice_light);
        } else {
            view_suggest.setText(R.string.advice_average);
        }

    }
}

Report.java 主要用到 “Activity.getIntent().getExtras()” 来获取传来的 “Intent” 对象实体  

七、信息提醒(Notification)

在 Report.java 中新增一个函数,用于消息提醒

public class Report extends Activity {
...
  private void showResults() { DecimalFormat nf = new DecimalFormat("0.00"); Bundle bundle = this.getIntent().getExtras(); double height = Double.parseDouble(bundle.getString("KEY_HEIGHT"))/100; double weight = Double.parseDouble(bundle.getString("KEY_WEIGHT")); double BMI = weight / (height * height); view_result.setText(getString(R.string.bmi_result) + nf.format(BMI)); //Give health advice if(BMI>25) { showNotification(BMI); view_suggest.setText(R.string.advice_heavy); } else if(BMI<20) { view_suggest.setText(R.string.advice_light); } else { view_suggest.setText(R.string.advice_average); } } protected void showNotification(double BMI) { PendingIntent pendingIntent = PendingIntent.getActivity(this,0,new Intent(this, MainActivity.class),0); String id ="channel_1";//channel的id String description = "123";//channel的描述信息 int importance = NotificationManager.IMPORTANCE_LOW;//channel的重要性 NotificationChannel channel = new NotificationChannel(id, "123", importance);//生成channel //为channel添加属性 //channel.enableVibration(true); 震动 //channel.enableLights(true);提示灯 NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); manager.createNotificationChannel(channel);//添加channel Notification notification = new Notification.Builder(this,id) //注意这里多了一个参数id,指配置的NotificationChannel的id //你可以自己去试一下 运行一次后 即配置完后 将这行代码以上的代 //码注释掉 将参数id直接改成“channel_1”也可以成功运行 //但改成别的如“channel_2”就不行了 .setCategory(Notification.CATEGORY_MESSAGE) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("This is a content title") .setContentText("This is a content text") .setContentIntent(pendingIntent) .setAutoCancel(true) .build(); manager.notify(1,notification); } }

参考:https://blog.csdn.net/weixin_40604111/article/details/78674563

      https://www.jianshu.com/p/a84ddaf530ec

八、优先级

  使用情景:当用户第一次输入身高体重值后,程序能帮我们预先记住上次输入过的身高,那么等到下次运行程序时,便只需要输入体重。

打开“MainActivity.java”,在“onCreate”和“onPalse”中添加“Preference”(优先级设置)相关的程序代码。

 

卡在 onPalse() 函数

 

posted @ 2019-05-08 14:57  哈撒king  阅读(642)  评论(0编辑  收藏  举报