小白,请多指教

GeoQuiz应用介绍

该应用是《Android权威编程指南》中的第一个DEMO,大概占了六-七章的篇幅,主要功能是:

  1. 用户通过点击“True”或“False”按钮回答屏幕上显示的判断题,并根据Toast出的信息检查自己的答案是否正确;
  2. 通过点击“上一题”或“下一题”切换题目;
  3. 该应用还提供了作弊功能,当用户点击“Cheat”按钮时,应用会告诉你正确答案,但通过作弊手段得到正确答案的题目将在用户回答该问题时Toast出“你是个作弊者”的信息;
  4. 该应用还提供了重置功能,当用户点击“RESET”时,所有作弊记录都将被清空。(这个功能书上没有);

本人还修复了该DEMO的若干个bug(这些bug实际上是该书故意留给读者解决的):

  1. 当用户来到作弊界面以后,可以通过旋转屏幕的方式来清除作弊痕迹;
  2. 作弊并返回答题界面后,用户可以通过旋转屏幕的方式来清除作弊痕迹;
  3. 在作弊界面,用户得到题目的结果后,若旋转屏幕,结果丢失;
  4. 用户可以通过切换题目的方式清除作弊记录;

通过该DEMO能学到的知识点:

  1. 通过onSaveInstanceState()方法保存Activity界面上的临时数据;
  2. 横竖屏切换和Activity生命周期的关系;
  3. UI控件AlertDialog的设计模式;
  4. layout布局中各控件带“layout”属性和不带“layout”属性的区别
  5. MVC设计模式;

一、 答题界面的activity和它的布局介绍

1、引用资源

首先,说一下应用中用到的资源:GeoQuiz应用使用了两张图片和一些字符串资源。

图片资源作为切换题目按钮的资源,保存于res/drawable中,如下所示: 

字符串资源用来保存题目的内容等,保存于res/values/strings.xml中(在商业应用中,除了需要放在Bundle中的键值对所对应的键和一些静态字符串变量需要在代码中用全大写变量声明外,其他的一些字符串资源应该放到res/values/strings.xml(从服务器的解析数据单说)),如下所示:

<resources><string name="app_name">GeoQiuz</string><string name="true_button">True</string><string name="false_button">False</string><string name="cheat_button">Cheat!</string><string name="correct_toast">Correct!</string><string name="incorrect_toast">Incorrect!</string><string name="question_oceans">The Pacific Ocrean is larger than the Atlantic Ocean.</string><string name="question_mideast">The Suez Canal connects the Red Sea.</string><string name="question_africa">The Source of the Mile River is in Egypt.</string><string name="question_americas">The Amazon River is the longest river in the Americas.</string><string name="question_asia">Lake Baikal is the world\'s oldest and deepest freshwater lake.</string><string name="desc_prev">click this button to turn to the previous question</string><string name="desc_next">click this button to turn to the next question</string><string name="warning_text">Are you sure u want to do this</string><string name="show_answer_button">Show answer</string><string name="cheater_judgement_toast">U are a cheater!</string><string name="cheat_reset_button">RESET</string></resources>

2、答题界面的布局

接着,我们将为答题界面(由主activity控制)布局做一简单解析(我们先通过所见即所得的Graphic Layout看看布局长啥样):

图2对应的XML被放在res/layout文件夹中,图3对应的XML被放在res/layout-land文件夹中,需要注意的是,这两个XML的名称相同,它们只是不同方向布局的不同呈现,在商业应用中,应该对不同方向的布局分别定制,而不能仅仅是让横竖布局的XML内容完全一样。

以下是这两个XML的代码:

