Android编程权威指南第五章学习

第五章—第二个activity
5.1.1 创建新的 activity
创建新的activity至少涉及三个文件: Java类、 XML布局和应用的manifest文件。这三个文
件关联密切,搞错了就是灾难。因此,强烈建议使用Android Studio的新建activity向导功能。
在项目工具窗口中,右键单击com.bignerdranch.android.geoquiz包,选择New → Activity →
Empty Activity菜单项启动新建activity向导,如图5-3所示
Android编程权威指南第五章学习

5.1.3 在 manifest 配置文件中声明 activity
manifest配置文件是一个包含元数据的XML文件,用来向Android操作系统描述应用。该文件
总是以AndroidManifest.xml命名,可在项目的app/manifests目录中找到它。
应用的所有activity都必须在manifest配置文件中声明,这样操作系统才能够找到它们。
创建QuizActivity时,因使用了新建应用向导,向导已自动完成声明工作。同样,新建activity
向导也自动声明了CheatActivity, 如代码清单5-3灰底部分所示
Android编程权威指南第五章学习

5.2 启动 activity
一个activity启动另一个activity最简单的方式是使用startActivity方法:
public void startActivity(Intent intent)
你也许会想当然地认为, startActivity(Intent)方法是一个静态方法,启动activity就是
调用Activity子类的该方法。实际并非如此。 activity调用startActivity(Intent)方法时,调
用请求实际发给了操作系统。
准确地说,调用请求发送给了操作系统的ActivityManager。 ActivityManager负责创建
Activity实例并调用其onCreate(Bundle)方法
,如图5-7所示。
Android编程权威指南第五章学习
ActivityManager该启动哪个activity呢?答案就在于传入startActivity(Intent)方法的
Intent参数。

基于 intent 的通信
在GeoQuiz应用中, intent用来告诉ActivityManager该启动哪个activity,因此可使用以下构
造方法:
public Intent(Context packageContext, Class<?> cls)
Android编程权威指南第五章学习

如果通过指定Context与Class对象,然后调用intent的构造方法来创建Intent,则创建的是
显式intent在同一应用中,我们使用显式intent来启动activity。

5.3 activity 间的数据传递
Android编程权威指南第五章学习

5.3.1 使用 intent extra
要将extra数据信息添加给intent,需要调用Intent.putExtra(…)方法。确切地说,是调用
如下方法:
public Intent putExtra(String name, boolean value)
Intent.putExtra(…)方法形式多变。不变的是,它总是有两个参数。一个参数是固定为
String类型的键,另一个参数是键值,可以是多种数据类型。该方法返回intent自身
,因此,需
要时可进行链式调用。
Android编程权威指南第五章学习
activity可能启动自不同的地方,所以,应该在获取和使用extra信息的activity那里,为它定义
键。如代码清单5-8所示,记得使用包名修饰extra数据信息,这样,可避免来自不同应用的extra
间发生命名冲突。

现在,可以返回到QuizActivity,将extra附加到intent上。不过我们有个更好的实现方法。
对于CheatActivity处理extra信息的实现细节, QuizActivity和应用的其他代码无需知道。因
此,我们可转而在newIntent(…)方法中封装这些逻辑。
在CheatActivity中,创建newIntent(…)方法,如代码清单5-9所示。
Android编程权威指南第五章学习
在QuizActivity的按钮监听器中,应用newIntent(…)方法,如代码清单5-10所示
Android编程权威指南第五章学习
这里只需一个extra,但如果有需要,也可以附加多个extra到同一个Intent上。如果附加多
个extra,也要给newIntent(…)方法相应添加多个参数。

要从extra获取数据,会用到如下方法:
public boolean getBooleanExtra(String name, boolean defaultValue)
第一个参数是extra的名字。 getBooleanExtra(…)方法的第二个参数是指定默认值(默认
答案),它在无法获得有效键值时使用。
Android编程权威指南第五章学习

然后再根据mAnswerIsTrue来设置对应的Toast信息:
Android编程权威指南第五章学习

5.3.2 从子 activity 获取返回结果
需要从子activity获取返回信息时(即子activity是否偷看答案),可调用以下Activity方法:
public void startActivityForResult(Intent intent, int requestCode)
该方法的第一个参数同前述的intent。**第二个参数是请求代码。 请求代码是先发送给子
activity,然后再返回给父activity的整数值,由用户定义。在一个activity启动多个不同类型的子
activity,且需要判断消息回馈方时,就会用到该请求代码。**虽然QuizActivity只启动一种类型
的子activity,但为应对未来的需求变化,现在就应设置请求代码常量。
在QuizActivity中,修改mCheatButton的监听器,调用startActivityForResult(Intent,
int)方法,如代码清单5-13所示。
Android编程权威指南第五章学习
1. 设置返回结果
实现子activity发送返回信息给父activity,有以下两种方法可用:
public final void setResult(int resultCode)
public final void setResult(int resultCode, Intent data)

一般来说,参数resultCode可以是以下任意一个预定义常量。
 Activity.RESULT_OK
 Activity.RESULT_CANCELED

