GeoQuiz初体验

小白,请多指教

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" >

    <Button
        android:id="@+id/cheat_reset_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/cheat_reset_button" />

    <TextView
        android: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="题目的位置"/>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center" >

        <Button
            android:id="@+id/true_button"

            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#69696969"
            android:text="@string/true_button" />

        <Button
            android: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>

    <Button
        android:id="@+id/cheat_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="@string/cheat_button" />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="10dp"
        android:gravity="center" >

        <ImageButton
            android: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" />

        <ImageButton
            android: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" >

    <Button
        android:id="@+id/cheat_reset_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/cheat_reset_button" />

    <TextView
        android: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="题目的位置" />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" >

        <Button
            android:id="@+id/true_button"

            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#69696969"
            android:text="@string/true_button" />

        <Button
            android: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>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_margin="5dp" >

        <ImageButton
            android: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" />

        <Button
            android:id="@+id/cheat_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="@string/cheat_button" />

        <ImageButton
            android: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();

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.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() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                checkAnswer(true);

            }

        });

        mFalseButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                checkAnswer(false);
            }
        });

        mPrevButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                if (mCurrentIndex == 0) {
                    mCurrentIndex = mQuestionBank.length - 1;
                    // updateQuestion();
                } else {
                    mCurrentIndex -= 1;
                    // updateQuestion();

                }
                // mIsCheater = false;

                updateQuestion();
            }
        });
        mNextButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
                // 重置参数
                // mIsCheater = false;

                updateQuestion();
            }
        });
        //点击Cheat按钮,将以显式intent的方式创建CheatActivity对象,
        //同时intent还携带了一个该题得正确答案
        mCheatButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent _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() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                new 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() {

                                    @Override
                                    public void onClick(DialogInterface dialog,
                                            int which) {
                                        // TODO Auto-generated method stub
                                        for (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() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;

                updateQuestion();
            }
        });

    }
    //接收作弊activity传过来的bundle,该bundle携带了用户是否作弊的信息
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // TODO Auto-generated method stub
        super.onActivityResult(requestCode, resultCode, data);
        if (data == null) {
            return;
        }
        mQuestionBank[mCurrentIndex].setCheated(data.getBooleanExtra(
                SecondActivity.EXTRA_ANSWER_IS_SHOWN, false));

    }

    //旋转屏幕时,系统会销毁该activity对象,在销毁之前保存一些有用的数据
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        // TODO Auto-generated method stub
        super.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" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="24dp"
        android:text="@string/warning_text" />

    <TextView
        android:id="@+id/answer_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="24dp"
        android:text="答案显示位置" />

    <Button
        android: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";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.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() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                if (mAnswerIsTrue) {
                    mAnswerTextView.setText(R.string.true_button);
                } else {
                    mAnswerTextView.setText(R.string.false_button);
                }
                mCheater = true;
                setAnswerShownResult(mCheater);
            }
        });
    }
    //将用户作弊的情况回传给主activity
    private void setAnswerShownResult(boolean isAnswerShown) {
        Intent data = new Intent();
        data.putExtra(EXTRA_ANSWER_IS_SHOWN, isAnswerShown);
        setResult(RESULT_OK, data);
    }

    //1、保存用户的作弊信息,防止用户通过旋转屏幕清除作弊痕迹
    //2、保存用户得到答案的信息,防止用户旋转屏幕而造成信息丢失
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        // TODO Auto-generated method stub
        super.onSaveInstanceState(outState);
        outState.putBoolean(CHEATER, mCheater);
        outState.putCharSequence(ANSWERISTRUE, mAnswerTextView.getText());
    }

}

 
 
 
 
时间: 2024-12-27 10:25:11

GeoQuiz初体验的相关文章

erlang 初体验

最近测试了一下 erlang的坑... 如不出意外.... 大家第一眼看到这语法... 心里第一句一定是"我擦.这TM都是啥!!!!!" 没有变量!!! 没有结构体!!! 没有循环!!! 好吧,至少我是这样想的. 找了半天..连个if也不知道怎么写.. 这记录一些基本常识.. -module(module_name)  %%定义模块 括号内的要和文件名相同. -export([fun1/1 fun2/2]) %%这里是导出2个函数对外使用  函数名/参数名. 一个简单的函数定义如下 f

