登录后台

页面导航

本文编写于 2410 天前,最后修改于 1668 天前,其中某些信息可能已经过时。

0 公告

  • 已开设交流群,回复「群」获取加群方式
  • 逐步减少 AIDE 的演示,因为 AS 和 AIDE 的代码一样,只是操作方式不一样。AIDE 的基本操作都已经演示过了。
  • Android Studio 以后均简称为 AS ,且不再重复

1 注意

为了暂时保持 AS 和 AIDE 代码的统一性,在 AS 新建项目时请不要勾选 「Backwards Compatibility (AppCompat)」。

1525940616529

2 讲作业

昨天的作业是做一个简易的加法计算器,先来考虑需求,应该是允许输入两个数点击一个按钮,出现结果。新建一个项目:「计算器」包名就是「edittext.test.jishuan」,

这样我们在设计布局的时候就加入两个 EditText 来让用户输入,然后安放一个 Button 来确定,最后在一个 TextView 来显示结果。在细节上来说,EditText 应该只允许输入数字:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edittext_first"
        android:text="第一个加数"
        android:gravity="center"
        android:inputType="number"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="+"
        android:gravity="center"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edittext_second"
        android:gravity="center"
        android:text="第二个加数"
        android:inputType="number"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textview_result"
        android:gravity="center"
        android:text="结果"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button_run"
        android:text="计算"/>

</LinearLayout>

虽然有点长但是都是我们熟悉的属性,效果如下:

布局代码操作演示:

现在来想一想逻辑,应该是用户点击按钮之后先判断一下两个输入框是否为空,然后获取两个输入框里的数值,再相加,最后显示到 TextView 上:

public class MainActivity extends Activity implements View.OnClickListener{  
    //接口方式实现 onClick()
    
    Button btn; //计算按钮
    EditText et1, et2; //两个输入框
    TextView tv; //显示结果的 TextView

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView(); //初始化控件
        et1.setOnClickListener(this); //绑定第一个输入框的点击监听器
        et2.setOnClickListener(this); //绑定第二个输入框的点击监听器
        btn.setOnClickListener(this); //绑定按钮的点击监听器
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.edittext_first:
                //点击第一个输入框时清空原有的数据
                et1.setText("");
                break;
            case R.id.edittext_second:
                //点击第二个输入框时清空原有的数据
                et2.setText("");
                break;
            case R.id.button_run:
                int result;
                try{
                    if(checkEmpty()) throw new Exception("输入的数据无效");
                    int first = Integer.parseInt(et1.getText().toString());
                    int second = Integer.parseInt(et2.getText().toString());
                    result = first + second;
                    Log.d("结果", Integer.toString(result));
                    tv.setText(Integer.toString(result));
                } catch (NumberFormatException e){
                    Toast.makeText(MainActivity.this,"请填写每一个输入框", Toast.LENGTH_SHORT).show();
                } catch (Exception e){
                    Toast.makeText(MainActivity.this,e.getMessage(), Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }
    }

    void initView(){
        et1 = (EditText)findViewById(R.id.edittext_first);
        et2 = (EditText)findViewById(R.id.edittext_second);
        btn = (Button)findViewById(R.id.button_run);
        tv = (TextView)findViewById(R.id.textview_result);
    }

    boolean checkEmpty(){
        //判断两个输入框是否为空
        if (TextUtils.isEmpty(et1.getText())) return true;
        if (TextUtils.isEmpty(et2.getText())) return true;
        return false;
    }
    
}

运行效果如下:

都是以前的知识堆积起来的,但是有两个新鲜的东西。判断一个字符串是否为空用 「TextUtils.isEmpty()」,比如判断字符串 a 是否为空,就这么写「TextUtils.isEmpty(a)」,如果为空就会返回 true,非空就会返回 false。另一个便是 try 块了。

3 捕获异常

异常捕获就是字面意思,把异常抓住。在之前,如果一段代码出错了,程序将崩溃。但是如果我们能预知或是觉得这一段代码可能会出错,那么我们就可以把错误抓住,也就是「异常捕获」。我们使用 try...catch 捕获异常,以下是异常捕获的典型代码:

try{
    //可能会出错的代码
} catch (Exception e){
    //出现问题时的处理方法
} finally {
    //最后执行的收尾工作
}