(如需自己定义结果代码,还可使用另一个常量: RESULT_FIRST_USER。)
在父activity需要依据子activity的完成结果采取不同操作时,设置结果代码就非常有用。
例如,假设子activity有一个OK按钮和一个Cancel按钮,并且每个按钮的单击动作分别设置
有不同的结果代码。那么,根据不同的结果代码,父activity就能采取不同的操作。
子activity可以不调用setResult(…)方法。如果不需要区分附加在intent上的结果或其他信
息,可让操作系统发送默认的结果代码。如果子activity是以调用startActivityForResult(…)
方法启动的,结果代码则总是会返回给父activity。在没有调用setResult(…)方法的情况下,
如果用户按了后退按钮,父activity则会收到Activity.RESULT_CANCELED的结果代码。
2. 返还intent
GeoQuiz应用中,数据信息需要回传给QuizActivity。因此,我们需要创建一个Intent,附
加上extra信息后,调用Activity.setResult(int, Intent)方法将信息回传给QuizActivity。
在CheatActivity代码中,为extra的键增加常量,再创建一个私有方法,用来创建intent、
附加extra并设置结果值。然后在SHOW ANSWER按钮的监听器代码中调用该方法。设置结果值
的方法如代码清单5-14所示
Android编程权威指南第五章学习
用户单击SHOW ANSWER按钮时, CheatActivity调用setResult(int, Intent)方法将
结果代码以及intent打包。
然后,在用户按后退键
回到QuizActivity时, ActivityManager调用父activity
的以下方法:
protected void onActivityResult(int requestCode, int resultCode, Intent data)
该方法的参数来自QuizActivity的原始请求代码以及传入setResult(int, Intent)方法
的结果代码和intent。
Android编程权威指南第五章学习
Android编程权威指南第五章学习

3. 处理返回结果
重写onActivityResult函数

Android编程权威指南第五章学习

Android编程权威指南第五章学习

1.跟第三章的方法一样,重写CheatActivity里的onSaveInstanceState(Bundle saveInstanceState)方法,把mAnswerIsTrue的值保留,旋转后在onCreate(Bundle savedInstanceState)方法里取出来,并判断然后显示。

    @Override
    protected void onSaveInstanceState(Bundle outState) {   //重写onSaveInstanceState方法
        super.onSaveInstanceState(outState);
        outState.putBoolean(KEY_ANSWER,mAnswerIsTrue);
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cheat);



        mAnswerIsTrue = getIntent().getBooleanExtra(EXTRA_ANSWER_IS_TRUE,false);
        mAnswerTextView=(TextView)findViewById(R.id.answer_text_view);
        mShowAnswerButtton=(Button)findViewById(R.id.show_answer_button);

        if(savedInstanceState!=null){    
            mAnswerIsTrue=savedInstanceState.getBoolean(KEY_ANSWER,true);  //将答案取出
            if(mAnswerIsTrue){
                mAnswerTextView.setText(R.string.true_button);
            }else{
                mAnswerTextView.setText(R.string.false_button);
            }

        }

        mShowAnswerButtton.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                if(mAnswerIsTrue){
                    mAnswerTextView.setText(R.string.true_button);
                }else{
                    mAnswerTextView.setText(R.string.false_button);
                }
                setAnswerShownResult(true);

            }
        });
    }
    

2.跟第一题类似,在MainActivity的onSaveInstanceState(Bundle saveInstanceState)里把IsCheater的值保留,旋转后自动调用onCreate函数,再取出来。

3.创建一个mIsCheater[]数组,把得到的每题是否作弊的记录保存在里面。
private boolean mIsCheater[]=new boolean[]{false,false,false,false,false,false};
这行要去掉:
Android编程权威指南第五章学习

**小bug:**测试过程中发现旋转后,回答已经回答了的问题又可以重复回答了,所以旋转之前也应该把每道题是否回答的变量保存下来,查阅了资料Bundle不能之间传对象数组,可以传递对象。但是这个我传递的是一个Boolean型的数组,即把每个问题的AnswerOrNot变量组成一个数组。代码如下:

 boolean AnswerOrNot_Array[]=new boolean[mQuestionBank.length];   //将每道题是否回答的数组传入Bundle
        for(int i=0;i<mQuestionBank.length;i++){
            AnswerOrNot_Array[i]=mQuestionBank[i].isAnswerOrNot();
        }
        saveInstanceState.putBooleanArray(KEY_ANSWERORNOTARRAY,AnswerOrNot_Array);

    if(savedInstanceState!=null){
        mCurrentIndex=savedInstanceState.getInt(KEY_INDEX,0);
        mIsCheater[mCurrentIndex]=savedInstanceState.getBoolean(KEY_ISCHEATER,false);
        boolean AnswerOrNot_Array[]=savedInstanceState.getBooleanArray(KEY_ANSWERORNOTARRAY);
        for (int i=0;i<mQuestionBank.length;i++){
            mQuestionBank[i].setAnswerOrNot(AnswerOrNot_Array[i]);
        }
    }