<!--纵向布置的答题界面(对书中的代码做了一些优化)-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><Buttonandroid:id="@+id/cheat_reset_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/cheat_reset_button" /><TextViewandroid:id="@+id/question_text_view"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:gravity="center"android:padding="24dp" android:text="题目的位置"/><RelativeLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:gravity="center" ><Buttonandroid:id="@+id/true_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="#69696969"android:text="@string/true_button" /><Buttonandroid:id="@+id/false_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="5dp"android:layout_toRightOf="@id/true_button"android:background="#69696969"android:text="@string/false_button" ></Button></RelativeLayout><Buttonandroid:id="@+id/cheat_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="@string/cheat_button" /><RelativeLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginTop="10dp"android:gravity="center" ><ImageButtonandroid:id="@+id/prev_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="#69696969"android:contentDescription="@string/desc_prev"android:padding="3dp"android:src="@drawable/image_view_button_share_prev" /><ImageButtonandroid:id="@+id/next_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="5dp"android:layout_toRightOf="@id/prev_button"android:background="#69696969"android:contentDescription="@string/desc_next"android:padding="3dp"android:src="@drawable/image_view_button_share" /></RelativeLayout></LinearLayout>
<!--横向布置的答题界面-->
<?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" ><Buttonandroid:id="@+id/cheat_reset_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/cheat_reset_button" /><TextViewandroid:id="@+id/question_text_view"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:padding="24dp"android:text="题目的位置" /><RelativeLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center" ><Buttonandroid:id="@+id/true_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="#69696969"android:text="@string/true_button" /><Buttonandroid:id="@+id/false_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="5dp"android:layout_toRightOf="@id/true_button"android:background="#69696969"android:text="@string/false_button" ></Button></RelativeLayout><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="bottom"android:layout_margin="5dp" ><ImageButtonandroid:id="@+id/prev_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:background="#69696969"android:contentDescription="@string/desc_prev"android:padding="3dp"android:src="@drawable/image_view_button_share_prev" /><Buttonandroid:id="@+id/cheat_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="@string/cheat_button" /><ImageButtonandroid:id="@+id/next_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:background="#69696969"android:contentDescription="@string/desc_next"android:padding="3dp"android:src="@drawable/image_view_button_share" /></RelativeLayout></FrameLayout>

在UI控件中,有的属性带“layout”前缀,有的不带,这区别可大了:所有带layout前缀的属性,它都表示该控件相对于它的父控件的位置,而不带layout的属性则表示该控件自身的内容相对于该控件的位置。比方说,layout_gravity这个属性,如果在一个Button中声明了这个属性,并设置为center,则表示该Button位于它的父容器的中心位置;如果为该Button声明了gravity这个属性,则表示它的text中的内容在这个Button控件的中心位置。

顺带再说一句Button这个控件(包含类似的附带图片展示的控件),android提供了一个很好的属性:contentDescription,当背景图片因为某种原因未正常显示时,该图片位置将显示contextDescription属性定义的内容。

3、控制答题界面的activity(主activity)

1、保存题目的TrueFalse类

首先,我们需要一个能保存每一道题目信息的类,该类就是一个简单的DTO对象,包含三个成员变量,分别用于存储题目、答案、用户是否做过弊,代码如下:

public class TrueFalse {//题目内容,题目保存于strings.xml中,需用R.string.....引用,//所以是int类型private int mQuestion;//题目的答案private boolean mTrueQuestion;//用户是否在该题上作弊private boolean mCheated;public boolean isCheated() {return mCheated;}public void setCheated(boolean cheated) {mCheated = cheated;}public TrueFalse(int question,boolean trueQuestion,boolean cheated){mQuestion = question;mTrueQuestion = trueQuestion;mCheated = cheated;}public int getQuestion() {return mQuestion;}public void setQuestion(int question) {mQuestion = question;}public boolean isTrueQuestion() {return mTrueQuestion;}public void setTrueQuestion(boolean trueQuestion) {mTrueQuestion = trueQuestion;}
}

2、QuizActivity类(主activity)

