您好,欢迎来到二三娱乐。
搜索
您的当前位置:首页安卓入门学习(八)第二个Activity

安卓入门学习(八)第二个Activity

来源:二三娱乐

本文是以Andorid Studio为开发工具,以Android编程权威指南为学习书籍

前言

  1. 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();
            }
        });
        ...
}
...
}

执行应用后,点击问题内容,看是否会跳转至下一题?

  1. 为应用添加后退按钮。当用户点击时,会跳转至上一题。
    首先先为应用添加一个按钮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)。而这个方法实际上是将请求直接发给操作系统的ActivityManagerActivityManager负责创建Activity实例并调用其onCreat()方法。

启动Activity.png

ActivityManager如何知道该启动哪一个Activity呢?答案就在于传入startActivity方法中的Intent参数。

intent对象是component(组件)用来与操作系统通信的一种媒介工具。到目前为止,我们唯一见到的component就是activity。实际上还有其他一些component:service、broadcast receiver以及contentprovider。

Intent是一种多功能通信工具。Intent类提供了多个构造方法,以满足不同的使用需求。在眼前的需求中,我们要用Intent告诉Activity到底哪个Activity是要被启用的。可用public Intent(Context pageContext,Class<?> cls)构造方法。关系图如下:

intent:Activity的信鸽.png
因此在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间进行数据传递。

两个activity间的对话.png
CheatActivity启动后,QuizActivity会将答案通知给它。相同的,在按后退按钮时,在CheatActivity销毁前,会将用户是否作弊的数据传给QuizActivity.
  1. 使用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);
            }
        });

如果有需求,可以加多个extraintent上去。而如果要从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来查看答案。

运行应用后查看答案.png

**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存在。要靠你们的能力给他们填补上啦。

  1. 用户作弊后,可通过旋转CheatActivity来清除作弊行为。
  2. 用户返回后,可通过旋转QuizActivity来消除作弊行为。
    ...

接下来就要靠英勇的战士们处理这些BUG了。

祝大家身体健康,早点休息。

Copyright © 2019- yule263.com 版权所有 湘ICP备2023023988号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务