《Android编程权威指南(第2版)》第五章挑战练习参考答案

挑战练习

        在给出答案解析前,我已经默认了你已经跟着本书敲完了本篇章中的必要代码,以及理解了这些代码的逻辑。

        GeoQuiz应用有一些大漏洞,我们的任务就是堵住这些漏洞。从易到难,以下为待解决的三个漏洞。

1. 用户作弊后,可以旋转CheatActivity来清除作弊痕迹。

        第一个问题是,为什么旋转CheatActivity会导致作弊痕迹丢失;根据这个问题,我们可以引出一个子问题是,程序原先是如何记录是否作弊的信息并返回给QuizActivity的。

        先来解决子问题,由源程序代码可知,在CheatActivity中,一旦用户点击“显示答案”按钮,就会调用setAnswerShownResult(true)方法,将已经作弊的信息预存入返回码中。当CheatActivity弹出栈时,返回码返回到QuizActivity中,由此把作弊信息带回。

        搞清楚子问题后,我们就可以来看父问题了。在用户查看答案后,此时预返回的返回码中已经带有作弊信息了,而旋转CheatActivity后CheatActivity会销毁并初始化一个新的CheatActivity实例,自然原先的返回码也不带有作弊信息了。

        所以我们要做的是,在旋转CheatActivity时,将这个是否已“作弊”的信息保存下来。其实只需要在“作弊”了后保存下来,如果没“作弊”,不保存也不影响结果。所以我的想法是设立一个标志位来表示用户是否已“作弊”,如果用户点击了“查看答案”按钮,则将该标志设置为true。

        接下来公布我写的答案。

        在CheatActivity中,添加一个私有boolean变量以及一个静态key常量

private boolean setResultFlag = false;
private static final String KEY_IS_CHEATED = "isCheated";

        在onCreate方法中,找到mShowAnswer设置监听事件的代码,修改为如下所示

mShowAnswer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //设置一个标志位,表示作弊过
                setResultFlag = true;
                if (answer == true){
                    mAnswerTextView.setText(R.string.true_btn);
                }else{
                    mAnswerTextView.setText(R.string.false_btn);
                }
                setAnswerShownResult(true);
            }
        });

        在旋转CheatActivity时,我们需要把标志位保存,所以在 onSaveInstanceState中添加如下代码

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        if(setResultFlag){
            //将标志位放入intent
            outState.putBoolean(KEY_IS_CHEATED,setResultFlag);
        }
}

         旋转后,在重建CheatActivity时,让其检查intent中的标志位,我们就知道之前是否作弊过

if(savedInstanceState != null){
    setAnswerShownResult(true);
    setResultFlag = true;
}

2. 作弊返回后,用户可以旋转QuizActivity来清除作弊痕迹。

        经过我的一番思考后,我认为先解决第三个问题,再回来看第二个问题比较合理。所以如果你按顺序看到了这里,请先跳过直接看第三问。

3. 用户可以不断单击NEXT按钮,跳到偷看过答案的问题,从而使作弊记录丢失。

        首先我们要清楚,为什么跳过偷看过答案的问题会使得作弊记录丢失,因为按下"pre"或者“next”按钮后会触发点击事件重置mIsCheater为false,那么作弊记录自然消失了。这不是我们想要的效果,mIsCheater的变化应当与当前问题联系起来,如果当前索引的问题作弊过,则mIsCheater为true;否则为false。我们应当创建一个与当前问题数量相同的boolean数组,以此来记录每个问题是否作弊过;并且当用户翻转Activity时,将这个数组保存起来,下次重建时恢复。

        下面我公布我写的答案。

        第一步,创建一个boolean数组和对应的key常量

private static final String KEY_ISCHEATS = "is_cheats";
private boolean[] mIsCheats = new boolean[]{false,false,false};

        第二步,在返回码返回时,用当前索引的boolean变量来接收它

@Override
protected void onActivityResult(int requestCode, int resultCode,Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode != RESULT_OK)
        return;
    if (requestCode == REQUEST_CODE_CHEAT) {
        if (data == null)
            return;
        mIsCheater = CheatActivity.wasAnswerShown(data);
        mIsCheats[mCurrentIndex] = mIsCheater;
    }
}

        第三步,在onSaveInstanceState方法里将boolean数组保存

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(KEY_INDEX,mCurrentIndex);
    outState.putBooleanArray(KEY_ISCHEATS,mIsCheats);
}

        第四步,重建QuizActivity(调用onCreate)时恢复boolean数组

if(savedInstanceState != null){
    mCurrentIndex = savedInstanceState.getInt(KEY_INDEX);
    Log.d(TAG,"index:" + mCurrentIndex);
    //mIsCheater = savedInstanceState.getBoolean(KEY_ISCHEATED);
    mIsCheats = savedInstanceState.getBooleanArray(KEY_ISCHEATS);
    if(mIsCheats[mCurrentIndex] == true)
        mIsCheater = true;
}

        第五步,修改"pre"和"next"按钮触发事件的逻辑(这里只放出next按钮的修改逻辑,pre同理)

 class NextQuestionListener implements View.OnClickListener{

        @Override
        public void onClick(View v) {
            //让index递增,然后获取对应Question实例的问题
            mCurrentIndex = (mCurrentIndex + 1) % mQuestions.length;
            Log.d(TAG,"index:" + mCurrentIndex);
            int textId = mQuestions[mCurrentIndex].getTextResId();
            mQuestionText.setText(textId);
            //mIsCheater = false;
            //mIsCheater由当前问题是否已作过弊决定
            if(mIsCheats[mCurrentIndex] == true)
                mIsCheater = true;
        }
    }

        至此,第三问解决,同时把第二问也解决了,所以大家可以不用回去看了。

回顾与总结

        通过从0开始跟着做GeoQuiz应用,我们还是学到了很多东西的。Activity如何创建,如何书写xml来设计UI,如何给按钮添加点击事件,MVC的分层思想(实际上我觉得这不是纯正的MVC。。),如何启动第二个Activity,Activity间如何传递数据,了解了下Activity的生命周期,还有如何调试安卓应用。基本上就是这些了,这个应用还真就是入门级别的,不过里面有很多小知识点是我之前没注意到的,比如如何解决旋转Activity数据丢失的问题,之前看天哥的教学视频似乎没有讲到这个,所以我认为多看看书还是有用的,书上会指出一些常见的往人踩过的坑以及给出对应的解决方法,这对于今后的项目实践会有很大的帮助。

        最后,我会把GeoQuiz的源代码开放在Github上,这里是链接,有需要的童鞋可以参考下我对于挑战练习给出的个人答案,那么本期内容就到这里了,我们下期再见!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值