public class MainActivity extends Activity {private Button mTrueButton;private Button mFalseButton;// private boolean mTrueQuestion;private ImageButton mNextButton;private ImageButton mPrevButton;private TextView mQuestionTextView;private Button mCheatButton;private Button mResetButton;// private AlertDialog.Builder mBuilder = new AlertDialog.Builder(this);private int mCurrentIndex = 0;// 当屏幕旋转时,保存数据private static final String KEY_INDEX = "Index";// 将该键值打包进Bundle后放入intent传递public static final String EXTRA_ANSWER_IS_TRUE = "com.text.geoquiz.answer_is_true";// private boolean mIsCheater;// 通过该键可确定user是否查看了答案private String CHEARTER = "USERISACHEATER";private TrueFalse[] mQuestionBank = new TrueFalse[] {new TrueFalse(R.string.question_oceans, true, false),new TrueFalse(R.string.question_mideast, false, false),new TrueFalse(R.string.question_americas, true, false),new TrueFalse(R.string.question_africa, false, false),new TrueFalse(R.string.question_asia, true, false) };//更新题目的内容private void updateQuestion() {int _question = mQuestionBank[mCurrentIndex].getQuestion();mQuestionTextView.setText(_question);}//判断用户的答案是否正确private void checkAnswer(boolean userPressedTrue) {boolean answerTrue = mQuestionBank[mCurrentIndex].isTrueQuestion();int messageResId = 0;//如果用户偷窥了答案,那么在作答时,将Toast出“你是个作弊者”的信息if (mQuestionBank[mCurrentIndex].isCheated()) {messageResId = R.string.cheater_judgement_toast;}//如果用户没有作弊,,那么在作答时,将Toast出作答的正确性 else {if (answerTrue == userPressedTrue) {messageResId = R.string.correct_toast;} else {messageResId = R.string.incorrect_toast;}}Toast.makeText(this, messageResId, Toast.LENGTH_SHORT).show();}@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(com.text.geoqiuz.R.layout.activity_main);//用于接收在activity被销毁之前,保存的临时数据,包含当前答题的题号和用户是否作弊的信息if (savedInstanceState != null) {mCurrentIndex = savedInstanceState.getInt(KEY_INDEX, 0);// mIsCheater = savedInstanceState.getBoolean(CHEARTER);mQuestionBank[mCurrentIndex].setCheated(savedInstanceState.getBoolean(CHEARTER));}mQuestionTextView = (TextView) findViewById(R.id.question_text_view);updateQuestion();mTrueButton = (Button) findViewById(R.id.true_button);mFalseButton = (Button) findViewById(R.id.false_button);mNextButton = (ImageButton) findViewById(R.id.next_button);mPrevButton = (ImageButton) findViewById(R.id.prev_button);mCheatButton = (Button) findViewById(R.id.cheat_button);mResetButton = (Button) findViewById(R.id.cheat_reset_button);mTrueButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubcheckAnswer(true);}});mFalseButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubcheckAnswer(false);}});mPrevButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubif (mCurrentIndex == 0) {mCurrentIndex = mQuestionBank.length - 1;// updateQuestion();} else {mCurrentIndex -= 1;// updateQuestion();}// mIsCheater = false;updateQuestion();}});mNextButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubmCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;// 重置参数// mIsCheater = false;updateQuestion();}});//点击Cheat按钮,将以显式intent的方式创建CheatActivity对象,//同时intent还携带了一个该题得正确答案mCheatButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubIntent _intent = new Intent(MainActivity.this,SecondActivity.class);boolean _answerIsTrue = mQuestionBank[mCurrentIndex].isTrueQuestion();_intent.putExtra(EXTRA_ANSWER_IS_TRUE, _answerIsTrue);startActivityForResult(_intent, 0);}});//新增一个RESET按钮,该按钮用于清除所有题目的作弊记录,//弹出一个AlertDialog防止用户操作失误mResetButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubnew AlertDialog.Builder(MainActivity.this).setTitle("RESET").setIcon(R.drawable.ic_launcher).setMessage("Are U Sure To Clean All Cheating Marks?").setPositiveButton("Clean",new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog,int which) {// TODO Auto-generated method stubfor (int i = 0; i < mQuestionBank.length; i++) {mQuestionBank[i].setCheated(false);}Toast.makeText(MainActivity.this,"all cheated marks cleaned!",Toast.LENGTH_SHORT).show();}}).setNegativeButton("Cancel", null).show();}});//通过点击题目也能切换至下一题mQuestionTextView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubmCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;updateQuestion();}});}//接收作弊activity传过来的bundle,该bundle携带了用户是否作弊的信息@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {// TODO Auto-generated method stubsuper.onActivityResult(requestCode, resultCode, data);if (data == null) {return;}mQuestionBank[mCurrentIndex].setCheated(data.getBooleanExtra(SecondActivity.EXTRA_ANSWER_IS_SHOWN, false));}//旋转屏幕时,系统会销毁该activity对象,在销毁之前保存一些有用的数据@Overrideprotected void onSaveInstanceState(Bundle outState) {// TODO Auto-generated method stubsuper.onSaveInstanceState(outState);outState.putInt(KEY_INDEX, mCurrentIndex);outState.putBoolean(CHEARTER, mQuestionBank[mCurrentIndex].isCheated());}}

先说一下UI控件中的AlertDialog:从它的创建模式跟一般的对象创建模式不太一样——AlertDialog用到了所谓的建造者(Builder)模式。众所周知,对话框是一个可以高度定制的UI控件,我们可以设置它的抬头,背景,标题,子标题,内容,确定和取消的按钮等,若用常规的初始化方法将dialog初始化,那构造函数的参数就得写上好几行,而且有些内容可设可不设,那么就要重载N多个构造方法,所以不妨对dialog的每一部分都设置一个方法,这样就可以有选择的构造每一部分,构造方法也不必是好几行了。 
再简单说一下onSaveInstanceState(),这个方法实际上和activity的生命周期有关:众做周知,在一个activity实例被销毁之前,都要回调onPause()、onStop()、onDestory()方法,因为系统一般不会销毁正在onResume的activity,而可能会回收处于暂停或停止状态的activity对象,所以,onSaveInstanceState()方法被回调的时刻有可能是在onPause()被调用之后(也就是onStop()被调用之前),或者onStop()被调用之后;但是还有一个问题,当系统销毁activity后,用onSaveInstanceState()将数据保存在系统中就安全了吗?有时候内存不够用了,或是用户通过back键退出应用一段时间了,这时候系统不仅会销毁activity,还会销毁应用所在进程,这时候数据可能就真的不在了。 
至于内存还剩多少不够用,或是说系统如何按照进程的优先级杀死应用,以及退出应用多长时间该进程被销毁,这就是系统的事了。

二、 作弊界面的activity和它的布局介绍

1、布局介绍

至于作弊界面的布局,就简单多了,如下所示:


以下是布局的xml文件:
<!--作弊界面的xml文件-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical" ><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="24dp"android:text="@string/warning_text" /><TextViewandroid:id="@+id/answer_text_view"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="24dp"android:text="答案显示位置" /><Buttonandroid:id="@+id/show_answer_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/show_answer_button" /></LinearLayout>

2、作弊界面的activity

该activity接收主activity传过来的答案信息,同时通过setResult()的bundle携带“用户是否触发了作弊按钮”信息回传给主activity,以下是代码:

public class SecondActivity extends Activity {private boolean mAnswerIsTrue;private Button mShowAnswerButton;private TextView mAnswerTextView;private String CHEATER = "CHEATER IS CHEAT";//用于获得用户是否作弊的信息private boolean mCheater = false;private String ANSWERISTRUE = "ANSWERISTRUE";public static final String EXTRA_ANSWER_IS_SHOWN = "com.text.geoquiz.answer_is_shown";@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(R.layout.activity_second);mAnswerIsTrue = getIntent().getBooleanExtra(MainActivity.EXTRA_ANSWER_IS_TRUE, false);mShowAnswerButton = (Button) findViewById(R.id.show_answer_button);mAnswerTextView = (TextView) findViewById(R.id.answer_text_view);if (savedInstanceState != null) {mCheater = savedInstanceState.getBoolean(CHEATER);mAnswerTextView.setText(savedInstanceState.getCharSequence(ANSWERISTRUE));}setAnswerShownResult(mCheater);mShowAnswerButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubif (mAnswerIsTrue) {mAnswerTextView.setText(R.string.true_button);} else {mAnswerTextView.setText(R.string.false_button);}mCheater = true;setAnswerShownResult(mCheater);}});}//将用户作弊的情况回传给主activityprivate void setAnswerShownResult(boolean isAnswerShown) {Intent data = new Intent();data.putExtra(EXTRA_ANSWER_IS_SHOWN, isAnswerShown);setResult(RESULT_OK, data);}//1、保存用户的作弊信息,防止用户通过旋转屏幕清除作弊痕迹//2、保存用户得到答案的信息,防止用户旋转屏幕而造成信息丢失@Overrideprotected void onSaveInstanceState(Bundle outState) {// TODO Auto-generated method stubsuper.onSaveInstanceState(outState);outState.putBoolean(CHEATER, mCheater);outState.putCharSequence(ANSWERISTRUE, mAnswerTextView.getText());}}

 
 
 
 

转载于:https://www.cnblogs.com/qhlc/p/7536157.html

GeoQuiz初体验相关推荐

