本文为学习类文档,通过学习B站up主longway777的视频,如有侵权,请联系博主进行删除
安卓口算测试app
为了将之前学过的ViewModel,Fragement,本地化,横竖屏设置等结合在一起,做一个安卓口算的app,可以用来给家里的小学生们做练习用,那么开始吧~
创建项目
- 创建Fragement分别显示标题,答题,输和赢的界面:
- 在资源中创建navigation文件并添加依赖,完成后将fragement导入创建逻辑:
- 将网上下载的图片存到Drawable中,在使用系统自带的矢量图时需要添加对应的依赖在此不做赘述,并在Gradle中添加需要dataBinding和ViewModelProvider依赖并sync同步:
dependencies {
...
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:1.0.0'
}
android {
...
defaultConfig {
...
dataBinding.enabled true
}
...
}
- 做界面:
fragement_title.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".TitleFragment">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.1" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.9" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_message"
android:textSize="@dimen/big_font"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.15" />
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="50dp"
android:layout_marginEnd="50dp"
android:contentDescription="@string/title_image_info"
android:src="@drawable/titleimage"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/title_button_message"
app:layout_constraintBottom_toBottomOf="@+id/imageView"
app:layout_constraintEnd_toEndOf="@+id/imageView"
app:layout_constraintStart_toStartOf="@+id/imageView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.8" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/high_score_message"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.883"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/imageView"
app:layout_constraintVertical_bias="0.049" />
</androidx.constraintlayout.widget.ConstraintLayout>
fragement_question.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".QuestionFragment">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.1" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/current_score"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline3" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.2" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.32" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.4" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.8" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.7" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline13"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.6" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline14"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.1" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.9" />
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/huge_font"
app:layout_constraintBottom_toBottomOf="@+id/textView5"
app:layout_constraintEnd_toStartOf="@+id/textView5"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/guideline9"
app:layout_constraintTop_toTopOf="@+id/textView5"
tools:text="8" />
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/huge_font"
app:layout_constraintBottom_toBottomOf="@+id/textView6"
app:layout_constraintEnd_toStartOf="@+id/textView6"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/textView4"
app:layout_constraintTop_toTopOf="@+id/textView6"
tools:text="+" />
<TextView
android:id="@+id/textView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/huge_font"
app:layout_constraintBottom_toBottomOf="@+id/textView7"
app:layout_constraintEnd_toStartOf="@+id/textView7"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/textView5"
app:layout_constraintTop_toTopOf="@+id/textView7"
tools:text="9" />
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/equal_symbol"
android:textSize="@dimen/huge_font"
app:layout_constraintBottom_toBottomOf="@+id/textView8"
app:layout_constraintEnd_toStartOf="@+id/textView8"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/textView6"
app:layout_constraintTop_toTopOf="@+id/textView8"
tools:text="=" />
<TextView
android:id="@+id/textView8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/question_mark"
android:textSize="@dimen/huge_font"
app:layout_constraintBottom_toTopOf="@+id/guideline4"
app:layout_constraintEnd_toStartOf="@+id/guideline10"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/textView7"
app:layout_constraintTop_toTopOf="@+id/guideline4"
tools:text="\?" />
<TextView
android:id="@+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/input_indicator"
android:textSize="@dimen/mid_font"
app:layout_constraintBottom_toTopOf="@+id/guideline5"
app:layout_constraintEnd_toStartOf="@+id/guideline10"
app:layout_constraintStart_toStartOf="@+id/guideline9"
app:layout_constraintTop_toTopOf="@+id/guideline5" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button1"
android:textSize="@dimen/button_font"
app:layout_constraintBottom_toTopOf="@+id/guideline14"
app:layout_constraintEnd_toStartOf="@+id/button2"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/guideline9"
app:layout_constraintTop_toTopOf="@+id/guideline6" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button2"
android:textSize="@dimen/button_font"
app:layout_constraintBottom_toBottomOf="@+id/button1"
app:layout_constraintEnd_toStartOf="@+id/button3"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/button1"
app:layout_constraintTop_toTopOf="@+id/button1" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button3"
android:textSize="@dimen/button_font"
app:layout_constraintBottom_toBottomOf="@+id/button2"
app:layout_constraintEnd_toStartOf="@+id/guideline10"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/button2"
app:layout_constraintTop_toTopOf="@+id/button2"
app:layout_constraintVertical_bias="0.0" />
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button4"
android:textSize="@dimen/button_font"
app:layout_constraintBottom_toTopOf="@+id/guideline13"
app:layout_constraintEnd_toStartOf="@+id/button5"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/guideline9"
app:layout_constraintTop_toTopOf="@+id/guideline14" />
<Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button5"
android:textSize="@dimen/button_font"
app:layout_constraintBottom_toBottomOf="@+id/button4"
app:layout_constraintEnd_toStartOf="@+id/button6"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/button4"
app:layout_constraintTop_toTopOf="@+id/button4" />
<Button
android:id="@+id/button6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button6"
android:textSize="@dimen/button_font"
app:layout_constraintBottom_toBottomOf="@+id/button5"
app:layout_constraintEnd_toStartOf="@+id/guideline10"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/button5"
app:layout_constraintTop_toTopOf="@+id/button5" />
<Button
android:id="@+id/button7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button7"
android:textSize="@dimen/button_font"
app:layout_constraintBottom_toTopOf="@+id/guideline12"
app:layout_constraintEnd_toStartOf="@+id/button8"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/guideline9"
app:layout_constraintTop_toTopOf="@+id/guideline13" />
<Button
android:id="@+id/button8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button8"
android:textSize="@dimen/button_font"
app:layout_constraintBottom_toBottomOf="@+id/button7"
app:layout_constraintEnd_toStartOf="@+id/button9"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/button7"
app:layout_constraintTop_toTopOf="@+id/button7" />
<Button
android:id="@+id/button9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button9"
android:textSize="@dimen/button_font"
app:layout_constraintBottom_toBottomOf="@+id/button8"
app:layout_constraintEnd_toStartOf="@+id/guideline10"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/button8"
app:layout_constraintTop_toTopOf="@+id/button8" />
<Button
android:id="@+id/buttonClear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/buttonClear"
android:textSize="@dimen/button_font"
app:layout_constraintBottom_toTopOf="@+id/guideline7"
app:layout_constraintEnd_toStartOf="@+id/button0"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/guideline9"
app:layout_constraintTop_toTopOf="@+id/guideline12" />
<Button
android:id="@+id/button0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button0"
android:textSize="@dimen/button_font"
app:layout_constraintBottom_toBottomOf="@+id/buttonClear"
app:layout_constraintEnd_toStartOf="@+id/buttonSubmit"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/buttonClear"
app:layout_constraintTop_toTopOf="@+id/buttonClear" />
<Button
android:id="@+id/buttonSubmit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/buttonSubmit"
android:textSize="@dimen/button_font"
app:layout_constraintBottom_toBottomOf="@+id/button0"
app:layout_constraintEnd_toStartOf="@+id/guideline10"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/button0"
app:layout_constraintTop_toTopOf="@+id/button0" />
</androidx.constraintlayout.widget.ConstraintLayout>
fragment_lose.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LoseFragment">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.1" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline11"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.45" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@string/lose_image_message"
android:src="@drawable/ic_sentiment_dissatisfied_black_24dp"
app:layout_constraintBottom_toTopOf="@+id/guideline11"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline8" />
<TextView
android:id="@+id/textView10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lose_message"
android:textSize="@dimen/big_font"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline11"
app:layout_constraintVertical_bias="0.1" />
<TextView
android:id="@+id/textView12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lose_score_message"
android:textSize="@dimen/big_font"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline11"
app:layout_constraintVertical_bias="0.3" />
<Button
android:id="@+id/button10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_back_to_title"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView2" />
</androidx.constraintlayout.widget.ConstraintLayout>
fragment_win.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".WinFragment">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline15"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.1" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline16"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.45" />
<ImageView
android:id="@+id/imageView3"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@string/win_image_messgae"
android:src="@drawable/ic_sentiment_satisfied_black_24dp"
app:layout_constraintBottom_toTopOf="@+id/guideline16"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline15" />
<TextView
android:id="@+id/textView11"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/win_message"
android:textSize="@dimen/big_font"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView3"
app:layout_constraintVertical_bias="0.1" />
<TextView
android:id="@+id/textView13"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/win_score_message"
android:textSize="@dimen/big_font"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView3"
app:layout_constraintVertical_bias="0.3" />
<Button
android:id="@+id/button11"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_back_to_title"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView3" />
</androidx.constraintlayout.widget.ConstraintLayout>
- 将fragement链接到activity中:
- ViewModel类中做数据的操作:
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.SavedStateHandle;
import java.util.Random;
@SuppressWarnings("ConstantConditions")
public class MyViewModel extends AndroidViewModel {
private SavedStateHandle handle;
private static String KEY_HIGE_SCORE = "key_high_score";
private static String KEY_LEFT_NUMBER = "key_left_number";
private static String KEY_RIGHT_NUMBER = "key_right_number";
private static String KEY_OPERATOR = "key_operator";
private static String KEY_ANSWER = "key_answer";
private static String SAVE_SHP_DATA_MANE = "save_shp_data_name";
private static String KEY_CURRENT_SCORE = "key_current_score";
boolean win_flag = false;
public MyViewModel(@NonNull Application application, SavedStateHandle handle) { //使用了savedstatehandle
super(application);
//管理的数据有:最高纪录存放之sharedpreferences,题目数字及运算符和答案,当前得分。
if (!handle.contains(KEY_HIGE_SCORE)){
SharedPreferences sharedPreferences = getApplication().getSharedPreferences(SAVE_SHP_DATA_MANE, Context.MODE_PRIVATE);
handle.set(KEY_HIGE_SCORE,sharedPreferences.getInt(KEY_HIGE_SCORE,0));
handle.set(KEY_LEFT_NUMBER,0);
handle.set(KEY_RIGHT_NUMBER,0);
handle.set(KEY_OPERATOR,"+");
handle.set(KEY_ANSWER,0);
handle.set(KEY_CURRENT_SCORE,0);
}
this.handle = handle;
}
MutableLiveData<Integer>getLeftNumber(){
return handle.getLiveData(KEY_LEFT_NUMBER);
}
MutableLiveData<Integer>getRightNumber(){
return handle.getLiveData(KEY_RIGHT_NUMBER);
}
MutableLiveData<String>getOperator(){
return handle.getLiveData(KEY_OPERATOR);
}
MutableLiveData<Integer>getHignScore(){
return handle.getLiveData(KEY_HIGE_SCORE);
}
MutableLiveData<Integer>getCurrentScore(){
return handle.getLiveData(KEY_CURRENT_SCORE);
}
MutableLiveData<Integer>getAnswer(){
return handle.getLiveData(KEY_ANSWER);
}
//创建题目
void generator(){
int LEVEL_NUMBER = 21;
Random random = new Random();
int x,y;
x = random.nextInt(LEVEL_NUMBER) + 1; //出现的随机数为0到LEVEL_NUMBER-1;为防止负数出现,需要加1
y = random.nextInt(LEVEL_NUMBER) + 1;
if (x%2 == 0){ //对2取余为0,使得加减法各占一半
getOperator().setValue("+");
//做处理防止超出学生难度范围,做大数作为来当加法的结果
if (x > y){ //x若为大数,x做结果,y做"+"前面的数,差值做“+”后面的数
getAnswer().setValue(x);
getLeftNumber().setValue(y);
getRightNumber().setValue(x - y);
}else {
getAnswer().setValue(y);
getLeftNumber().setValue(x);
getRightNumber().setValue(y - x);
}
}else {
getOperator().setValue("-");
if (x > y){
getAnswer().setValue(x - y);
getLeftNumber().setValue(x);
getRightNumber().setValue(y);
}else {
getAnswer().setValue(y - x);
getLeftNumber().setValue(y);
getRightNumber().setValue(x);
}
}
}
//保存
void save() {
SharedPreferences sharedPreferences = getApplication().getSharedPreferences(SAVE_SHP_DATA_MANE,Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putInt(KEY_HIGE_SCORE,getHignScore().getValue());
editor.apply();
}
//根据答题情况区别对待
void answerCorrect(){
getCurrentScore().setValue(getCurrentScore().getValue() + 1);
if (getCurrentScore().getValue() > getHignScore().getValue()){
getHignScore().setValue(getCurrentScore().getValue());//刷新最高分
win_flag = true;
}
generator();
}
}
- 对界面数据绑定,转换databinding layout并绑定数据
(1) 标题界面的数据绑定——右上角的最高纪录:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="data"
type="com.example.calculationtest.MyViewModel" />
</data>
...
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{@string/high_score_message(data.hignScore)}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.883"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/imageView"
app:layout_constraintVertical_bias="0.049"
tools:text="@string/high_score_message" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(2) 答题界面的数据绑定——当前得分、出题数字与运算符:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="data"
type="com.example.calculationtest.MyViewModel" />
</data>
...
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{@string/current_score(data.currentScore)}"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline3"
tools:text="Score:3" />
...
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/huge_font"
android:text="@{String.valueOf(data.leftNumber)}"
app:layout_constraintBottom_toBottomOf="@+id/textView5"
app:layout_constraintEnd_toStartOf="@+id/textView5"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/guideline9"
app:layout_constraintTop_toTopOf="@+id/textView5"
tools:text="8" />
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/huge_font"
android:text="@{data.operator}"
app:layout_constraintBottom_toBottomOf="@+id/textView6"
app:layout_constraintEnd_toStartOf="@+id/textView6"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/textView4"
app:layout_constraintTop_toTopOf="@+id/textView6"
tools:text="+" />
<TextView
android:id="@+id/textView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/huge_font"
android:text="@{String.valueOf(data.rightNumber)}"
app:layout_constraintBottom_toBottomOf="@+id/textView7"
app:layout_constraintEnd_toStartOf="@+id/textView7"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/textView5"
app:layout_constraintTop_toTopOf="@+id/textView7"
tools:text="9" />
...
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(3) 挑战成功创造新纪录的界面——新纪录高分:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="data"
type="com.example.calculationtest.MyViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".WinFragment">
...
<TextView
android:id="@+id/textView13"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{@string/win_score_message(data.hignScore)}"
android:textSize="@dimen/big_font"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView3"
app:layout_constraintVertical_bias="0.3"
tools:text="New Record:" />
...
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(4) 挑战失败没有创造新纪录显示本次得分:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="data"
type="com.example.calculationtest.MyViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LoseFragment">
...
<TextView
android:id="@+id/textView12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{@string/lose_score_message(data.currentScore)}"
android:textSize="@dimen/big_font"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline11"
app:layout_constraintVertical_bias="0.3"
tools:text="Your Score" />
...
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
- 编写Fragement中的代码(每个Fragement中都需要写绑定):
标题fragement:
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.SavedStateViewModelFactory;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.calculationtest.databinding.FragmentTitleBinding;
...
@Override
public View onCreateView(@NonNull LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
MyViewModel myViewModel;
myViewModel = new ViewModelProvider(requireActivity(), new SavedStateViewModelFactory(requireActivity()
.getApplication(), this)).get(MyViewModel.class); //新版大长式子恶心人 TvT ~~~
FragmentTitleBinding binding;
binding = DataBindingUtil.inflate(inflater,R.layout.fragment_title,container,false);
binding.setData(myViewModel);
binding.setLifecycleOwner(requireActivity());
binding.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
NavController controller = Navigation.findNavController(v);
controller.navigate(R.id.action_titleFragment_to_questionFragment);
}
});
return binding.getRoot(); //返回值为View类型
}
}
答题界面:
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.SavedStateViewModelFactory;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.calculationtest.databinding.FragmentQuestionBinding;
...
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final MyViewModel myViewModel;
myViewModel = new ViewModelProvider(requireActivity(), new SavedStateViewModelFactory(requireActivity()
.getApplication(), this)).get(MyViewModel.class);
myViewModel.generator();
myViewModel.getCurrentScore().setValue(0);
final FragmentQuestionBinding binding;
binding = DataBindingUtil.inflate(inflater,R.layout.fragment_question,container,false);
binding.setData(myViewModel);
binding.setLifecycleOwner(requireActivity());
//借用stringbuilder来处理用户输入的数据
final StringBuilder builder = new StringBuilder();
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.button0:
builder.append(0);
break;
case R.id.button1:
builder.append(1);
break;
case R.id.button2:
builder.append(2);
break;
case R.id.button3:
builder.append(3);
break;
case R.id.button4:
builder.append(4);
break;
case R.id.button5:
builder.append(5);
break;
case R.id.button6:
builder.append(6);
break;
case R.id.button7:
builder.append(7);
break;
case R.id.button8:
builder.append(8);
break;
case R.id.button9:
builder.append(9);
break;
case R.id.buttonClear:
builder.setLength(0);
//或者builder = new StringBuilder();
break;
}
if (builder.length() == 0){
binding.textView9.setText(R.string.input_indicator);
}else {
binding.textView9.setText(builder.toString());
}
}
};
binding.button0.setOnClickListener(listener);
binding.button1.setOnClickListener(listener);
binding.button2.setOnClickListener(listener);
binding.button3.setOnClickListener(listener);
binding.button4.setOnClickListener(listener);
binding.button5.setOnClickListener(listener);
binding.button6.setOnClickListener(listener);
binding.button7.setOnClickListener(listener);
binding.button8.setOnClickListener(listener);
binding.button9.setOnClickListener(listener);
binding.buttonClear.setOnClickListener(listener);
binding.buttonSubmit.setOnClickListener(new View.OnClickListener() {
@SuppressWarnings("ConstantConditions")
@Override
public void onClick(View v) {
if (builder.length() == 0) {
builder.append("-1"); //当前加减法算式中不可能有负数答案,因此设为-1,来作为不输入直接确定的错误答案
}
if (Integer.valueOf(builder.toString()).intValue() == myViewModel.getAnswer().getValue()){ //转换为简单int型作比较,如果什么都不输入会返回空值,需要在此之前做拦截
myViewModel.answerCorrect();
builder.setLength(0);
binding.textView9.setText(R.string.answer_correct_message);
//builder.append(getString(R.string.answer_correct_message));
}else {
NavController controller = Navigation.findNavController(v);
if (myViewModel.win_flag){
controller.navigate(R.id.action_questionFragment_to_winFragment);
myViewModel.win_flag = false;
myViewModel.save();
}else {
controller.navigate(R.id.action_questionFragment_to_loseFragment);
}
}
}
});
return binding.getRoot();
}
}
创造新纪录界面:
import android.os.Bundle;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.SavedStateViewModelFactory;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.Navigation;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.calculationtest.databinding.FragmentWinBinding;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
MyViewModel myViewModel = new ViewModelProvider(requireActivity(),new SavedStateViewModelFactory(requireActivity()
.getApplication(),this)).get(MyViewModel.class);
FragmentWinBinding binding;
binding = DataBindingUtil.inflate(inflater,R.layout.fragment_win, container, false);
binding.setData(myViewModel);
binding.setLifecycleOwner(requireActivity());
binding.button11.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Navigation.findNavController(v).navigate(R.id.action_winFragment_to_titleFragment);
}
});
return binding.getRoot();
}
}
挑战失败当前得分界面:
import android.os.Bundle;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.SavedStateViewModelFactory;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.Navigation;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.calculationtest.databinding.FragmentLoseBinding;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
MyViewModel myViewModel = new ViewModelProvider(requireActivity(),new SavedStateViewModelFactory(requireActivity()
.getApplication(),this)).get(MyViewModel.class);
final FragmentLoseBinding binding;
binding = DataBindingUtil.inflate(inflater,R.layout.fragment_lose, container, false);
binding.setData(myViewModel);
binding.setLifecycleOwner(requireActivity());
binding.button10.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Navigation.findNavController(v).navigate(R.id.action_loseFragment_to_titleFragment);
}
});
return binding.getRoot();
}
}
- 完善ActionBar实现误操作提示——界面人性化返回:在MainActivity中填写代码
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.NavigationUI;
import android.content.DialogInterface;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
NavController controller;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//左上角的返回按键
controller = Navigation.findNavController(this,R.id.fragment);
NavigationUI.setupActionBarWithNavController(this,controller);
}
@Override //响应返回按键实现询问是否返回?
public boolean onSupportNavigateUp() {
if (controller.getCurrentDestination().getId() == R.id.questionFragment){ //答题界面实现询问返回
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.quit_dialog_title));
builder.setPositiveButton(R.string.dialog_positive_message, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
controller.navigateUp();
}
});
builder.setNegativeButton(R.string.dialog_negative_message, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
AlertDialog dialog = builder.create(); //创建对话框并显示
dialog.show();
}else if (controller.getCurrentDestination().getId() == R.id.titleFragment) { //标题界面实现退出程序
finish();
}
else { //其他界面(输赢界面)实现跳转到标题界面
controller.navigate(R.id.titleFragment);
}
return super.onSupportNavigateUp();
}
@Override //设备自带返回按键做处理,继承上面的即可
public void onBackPressed() {
onSupportNavigateUp();
//super.onBackPressed();
}
}
- String.xml实现汉化:此处也附上当前使用的dimens
dimens.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="huge_font">60sp</dimen>
<dimen name="big_font">40sp</dimen>
<dimen name="mid_font">30sp</dimen>
<dimen name="button_font">20sp</dimen>
</resources>
strings.xml
<resources>
<string name="app_name">CalculationTest</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment" translatable="false">Hello blank fragment</string>
<string name="title_message">Calculation Test</string>
<string name="title_image_info" translatable="false">title_image</string>
<string name="title_button_message">Enter</string>
<string name="high_score_message">High Score: %d</string>
<string name="button0" translatable="false">0</string>
<string name="button1" translatable="false">1</string>
<string name="button2" translatable="false">2</string>
<string name="button3" translatable="false">3</string>
<string name="button4" translatable="false">4</string>
<string name="button5" translatable="false">5</string>
<string name="button6" translatable="false">6</string>
<string name="button7" translatable="false">7</string>
<string name="button8" translatable="false">8</string>
<string name="button9" translatable="false">9</string>
<string name="buttonClear">C</string>
<string name="buttonSubmit">OK</string>
<string name="equal_symbol" translatable="false">=</string>
<string name="question_mark" translatable="false">\?</string>
<string name="current_score">Score:%d</string>
<string name="input_indicator">Your answer</string>
<string name="lose_image_message" translatable="false">lose image</string>
<string name="win_image_messgae" translatable="false">win message</string>
<string name="lose_message">You Lose!</string>
<string name="win_message">You Win!!!</string>
<string name="lose_score_message">Your Score:%d</string>
<string name="win_score_message">New Record:%d</string>
<string name="button_back_to_title">Back</string>
<string name="answer_correct_message">Correct!Go on!</string>
<string name="quit_dialog_title">Are you sure to quit?</string>
<string name="dialog_positive_message">OK</string>
<string name="dialog_negative_message">Cancle</string>
</resources>
在string页面点击右上角的open editor,寻找中文并选择,在多出来的文本框中填写与英文对应的中文,阿拉伯数字与运算符可以设置为唯一:
string.xml(zh-rCN)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">口算测试</string>
<string name="answer_correct_message">回答正确!请继续!</string>
<string name="buttonClear">清除</string>
<string name="buttonSubmit">确定</string>
<string name="button_back_to_title">返回</string>
<string name="current_score">得分:%d</string>
<string name="dialog_negative_message">取消</string>
<string name="dialog_positive_message">确定</string>
<string name="high_score_message">最高记录:%d</string>
<string name="input_indicator">请开始答题</string>
<string name="lose_message">挑战失败!</string>
<string name="lose_score_message">你的得分:%d</string>
<string name="quit_dialog_title">确定退出吗?</string>
<string name="title_button_message">开始挑战</string>
<string name="title_message">口算测试</string>
<string name="win_message">挑战成功!!!</string>
<string name="win_score_message">新纪录:%d</string>
</resources>
- fragement名称更改:在导航图中的图形化界面中选中对应的fragement的label进行更改,也可以创建资源进行系统化管理
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav"
app:startDestination="@id/titleFragment">
<fragment
android:id="@+id/titleFragment"
android:name="com.example.calculationtest.TitleFragment"
android:label="@string/title_nav_message"
tools:layout="@layout/fragment_title" >
<action
android:id="@+id/action_titleFragment_to_questionFragment"
app:destination="@id/questionFragment" />
</fragment>
<fragment
android:id="@+id/questionFragment"
android:name="com.example.calculationtest.QuestionFragment"
android:label="@string/question_nav_message"
tools:layout="@layout/fragment_question" >
<action
android:id="@+id/action_questionFragment_to_winFragment"
app:destination="@id/winFragment" />
<action
android:id="@+id/action_questionFragment_to_loseFragment"
app:destination="@id/loseFragment" />
</fragment>
<fragment
android:id="@+id/winFragment"
android:name="com.example.calculationtest.WinFragment"
android:label="@string/win_nav_message"
tools:layout="@layout/fragment_win" >
<action
android:id="@+id/action_winFragment_to_titleFragment"
app:destination="@id/titleFragment" />
</fragment>
<fragment
android:id="@+id/loseFragment"
android:name="com.example.calculationtest.LoseFragment"
android:label="@string/lose_nav_message"
tools:layout="@layout/fragment_lose" >
<action
android:id="@+id/action_loseFragment_to_titleFragment"
app:destination="@id/titleFragment" />
</fragment>
</navigation>
运行程序
各个界面测试如下:
标题界面:
答题界面:
挑战成功界面:
挑战失败界面:
横屏处理
当前如果横屏的话:
界面全部被打乱,不是我们想要的结果,因此需要设置横屏布局(如果强制不允许横屏处理需要在AndroidManifest.xml配置文件中设置相对应的Activity就可以):
<application
...
android:screenOrientation="portrait"
...
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
对每一个页面做单独的横板副本(根据需求做即可,此处不做代码的粘贴):
结束
目前还有一个小bug的问题就是在横竖屏切换的时候,问答界面中的算式会因为重新构建界面而刷新界面,因此界面上的式子也会因此而变动。