如果在执行 try 块里的程序代码时出现异常,系统会自动生成一个异常对象,该异常对象被提交给了 Java 运行时环境。这个过程被称作抛出『throw』异常。当 Java 运行时环境收到异常对象时时,会寻找该异常对象的 catch 块,如果找到合适的 catch 块,则把该异常对象交给该 catch 块执行,这个过程叫做捕获『catch』异常;如果 Java 运行时环境找不到捕获异常的 catch 块,运行时环境将会终止,程序也会退出。但是需要注意的是,不管代码是否在 try 块中,只要执行该代码块时出现了异常,系统总是会生成一个异常对象,如果系统没有找到 catch 块,程序直接「crash」退出。异常处理中只有 try 块是必须的,也就是说,没有 try 块,就不能有后面的 catch 块和 finally 块;catch 块和 finally 块都是可选的,但 catch 和 finally 至少出现一个,也可以同时出现;可以有多个 catch 块,多个 catch 块必须在 try 块后面 finally 块前边,也就是说 finally 块必须位于所有的 catch 块之后。常见的异常如下:

  • NullPointerException(空指针异常)
  • NumberFormatException(字符串转换为数字异常)
  • IndexOutOfBoundsException(数组下标越界异常)
  • ArithmeticException(数学运算异常)
  • Exception(所有异常)

比如我们想要捕获「ArithmeticException(数学运算异常)」,则可以这么写:

try{
    //可能会出现数学运算异常的代码
} catch (ArithmeticException e){
    //出现问题时的处理方法
}

tips:「数学运算异常」通常就是一个数除以零造成的;

也可以捕获多种异常:

try{
    //可能会出现多种异常的代码
} catch (ArithmeticException e){
    //出现数学运算异常时的处理办法
} catch (NumberFormatException e){
    //出现字符串转化异常时的处理办法
} catch (Exception e){
    //其余所有的错误的处理办法
}

tips:由于「Exception」是异常的父类,所以他可以代表所有错误

我们继续看向今天的程序,

try{
    if(checkEmpty()) throw new Exception("输入的数据无效");
    int first = Integer.parseInt(et1.getText().toString());
                    int second = Integer.parseInt(et2.getText().toString());
                    result = first + second;
                    Log.d("结果", Integer.toString(result));
                    tv.setText(Integer.toString(result));
} catch (NumberFormatException e){
                    Toast.makeText(MainActivity.this,
                                   "请填写每一个输入框", 
                                   Toast.LENGTH_SHORT).show();
                }  catch (Exception e){
                    Toast.makeText(MainActivity.this,e.getMessage(), Toast.LENGTH_SHORT).show();
                }
}

由于用户可能输入中文,有可能出现运算错误,所以要进行异常捕获,大体来看可以这么简化一下:

try{
    //进行计算的代码
} catch (ArithmeticException e){
    //出现数学运算异常时提示用户检查输入的数据
} catch (Exception e){
    //其余出现所有的错误直接提示用户
    //可通过 e.getMessage() 来获取异常信息
}

开发中可以调用「getMessage」方法来获取具体的异常信息。

4 抛出异常

在程序中出现了我们可以判断的错误时我们也可以手动制造一个异常来让当前代码块结束运行。譬如在今天的程序中:

 if(checkEmpty()) throw new Exception("输入的数据无效");

当发现输入框时空的,程序当然不能继续计算,所以我手动抛出了一个异常,内容是「输入的数据无效」。抛出异常的语法如下:

throw new 异常类型("异常内容");

tips:当 if 语句只有一行时,{}大括号可以省略。

现在来整理一下,计算的过程,先判断输入框是否为空,空的话抛出异常;然后先「getText」获取输入框的内的字符串,然后为了计算,调用「Integer.parseInt(int)」来把字符串转化为整数,但是这一步的输入如果有非数字内容的话会抛出 「NumberFormatException」异常,要进行适当的异常捕获,防止崩溃;这样就可以获取到了两个输入框中的数字,将他俩相加,然后调用「Integer.toString(int)」来把结果的数字转化为字符串。这里贴一个刚踩的坑,textview.setText()不能传 如int ,如果传入 int 的话编译可以通过,但是运行报错。为什么呢?还记得可以通过 R.String.xxx 来获取字符串吧(其实估计你忘了,忘了就直接跳过吧),这个其实返回的是一个 int 。这时如果我们传入 int 系统就会把他当作一个 resourse 资源。当然这不是一个资源值,所以会崩溃。所以在 setText 一个数字时一定要先将他转化为字符串。

5 总结

今天本来是做浏览器的来着,但是我发现这个作业躲不过异常捕获,因为我们虽然限制只能输入数字,但是我们默认的提示词用户如果不更改就点击计算就会导致程序崩溃。我转念一想,这是一个非常好的介绍异常捕获的机会,人不是神,写的代码总归会有崩溃的时候,做好异常处理,主动出击,可以使程序更加健壮。今天的作业就是改进一下昨天的计算器,让他「百毒不侵」。

2020/5/24 注:
这是我 2018 年高中毕业的假期写的,当时并没有自己搭建博客,近期我给这一系列的文章都放到我自己的博客上。
当时写这个系列文章的想法就是扎实下基础,然后能帮到更多的人就更好了,但是大学开学之后就开始了新生活,没有大块的时间去编撰,系列文章也没有再更新过。但是我其实并不想放弃这个系列,那就,有缘再见吧,拜拜ヾ(•ω•`)o