linux初体验

第一次听到linux这个'词语'是在一次偶然的朋友聊天中朋友提到的,之前压根没听到过'这个东西',所以我可以说是个linux的新新手,菜鸟都不算. 截至到目前,我已经开始linux系统运维学习有差不多10天时间了.在没接触linux之前,我对它的认识仅仅是:它是个计算机系统.决定学习linux系统运维之前,自我以为运维应该是对系统的一些日常维护之类的,不会很难的东西,我更希望运维是个不难的东西,我个人很笨,对难的东西可能接受的很慢,所以我愿意认为运维是很简单的,这样我就可以轻轻松松的掌握运维相关

【Spark深入学习 -15】Spark Streaming前奏-Kafka初体验

----本节内容------- 1.Kafka基础概念 1.1 出世背景 1.2 基本原理 1.2.1.前置知识 1.2.2.架构和原理 1.2.3.基本概念 1.2.4.kafka特点 2.Kafka初体验 2.1 环境准备 2.2 Kafka小试牛刀 2.2.1单个broker初体验 2.2.2 多个broker初体验 2.3 Kafka分布式集群构建 2.3.1 Kafka分布式集群构建 2.3.2 Kafka主题创建 2.3.3 生产者生产数据 2.3.4消费者消费数据 2.3.5消息的

Java8初体验(二)Stream语法详解

原文链接:http://ifeve.com/stream/ 1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel aggregate operations. 我们来解读一下上面的那句话: Stream是元素的集合,这点让Stream看起来用些类似Iterator: 可以支持顺序和并行的对原Stream进行汇聚的操作: 大家可以把Stream当成一个高级版本的

hibernate--CRUD初体验

hibernate的crud操作初体验. 看具体实例 package com.fuwh.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import org.hibernate.annotations.GenericGenerator; @Entity publ

Oracle SQL篇(一)null值之初体验

    从我第一次正式的写sql语句到现在,已经超过10年的时间了.我写报表,做统计分析和财务对账,我一点点的接触oracle数据库,并尝试深入了解.这条路,一走就是10年,从充满热情,到开始厌倦,我不知道我还能坚持多久,未来的路,谁知道呢? 也许是该抓紧时间,做一点什么了,我不知道该开始写些什么,我从来没有在网上写东西的习惯.     先从简单的开始吧,那当然就是SQL,这是我SQL系列的第一篇,希望我能够坚持. 在Oracle数据库中,如果一个表中的列没有值的话,我们可以说是空值,比如IT员

AngularJS路由系列(3)-- UI-Router初体验

本系列探寻AngularJS的路由机制,在WebStorm下开发. AngularJS路由系列包括: 1.AngularJS路由系列(1)--基本路由配置2.AngularJS路由系列(2)--刷新.查看路由,路由事件和URL格式,获取路由参数,路由的Resolve3.AngularJS路由系列(3)-- UI-Router初体验4.AngularJS路由系列(4)-- UI-Router的$state服务.路由事件.获取路由参数5.AngularJS路由系列(5)-- UI-Router的路由

Android Studio初体验之启动AVD模拟器异常:cannot set up guest memory &#39;pc.ram&#39;

启动AVD模拟器异常:Cannot set up guest memory 'pc.ram' 错误信息: HAX is working and emulator runs in fast virt mode Cannot set up guest memory 'pc.ram': Invalid argument Error accepting connect 分析 各种查资料,没有发现网上有同样问题的,在一篇相关文章中找到类似的解决方法. 从语意看,应该是hax安装后没有启动.(不懂hax是什

leaflet开源地图库源码阅读笔记(一)——简介&amp;Demo初体验(by vczero)

一.简介 电子地图已经在O2O.生活服务.出行等领域布局,传统的GIS也孕育着互联网基因.传统GIS的图商有ESRI(Arc系列).四维.高德.超图.中地等等.在web互联网领域,百度和高德较为出色.但是开放源代码都远远做的不够,相比而言国外开源较多且丰富,最近认真研读了openlayers2/3和leaflet的源码,收获颇多,不仅加强对浏览器兼容性.HTML5.CSS3等基础,还增强了GIS的基础.本人现在也在一家互联网公司做js api的研发,因此,需要不断地吸取开源代码的营养. leaf