Android 学习之《Android编程权威指南》第二版 代码+笔记整理(三)

(代码)解决GeoQuiz应用旋转恢复第一题的BUG

GeoQuiz应用初步开发

GeoQuiz应用升级开发

一、产生BUG的原因

1. 设备旋转时,系统会销毁当前的QuizActivity实例,然后创建一个新的实例,这时数组索引(mCurrentIndex)会初始化为0,因此用户看到的还是第一道题目。

2. 旋转设备会改变设备配置。设备配置是用来描述设备当前状态的一系列特征:屏幕方向、密度、尺寸、键盘类型、底座模式以及语言等。在应用运行中,只要设备配置发生改变,Android就会销毁当前activity,然后创建新的activity。

二、解决方法

采用某种方式保存以前的数据:(实现方式之一)

1. 重写protected void onSaveInstanceState(Bundle outState)
2. 该方法通常在onPause()、onStop()以及onDestroy()方法之前由系统调用。
3. Bundle是存储字符串键与限定类型值直接映射关系(键值对)的一种结构。
4. 在Bundle中存储和恢复的数据类型只能是基本数据类型以及可以实现Serializable或Parcelable接口的对象。
5. 在Bundle中保存定制类对象不是一个好主意,因为你所取回的对象可能已经过时了,比较好的做法是:通过其它方式保存定制类对象,而在Bundle中保存对象对应的基本数据类型的标示符。

三、修改QuizActivity.java

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class QuizActivity extends AppCompatActivity {

    private Button mTrueButton; //true选项按钮
    private Button mFalseButton; //false选项按钮
    private Button mNextButton; //next选项按钮
    private TextView mQuestionTextView; //textView文本显示
    private Question[] mQuestionBank = new Question[]{ //Question对象数组
            new Question(R.string.question_oceans,true),
            new Question(R.string.question_mideast,false),
            new Question(R.string.question_africa,false),
            new Question(R.string.question_americas,true),
            new Question(R.string.question_asia,true),
    };
    //KEY_INDEX常量作为将要存储在Bundle中的数组索引变量的键值对的键
    private static final String KEY_INDEX = "index";
    private int mCurrentIndex = 0; //数组索引变量

    private void updateQuestion(){ //更新问题文本内容函数
        int question = mQuestionBank[mCurrentIndex].getTextResId(); //获取资源ID
        mQuestionTextView.setText(question); //设置文本内容
    }

    private void checkAnswer(boolean userPressedTrue){ //检查问题答案函数
        boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue(); //获取对应问题的答案

        int messageResId = 0;
        //根据答案正确与否分配资源ID
        if(userPressedTrue == answerIsTrue){
            messageResId = R.string.correct_toast;
        }else{
            messageResId = R.string.incorrect_toast;
        }
        Toast.makeText(this,messageResId,Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_quiz); //加载布局

        mQuestionTextView = (TextView) findViewById(R.id.question_text_view); //获取TextView对象

        mTrueButton = (Button) findViewById(R.id.true_button); //获取trueButton按钮对象
        mTrueButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                checkAnswer(true);
            }
        });

        mFalseButton = (Button) findViewById(R.id.false_button); //获取falseButton按钮对象
        mFalseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                checkAnswer(false);
            }
        });

        mNextButton = (Button) findViewById(R.id.next_button); //获取NextButton按钮对象
        mNextButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mCurrentIndex = (mCurrentIndex+1) % mQuestionBank.length; //索引值增加1
                updateQuestion();
            }
        });
        //检查是否有存储的键值对,有则拿出来赋值。
        if(savedInstanceState != null){
            mCurrentIndex = savedInstanceState.getInt(KEY_INDEX,0);
        }
        updateQuestion(); //更新问题
    }

	//重写onSaveInstanceState()函数,将要数组索引值存入Bundle当中
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(KEY_INDEX,mCurrentIndex);
    }
}

(代码)创建水平模式布局

为GeoQuiz这个小应用创建水平模式布局以适应设备处于水平方向时的布局显示。

一、效果图

水平模式布局效果图

二、创建水平模式布局

1. 创建资源文件夹 layout-land

创建水平模式布局1
创建水平模式布局2
创建水平模式布局3

2. layout-land/activity_quiz.xml

A. 现在拥有一个水平模式布局以及一个默认布局,这两个布局的文件名称必须相同,这样它们才能以同一个资源ID被引用。

B. 代码:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/question_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:padding="24dp" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical|center_horizontal"
        android:orientation="horizontal">

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

    </LinearLayout>
    <Button
        android:id="@+id/next_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|right"
        android:text="@string/next_button"
        android:drawableRight="@drawable/arrow_right"
        android:drawablePadding="4dp"/>
</FrameLayout>

C. 代码注解:

FrameLayout是最简单的ViewGroup组件,它不以特定方式安排其子视图的位置。子视图的位置安排取决于它们各自的android:layout_gravity属性。

(代码)探究Activity生命周期

不展示编译器自动完成的代码,仅提供手动修改或者编写的代码。

QuizActivity .java(主活动)

通过LogCat查看日志消息

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class QuizActivity extends AppCompatActivity {

    private static final String TAG = "QuizActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"onCreate(Bundle) called");
        setContentView(R.layout.activity_quiz);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG,"onStart() called");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG,"onPause() called");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG,"onResume() called");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG,"onStop() called");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"onDestroy() called");
    }
}

(笔记)第3章 Activity的生命周期

1. 每个Activity实例都有生命周期,在它的生命周期内,activity在 运行、暂停和停止 三种可能的状态间进行转换,当转换发生时,都会有对应的方法将状态改变的消息通知给activity。
Activity状态图解
2. 千万不要自己去调用Activity的任何生命周期方法。
3. Log类有好几个日志记录方法,其中最为常用的是 public static int d(String tag,String msg) 其中d代表debug的意思,是日志信息的级别,第一个参数为日志来源,第二个参数为日志具体内容。第一个参数通常以类名为值的 TAG常量 传入。
日志记录的级别与方法

Log Level Method 说明
ERROR Log.e(…) 错误
WARNING Log.w(…) 警告
INFO Log.i(…) 信息型消息
DEBUG Log.d(…) 调试输出:可能被过滤掉
VERBOSE Log.v(…) 只用于开发

4. 暂停了的activity能够存在多久,无法确定。当系统需要回收内存时,它将首先销毁那些停止的activity。


来自一名刚刚开始学习Android的小菜鸟~

猜你喜欢

转载自blog.csdn.net/Nicholas1hzf/article/details/82953173