本文是以
Andorid Studio
为开发工具,以Android编程权威指南
为学习书籍
前言
- 为
TextView
添加监听器,使得用户点击问题时也同时能将问题切换至下一题。
public class QuizActivity extends AppCompatActivity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
mQuestionTextView = (TextView)findViewById(R.id.question_text_view);
mQuestionTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex+1)%mQuestionBank.length;
updateQuestion();
}
});
...
}
...
}
执行应用后,点击问题内容,看是否会跳转至下一题?
- 为应用添加后退按钮。当用户点击时,会跳转至上一题。
首先先为应用添加一个按钮Prev
,这里要跟Next
在新的LinearLayout
中。XML布局如下:
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/pre_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pre_button" />
<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next_button" />
</LinearLayout>
相应的,对res/value/string.xml
添加一行<string name="pre_button">Pre</string>
。加完按钮后,对这个按钮进行监听操作。
代码清单3 为Pre添加监听操作
public class QuizActivity extends AppCompatActivity {
...
private Button mPreButton;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
mPreButton = (Button)findViewById(R.id.pre_button);
mPreButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mCurrentIndex == 0) {
mCurrentIndex = mQuestionBank.length-1;
} else {
mCurrentIndex = (mCurrentIndex - 1) % mQuestionBank.length;
}
updateQuestion();
}
});
...
}
...
}
向前按钮功能实现
接下来,我们将创建第二个Activity。创建第二个Activity中,将会学习到:
- 不借助应用向导,创建新的Activity及配套布局。
- 从一个activity中启动另一个activity。启动activity意味着请求操作系统创建新的activty实例并调用此activity的
onCreate(Bundle)
方法。 - 在启动方的Activity与被启动方的Activity间进行数据会传递。
第一节 创建第二个Activity
我们为应用新创建一个Activity,用来查看问题的答案。以方便用户查看当前题目的答案。但在先创建新Activity前,先对原先的旧Activity进行处理,用于新需求的扩展。新增个按钮Cheat!
。
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/cheat_button"
android:text="@string/cheat_button"/>
相应的res/values/string.xml
也添加相应字符串值。
<string name="cheat_button">cheat!</string>
<string name="waring_text">Are you sure you want to do this?</string>
<string name="show_answer_button">Show Answer!</string>
<string name="judgment_toast">Cheating is wrong.</string>
最后在QuizActivity
启动Cheat
按钮。
public class QuizActivity extends AppCompatActivity {
...
private Button mCheatButton;
...
protected void onCreate(Bundle savedInstanceState) {
...
mCheatButton =(Button)findViewById(R.id.cheat_button);
mCheatButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Start CheatActivity
}
});
...
}
}
接下来创建新布局。
新建布局文件.png新创建一个名为
activity_cheat.xml
的布局文件。具体代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/waring_text"
android:padding="24dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/answerTextView"
android:padding="24dp"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/showAnswerButton"
android:text="@string/show_answer_button"/>
</LinearLayout>
显示如下:
新的布局预览-横屏模式.png有了布局文件后,要对此布局操作,得新加个新的
activity
子类。
新建子类.png
打开新创建的子类,发现里面为空的。没有任何方法。我们要为
CheatActivity.java
中覆盖onCreat(Bundle)
方法。将定义好的activity_cheat
布局ID传入setContentView(Int)
import android.app.Activity;
import android.os.Bundle;
/**
* Created by zrthas on 2016/3/26.
*/
public class CheatActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cheat);
}
}
至此,我们将Activity子类与布局文件关联起来了。但这样还是永永不够。接下来我们要到全局manifest
配置文件中声明activity
。
创建QuizActivity
时,因使用了应用向导,向导会自动完成声明工作。而CheatAcitvity
则需要手工完成声明工作。打开项目,在根目录中有AndroidManifest.xml
,双击切换到编辑模式,完成CheatActivity
声明。
<activity
android:name=".CheatActivity"
android:label="@string/app_name" />
这里是加在QuizActivity
的配置后面,而android:name
是必备属性。属性前的.
会告诉OS,在manifast
配置文件头部包属性值指定的包路径下。
第二节 启动第二个Activity
一个activtiy启动另一个activity最简单的方法是用public void startActivity(Intent intent)
。而这个方法实际上是将请求直接发给操作系统的ActivityManager
。ActivityManager
负责创建Activity
实例并调用其onCreat()
方法。
ActivityManager
如何知道该启动哪一个Activity
呢?答案就在于传入startActivity
方法中的Intent
参数。
intent对象是component(组件)用来与操作系统通信的一种媒介工具。到目前为止,我们唯一见到的component就是activity。实际上还有其他一些component:service、broadcast receiver以及contentprovider。
Intent
是一种多功能通信工具。Intent
类提供了多个构造方法,以满足不同的使用需求。在眼前的需求中,我们要用Intent
告诉Activity
到底哪个Activity
是要被启用的。可用public Intent(Context pageContext,Class<?> cls)
构造方法。关系图如下:
因此在
mCheatButton
监听器中,可添加如下代码实现启动新的Activity
mCheatButton =(Button)findViewById(R.id.cheat_button);
mCheatButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(QuizActivity.this,CheatActivity.class);
startActivity(i);
}
});
在启动新的Activity
前,ActivityManager
会检查确认指定的Class
是否在配置文件中存在。如果已完成声明,则启动activity,否则会抛出ActivityNoFoundException
异常。这就是为什么我们要在manifest
配置文件中声明应用全部activity
的原因所在。
显式与隐式Intent
如果通过指定Context与Class对象,然后调用intent的构造方法来构建Intent,则创建的是显式intent。当一个应用的activity如需启动另一个应用的activity,可通过隐式的Intent来进行调用。以后的章节将会详细说明。
接下来,我们执行应用,在手机中操作点击Cheat
看是否会跳出另一个activity。
第三节 activity间的数据传递
以目前需求为例,用户点击Cheat
后,跳转到新的界面,这个新界面该要给用户答案。那如何让CheatActivity
知道答案呢?解决方案就是两个Activity间进行数据传递。
CheatActivity
启动后,QuizActivity
会将答案通知给它。相同的,在按后退按钮时,在CheatActivity
销毁前,会将用户是否作弊的数据传给QuizActivity
.
-
使用intent extra
将答案传给CheatActivity
,需要将以下值传给它:
mQuestionBank[mCurrentIndex].isTrueQuestion();
该值作为extra信息,附加传入startActivity(Intent)
方法的Intent上。
extra可以是任意数据,被包含在Intent中,由启动方的Activity负责发送。而接收方的acitvity接收到系统转发的Intent信息后,访问并获取包含在其中的extra信息。如下图所示:
activity间通信与数据传递.png
如同QuizActivity.onSaveInstanceState(Bundle)
方法中保存mCurrentIndex
值的key-value
一样,extra
也是一种键值的结构。通过public Intent putExtra(String name,boolean value)
将extra
数据添加给intent
。接下来在CheatActivity
中,为extra
新增key
。
public class CheatActivity extends Activity {
public static final String EXTRA_ANSWER_IS_TRUE=
"com.example.administrator.geoquiz.answer_is_true";
...
}
接下来到QuizActivity
,将extra
附加到intent
上。
mCheatButton =(Button)findViewById(R.id.cheat_button);
mCheatButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(QuizActivity.this,CheatActivity.class);
boolean answerIsTrue = mQuestionBank[mCurrentIndex].ismTrueQuestion();
i.putExtra(CheatActivity.EXTRA_ANSWER_IS_TRUE,answerIsTrue);
startActivity(i);
}
});
如果有需求,可以加多个extra
到intent
上去。而如果要从extra
获取数据,会用到public boolean getBooleanExtra(String name,boolean defaultValue)
。这方法中第一个参数是extra
的名字。第二个参数是指定默认值,它在无法获取值时使用。
在CheatActivity.java
添加如下代码:
public class CheatActivity extends Activity {
public static final String EXTRA_ANSWER_IS_TRUE=
com.example.administrator.geoquiz.answer_is_true";
private boolean mAnswerIsTrue;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cheat);
mAnswerIsTrue = getIntent().getBooleanExtra(EXTRA_ANSWER_IS_TRUE,false);
}
}
最后,在CheatActivity
中单击Show Answer
就可以看到答案了。
public class CheatActivity extends Activity {
public static final String EXTRA_ANSWER_IS_TRUE=
"com.example.administrator.geoquiz.answer_is_true";
private boolean mAnswerIsTrue;
private TextView mAnswerTextView;
private Button mShowAnswer;
@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.answerTextView);
mShowAnswer = (Button)findViewById(R.id.showAnswerButton);
mShowAnswer.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);
}
}
});
}
}
最后执行应用,在CheatActivity
中点击Show Answer
来查看答案。
**2.从子activity返回结果 **
当用户按返回按钮时,我们能否知道用户是不是做过弊?答案是用public void startActivityForResult(Intent intent,int requestCode)
方法可从子activity中获得返回值。在QuizActivity
中,修改mCheatButton
监听器,调用刚才说的方法。
mCheatButton =(Button)findViewById(R.id.cheat_button);
mCheatButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(QuizActivity.this,CheatActivity.class);
boolean answerIsTrue = mQuestionBank[mCurrentIndex].ismTrueQuestion();
i.putExtra(CheatActivity.EXTRA_ANSWER_IS_TRUE, answerIsTrue);
//startActivity(i);
startActivityForResult(i,0);
}
});
public void startActivityForResult(Intent intent,int requestCode)
第一个参数为Intent,这个大家都是知道的,而第二个参是请求代码。它是先发送给子Activity,然后再返回给父Activity的用户定义整数值。当一个activity启动多个不同类型的子activity,且需要判断区分消息回馈方时,我们通常会用到这个代码。
实现子activity发送返回信息给父activity,有下面两种方法供调用。
public final void setResult(int resultCode)
public final void setResult(int resultCode,Intent data)
通常来说,参数result code
都是下面两个预定义常量中的一个:
- Activity.RESULT_OK
- Activity.RESULT_CANCELED
在本应用中,CheatActivity
需要将数据返还给QuizActivity
。因此,我们需求创建一个Intent
,附加上extra
信息后,调用Activity.setResult(int,Intent)
方法将信息回传给QuizActivity
。因此在代码中,为extra
添加常量,再创建一个私有方法,用来创建Intent
。然后在Show Answer
中调用此方法。
public class CheatActivity extends Activity {
public static final String EXTRA_ANSWER_IS_TRUE=
"com.example.administrator.geoquiz.answer_is_true";
public static final String EXTRA_ANSWER_SHOWN=
"com.example.administrator.geoquiz.answer_show";
private boolean mAnswerIsTrue;
private TextView mAnswerTextView;
private Button mShowAnswer;
private void setAnswerShownResult(boolean isAnswerShown){
Intent data = new Intent();
data.putExtra(EXTRA_ANSWER_SHOWN,isAnswerShown);
setResult(RESULT_OK,data);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
...
//if user not click the show answer button
//we will not know him cheat
setAnswerShownResult(false);
mShowAnswer.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);
}
...
}
3.处理返回结果
在QuizActivity
中,新加一个成员变量保存CheatActivity
返回的值。然后覆盖onActivityResult(...)
方法去获取它。
private boolean mIsCheat;
@Override
public void onActivityResult(int requestCode,int resultCode,Intent data){
if (data==null){
return;
}else {
mIsCheat = data.getBooleanExtra(CheatActivity.EXTRA_ANSWER_SHOWN,false);
}
}
最后,修改checkAnswer
方法,确认用户是否偷看。
private void checkAnswer(boolean userPressedAnswer) {
boolean answerIsTrue = mQuestionBank[mCurrentIndex].ismTrueQuestion();
int messageResId = 0;
if (mIsCheat) {
messageResId = R.string.judgment_toast;
} else {
if (answerIsTrue == userPressedAnswer) {
messageResId = R.string.correct_toast;
} else {
messageResId = R.string.incorrect_toast;
}
}
Toast.makeText(this, messageResId, Toast.LENGTH_SHORT).show();
}
运行下应用,偷看答案后会有什么事情发生呢?
小结
通过本章的学习,我们学习到如何创建第二个Activity,如何在全局配置,如何两个activity进行相互交流数据。学习好这些后,这个应用还是有不少的BUG存在。要靠你们的能力给他们填补上啦。
- 用户作弊后,可通过旋转CheatActivity来清除作弊行为。
- 用户返回后,可通过旋转QuizActivity来消除作弊行为。
...
接下来就要靠英勇的战士们处理这些BUG了。
祝大家身体健康,早点休息。