  1. 苹果电脑安装python3密码_mac系统安装Python3初体验

    前沿 对于iOS开发不要随便拆卸系统自带的Python,因为有很多 library 还是使用 Python2.7. 1 安装Xcode 1.1 App Store 搜索Xcode 并安装 1.2 安装 ...

  2. MapReduce编程初体验

    需求:在给定的文本文件中统计输出每一个单词出现的总次数 第一步: 准备一个aaa.txt文本文档 第二步: 在文本文档中随便写入一些测试数据,这里我写入的是 hello,world,hadoop he ...

  3. 小程序 缩放_缩放流星应用程序的初体验

    小程序 缩放 by Elie Steinbock 埃莉·斯坦博克(Elie Steinbock) 缩放流星应用程序的初体验 (First Experiences Scaling a Meteor Ap ...

  4. wxWidgets刚開始学习的人导引(3)——wxWidgets应用程序初体验

    wxWidgets刚開始学习的人导引全文件夹   PDF版及附件下载 1 前言 2 下载.安装wxWidgets 3 wxWidgets应用程序初体验 4 wxWidgets学习资料及利用方法指导 5 ...

  5. 用鸿蒙跑了个 “hello world”!鸿蒙开发初体验

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源 | https://my.oschina.net/u ...

  6. Windows Embedded Standard开发初体验(二)

    支持Silverlight的Windows Embedded Standard 好了,完成安装之后,我们就可以来做Windows Embedded Standard的第一个操作系统镜像了.在开始菜单中 ...

  7. 深度探索Hyperledger技术与应用之超级账本初体验(附部署代码)

    2019独角兽企业重金招聘Python工程师标准>>> 本章零基础地介绍了如何快速体验超级账本搭建的区块链网络,我们先绕过了比较复杂的初始化配置,用官方提供的fabric-sampl ...

  8. Spring环境搭建,IoC容器初体验~

    由于最近的任务是关于IoC配置文件格式的转换,所以需要从Spring的IoC容器开始学起,今天根据网上的介绍搭建了Spring环境,并对其IoC容器进行了初体验.文章中涉及到的软件以及推荐的一本关于S ...

  9. 来自新手Banana Pi香蕉派初体验

    2019独角兽企业重金招聘Python工程师标准>>> 一.前言 一段时间来对有强大的技术支持和完善的社区的Raspberry Pi很感兴趣,本想入一片学习学习,但转念一想Raspb ...

  10. 《深入理解Spark:核心思想与源码分析》——1.2节Spark初体验

    本节书摘来自华章社区<深入理解Spark:核心思想与源码分析>一书中的第1章,第1.2节Spark初体验,作者耿嘉安,更多章节内容可以访问云栖社区"华章社区"公众号查看 ...

最新文章

  1. Spring Boot入门——全局异常处理
  2. vue 插入dom_vue内部复用问题以及虚拟dom的更新
  3. linux调用信号处理程序后返回,如何在Linux上执行异步信号处理程序?
  4. Linux iostat监测IO状态
  5. 手把手教你:亲手打造Silverlight的Win8外观(1) 前言
  6. linux安装mysql 5.6.33
  7. RAISERROR (Transact-SQL)
  8. tensorflow函数记录
  9. 【小梅哥SOPC学习笔记】系统时钟的使用
  10. 零拷贝的基本原理及使用Java通过零拷贝实现数据传输
  11. r语言 怎么把字调大_R语言中字体设置
  12. 计算机硬件 试题,计算机硬件试题150完整版
  13. oracle术语英文,LOL各种英文术语,英雄联盟英文术语
  14. hdu5385 wyh2000 and pupil 二分图
  15. spring-boot2 + vue2+element-ui + avue + uni-app (兮家开源商城)
  16. Java定义获取所有偶数元素集合的方法
  17. UE4雷达图(纯蓝图)
  18. 2017华为软件精英挑战赛决赛思路分享
  19. Python!Python!
  20. mysql temporary_MySQL内部临时表(Internal Temporary Table)

热门文章

  1. iOS——json数据解析
  2. VC++ 用setsockopt()来控制recv()与send()的超时
  3. kerberos的系统搭建
  4. 用XAML做网页!!—广告展示区
  5. JSP的9种基本内置组件
  6. SQL Server的锁机制
  7. vue 双向数据绑定
  8. 阿里云中获取文件及目录列表的方法
  9. 94-《纪元2205》游戏体会.(2015.11.12)
  10. 悲剧,当用cywin 写Linux脚本