├── .gitignore
├── .idea
├── codeStyles
│ └── Project.xml
├── gradle.xml
├── misc.xml
└── runConfigurations.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── onelinetoend
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher-web.png
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── onelinetoend
│ │ │ ├── Adapter
│ │ │ ├── Adapter_Yibi.java
│ │ │ ├── Adapter_Yibi_Collection.java
│ │ │ ├── Adapter_difficulty.java
│ │ │ └── Adapter_difficulty_detail.java
│ │ │ ├── Model
│ │ │ ├── Bean
│ │ │ │ ├── Bean_Collection.java
│ │ │ │ └── Bean_Road.java
│ │ │ └── MySql.java
│ │ │ ├── Util
│ │ │ ├── ActivityUtil.java
│ │ │ ├── AnimUtil.java
│ │ │ ├── FileUtil.java
│ │ │ ├── FindRoadUtil.java
│ │ │ ├── OtherUtil.java
│ │ │ ├── PermissionUtil.java
│ │ │ ├── RoadValuesUtil.java
│ │ │ ├── Services
│ │ │ │ └── MusicService.java
│ │ │ ├── ThreadUtil.java
│ │ │ ├── ValueUtil.java
│ │ │ └── ViewUtil.java
│ │ │ └── View
│ │ │ ├── Activity
│ │ │ ├── BaseActivity.java
│ │ │ └── MainActivity.java
│ │ │ ├── Fragment
│ │ │ ├── BaseFragment.java
│ │ │ ├── DifficultyDetailFragment.java
│ │ │ ├── DifficultyFragment.java
│ │ │ ├── IndexFragment.java
│ │ │ ├── RandomRoadFragment.java
│ │ │ └── RoadFragment.java
│ │ │ └── UtilView
│ │ │ ├── CanClearEditText.java
│ │ │ ├── Grid_Yibi.java
│ │ │ └── MyNumPicker.java
│ └── res
│ │ ├── anim
│ │ ├── accelerate_quart.xml
│ │ ├── bottomin.xml
│ │ ├── bottomout.xml
│ │ ├── decelerate_quart.xml
│ │ ├── decelerate_quint.xml
│ │ ├── decelerate_terz.xml
│ │ ├── magnify.xml
│ │ ├── rotate.xml
│ │ ├── scale.xml
│ │ ├── scene_close_enter.xml
│ │ ├── scene_close_exit.xml
│ │ ├── scene_open_enter.xml
│ │ └── scene_open_exit.xml
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── app_icon.jpg
│ │ ├── background_shape_white.xml
│ │ ├── ic_collection.xml
│ │ ├── ic_delete.xml
│ │ ├── ic_help.xml
│ │ ├── ic_helping.xml
│ │ ├── ic_home.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_launcher_foreground.xml
│ │ ├── ic_menu.xml
│ │ ├── ic_refresh.xml
│ │ ├── ic_return.xml
│ │ ├── ic_right.xml
│ │ ├── shape_ellipse_gray.xml
│ │ ├── shape_ellipse_tran.xml
│ │ ├── shape_gray_deep_selected.xml
│ │ ├── shape_gray_selected.xml
│ │ ├── shape_gray_tran_selected.xml
│ │ ├── shape_gray_unselected.xml
│ │ └── shape_red.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── item_difficulty.xml
│ │ ├── item_difficulty_detail.xml
│ │ ├── item_yibi.xml
│ │ ├── item_yibi_collection.xml
│ │ ├── layout_collection.xml
│ │ ├── layout_difficulty.xml
│ │ ├── layout_edittext_number.xml
│ │ ├── layout_index.xml
│ │ ├── layout_road.xml
│ │ ├── layout_road_random.xml
│ │ ├── layout_selector_yibi.xml
│ │ ├── layout_setting.xml
│ │ └── layout_waiting.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── raw
│ │ └── bgm.mp3
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── onelinetoend
│ └── ExampleUnitTest.java
├── build.gradle
├── demo.gif
├── demo1.gif
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | xmlns:android
11 |
12 | ^$
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | xmlns:.*
22 |
23 | ^$
24 |
25 |
26 | BY_NAME
27 |
28 |
29 |
30 |
31 |
32 |
33 | .*:id
34 |
35 | http://schemas.android.com/apk/res/android
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | .*:name
45 |
46 | http://schemas.android.com/apk/res/android
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | name
56 |
57 | ^$
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | style
67 |
68 | ^$
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | .*
78 |
79 | ^$
80 |
81 |
82 | BY_NAME
83 |
84 |
85 |
86 |
87 |
88 |
89 | .*
90 |
91 | http://schemas.android.com/apk/res/android
92 |
93 |
94 | ANDROID_ATTRIBUTE_ORDER
95 |
96 |
97 |
98 |
99 |
100 |
101 | .*
102 |
103 | .*
104 |
105 |
106 | BY_NAME
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OneLineFinishedDrawing
2 | Android版一笔画完小游戏
3 |
4 | 采用单个activity多个fragment构造场景。 具有随机模式,随机模式采用深度优先遍历算法和全组合算法构图。
5 |
6 | 项目为本人大学时期的Android试验作品,有许多不足之处,但如需将本项目源码用于商业发布,请提前知悉本人。
7 |
8 | 交流联系:eaken1063874640@gmail.com,标题写Android,有空回
9 |
10 | 
11 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | buildToolsVersion "28.0.3"
6 | defaultConfig {
7 | applicationId "com.example.onelinetoend"
8 | minSdkVersion 22
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | compileOptions {
21 | sourceCompatibility = 1.8
22 | targetCompatibility = 1.8
23 | }
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 | implementation 'com.jakewharton:butterknife:10.0.0'
29 | implementation 'androidx.appcompat:appcompat:1.1.0'
30 | implementation 'androidx.recyclerview:recyclerview:1.1.0'
31 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
32 | debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'
33 | testImplementation 'junit:junit:4.12'
34 | annotationProcessor 'com.jakewharton:butterknife-compiler:10.0.0'
35 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
36 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
37 | }
38 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/example/onelinetoend/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 |
25 | assertEquals("com.example.onelinetoend", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Adapter/Adapter_Yibi.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Adapter;
2 |
3 | import android.view.LayoutInflater;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.BaseAdapter;
7 |
8 | import com.example.onelinetoend.Model.Bean.Bean_Road;
9 | import com.example.onelinetoend.R;
10 | import com.example.onelinetoend.Util.ValueUtil;
11 |
12 | import java.util.List;
13 |
14 | public class Adapter_Yibi extends BaseAdapter {
15 |
16 | private final int size;
17 | private final int startPosition;
18 | private final List road;
19 |
20 | public Adapter_Yibi(Bean_Road bean_road){
21 | this.road=bean_road.getRoadList();
22 | if(ValueUtil.isListEmpty(road)){
23 | this.size=0;
24 | this.startPosition=0;
25 | }else {
26 | this.size=bean_road.getRows()*bean_road.getColumns();
27 | this.startPosition=road.get(0);
28 | }
29 | }
30 |
31 |
32 | @Override
33 | public int getCount() {
34 | return size;
35 | }
36 |
37 | @Override
38 | public Object getItem(int position) {
39 | return position;
40 | }
41 |
42 | @Override
43 | public long getItemId(int position) {
44 | return position;
45 | }
46 |
47 | @Override
48 | public View getView(int position, View convertView, ViewGroup parent) {
49 | if(convertView==null){
50 | convertView= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_yibi,parent,false).findViewById(R.id.parentyibi);
51 | }
52 | if(startPosition==position){
53 | convertView.findViewById(R.id.baseyibi).setBackgroundResource(R.drawable.shape_gray_deep_selected);
54 | }
55 | boolean isAllowed=false;
56 | for(int p:road){
57 | if(p==position){
58 | isAllowed=true;
59 | }
60 | }
61 | if(!isAllowed){
62 | convertView.setTag("forbidden");
63 | convertView.findViewById(R.id.baseyibi).setBackgroundResource(R.color.colortransparency);
64 | }
65 | return convertView;
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Adapter/Adapter_Yibi_Collection.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Adapter;
2 |
3 | import android.view.LayoutInflater;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.BaseAdapter;
7 | import android.widget.TextView;
8 |
9 | import com.example.onelinetoend.Model.Bean.Bean_Collection;
10 | import com.example.onelinetoend.R;
11 |
12 | import java.util.List;
13 |
14 | public class Adapter_Yibi_Collection extends BaseAdapter {
15 |
16 | private List collections;
17 |
18 | public Adapter_Yibi_Collection(List collections) {
19 | this.collections = collections;
20 | if(collections.size()==0){
21 | collections.add(new Bean_Collection(0,0,0));
22 | }
23 | }
24 |
25 | @Override
26 | public int getCount() {
27 | return collections.size();
28 | }
29 |
30 | @Override
31 | public Object getItem(int position) {
32 | return collections.get(position);
33 | }
34 |
35 | @Override
36 | public long getItemId(int position) {
37 | return position;
38 | }
39 |
40 | class ViewHolder{
41 | TextView rows;
42 | TextView columns;
43 | TextView difficulties;
44 | TextView passedSum;
45 | }
46 |
47 | @Override
48 | public View getView(int position, View convertView, ViewGroup parent) {
49 | ViewHolder viewHolder;
50 | if(convertView==null){
51 | convertView= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_yibi_collection,null);
52 | viewHolder=new ViewHolder();
53 | viewHolder.columns=convertView.findViewById(R.id.columns);
54 | viewHolder.rows=convertView.findViewById(R.id.rows);
55 | viewHolder.difficulties=convertView.findViewById(R.id.difficulties);
56 | viewHolder.passedSum=convertView.findViewById(R.id.passedSum);
57 | convertView.setTag(viewHolder);
58 | }else {
59 | viewHolder=(ViewHolder)convertView.getTag();
60 | }
61 | viewHolder.passedSum.setText(collections.get(position).getSum()+"");
62 | viewHolder.columns.setText(collections.get(position).getColumns()+"");
63 | viewHolder.rows.setText(collections.get(position).getRows()+"");
64 | viewHolder.difficulties.setText(collections.get(position).getDifficulties()+"");
65 | return convertView;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Adapter/Adapter_difficulty.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Adapter;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.ViewGroup;
7 | import android.widget.Button;
8 | import android.widget.Toast;
9 |
10 | import com.example.onelinetoend.Model.Bean.Bean_Road;
11 | import com.example.onelinetoend.R;
12 | import com.example.onelinetoend.Util.ActivityUtil;
13 | import com.example.onelinetoend.Util.ValueUtil;
14 | import com.example.onelinetoend.View.Activity.MainActivity;
15 | import com.example.onelinetoend.View.Fragment.DifficultyDetailFragment;
16 |
17 | import java.util.List;
18 |
19 | import androidx.annotation.NonNull;
20 | import androidx.recyclerview.widget.RecyclerView;
21 |
22 | public class Adapter_difficulty extends RecyclerView.Adapter {
23 |
24 | private List> roadValuesList;
25 |
26 | public Adapter_difficulty(List> roadValuesList){
27 | this.roadValuesList = roadValuesList;
28 | }
29 |
30 | @NonNull
31 | @Override
32 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
33 | return new RecyclerView.ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_difficulty,parent,false)){};
34 | }
35 |
36 | @Override
37 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
38 | Button button = holder.itemView.findViewById(R.id.itemdifficulty);
39 | button.setText("关卡"+(position+1));
40 | button.setOnClickListener(v->{
41 | final Activity activity = ActivityUtil.getInstance().getCurActivity();
42 | if(activity instanceof MainActivity){
43 | Bundle bundle = new Bundle();
44 | bundle.putInt(ValueUtil.Cur_Detail_Position,position);
45 | ((MainActivity)activity).startFragment(DifficultyDetailFragment.class,0,bundle,null);
46 | }else {
47 | button.post(()->Toast.makeText(button.getContext(),"跳转失败,请联系管理员",Toast.LENGTH_SHORT).show());
48 | }
49 | });
50 | }
51 |
52 | @Override
53 | public int getItemCount() {
54 | return roadValuesList.size();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Adapter/Adapter_difficulty_detail.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Adapter;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.ViewGroup;
7 | import android.widget.Button;
8 | import android.widget.TextView;
9 | import android.widget.Toast;
10 |
11 | import com.example.onelinetoend.Model.Bean.Bean_Road;
12 | import com.example.onelinetoend.Model.MySql;
13 | import com.example.onelinetoend.R;
14 | import com.example.onelinetoend.Util.ActivityUtil;
15 | import com.example.onelinetoend.Util.RoadValuesUtil;
16 | import com.example.onelinetoend.Util.ValueUtil;
17 | import com.example.onelinetoend.View.Activity.MainActivity;
18 | import com.example.onelinetoend.View.Fragment.RoadFragment;
19 |
20 | import java.util.List;
21 |
22 | import androidx.annotation.NonNull;
23 | import androidx.core.widget.TextViewCompat;
24 | import androidx.recyclerview.widget.RecyclerView;
25 |
26 | public class Adapter_difficulty_detail extends RecyclerView.Adapter {
27 |
28 | private List roadList;
29 | private MySql mySql;
30 | private int curDetailPosition;
31 |
32 | public Adapter_difficulty_detail(int curDetailPosition){
33 | this.roadList = RoadValuesUtil.roadValuesList.get(curDetailPosition);
34 | this.curDetailPosition = curDetailPosition;
35 | }
36 |
37 | @NonNull
38 | @Override
39 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
40 | return new RecyclerView.ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_difficulty_detail,parent,false)){};
41 | }
42 |
43 | @Override
44 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
45 | if(mySql == null) mySql=new MySql(holder.itemView.getContext());
46 | TextView textView = holder.itemView.findViewById(R.id.itemDifficultyDetail);
47 | TextViewCompat.setAutoSizeTextTypeWithDefaults(textView, TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
48 | textView.setText(""+(position+1));
49 | if(mySql.checkPassedYibi(roadList.get(position))){
50 | textView.setBackgroundResource(R.drawable.shape_gray_selected);
51 | }else {
52 | textView.setBackgroundResource(R.drawable.shape_gray_unselected);
53 | }
54 | textView.setOnClickListener(v->{
55 | final Activity activity = ActivityUtil.getInstance().getCurActivity();
56 | if(activity instanceof MainActivity){
57 | Bundle bundle = new Bundle();
58 | bundle.putInt(ValueUtil.Cur_Detail_Position,curDetailPosition);
59 | bundle.putInt(ValueUtil.Cur_Road_Position,position);
60 | ((MainActivity)activity).startFragment(RoadFragment.class,0,bundle,null);
61 | }else {
62 | textView.post(()->Toast.makeText(textView.getContext(),"跳转失败,请联系管理员",Toast.LENGTH_SHORT).show());
63 | }
64 | });
65 | }
66 |
67 | @Override
68 | public int getItemCount() {
69 | return roadList.size();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Model/Bean/Bean_Collection.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Model.Bean;
2 |
3 | public class Bean_Collection {
4 | private int rows,columns,difficulties,sum=0;
5 |
6 | public int getSum() {
7 | return sum;
8 | }
9 |
10 | public void setSum(int sum) {
11 | this.sum = sum;
12 | }
13 |
14 | public void addSum(){
15 | sum++;
16 | }
17 |
18 | public int getRows() {
19 | return rows;
20 | }
21 |
22 | public void setRows(int rows) {
23 | this.rows = rows;
24 | }
25 |
26 | public int getColumns() {
27 | return columns;
28 | }
29 |
30 | public void setColumns(int columns) {
31 | this.columns = columns;
32 | }
33 |
34 | public int getDifficulties() {
35 | return difficulties;
36 | }
37 |
38 | public void setDifficulties(int difficulties) {
39 | this.difficulties = difficulties;
40 | }
41 |
42 | public Bean_Collection(int rows, int columns, int difficulties) {
43 | this.rows = rows;
44 | this.columns = columns;
45 | this.difficulties = difficulties;
46 | }
47 |
48 | public boolean equals(Bean_Collection collection) {
49 | return this.columns==collection.columns
50 | &&this.difficulties==collection.difficulties
51 | &&this.rows==collection.rows;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Model/Bean/Bean_Road.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Model.Bean;
2 |
3 | import com.example.onelinetoend.Util.ValueUtil;
4 |
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | import androidx.annotation.NonNull;
9 |
10 | public class Bean_Road {
11 | private final int rows;
12 | private final int columns;
13 | private final List roadList;
14 |
15 | public Bean_Road(int rows, int columns, @NonNull List road) {
16 | if(road==null) throw new NullPointerException("List road 不能为null");
17 | this.rows = rows;
18 | this.columns = columns;
19 | this.roadList = road;
20 | }
21 |
22 | public Bean_Road(int rows, int columns, Integer[] roads) {
23 | this.rows = rows;
24 | this.columns = columns;
25 | this.roadList = Arrays.asList(roads);
26 | }
27 |
28 | public int getRows() {
29 | return rows;
30 | }
31 |
32 | public int getColumns() {
33 | return columns;
34 | }
35 |
36 | public List getRoadList() {
37 | return roadList;
38 | }
39 |
40 | public String getRoadString(){
41 | return ValueUtil.getListString(roadList);
42 | }
43 |
44 | public int getDifficulties(){
45 | return rows*columns-roadList.size();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Model/MySql.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Model;
2 |
3 | import android.content.ContentValues;
4 | import android.content.Context;
5 | import android.database.Cursor;
6 | import android.database.sqlite.SQLiteDatabase;
7 | import android.database.sqlite.SQLiteOpenHelper;
8 |
9 | import com.example.onelinetoend.Model.Bean.Bean_Road;
10 | import com.example.onelinetoend.Util.ValueUtil;
11 |
12 | public class MySql extends SQLiteOpenHelper {
13 |
14 | public MySql(Context context){
15 | super(context, "Yibi.db3", null, 1);
16 | }
17 |
18 | @Override
19 | public void onCreate(SQLiteDatabase db) {
20 | db.execSQL("create table passedYibi(_no Integer primary key autoincrement," +
21 | "rows Integer not null," +
22 | "columns Integer not null," +
23 | "difficulties Integer not null," +
24 | "road Text not null)");
25 | db.execSQL("create table savedYibi(_no Integer primary key autoincrement," +
26 | "rows Integer not null," +
27 | "columns Integer not null," +
28 | "difficulties Integer not null," +
29 | "road Text not null)");
30 | db.execSQL("create table errorYibi(_no Integer primary key autoincrement," +
31 | "rows Integer not null," +
32 | "columns Integer not null," +
33 | "difficultiesStr Text not null," +
34 | "startPosition Integer not null)");
35 | }
36 |
37 | @Override
38 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
39 | db.execSQL("DROP TABLE IF EXISTS passedYibi");
40 | db.execSQL("DROP TABLE IF EXISTS savedYibi");
41 | db.execSQL("DROP TABLE IF EXISTS errorYibi");
42 | onCreate(db);
43 | }
44 |
45 | public boolean checkErrorYibi(int rows, int columns, String difficultiesStr,int startPosition){
46 | if(ValueUtil.isEmpty(difficultiesStr)) return true;
47 | Cursor cursor=getReadableDatabase().query("errorYibi",null,"rows=? and columns=? and difficultiesStr=? and startPosition=?"
48 | ,new String[]{rows+"",columns+"",difficultiesStr,startPosition+""},null,null,null);
49 | boolean f=cursor.getCount()>0;
50 | cursor.close();
51 | return f;
52 | }
53 |
54 | public void insertErrorYibi(int rows, int columns, String difficultiesStr,int startPosition){
55 | if(checkErrorYibi(rows,columns,difficultiesStr,startPosition)) return;
56 | ContentValues values=new ContentValues();
57 | values.put("rows",rows);
58 | values.put("columns",columns);
59 | values.put("difficultiesStr",difficultiesStr);
60 | values.put("startPosition",startPosition);
61 | getReadableDatabase().insert("errorYibi",null,values);
62 | }
63 |
64 | public boolean checkPassedYibiWithRoad(String roadstring){
65 | Cursor cursor=getReadableDatabase().query("passedYibi",null,"road=?"
66 | ,new String[]{roadstring},null,null,null);
67 | boolean f=cursor.getCount()>0;
68 | cursor.close();
69 | return f;
70 | }
71 |
72 | public boolean checkPassedYibi(Bean_Road road){
73 | if(road==null) return false;
74 | Cursor cursor=getReadableDatabase().query("passedYibi",null,"rows=? and columns=? and difficulties=? and road=?"
75 | ,new String[]{road.getRows()+"",road.getColumns()+"",road.getDifficulties()+"",road.getRoadString()},null,null,null);
76 | boolean f=cursor.getCount()>0;
77 | cursor.close();
78 | return f;
79 | }
80 |
81 | public void insertPassedYibi(Bean_Road road){
82 | if(checkPassedYibi(road) || road==null) return;
83 | ContentValues values=new ContentValues();
84 | values.put("rows",road.getRows());
85 | values.put("columns",road.getColumns());
86 | values.put("difficulties",road.getDifficulties());
87 | values.put("road",road.getRoadString());
88 | getReadableDatabase().insert("passedYibi",null,values);
89 | }
90 |
91 | public Cursor getAllPassedYibi(){
92 | return getReadableDatabase().query("passedYibi",null,null
93 | ,null,null,null,null);
94 | }
95 |
96 | public void cleanPassedYibi(){
97 | getReadableDatabase().delete("passedYibi",null,null);
98 | }
99 |
100 | public boolean checkSavedYibi(Bean_Road road){
101 | Cursor cursor=getReadableDatabase().query("savedYibi",null,"rows=? and columns=? and difficulties=? and road=?"
102 | ,new String[]{road.getRows()+"",road.getColumns()+"",road.getDifficulties()+"",road.getRoadString()},null,null,null);
103 | boolean f=cursor.getCount()>0;
104 | cursor.close();
105 | return f;
106 | }
107 |
108 | public void insertSavedYibi(Bean_Road road){
109 | if(checkSavedYibi(road)) return;
110 | ContentValues values=new ContentValues();
111 | values.put("rows",road.getRows());
112 | values.put("columns",road.getColumns());
113 | values.put("difficulties",road.getDifficulties());
114 | values.put("road",road.getRoadString());
115 | getReadableDatabase().insert("savedYibi",null,values);
116 | }
117 |
118 | public int getSavedCount( int rows, int columns, int difficulties){
119 | Cursor cursor=getReadableDatabase().query("savedYibi",null,"rows=? and columns=? and difficulties=?"
120 | ,new String[]{rows+"",columns+"",difficulties+""},null,null,null);
121 | int c=cursor.getCount();
122 | cursor.close();
123 | return c;
124 | }
125 |
126 | public Cursor getSavedYibi( int rows, int columns, int difficulties){
127 | return getReadableDatabase().query("savedYibi",null,"rows=? and columns=? and difficulties=?"
128 | ,new String[]{rows+"",columns+"",difficulties+""},null,null,null);
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Util/ActivityUtil.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Util;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Activity;
5 |
6 | import java.util.List;
7 | import java.util.concurrent.CopyOnWriteArrayList;
8 |
9 | public class ActivityUtil {
10 |
11 | List list = new CopyOnWriteArrayList<>();
12 | @SuppressLint("StaticFieldLeak")
13 | private volatile static ActivityUtil ACTIVITY_UTIL;
14 | private Activity curActivity;
15 |
16 | public static ActivityUtil getInstance(){
17 | if(ACTIVITY_UTIL==null){
18 | synchronized (ActivityUtil.class){
19 | if (ACTIVITY_UTIL==null){
20 | ACTIVITY_UTIL=new ActivityUtil();
21 | }
22 | }
23 | }
24 | return ACTIVITY_UTIL;
25 | }
26 |
27 | public Activity getCurActivity() {
28 | return curActivity;
29 | }
30 |
31 | public void setCurActivity(Activity curActivity) {
32 | this.curActivity = curActivity;
33 | }
34 |
35 |
36 | /**
37 | * Activity关闭时,删除Activity列表中的Activity对象*/
38 | public void removeActivity(Activity a){
39 | list.remove(a);
40 | }
41 |
42 | /**
43 | * 向Activity列表中添加Activity对象*/
44 | public void addActivity(Activity a){
45 | list.add(a);
46 | }
47 |
48 | public void finishActivity(){
49 | for (Activity activity : list) {
50 | if (null != activity) {
51 | activity.finish();
52 | }
53 | }
54 | //杀死该应用进程
55 | android.os.Process.killProcess(android.os.Process.myPid());
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Util/AnimUtil.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Util;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.AnimatorSet;
6 | import android.animation.ObjectAnimator;
7 | import android.annotation.SuppressLint;
8 | import android.transition.ChangeBounds;
9 | import android.transition.ChangeClipBounds;
10 | import android.transition.ChangeImageTransform;
11 | import android.transition.ChangeTransform;
12 | import android.transition.Fade;
13 | import android.transition.Transition;
14 | import android.transition.TransitionSet;
15 | import android.view.View;
16 | import android.view.animation.AlphaAnimation;
17 | import android.view.animation.Animation;
18 | import android.view.animation.LinearInterpolator;
19 | import android.view.animation.RotateAnimation;
20 | import android.view.animation.ScaleAnimation;
21 | import android.view.animation.TranslateAnimation;
22 |
23 | import java.util.concurrent.ConcurrentHashMap;
24 |
25 | import androidx.annotation.NonNull;
26 |
27 | public class AnimUtil {
28 |
29 | private final static ConcurrentHashMap animatorMap = new ConcurrentHashMap<>();
30 |
31 | public static void removeAnimator(View target){
32 | if(target==null) return;
33 | Animator animator = animatorMap.get(target);
34 | if(animator!=null){
35 | animator.cancel();
36 | animatorMap.remove(target);
37 | }
38 | }
39 |
40 | public static void clearAnimator(){
41 | animatorMap.clear();
42 | }
43 |
44 | public static Animation doTranslationAnimation(View view, float rightF, float downF, boolean isRepeat, Animation.AnimationListener listener,boolean isStartNow){
45 | Animation anim = new TranslateAnimation(0,rightF,0,downF);
46 | anim.setDuration(ValueUtil.animateTime);
47 | anim.setAnimationListener(listener);
48 | view.setAnimation(anim);
49 | if(isRepeat){
50 | anim.setRepeatMode(Animation.RESTART);
51 | anim.setRepeatCount(Animation.INFINITE);
52 | }
53 | if(isStartNow){
54 | view.startAnimation(anim);
55 | }
56 | return anim;
57 | }
58 |
59 | public static Animation doScaleAnimation(View view, float fromScaleX, float toScaleX, float fromScaleY, float toScaleY, boolean isRepeat, Animation.AnimationListener listener,boolean isStartNow){
60 | Animation anim = new ScaleAnimation(fromScaleX, toScaleX, fromScaleY, toScaleY,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
61 | anim.setDuration(ValueUtil.animateTime);
62 | anim.setAnimationListener(listener);
63 | view.setAnimation(anim);
64 | if(isRepeat){
65 | anim.setRepeatMode(Animation.RESTART);
66 | anim.setRepeatCount(Animation.INFINITE);
67 | }
68 | if(isStartNow){
69 | view.startAnimation(anim);
70 | }
71 | return anim;
72 | }
73 |
74 | public static Animation doAlphaAnimation(View view, float from, float to, boolean isRepeat, Animation.AnimationListener listener,boolean isStartNow){
75 | Animation anim = new AlphaAnimation(from,to);
76 | anim.setDuration(ValueUtil.animateTime);
77 | anim.setAnimationListener(listener);
78 | view.setAnimation(anim);
79 | if(isRepeat){
80 | anim.setRepeatMode(Animation.RESTART);
81 | anim.setRepeatCount(Animation.INFINITE);
82 | }
83 | if(isStartNow){
84 | view.startAnimation(anim);
85 | }
86 | return anim;
87 | }
88 |
89 | public static Animation doRotateAnimation(View view, float from, float to, boolean isRepeat, Animation.AnimationListener listener,boolean isStartNow){
90 | Animation anim = new RotateAnimation(from,to, Animation.RELATIVE_TO_SELF,0.5f, Animation.RELATIVE_TO_SELF,0.5f);
91 | anim.setDuration(ValueUtil.animateTime*2);
92 | anim.setAnimationListener(listener);
93 | anim.setInterpolator(new LinearInterpolator());
94 | view.setAnimation(anim);
95 | if(isRepeat){
96 | anim.setRepeatMode(Animation.RESTART);
97 | anim.setRepeatCount(Animation.INFINITE);
98 | }
99 | if(isStartNow){
100 | view.startAnimation(anim);
101 | }
102 | return anim;
103 | }
104 |
105 | public static Animator doTranslation(View view, float fromRightF, float toRightF, float fromDownF, float toDownF, Animator.AnimatorListener listener,boolean isStartNow){
106 | @SuppressLint("ObjectAnimatorBinding") ObjectAnimator right = ObjectAnimator.ofFloat(view,ValueUtil.anim_translationX,fromRightF,toRightF);
107 | ObjectAnimator up = ObjectAnimator.ofFloat(view,ValueUtil.anim_translationY,fromDownF,toDownF);
108 | AnimatorSet set = new AnimatorSet();
109 | set.addListener(listener==null?new AnimatorListenerAdapter(){}:listener);
110 | set.setDuration(ValueUtil.animateTime).play(right).with(up);
111 | if(isStartNow)
112 | set.start();
113 | animatorMap.put(view,set);
114 | return set;
115 | }
116 |
117 | public static Animator doScale(View view, float fromScaleX, float toScaleX, float fromScaleY, float toScaleY, Animator.AnimatorListener listener,boolean isStartNow){
118 | ObjectAnimator x = ObjectAnimator.ofFloat(view,ValueUtil.anim_scaleX,fromScaleX,toScaleX);
119 | ObjectAnimator y = ObjectAnimator.ofFloat(view,ValueUtil.anim_scaleY,fromScaleY,toScaleY);
120 | AnimatorSet set = new AnimatorSet();
121 | set.addListener(listener==null?new AnimatorListenerAdapter(){}:listener);
122 | set.setDuration(ValueUtil.animateTime).play(x).with(y);
123 | if(isStartNow)
124 | set.start();
125 | animatorMap.put(view,set);
126 | return set;
127 | }
128 |
129 | public static Animator doAlpha(View view, float from, float to, Animator.AnimatorListener listener,boolean isStartNow){
130 | ObjectAnimator alpha = ObjectAnimator.ofFloat(view,ValueUtil.anim_alpha,from,to);
131 | AnimatorSet set = new AnimatorSet();
132 | set.addListener(listener==null?new AnimatorListenerAdapter(){}:listener);
133 | set.play(alpha);
134 | if(isStartNow)
135 | set.start();
136 | animatorMap.put(view,set);
137 | return set;
138 | }
139 |
140 | public static TransitionSet getScaleTransition(){
141 | TransitionSet transitionSet = new TransitionSet();
142 | transitionSet.addTransition(new ChangeBounds());
143 | transitionSet.addTransition(new ChangeTransform());
144 | transitionSet.addTransition(new ChangeClipBounds());
145 | transitionSet.addTransition(new ChangeImageTransform());
146 | return transitionSet;
147 | }
148 |
149 | }
150 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Util/FileUtil.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Util;
2 |
3 | import android.os.Environment;
4 |
5 | import java.io.BufferedWriter;
6 | import java.io.File;
7 | import java.io.FileWriter;
8 | import java.text.SimpleDateFormat;
9 | import java.util.Collections;
10 | import java.util.Date;
11 | import java.util.IdentityHashMap;
12 | import java.util.Set;
13 |
14 | public class FileUtil {
15 |
16 |
17 | public static String getBaseFilePath(){
18 | File file=new File(Environment.getExternalStorageDirectory()+"/"+ValueUtil.appName);
19 | try {
20 | if(!file.exists()){
21 | file.mkdirs();
22 | }
23 | return file.getAbsolutePath();
24 | }catch (Exception e){
25 | OtherUtil.DebuggerLog(e.getMessage());
26 | return Environment.getExternalStorageDirectory().getAbsolutePath();
27 | }
28 | }
29 |
30 |
31 | public static File getLogFile(){
32 | File file=new File(getBaseFilePath()+"/Log","log.txt");
33 | try {
34 | if(!file.exists()){
35 | file.getParentFile().mkdirs();
36 | file.createNewFile();
37 | }
38 | return file;
39 | }catch (Exception e){
40 | OtherUtil.DebuggerLog(e.getMessage());
41 | return null;
42 | }
43 | }
44 |
45 |
46 | public static void wirteToFile(File fileWrite, String string, boolean isAppend) {
47 | if(fileWrite==null) return;
48 |
49 | try {
50 | // 首先判断文件是否存在
51 | if (!fileWrite.exists()) {
52 | fileWrite.getParentFile().mkdirs();
53 | if (!fileWrite.createNewFile()) { // 文件不存在则创建文件
54 | OtherUtil.DebuggerLog("wirteToFile:创建文件失败");
55 | return;
56 | }
57 | }
58 | FileWriter filerWriter = new FileWriter(fileWrite, isAppend);// 后面这个参数代表是不是要接上文件中原来的数据,不进行覆盖
59 | BufferedWriter bufWriter = new BufferedWriter(filerWriter);
60 |
61 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// HH:mm:ss
62 | //获取当前时间
63 | Date date = new Date(System.currentTimeMillis());
64 | bufWriter.write("\n-------------------------------" +
65 | "\n-------------------------------" +
66 | "\n-------------------------------" +
67 | "\n-------------------------------" +
68 | "\n-------------------------------" +
69 | "\n"+simpleDateFormat.format(date)+":\n");
70 | bufWriter.write(string);
71 | bufWriter.newLine();
72 | bufWriter.close();
73 | filerWriter.close();
74 | }catch (Exception e){
75 | OtherUtil.DebuggerLog("wirteToFile:输出文件失败\n"+e.getMessage());
76 | }
77 |
78 | }
79 |
80 | public static void writeThrowleToLog(Throwable throwable){
81 | if(PermissionUtil.checkSavePermission(ActivityUtil.getInstance().getCurActivity(),100)){
82 | File logFile = FileUtil.getLogFile();
83 | if(logFile!=null){
84 | StringBuilder logStringBuilder = new StringBuilder();
85 | Set dejaVu =
86 | Collections.newSetFromMap(new IdentityHashMap<>());
87 | dejaVu.add(throwable);
88 |
89 | // Print our stack trace
90 | logStringBuilder.append(throwable).append("\n");
91 | StackTraceElement[] trace = throwable.getStackTrace();
92 | for (StackTraceElement traceElement : trace){
93 | logStringBuilder.append("\tat ").append(traceElement).append("\n");
94 | }
95 |
96 | // Print suppressed exceptions, if any
97 | for (Throwable se : throwable.getSuppressed()){
98 | printEnclosedStackTrace(trace, "Suppressed: ", "\t", dejaVu,se,logStringBuilder);
99 | }
100 |
101 | // Print cause, if any
102 | Throwable ourCause = throwable.getCause();
103 | if (ourCause != null)
104 | printEnclosedStackTrace(trace, "Caused by: ", "", dejaVu,ourCause,logStringBuilder);
105 |
106 |
107 | FileUtil.wirteToFile(logFile,logStringBuilder.toString(),true);
108 | }
109 | }
110 | }
111 |
112 | private static void printEnclosedStackTrace(StackTraceElement[] enclosingTrace,
113 | String caption,
114 | String prefix,
115 | Set dejaVu, Throwable throwable, StringBuilder stringBuilder) {
116 | if (dejaVu.contains(throwable)) {
117 | stringBuilder.append("\t[CIRCULAR REFERENCE:").append(throwable).append("]").append("\n");
118 | } else {
119 | dejaVu.add(throwable);
120 | // Compute number of frames in common between this and enclosing trace
121 | StackTraceElement[] trace = throwable.getStackTrace();
122 | int m = trace.length - 1;
123 | int n = enclosingTrace.length - 1;
124 | while (m >= 0 && n >=0 && trace[m].equals(enclosingTrace[n])) {
125 | m--; n--;
126 | }
127 | int framesInCommon = trace.length - 1 - m;
128 |
129 | // Print our stack trace
130 | stringBuilder.append(prefix).append(caption).append(throwable);
131 | for (int i = 0; i <= m; i++)
132 | stringBuilder.append(prefix).append("\tat ").append(trace[i]);
133 | if (framesInCommon != 0)
134 | stringBuilder.append(prefix).append("\t... ").append(framesInCommon).append(" more");
135 |
136 | // Print suppressed exceptions, if any
137 | for (Throwable se : throwable.getSuppressed())
138 | printEnclosedStackTrace( trace, "Suppressed: ",
139 | prefix +"\t", dejaVu,se,stringBuilder);
140 |
141 | // Print cause, if any
142 | Throwable ourCause = throwable.getCause();
143 | if (ourCause != null)
144 | printEnclosedStackTrace(trace, "Caused by: ", prefix, dejaVu,ourCause,stringBuilder);
145 | }
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Util/FindRoadUtil.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Util;
2 |
3 | import android.content.Context;
4 |
5 | import com.example.onelinetoend.Model.Bean.Bean_Road;
6 | import com.example.onelinetoend.Model.MySql;
7 |
8 | import java.util.ArrayList;
9 | import java.util.Collections;
10 | import java.util.List;
11 | import java.util.Random;
12 |
13 | import androidx.annotation.Nullable;
14 |
15 | public class FindRoadUtil {
16 |
17 | private Context context;
18 | public MySql mySql;
19 | private Random random = new Random();
20 |
21 | public FindRoadUtil(Context context){
22 | this.context = context;
23 | mySql = new MySql(context);
24 | }
25 |
26 | public Context getContext() {
27 | return context;
28 | }
29 |
30 | public boolean startToFindRoad=false;//用来开始寻路
31 | public boolean findingRoad=false;//用来判断是否获取了一张指定图的指定数量的路径
32 | public int rows,columns,difficulties;
33 |
34 |
35 | //根据行列数和难度获取一条指定路径,有可能很长时间都得不到路径,所以最好放在子线程进行
36 | public Bean_Road getAppointedRoad(int rows, int columns, int difficulties,boolean passPassed){
37 | this.rows=rows;
38 | this.columns=columns;
39 | this.difficulties=difficulties;
40 | startToFindRoad=true;
41 | Bean_Road road=null;
42 | while (startToFindRoad){
43 | final int sp =random.nextInt(rows*columns);
44 | List pList = new ArrayList<>();
45 | for(int i=0;i> comForbiddensList = ValueUtil.comNumLists(null, 20, pList.toArray(new Integer[0]), difficulties, new ValueUtil.checkBooleanInterface() {
50 | @Override
51 | public boolean checkBoolean(String obj) {
52 | if(mySql==null) return true;
53 | return !mySql.checkErrorYibi(rows,columns,obj,sp);
54 | }
55 | });
56 | for (List forbiddenPositionList : comForbiddensList){
57 | if(!startToFindRoad) break;
58 | if(mySql.checkErrorYibi(rows,columns,ValueUtil.getListString(forbiddenPositionList),sp)) continue;
59 | road = getARoad(rows,columns,sp,forbiddenPositionList,passPassed);
60 | }
61 | }
62 | return road;
63 | }
64 |
65 | //根据行列数和起始点以及禁止点尝试获取一条可行路径
66 | public Bean_Road getARoad(int rows, int columns, int startPosition, List forbiddenPositionList, boolean passPassed){
67 | List roads=new ArrayList<>();
68 | Bean_Road road=null;
69 | findingRoad=true;
70 | findRoads(rows,columns,startPosition,null,forbiddenPositionList,roads,null);
71 | if(roads.size()>0 && (road=roads.get(0))!=null){
72 | if(passPassed && mySql.checkPassedYibi(road)){
73 | return null;
74 | }
75 | startToFindRoad=false;
76 | }
77 | return road;
78 | }
79 |
80 | public void findRoads(final int row, final int column, int curPosition, @Nullable List choosedPositions, final List forbiddenList,@Nullable List roads,int[] nos) {
81 | if(!startToFindRoad || !findingRoad) return;
82 |
83 | if(forbiddenList.indexOf(curPosition) != -1){
84 | return;
85 | }
86 |
87 | if(choosedPositions==null){
88 | choosedPositions = new ArrayList<>();
89 | }
90 | if(choosedPositions.isEmpty()){
91 | nos = new int[]{0};
92 | choosedPositions.add(curPosition);
93 | }
94 |
95 | if(nos==null || nos.length!=1){
96 | nos = new int[]{0};
97 | }
98 |
99 | //当路线完成时,退出方法,nos的数不会减
100 | if((choosedPositions.size()+forbiddenList.size())==row*column){
101 | //得到的路径只要不重复全扔到数据库,以备后用
102 | final List cps = new ArrayList<>(choosedPositions);
103 | ThreadUtil.getInstance().runOnChildThread(() -> mySql.insertSavedYibi(new Bean_Road(row,column,cps)));
104 |
105 | if(roads!=null){
106 | roads.add(new Bean_Road(rows,columns,choosedPositions));
107 | }
108 |
109 | findingRoad=false;
110 | return;
111 | }
112 |
113 |
114 | //四方寻路,且路线互不影响
115 | if(findUpRoad(curPosition-column, choosedPositions,forbiddenList)){
116 | nos[0] = nos[0]+1;
117 | List nextChoosedPositions = new ArrayList<>(choosedPositions);
118 | nextChoosedPositions.add(curPosition-column);
119 | findRoads(row,column,curPosition-column,nextChoosedPositions,forbiddenList,roads,nos);
120 | }
121 | if(findLeftRoad(curPosition-1,column,choosedPositions,forbiddenList)){
122 | nos[0] = nos[0]+1;
123 | List nextChoosedPositions = new ArrayList<>(choosedPositions);
124 | nextChoosedPositions.add(curPosition-1);
125 | findRoads(row,column,curPosition-1,nextChoosedPositions,forbiddenList,roads,nos);
126 | }
127 | if(findDownRoad(curPosition+column,row*column,choosedPositions,forbiddenList)){
128 | nos[0] = nos[0]+1;
129 | List nextChoosedPositions = new ArrayList<>(choosedPositions);
130 | nextChoosedPositions.add(curPosition+column);
131 | findRoads(row,column,curPosition+column,nextChoosedPositions,forbiddenList,roads,nos);
132 | }
133 | if(findRightRoad(curPosition+1,row*column,column,choosedPositions,forbiddenList)){
134 | nos[0] = nos[0]+1;
135 | List nextChoosedPositions = new ArrayList<>(choosedPositions);
136 | nextChoosedPositions.add(curPosition+1);
137 | findRoads(row,column,curPosition+1,nextChoosedPositions,forbiddenList,roads,nos);
138 | }
139 |
140 | //当前路线未完成才减1
141 | nos[0] = nos[0]-1;
142 |
143 | //nos的数≤0时,即所有路线都走不通,记录错误图,一张图由起点和障碍位置确定
144 | if(nos[0]<=0){
145 | final int sp;
146 | if(choosedPositions.size()>0) sp = choosedPositions.get(0);
147 | else sp = curPosition;
148 | //记录错误图,减少下次寻路时间
149 | ThreadUtil.getInstance().runOnChildThread(() -> mySql.insertErrorYibi(row,column,ValueUtil.getListString(forbiddenList),sp));
150 | }
151 |
152 | }
153 |
154 | //向上寻路
155 | private static boolean findUpRoad(int upPosition, List choosedPositions, List forbiddenCount) {
156 | return upPosition >= 0
157 | && choosedPositions.lastIndexOf(upPosition) == -1
158 | && forbiddenCount.indexOf(upPosition) == -1;
159 | }
160 |
161 | //向左寻路
162 | private static boolean findLeftRoad(int leftPosition, int column, List choosedPositions, List forbiddenCount) {
163 | return leftPosition >= 0
164 | && choosedPositions.lastIndexOf(leftPosition) == -1
165 | && forbiddenCount.indexOf(leftPosition) == -1
166 | &&(leftPosition+1)%column!=0;
167 | }
168 |
169 | //向下寻路
170 | private static boolean findDownRoad(int downPosition, int size, List choosedPositions, List forbiddenCount) {
171 | return downPosition < size
172 | && choosedPositions.lastIndexOf(downPosition) == -1
173 | && forbiddenCount.indexOf(downPosition) == -1;
174 | }
175 |
176 | //向右寻路
177 | private static boolean findRightRoad(int rightPosition, int size, int column, List choosedPositions, List forbiddenCount) {
178 | return rightPosition max_str_length) {
25 | Log.e(ValueUtil.debugerTag, msg.substring(0, max_str_length));
26 | msg = msg.substring(max_str_length);
27 | }
28 | //剩余部分
29 | Log.e(ValueUtil.debugerTag, msg);
30 | }else {
31 | while (msg.length() > max_str_length) {
32 | System.out.println(msg.substring(0, max_str_length));
33 | msg = msg.substring(max_str_length);
34 | }
35 | System.out.println(msg);
36 | }
37 | }
38 |
39 | public interface OnCallBackListener extends Serializable {
40 | void OnCallBackFirst(t... params);
41 | void OnCallBackSecond(p... params);
42 | void OnCallBackThrid(q... params);
43 | }
44 |
45 | public static class OnCallBackListenerImpl implements OnCallBackListener{
46 |
47 | @Override
48 | public void OnCallBackFirst(t... params) {
49 |
50 | }
51 |
52 | @Override
53 | public void OnCallBackSecond(t... params) {
54 |
55 | }
56 |
57 | @Override
58 | public void OnCallBackThrid(t... params) {
59 |
60 | }
61 | }
62 |
63 | public static class OnCallBackListenerImpl2 implements OnCallBackListener{
64 |
65 | @Override
66 | public void OnCallBackFirst(t... params) {
67 |
68 | }
69 |
70 | @Override
71 | public void OnCallBackSecond(p... params) {
72 |
73 | }
74 |
75 | @Override
76 | public void OnCallBackThrid(Object... params) {
77 |
78 | }
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Util/PermissionUtil.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Util;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.content.pm.PackageManager;
6 | import android.widget.Toast;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.core.app.ActivityCompat;
10 | import androidx.core.content.ContextCompat;
11 | import androidx.fragment.app.Fragment;
12 |
13 | public class PermissionUtil {
14 |
15 | public static boolean checkSavePermission(Activity activity, int requestCode){
16 | if(ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
17 | ActivityCompat.requestPermissions(activity,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},requestCode);
18 | return false;
19 | }else {
20 | return true;
21 | }
22 | }
23 |
24 | public static boolean checkSavePermissionInFragment(@NonNull Fragment fragment, int requestCode){
25 | if(ContextCompat.checkSelfPermission(fragment.getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
26 | fragment.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},requestCode);
27 | return false;
28 | }else {
29 | return true;
30 | }
31 | }
32 |
33 | public static boolean checkCameraPermission(Activity activity, int requestCode){
34 | if(ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
35 | ActivityCompat.requestPermissions(activity,new String[]{Manifest.permission.CAMERA},requestCode);
36 | return false;
37 | }else {
38 | return true;
39 | }
40 | }
41 |
42 | public static boolean checkCameraPermissionInFragment(@NonNull Fragment fragment, int requestCode){
43 | if(ContextCompat.checkSelfPermission(fragment.getContext(), Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
44 | fragment.requestPermissions(new String[]{Manifest.permission.CAMERA},requestCode);
45 | return false;
46 | }else {
47 | return true;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Util/Services/MusicService.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Util.Services;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.media.MediaPlayer;
6 | import android.os.IBinder;
7 |
8 | import com.example.onelinetoend.R;
9 | import com.example.onelinetoend.Util.ValueUtil;
10 |
11 | import androidx.annotation.Nullable;
12 |
13 | public class MusicService extends Service {
14 |
15 | private MediaPlayer mediaPlayer;
16 | private boolean isStop = true;
17 |
18 | @Nullable
19 | @Override
20 | public IBinder onBind(Intent intent) {
21 | return null;
22 | }
23 |
24 | @Override
25 | public int onStartCommand(Intent intent, int flags, int startId) {
26 | final int op;
27 | if(intent==null) op = 2;
28 | else {
29 | op = intent.getIntExtra(ValueUtil.key_toPlayMusic,2);
30 | }
31 | switch (op){
32 | case 0:
33 | if(mediaPlayer==null || isStop){
34 | if(mediaPlayer!=null) mediaPlayer.release();
35 | mediaPlayer = MediaPlayer.create(this, R.raw.bgm);
36 | mediaPlayer.setLooping(true);
37 | }
38 | mediaPlayer.start();
39 | isStop = false;
40 | break;
41 | case 1:
42 | if(mediaPlayer!=null){
43 | mediaPlayer.pause();
44 | isStop = false;
45 | }
46 | break;
47 | default:
48 | if(mediaPlayer!=null){
49 | mediaPlayer.stop();
50 | isStop = true;
51 | }
52 | break;
53 | }
54 |
55 | return START_STICKY;
56 | }
57 |
58 | @Override
59 | public void onDestroy() {
60 | if(mediaPlayer!=null){
61 | mediaPlayer.stop();
62 | mediaPlayer.release();
63 | }
64 | super.onDestroy();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Util/ThreadUtil.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Util;
2 |
3 |
4 | import java.util.Map;
5 | import java.util.Queue;
6 | import java.util.concurrent.ConcurrentHashMap;
7 | import java.util.concurrent.ConcurrentLinkedQueue;
8 | import java.util.concurrent.ExecutorService;
9 | import java.util.concurrent.Executors;
10 | import java.util.concurrent.ScheduledExecutorService;
11 | import java.util.concurrent.TimeUnit;
12 |
13 | public class ThreadUtil {
14 |
15 | final private ExecutorService threads = Executors.newFixedThreadPool(10);
16 | final private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
17 | final private Map> runableMap=new ConcurrentHashMap<>();
18 |
19 | private static volatile ThreadUtil threadUtil;
20 |
21 | public static ThreadUtil getInstance(){
22 | if(threadUtil ==null){
23 | synchronized (ThreadUtil.class){
24 | if(threadUtil ==null){
25 | threadUtil =new ThreadUtil();
26 | }
27 | }
28 | }
29 | return threadUtil;
30 | }
31 |
32 | public void removeRunable(String key){
33 | runableMap.remove(key);
34 | }
35 |
36 | public void addRunableToSingleThead(String key,Runnable runnable){
37 | Queue runnableQueue = runableMap.get(key);
38 | if(runnableQueue==null){
39 | runnableQueue = new ConcurrentLinkedQueue<>();
40 | runableMap.put(key,runnableQueue);
41 | new Thread(()->{
42 | while (true){
43 | try {
44 | Thread.sleep(10);
45 | }catch (Exception e){
46 | OtherUtil.DebuggerLog(e);
47 | }
48 | Runnable r;
49 | final Queue runnableQueue1 = runableMap.get(key);
50 | if(runnableQueue1==null) break;
51 | while ((r=runnableQueue1.poll())!=null){
52 | r.run();
53 | }
54 | }
55 | }).start();
56 | OtherUtil.DebuggerLog("创建线程数"+runableMap.size());
57 | }
58 | runnableQueue.offer(runnable);
59 | }
60 |
61 | public void runOnChildThread(Runnable runnable){
62 | try {
63 | threads.submit(runnable).get();
64 | }catch (Exception e){
65 | OtherUtil.DebuggerLog(e);
66 | }
67 | }
68 |
69 | public void schedule(Runnable runnable,long period){
70 | scheduledExecutorService.scheduleWithFixedDelay(runnable,100,period, TimeUnit.MILLISECONDS);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/Util/ViewUtil.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.Util;
2 |
3 | import android.app.Activity;
4 | import android.app.ActivityManager;
5 | import android.app.Application;
6 | import android.app.Service;
7 | import android.content.ComponentName;
8 | import android.content.Context;
9 | import android.content.DialogInterface;
10 | import android.content.Intent;
11 | import android.content.SharedPreferences;
12 | import android.graphics.Color;
13 | import android.graphics.PorterDuff;
14 | import android.graphics.drawable.ColorDrawable;
15 | import android.os.Bundle;
16 | import android.transition.Fade;
17 | import android.view.LayoutInflater;
18 | import android.view.View;
19 | import android.view.Window;
20 | import android.view.WindowManager;
21 | import android.view.animation.Animation;
22 | import android.widget.Button;
23 | import android.widget.ListView;
24 | import android.widget.NumberPicker;
25 | import android.widget.ProgressBar;
26 | import android.widget.Switch;
27 | import android.widget.TextView;
28 |
29 | import com.example.onelinetoend.Adapter.Adapter_Yibi_Collection;
30 | import com.example.onelinetoend.Model.Bean.Bean_Collection;
31 | import com.example.onelinetoend.R;
32 | import com.example.onelinetoend.Util.Services.MusicService;
33 | import com.example.onelinetoend.View.Fragment.BaseFragment;
34 |
35 | import java.lang.reflect.Field;
36 | import java.util.List;
37 |
38 | import androidx.annotation.IdRes;
39 | import androidx.annotation.NonNull;
40 | import androidx.annotation.Nullable;
41 | import androidx.appcompat.app.AlertDialog;
42 | import androidx.core.content.ContextCompat;
43 | import androidx.fragment.app.Fragment;
44 | import androidx.fragment.app.FragmentManager;
45 | import androidx.fragment.app.FragmentTransaction;
46 |
47 | public class ViewUtil {
48 |
49 | public static void startFragment(@NonNull FragmentManager fragmentManager,Fragment currentFragment,@IdRes int containerId, @NonNull Class fragmentClass, int requestCode, @Nullable Bundle bundle, @Nullable List shareElements){
50 | int launchMode = ValueUtil.getFragmentLaunchMode(fragmentClass);
51 | FragmentTransaction transaction = fragmentManager.beginTransaction();
52 | if(launchMode==ValueUtil.Fragment_LaunchModel_SingleTask){
53 | List fragments = fragmentManager.getFragments();
54 | for(int i = 0, n = fragments.size();i getShareElements();
126 | }
127 |
128 | public static AlertDialog getMyDialog(@NonNull boolean[] checks, @NonNull String[] strings, DialogInterface.OnClickListener listener) {
129 | final Activity context = ActivityUtil.getInstance().getCurActivity();
130 | boolean showCheck = checks.length>0 && checks.length==strings.length;
131 | AlertDialog.Builder builder = new AlertDialog.Builder(context);
132 | if(showCheck) {
133 | builder.setMultiChoiceItems(strings, checks, (dialog, which, isChecked) -> checks[which] = isChecked);
134 | }else if(strings.length>0){
135 | builder.setItems(strings,listener);
136 | }
137 | AlertDialog dialog = builder.create();
138 | Window window = dialog.getWindow();
139 | if (window != null) {
140 | window.setBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.background_shape_white));
141 | window.setWindowAnimations(R.style.scale_anim);
142 | window.clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
143 | window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
144 | | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
145 | }
146 | return dialog;
147 | }
148 |
149 | public static AlertDialog getMyDialog(){
150 | return getMyDialog(new boolean[0],new String[0],null);
151 | }
152 |
153 | public static AlertDialog getMyDialog(String[] strings, DialogInterface.OnClickListener listener){
154 | return getMyDialog(new boolean[0],strings,listener);
155 | }
156 |
157 | public static AlertDialog getWaittingDialog(){
158 | Activity context = ActivityUtil.getInstance().getCurActivity();
159 |
160 | final AlertDialog dialog=getMyDialog();
161 | dialog.setCanceledOnTouchOutside(false);
162 |
163 | View waittingView= LayoutInflater.from(context).inflate(R.layout.layout_waiting,null);
164 |
165 | ProgressBar progressBar=waittingView.findViewById(R.id.progress);
166 | progressBar.getIndeterminateDrawable().setColorFilter(ContextCompat.getColor(context,R.color.colorGray), PorterDuff.Mode.MULTIPLY);
167 |
168 | Button qx=waittingView.findViewById(R.id.qx);
169 | qx.setOnClickListener(v -> dialog.dismiss());
170 |
171 | final TextView hint=waittingView.findViewById(R.id.hint);
172 |
173 | dialog.setOnShowListener(dialog1 -> {
174 | hint.clearAnimation();
175 | hint.setVisibility(View.VISIBLE);
176 | hint.postDelayed(()->AnimUtil.doScaleAnimation(hint, 1, 0, 1, 0, false, new Animation.AnimationListener() {
177 | @Override
178 | public void onAnimationStart(Animation animation) {
179 |
180 | }
181 |
182 | @Override
183 | public void onAnimationEnd(Animation animation) {
184 | hint.setVisibility(View.GONE);
185 | }
186 |
187 | @Override
188 | public void onAnimationRepeat(Animation animation) {
189 |
190 | }
191 | }, true),3000);
192 | });
193 |
194 | dialog.show();
195 | dialog.setContentView(waittingView);
196 | return dialog;
197 | }
198 |
199 | public static View getSelectorView(){
200 | Activity context = ActivityUtil.getInstance().getCurActivity();
201 |
202 | AlertDialog dialog=getMyDialog();
203 |
204 | View view=LayoutInflater.from(context).inflate(R.layout.layout_selector_yibi,null);
205 |
206 | NumberPicker row=view.findViewById(R.id.rowPicker);
207 | setDividerColor(row);
208 | NumberPicker column=view.findViewById(R.id.columnPicker);
209 | setDividerColor(column);
210 | NumberPicker forbidden=view.findViewById(R.id.forbiddenPicker);
211 | setDividerColor(forbidden);
212 |
213 | dialog.show();
214 | dialog.setContentView(view);
215 | view.setTag(dialog);
216 | return view;
217 | }
218 |
219 | private static void setDividerColor(NumberPicker picker) {
220 | try {
221 | Field field=NumberPicker.class.getDeclaredField("mSelectionDivider");
222 | field.setAccessible(true);
223 | field.set(picker,new ColorDrawable(Color.TRANSPARENT));
224 | }catch (Exception e){
225 | OtherUtil.DebuggerLog(e);
226 | }
227 | }
228 |
229 | public static AlertDialog getAskDialog(String title, String content, OtherUtil.OnCallBackListenerImpl listener, String... texts) {
230 | String qdText = texts.length>0?texts[0]:"确定";
231 | String qxText = texts.length>1?texts[1]:"取消";
232 | String qtText = texts.length>2?texts[2]:"";
233 | String checkText = texts.length>3?texts[3]:"";
234 | final boolean[] checkStaus;
235 | if(ValueUtil.isNotEmpty(checkText)){
236 | checkStaus = new boolean[1];
237 | }else {
238 | checkStaus = new boolean[0];
239 | }
240 | AlertDialog dialog = getMyDialog(checkStaus,new String[]{checkText},null);
241 | if(checkStaus.length>0){
242 | dialog.setTitle(content);
243 | }else {
244 | if(ValueUtil.isNotEmpty(title)) dialog.setTitle(title);
245 | dialog.setMessage(content);
246 | }
247 | dialog.setButton(DialogInterface.BUTTON_POSITIVE, qdText, (dialog13, which) -> {
248 | Boolean check = checkStaus.length > 0 && checkStaus[0];
249 | listener.OnCallBackFirst(check);
250 | });
251 | dialog.setButton(DialogInterface.BUTTON_NEGATIVE, qxText, (dialog12, which) -> {
252 | Boolean check = checkStaus.length > 0 && checkStaus[0];
253 | listener.OnCallBackSecond(check);
254 | });
255 | if (ValueUtil.isNotEmpty(qtText)) {
256 | dialog.setButton(DialogInterface.BUTTON_NEUTRAL, qtText, (dialog1, which) -> {
257 | Boolean check = checkStaus.length > 0 && checkStaus[0];
258 | listener.OnCallBackThrid(check);
259 | });
260 | }
261 | dialog.show();
262 | return dialog;
263 | }
264 |
265 | public static AlertDialog getCollectionDialog(List collections,View.OnClickListener onClickListener){
266 | Activity context = ActivityUtil.getInstance().getCurActivity();
267 | final AlertDialog dialog=getMyDialog();
268 | View view=LayoutInflater.from(context).inflate(R.layout.layout_collection,null);
269 | ListView listView=view.findViewById(R.id.list_collection);
270 | listView.setAdapter(new Adapter_Yibi_Collection(collections));
271 | View clean=view.findViewById(R.id.clean);
272 | clean.setOnClickListener(onClickListener);
273 | dialog.show();
274 | dialog.setContentView(view);
275 | return dialog;
276 | }
277 |
278 | public static AlertDialog getSettingDialog(@NonNull SharedPreferences.Editor editor){
279 | Activity context = ActivityUtil.getInstance().getCurActivity();
280 | final AlertDialog dialog=getMyDialog();
281 | View view=LayoutInflater.from(context).inflate(R.layout.layout_setting,null);
282 | Switch switchMusic = view.findViewById(R.id.switchMusic);
283 | Switch sqlSwitch = view.findViewById(R.id.switchSql);
284 |
285 | switchMusic.setChecked(ValueUtil.toPlayMusic);
286 | switchMusic.setOnCheckedChangeListener((buttonView, isChecked) -> {
287 | ValueUtil.toPlayMusic = isChecked;
288 | editor.putBoolean(ValueUtil.key_toPlayMusic,ValueUtil.toPlayMusic);
289 | editor.apply();
290 | //启动服务,播放音乐
291 | Intent intent=new Intent(context, MusicService.class);
292 | intent.putExtra(ValueUtil.key_toPlayMusic,ValueUtil.toPlayMusic?0:2);
293 | context.startService(intent);
294 | });
295 |
296 | sqlSwitch.setChecked(ValueUtil.toFindRoadToSql);
297 | sqlSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
298 | ValueUtil.toFindRoadToSql = isChecked;
299 | editor.putBoolean(ValueUtil.key_toFindRoadToSql,ValueUtil.toFindRoadToSql);
300 | editor.apply();
301 | if(ValueUtil.toFindRoadToSql){
302 | ValueUtil.findRanRoadToSql(context);
303 | }
304 | });
305 | dialog.show();
306 | dialog.setContentView(view);
307 | return dialog;
308 | }
309 |
310 |
311 | public static boolean isServiceExisted(Context context, String className) {
312 | try {
313 | ActivityManager activityManager = (ActivityManager) context
314 | .getSystemService(Context.ACTIVITY_SERVICE);
315 | List serviceList = activityManager
316 | .getRunningServices(Integer.MAX_VALUE);
317 |
318 | if (!(serviceList.size() > 0)) {
319 | return false;
320 | }
321 |
322 | for (int i = 0; i < serviceList.size(); i++) {
323 | ActivityManager.RunningServiceInfo serviceInfo = serviceList.get(i);
324 | ComponentName serviceName = serviceInfo.service;
325 |
326 | if (serviceName.getClassName().equals(className)) {
327 | return true;
328 | }
329 | }
330 | }catch (Exception e){
331 | OtherUtil.DebuggerLog(e);
332 | }
333 | return false;
334 | }
335 |
336 | public static boolean isContextExisted(Context context) {
337 | if (context instanceof Activity) {
338 | return !((Activity) context).isFinishing();
339 | } else if (context instanceof Service) {
340 | return isServiceExisted(context, context.getClass().getName());
341 | } else return context instanceof Application;
342 | }
343 |
344 | }
345 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/View/Activity/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.View.Activity;
2 |
3 | import android.app.Activity;
4 | import android.app.Application;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.SharedPreferences;
8 | import android.os.Bundle;
9 | import android.os.Handler;
10 | import android.os.PersistableBundle;
11 | import android.transition.Fade;
12 | import android.view.View;
13 | import android.view.Window;
14 | import android.widget.Toast;
15 |
16 | import com.example.onelinetoend.R;
17 | import com.example.onelinetoend.Util.ActivityUtil;
18 | import com.example.onelinetoend.Util.AnimUtil;
19 | import com.example.onelinetoend.Util.OtherUtil;
20 | import com.example.onelinetoend.Util.ThreadUtil;
21 | import com.example.onelinetoend.Util.ValueUtil;
22 | import com.example.onelinetoend.Util.ViewUtil;
23 | import com.example.onelinetoend.View.Fragment.BaseFragment;
24 |
25 | import java.util.List;
26 |
27 | import androidx.annotation.LayoutRes;
28 | import androidx.annotation.NonNull;
29 | import androidx.annotation.Nullable;
30 | import androidx.appcompat.app.AppCompatActivity;
31 | import androidx.core.content.ContextCompat;
32 | import androidx.fragment.app.Fragment;
33 | import androidx.fragment.app.FragmentTransaction;
34 | import butterknife.ButterKnife;
35 | import butterknife.Unbinder;
36 |
37 | public abstract class BaseActivity extends AppCompatActivity {
38 |
39 | private Unbinder unbinder;
40 | private final Handler mhandler = new Handler();
41 | private long lastBackClickTime = 0;
42 | private SharedPreferences preferences;
43 | private SharedPreferences.Editor preferencesEditor;
44 |
45 | @Override
46 | public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
47 | super.onSaveInstanceState(outState, outPersistentState);
48 | if(getIntent()!=null && getIntent().getExtras()!=null) outState.putAll(getIntent().getExtras());
49 | }
50 |
51 | public @NonNull
52 | Intent getNonNullIntent(){
53 | Intent intent = getIntent();
54 | if(intent==null){
55 | intent = new Intent();
56 | setIntent(intent);
57 | }
58 | return getIntent();
59 | }
60 |
61 | @Override
62 | protected void onCreate(@Nullable Bundle savedInstanceState) {
63 | if(savedInstanceState!=null){
64 | getNonNullIntent().putExtras(savedInstanceState);
65 | }
66 | createPreferences();
67 | // 设置一个exit transition
68 | getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
69 | getWindow().setEnterTransition(new Fade());
70 | getWindow().setExitTransition(new Fade());
71 | getApplication().registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
72 | @Override
73 | public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
74 | ActivityUtil.getInstance().addActivity(activity);
75 | }
76 |
77 | @Override
78 | public void onActivityStarted(@NonNull Activity activity) {
79 |
80 | }
81 |
82 | @Override
83 | public void onActivityResumed(@NonNull Activity activity) {
84 | ActivityUtil.getInstance().setCurActivity(activity);
85 | }
86 |
87 | @Override
88 | public void onActivityPaused(@NonNull Activity activity) {
89 |
90 | }
91 |
92 | @Override
93 | public void onActivityStopped(@NonNull Activity activity) {
94 |
95 | }
96 |
97 | @Override
98 | public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
99 |
100 | }
101 |
102 | @Override
103 | public void onActivityDestroyed(@NonNull Activity activity) {
104 | ActivityUtil.getInstance().removeActivity(activity);
105 | }
106 | });
107 | super.onCreate(savedInstanceState);
108 | setContentView(getLayoutId());
109 | getWindow().getDecorView().setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary));
110 | unbinder = ButterKnife.bind(this);
111 | if(isNeedCleanFragments()) cleanFragments();
112 | initView(savedInstanceState);
113 | }
114 |
115 | public boolean isNeedCleanFragments(){
116 | return true;
117 | }
118 |
119 | protected void cleanFragments(){
120 | try {
121 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
122 | for(Fragment f : getSupportFragmentManager().getFragments()){
123 | transaction.remove(f);
124 | }
125 | transaction.commitNowAllowingStateLoss();
126 | }catch (Exception e){
127 | OtherUtil.DebuggerLog(e);
128 | }
129 | }
130 |
131 | @Override
132 | protected void onDestroy() {
133 | super.onDestroy();
134 | if(unbinder!=null){
135 | unbinder.unbind();
136 | unbinder=null;
137 | }
138 | preferences = null;
139 | preferencesEditor = null;
140 | }
141 |
142 | /**
143 | * 设置布局id
144 | *
145 | * @return layoutid
146 | */
147 | public abstract @LayoutRes
148 | int getLayoutId();
149 |
150 | /**
151 | * 初始化视图
152 | */
153 | public abstract void initView(@Nullable Bundle savedInstanceState);
154 |
155 | public void showToast(String msg) {
156 | runOnUiThread(() -> Toast.makeText(getApplicationContext(),msg, Toast.LENGTH_SHORT).show());
157 | }
158 |
159 | public void runOnUiThread(Runnable runnable, long delay) {
160 | mhandler.postDelayed(runnable,delay);
161 | }
162 |
163 | public void runOnChildThread(Runnable runnable) {
164 | ThreadUtil.getInstance().runOnChildThread(runnable);
165 | }
166 |
167 | @Override
168 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
169 | final Fragment fragment = getCurrentFragment();
170 | if(fragment instanceof BaseFragment && fragment.isVisible()) {
171 | if (((BaseFragment)fragment).isLoaded()) {
172 | fragment.onActivityResult(requestCode,resultCode, data);
173 | }
174 | }else if(fragment!=null && fragment.isResumed()){
175 | fragment.onActivityResult(requestCode,resultCode, data);
176 | }
177 | super.onActivityResult(requestCode, resultCode, data);
178 | }
179 |
180 |
181 | public interface OnFragmentBackPressedListener{
182 | boolean onBackPressed();
183 | }
184 |
185 | protected OnFragmentBackPressedListener onFragmentBackPressedListener;
186 |
187 | public void setOnFragmentBackPressedListener(OnFragmentBackPressedListener listener){
188 | onFragmentBackPressedListener = listener;
189 | }
190 |
191 | @Override
192 | public void onBackPressed() {
193 | if(onFragmentBackPressedListener!=null){
194 | if(onFragmentBackPressedListener.onBackPressed()){
195 | return;
196 | }
197 | }
198 | if(popbackFragment()){
199 | return;
200 | }
201 | if(needDoubleClickToBack()){
202 | final long nowTime = System.currentTimeMillis();
203 | if(nowTime-lastBackClickTime>1500){
204 | lastBackClickTime = nowTime;
205 | showToast("再按一次后退键退出");
206 | return;
207 | }
208 | }
209 | super.onBackPressed();
210 | }
211 |
212 | protected boolean popbackFragment(){
213 | final int fSize = getSupportFragmentManager().getFragments().size();
214 | if(fSize>=2){
215 | Fragment currentFragment = getCurrentFragment();
216 | Fragment secondFragment = getSecondFragment();
217 | if(currentFragment==null || secondFragment==null){
218 | return false;
219 | }
220 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
221 | if(currentFragment instanceof BaseFragment && secondFragment instanceof BaseFragment){
222 | BaseFragment cf = (BaseFragment)currentFragment;
223 | if(cf.getRequestCode()!=ValueUtil.Default_Unused_Code && cf.getResultCode()!=ValueUtil.Default_Unused_Code){
224 | ((BaseFragment)secondFragment).addRunnableToQueue(()-> secondFragment.onActivityResult(cf.getRequestCode(),cf.getResultCode(),cf.getResultData()));
225 | }
226 | }
227 | if(currentFragment instanceof ViewUtil.ShareElementsListener){
228 | final List shareElements = ((ViewUtil.ShareElementsListener)currentFragment).getShareElements();
229 | boolean hasShareElement = false;
230 | if(ValueUtil.isListNotEmpty(shareElements)){
231 | for (View shareElement : shareElements){
232 | if(shareElement!=null && shareElement.getTransitionName()!=null){
233 | hasShareElement = true;
234 | transaction.addSharedElement(shareElement,shareElement.getTransitionName());
235 | }
236 | }
237 | }
238 | if(hasShareElement){
239 | currentFragment.setEnterTransition(new Fade());
240 | currentFragment.setExitTransition(new Fade());
241 | secondFragment.setEnterTransition(new Fade());
242 | secondFragment.setExitTransition(new Fade());
243 | currentFragment.setSharedElementReturnTransition(AnimUtil.getScaleTransition());
244 | secondFragment.setSharedElementEnterTransition(AnimUtil.getScaleTransition());
245 | }else {
246 | currentFragment.setEnterTransition(null);
247 | currentFragment.setExitTransition(null);
248 | secondFragment.setEnterTransition(null);
249 | secondFragment.setExitTransition(null);
250 | currentFragment.setSharedElementReturnTransition(null);
251 | secondFragment.setSharedElementEnterTransition(null);
252 | transaction.setCustomAnimations(R.anim.scene_close_enter,R.anim.scene_close_exit);
253 | }
254 | }
255 | transaction.remove(currentFragment)
256 | .show(secondFragment)
257 | .commitAllowingStateLoss();
258 | return true;
259 | }
260 | return false;
261 | }
262 |
263 | protected boolean needDoubleClickToBack(){
264 | return true;
265 | }
266 |
267 | public Fragment getCurrentFragment(){
268 | Fragment currentFragment = null;
269 | final List fragments = getSupportFragmentManager().getFragments();
270 | for (int p=fragments.size()-1;p>=0;p--){
271 | Fragment f = fragments.get(p);
272 | if(f!=null && f.isVisible() && f.isResumed() && !f.isDetached()){
273 | currentFragment = f;
274 | break;
275 | }
276 | }
277 | return currentFragment;
278 | }
279 |
280 | protected Fragment getSecondFragment(){
281 | Fragment currentFragment = getCurrentFragment();
282 | if(currentFragment==null) return null;
283 | Fragment secondFragment = null;
284 | final List fragments = getSupportFragmentManager().getFragments();
285 | for (int p=fragments.size()-1;p>=0;p--){
286 | Fragment f = fragments.get(p);
287 | if(f!=null && f!=currentFragment){
288 | secondFragment = f;
289 | break;
290 | }
291 | }
292 | return secondFragment;
293 | }
294 |
295 | private void createPreferences(){
296 | preferences = getSharedPreferences(ValueUtil.appName, Context.MODE_PRIVATE);
297 | preferencesEditor = preferences.edit();
298 | }
299 |
300 | @NonNull
301 | public SharedPreferences.Editor getPreferencesEditor(){
302 | if(preferencesEditor == null){
303 | createPreferences();
304 | }
305 | return preferencesEditor;
306 | }
307 |
308 | public SharedPreferences getPreferences() {
309 | if(preferences == null){
310 | createPreferences();
311 | }
312 | return preferences;
313 | }
314 |
315 | }
316 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/View/Activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.View.Activity;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.annotation.Nullable;
5 | import androidx.fragment.app.Fragment;
6 |
7 | import android.content.Intent;
8 | import android.os.Bundle;
9 | import android.view.View;
10 |
11 | import com.example.onelinetoend.R;
12 | import com.example.onelinetoend.Util.Services.MusicService;
13 | import com.example.onelinetoend.Util.ThreadUtil;
14 | import com.example.onelinetoend.Util.ValueUtil;
15 | import com.example.onelinetoend.Util.ViewUtil;
16 | import com.example.onelinetoend.View.Activity.BaseActivity;
17 | import com.example.onelinetoend.View.Fragment.BaseFragment;
18 | import com.example.onelinetoend.View.Fragment.IndexFragment;
19 |
20 | import java.util.List;
21 |
22 | public class MainActivity extends BaseActivity implements BaseFragment.OnBackClickListener{
23 |
24 | public void startFragment(@NonNull Class fragmentClass ,int requestCode, @Nullable Bundle bundle, @Nullable List shareElements) {
25 | ViewUtil.startFragment(getSupportFragmentManager(),getCurrentFragment(),R.id.fragmentLayout, fragmentClass, requestCode, bundle, shareElements);
26 | }
27 |
28 | @Override
29 | public int getLayoutId() {
30 | return R.layout.activity_main;
31 | }
32 |
33 | @Override
34 | public void initView(@Nullable Bundle savedInstanceState) {
35 | ValueUtil.toFindRoadToSql = getPreferences().getBoolean(ValueUtil.key_toFindRoadToSql,true);
36 | ValueUtil.toPlayMusic = getPreferences().getBoolean(ValueUtil.key_toPlayMusic,true);
37 | if(ValueUtil.toFindRoadToSql) ValueUtil.findRanRoadToSql(this);
38 | final List fragments = getSupportFragmentManager().getFragments();
39 | if(ValueUtil.isListEmpty(fragments)){
40 | startFragment(IndexFragment.class,0,null,null);
41 | }else {
42 | boolean hasIndex = false;
43 | for(Fragment fragment : fragments){
44 | if(fragment instanceof IndexFragment){
45 | hasIndex = true;
46 | break;
47 | }
48 | }
49 | if(!hasIndex){
50 | startFragment(IndexFragment.class,0,null,null);
51 | }
52 | }
53 | }
54 |
55 | @Override
56 | protected void onResume() {
57 | super.onResume();
58 | Intent intent = new Intent(this, MusicService.class);
59 | intent.putExtra(ValueUtil.key_toPlayMusic,ValueUtil.toPlayMusic?0:2);
60 | startService(intent);
61 | }
62 |
63 | @Override
64 | protected void onPause() {
65 | super.onPause();
66 | Intent intent = new Intent(this, MusicService.class);
67 | intent.putExtra(ValueUtil.key_toPlayMusic,1);
68 | startService(intent);
69 | }
70 |
71 | @Override
72 | protected void onDestroy() {
73 | stopService(new Intent(this, MusicService.class));
74 | ThreadUtil.getInstance().removeRunable("findRoadToSql");
75 | super.onDestroy();
76 | }
77 |
78 | @Override
79 | public boolean isNeedCleanFragments() {
80 | return false;
81 | }
82 |
83 | @Override
84 | public void onBackClick() {
85 | onBackPressed();
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/View/Fragment/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.View.Fragment;
2 |
3 |
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.SharedPreferences;
7 | import android.os.Bundle;
8 | import android.os.Handler;
9 | import android.transition.Fade;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.Toast;
14 |
15 | import com.example.onelinetoend.Model.MySql;
16 | import com.example.onelinetoend.R;
17 | import com.example.onelinetoend.Util.ActivityUtil;
18 | import com.example.onelinetoend.Util.OtherUtil;
19 | import com.example.onelinetoend.Util.ThreadUtil;
20 | import com.example.onelinetoend.Util.ValueUtil;
21 | import com.example.onelinetoend.Util.ViewUtil;
22 | import com.example.onelinetoend.View.Activity.BaseActivity;
23 |
24 | import java.util.List;
25 | import java.util.concurrent.ConcurrentLinkedQueue;
26 |
27 | import androidx.annotation.IdRes;
28 | import androidx.annotation.LayoutRes;
29 | import androidx.annotation.NonNull;
30 | import androidx.annotation.Nullable;
31 | import androidx.core.content.ContextCompat;
32 | import androidx.fragment.app.Fragment;
33 | import androidx.fragment.app.FragmentTransaction;
34 | import butterknife.ButterKnife;
35 | import butterknife.Unbinder;
36 |
37 | /**
38 | * A simple {@link Fragment} subclass.
39 | */
40 | public abstract class BaseFragment extends Fragment implements BaseActivity.OnFragmentBackPressedListener, ViewUtil.ShareElementsListener {
41 |
42 | private ConcurrentLinkedQueue runnableQueue = new ConcurrentLinkedQueue<>();
43 |
44 | protected OnBackClickListener onBackClickListener;
45 |
46 | protected boolean isLoaded = false;
47 |
48 | protected Unbinder unbinder;
49 | private final Handler mhandler = new Handler();
50 | private SharedPreferences preferences;
51 | private SharedPreferences.Editor preferencesEditor;
52 | private MySql mySql;
53 |
54 | private int requestCode = ValueUtil.Default_Unused_Code,resultCode = ValueUtil.Default_Unused_Code;
55 | private Intent resultData;
56 |
57 | public void setRequestCode(int requestCode) {
58 | this.requestCode = requestCode;
59 | }
60 |
61 | public void setResult(int resultCode){
62 | setResult(resultCode,null);
63 | }
64 |
65 | public void setResult(int resultCode, Intent resultData) {
66 | this.resultCode = resultCode;
67 | this.resultData = resultData;
68 | }
69 |
70 | public int getRequestCode() {
71 | return requestCode;
72 | }
73 |
74 | public int getResultCode() {
75 | return resultCode;
76 | }
77 |
78 | public Intent getResultData() {
79 | return resultData;
80 | }
81 |
82 | public BaseFragment() {}
83 |
84 | public static BaseFragment newInstance(Class extends BaseFragment> fraggemntClass,Bundle bundle,int requestCode){
85 | try {
86 | if(requestCode!=ValueUtil.Default_Unused_Code){
87 | if(bundle==null) bundle=new Bundle();
88 | bundle.putInt(ValueUtil.Fragment_Reqest_Code,requestCode);
89 | }
90 | BaseFragment fragment = fraggemntClass.newInstance();
91 | fragment.setArguments(bundle);
92 | fragment.setEnterTransition(new Fade());
93 | fragment.setExitTransition(new Fade());
94 | return fragment;
95 | }catch (Exception e){
96 | throw new RuntimeException(e);
97 | }
98 | }
99 |
100 | public void onNewArguments(){
101 | setRequestCode(getNonNullArguments().getInt(ValueUtil.Fragment_Reqest_Code,ValueUtil.Default_Unused_Code));
102 | }
103 |
104 |
105 | public void addRunnableToQueue(@NonNull Runnable runnable){
106 | runnableQueue.offer(runnable);
107 | }
108 |
109 | public void runOnUiThread(Runnable runnable) {
110 | postRunnaleQueue();
111 | mhandler.post(() -> {
112 | if(!isLoaded) return;
113 | runnable.run();
114 | });
115 | }
116 |
117 | public void runOnUiThread(Runnable runnable, long delay) {
118 | postRunnaleQueue();
119 | mhandler.postDelayed(() -> {
120 | if(!isLoaded) return;
121 | runnable.run();
122 | },delay);
123 | }
124 |
125 | public void runOnChildThread(Runnable runnable) {
126 | postRunnaleQueue();
127 | ThreadUtil.getInstance().runOnChildThread(()->{
128 | if(!isLoaded) return;
129 | runnable.run();
130 | });
131 | }
132 |
133 | private void postRunnaleQueue(){
134 | Runnable r;
135 | while ((r=runnableQueue.poll())!=null){
136 | r.run();
137 | }
138 | }
139 |
140 | @Override
141 | public void onHiddenChanged(boolean hidden) {
142 | super.onHiddenChanged(hidden);
143 | if(!hidden){
144 | postRunnaleQueue();
145 | }
146 | }
147 |
148 | @Nullable
149 | @Override
150 | public List getShareElements() {
151 | return null;
152 | }
153 |
154 | @Override
155 | public void onResume() {
156 | super.onResume();
157 | if(getActivity() instanceof BaseActivity){
158 | ((BaseActivity)getActivity()).setOnFragmentBackPressedListener(this);
159 | }
160 | if(!isLoaded){
161 | mhandler.post(()->{
162 | if(getView()!=null){
163 | try {
164 | FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
165 | for(Fragment f : getChildFragmentManager().getFragments()){
166 | transaction.remove(f);
167 | }
168 | transaction.commitNowAllowingStateLoss();
169 | }catch (Exception e){
170 | OtherUtil.DebuggerLog(e);
171 | }
172 | createPreferences();
173 | getView().setBackgroundColor(ContextCompat.getColor(ActivityUtil.getInstance().getCurActivity(), R.color.colorPrimary));
174 | unbinder = ButterKnife.bind(BaseFragment.this,getView());
175 | isLoaded = initView();
176 | setRequestCode(getNonNullArguments().getInt(ValueUtil.Fragment_Reqest_Code,ValueUtil.Default_Unused_Code));
177 | }
178 | });
179 | }
180 | postRunnaleQueue();
181 | }
182 |
183 | @Override
184 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
185 | Bundle savedInstanceState) {
186 | if(savedInstanceState!=null) getNonNullArguments().putAll(savedInstanceState);
187 | return inflater.inflate(getLayoutId(),container,false);
188 | }
189 |
190 | public MySql getMySql() {
191 | if(mySql==null){
192 | mySql=new MySql(getContext());
193 | }
194 | return mySql;
195 | }
196 |
197 | public @NonNull
198 | Bundle getNonNullArguments(){
199 | Bundle bundle = getArguments();
200 | if(bundle==null){
201 | bundle = new Bundle();
202 | setArguments(bundle);
203 | }
204 | return getArguments();
205 | }
206 |
207 | @Override
208 | public void onSaveInstanceState(@NonNull Bundle outState) {
209 | if(getArguments()!=null){
210 | outState.putAll(getArguments());
211 | }
212 | super.onSaveInstanceState(outState);
213 | }
214 |
215 | public abstract @LayoutRes int getLayoutId();
216 |
217 | public abstract boolean initView();
218 |
219 | public V findViewById(@IdRes int id){
220 | if(getView()==null) return null;
221 | return getView().findViewById(id);
222 | }
223 |
224 | public void showToast(String msg) {
225 | runOnUiThread(() -> Toast.makeText(getContext(),msg, Toast.LENGTH_SHORT).show());
226 | }
227 |
228 | @Override
229 | public void onDestroyView() {
230 | isLoaded = false;
231 | if(unbinder!=null){
232 | unbinder.unbind();
233 | unbinder = null;
234 | }
235 | preferences = null;
236 | preferencesEditor = null;
237 | super.onDestroyView();
238 | }
239 |
240 |
241 | @NonNull
242 | @Override
243 | public Context getContext() {
244 | if(super.getContext()!=null)
245 | return super.getContext();
246 | if(getActivity()!=null) return getActivity();
247 | return ActivityUtil.getInstance().getCurActivity();
248 | }
249 |
250 |
251 | @Override
252 | public boolean onBackPressed() {
253 | return false;
254 | }
255 |
256 | @Override
257 | public void onAttach(@NonNull Context context) {
258 | super.onAttach(context);
259 | if(context instanceof OnBackClickListener){
260 | onBackClickListener = (OnBackClickListener)context;
261 | }
262 | }
263 |
264 | public void onBackClick(){
265 | if(onBackClickListener!=null){
266 | onBackClickListener.onBackClick();
267 | }
268 | }
269 |
270 |
271 | public boolean isLoaded() {
272 | return isLoaded;
273 | }
274 |
275 |
276 | protected void createPreferences(){
277 | preferences = getContext().getSharedPreferences(ValueUtil.appName, Context.MODE_PRIVATE);
278 | preferencesEditor = preferences.edit();
279 | }
280 |
281 | @NonNull
282 | public SharedPreferences.Editor getPreferencesEditor(){
283 | if(preferencesEditor == null){
284 | createPreferences();
285 | }
286 | return preferencesEditor;
287 | }
288 |
289 | public SharedPreferences getPreferences() {
290 | if(preferences == null){
291 | createPreferences();
292 | }
293 | return preferences;
294 | }
295 |
296 | public interface OnBackClickListener{
297 | void onBackClick();
298 | }
299 |
300 | }
301 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/View/Fragment/DifficultyDetailFragment.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.View.Fragment;
2 |
3 | import android.content.Intent;
4 | import android.view.View;
5 | import android.widget.TextView;
6 |
7 | import com.example.onelinetoend.Adapter.Adapter_difficulty_detail;
8 | import com.example.onelinetoend.R;
9 | import com.example.onelinetoend.Util.RoadValuesUtil;
10 | import com.example.onelinetoend.Util.ValueUtil;
11 |
12 | import androidx.annotation.Nullable;
13 | import androidx.recyclerview.widget.GridLayoutManager;
14 | import androidx.recyclerview.widget.RecyclerView;
15 | import butterknife.BindView;
16 |
17 | public class DifficultyDetailFragment extends BaseFragment {
18 |
19 | @BindView(R.id.difficultyHint)
20 | TextView difficultyHint;
21 |
22 | @BindView(R.id.returnButton)
23 | View returnButton;
24 |
25 | @BindView(R.id.recyclerView)
26 | RecyclerView recyclerView;
27 |
28 | @Override
29 | public int getLayoutId() {
30 | return R.layout.layout_difficulty;
31 | }
32 |
33 | @Override
34 | public boolean initView() {
35 | int position = getNonNullArguments().getInt(ValueUtil.Cur_Detail_Position,0);
36 | returnButton.setOnClickListener(v -> onBackClick());
37 | recyclerView.setLayoutManager(new GridLayoutManager(getContext(),5));
38 | if(position>=0 && position onBackClick());
32 | recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
33 | recyclerView.setAdapter(new Adapter_difficulty(RoadValuesUtil.roadValuesList));
34 | difficultyHint.setVisibility(View.GONE);
35 | return true;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/View/Fragment/IndexFragment.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.View.Fragment;
2 |
3 | import android.widget.Button;
4 |
5 | import com.example.onelinetoend.R;
6 | import com.example.onelinetoend.Util.ActivityUtil;
7 | import com.example.onelinetoend.Util.ValueUtil;
8 | import com.example.onelinetoend.Util.ViewUtil;
9 | import com.example.onelinetoend.View.Activity.MainActivity;
10 |
11 | import butterknife.BindView;
12 |
13 | public class IndexFragment extends BaseFragment {
14 |
15 | @BindView(R.id.btPrimary)
16 | Button btPrimary;
17 |
18 | @BindView(R.id.btRandom)
19 | Button btRandom;
20 |
21 | @BindView(R.id.btSetting)
22 | Button btSetting;
23 |
24 | @BindView(R.id.btFinsh)
25 | Button btFinsh;
26 |
27 | @Override
28 | public int getLayoutId() {
29 | return R.layout.layout_index;
30 | }
31 |
32 | @Override
33 | public boolean initView() {
34 | btPrimary.setOnClickListener(v -> {
35 | if(getActivity() instanceof MainActivity){
36 | ((MainActivity)getActivity()).startFragment(DifficultyFragment.class,0,null,null);
37 | }else {
38 | showToast("跳转失败,请联系开发人员");
39 | }
40 | });
41 | btRandom.setOnClickListener(v -> {
42 | if(getActivity() instanceof MainActivity){
43 | ((MainActivity)getActivity()).startFragment(RandomRoadFragment.class,0,null,null);
44 | }else {
45 | showToast("跳转失败,请联系开发人员");
46 | }
47 | });
48 | btSetting.setOnClickListener(v-> ViewUtil.getSettingDialog(getPreferencesEditor()));
49 | btFinsh.setOnClickListener(v -> ActivityUtil.getInstance().finishActivity());
50 | return true;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/View/Fragment/RoadFragment.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.View.Fragment;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.widget.CheckBox;
6 | import android.widget.ImageButton;
7 | import android.widget.TextView;
8 |
9 | import com.example.onelinetoend.Model.Bean.Bean_Road;
10 | import com.example.onelinetoend.R;
11 | import com.example.onelinetoend.Util.OtherUtil;
12 | import com.example.onelinetoend.Util.RoadValuesUtil;
13 | import com.example.onelinetoend.Util.ValueUtil;
14 | import com.example.onelinetoend.Util.ViewUtil;
15 | import com.example.onelinetoend.View.Activity.MainActivity;
16 | import com.example.onelinetoend.View.UtilView.Grid_Yibi;
17 |
18 | import java.util.List;
19 |
20 | import butterknife.BindView;
21 |
22 | public class RoadFragment extends BaseFragment implements Grid_Yibi.yibiListener {
23 |
24 | @BindView(R.id.roadHint)
25 | TextView roadHint;
26 |
27 | @BindView(R.id.grid_yibi)
28 | Grid_Yibi grid_yibi;
29 |
30 | @BindView(R.id.nextButton)
31 | ImageButton nextButton;
32 |
33 | @BindView(R.id.refreshButton)
34 | ImageButton refreshButton;
35 |
36 | @BindView(R.id.returnButton)
37 | ImageButton returnButton;
38 |
39 | @BindView(R.id.helpButton)
40 | ImageButton helpButton;
41 |
42 | @BindView(R.id.homeButton)
43 | ImageButton homeButton;
44 |
45 | private boolean firstPassed = false;
46 | private boolean ishelping=false;
47 | private int curDetailPosition = -1;
48 | private int curRoadPosition = -1;
49 |
50 | @Override
51 | public int getLayoutId() {
52 | return R.layout.layout_road;
53 | }
54 |
55 | @Override
56 | public void onNewArguments() {
57 | super.onNewArguments();
58 | isLoaded = initView();
59 | }
60 |
61 | @Override
62 | public boolean initView() {
63 | firstPassed = false;
64 | ishelping=false;
65 | returnButton.setOnClickListener(v->onBackClick());
66 | refreshButton.setOnClickListener(v->runOnUiThread(()->grid_yibi.refreshGrid()));
67 | helpButton.setOnClickListener(v->{
68 | if(!ishelping){
69 | grid_yibi.getHelp();
70 | }else {
71 | runOnUiThread(()->grid_yibi.refreshGrid());
72 | }
73 | });
74 | homeButton.setOnClickListener(v -> runOnUiThread(()->{
75 | if(getActivity() instanceof MainActivity){
76 | ((MainActivity)getActivity()).startFragment(IndexFragment.class,0,null,null);
77 | }else {
78 | showToast("跳转失败,请联系开发人员");
79 | }
80 | }));
81 | nextButton.setOnClickListener(v -> goNext());
82 |
83 | curDetailPosition = getNonNullArguments().getInt(ValueUtil.Cur_Detail_Position,-1);
84 | curRoadPosition = getNonNullArguments().getInt(ValueUtil.Cur_Road_Position,-1);
85 |
86 | if(getCurRoad()!=null){
87 | grid_yibi.initGrid(getCurRoad(),this);
88 | }else {
89 | grid_yibi.refreshGrid();
90 | }
91 | roadHint.setText((curDetailPosition+1)+"-"+(curRoadPosition+1));
92 | setResult(0,new Intent(){{putExtras(getNonNullArguments());}});
93 | return true;
94 | }
95 |
96 | private void goNext(){
97 | if(checkPosition()){
98 | int cdp = curDetailPosition;
99 | int crp = curRoadPosition+1;
100 | if(crp>=RoadValuesUtil.roadValuesList.get(cdp).size()){
101 | crp=0;
102 | cdp++;
103 | }
104 | if(cdp>=RoadValuesUtil.roadValuesList.size()){
105 | showToast("当前已是最后一关!");
106 | return;
107 | }
108 | if(getActivity() instanceof MainActivity){
109 | Bundle bundle = getNonNullArguments();
110 | bundle.putInt(ValueUtil.Cur_Detail_Position,cdp);
111 | bundle.putInt(ValueUtil.Cur_Road_Position,crp);
112 | ((MainActivity)getActivity()).startFragment(RoadFragment.class,0,bundle,null);
113 | }else {
114 | showToast("跳转失败,请联系开发人员");
115 | }
116 | }
117 | }
118 |
119 | private boolean checkPosition(){
120 | if(curDetailPosition>=0
121 | && curRoadPosition>=0
122 | && curDetailPosition passedPositions) {
146 |
147 | }
148 |
149 | @Override
150 | public void passed(Bean_Road road) {
151 | if(road==null) return;
152 | if(!firstPassed){
153 | firstPassed=true;
154 | getMySql().insertPassedYibi(road);
155 | ViewUtil.getAskDialog( "","恭喜通过",new OtherUtil.OnCallBackListenerImpl(){
156 | @Override
157 | public void OnCallBackFirst(Boolean... params) {
158 | goNext();
159 | }
160 | },"下一关", "算了");
161 | }
162 | }
163 |
164 | @Override
165 | public void setIsHelping(boolean isHelping) {
166 | runOnUiThread(()->{
167 | if(isHelping){
168 | helpButton.setBackgroundResource(R.drawable.ic_helping);
169 | }else {
170 | helpButton.setBackgroundResource(R.drawable.ic_help);
171 | }
172 | this.ishelping=isHelping;
173 | });
174 | }
175 |
176 | @Override
177 | public boolean isHelping() {
178 | return ishelping;
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/View/UtilView/CanClearEditText.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.View.UtilView;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.PorterDuff;
6 | import android.graphics.Rect;
7 | import android.graphics.drawable.Drawable;
8 | import android.text.Editable;
9 | import android.text.TextWatcher;
10 | import android.util.AttributeSet;
11 | import android.view.MotionEvent;
12 | import android.view.inputmethod.EditorInfo;
13 |
14 | import com.example.onelinetoend.R;
15 |
16 | import androidx.appcompat.widget.AppCompatAutoCompleteTextView;
17 | import androidx.core.content.ContextCompat;
18 |
19 | public class CanClearEditText extends AppCompatAutoCompleteTextView {
20 |
21 | private Drawable deleceImg;
22 | boolean isSearchType = false;
23 |
24 | public boolean isSearchType() {
25 | return isSearchType;
26 | }
27 |
28 | public void setSearchType(boolean searchType) {
29 | isSearchType = searchType;
30 | init();
31 | }
32 |
33 | public CanClearEditText(Context context) {
34 | super(context);
35 | init();
36 | }
37 |
38 | public CanClearEditText(Context context, AttributeSet attrs) {
39 | super(context, attrs);
40 | initParams(attrs);
41 | }
42 |
43 | public CanClearEditText(Context context, AttributeSet attrs, int defStyleAttr) {
44 | super(context, attrs, defStyleAttr);
45 | initParams(attrs);
46 | }
47 |
48 |
49 | private void initParams(AttributeSet attrs) {
50 | TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CanClearEditText);
51 | isSearchType = typedArray.getBoolean(R.styleable.CanClearEditText_isSearchType,false);
52 | typedArray.recycle();
53 | init();
54 | }
55 |
56 | private void init(){
57 | setClickable(true);
58 | if(deleceImg==null){
59 | deleceImg = ContextCompat.getDrawable(getContext(), R.drawable.ic_delete);
60 | }
61 | if(isSearchType) {
62 | setHint("✎ 搜索");
63 | setImeOptions(EditorInfo.IME_ACTION_SEARCH);
64 | setSingleLine(true);
65 | setMaxLines(1);
66 | setThreshold(1);
67 | }else {
68 | setSingleLine(false);
69 | setMaxLines(Integer.MAX_VALUE);
70 | setHint("");
71 | setImeOptions(EditorInfo.IME_NULL);
72 | }
73 | if(deleceImg!=null){
74 | deleceImg.setColorFilter(getCurrentHintTextColor(), PorterDuff.Mode.SRC_ATOP);
75 | }
76 | addTextChangedListener(new TextWatcher() {
77 | @Override
78 | public void onTextChanged(CharSequence s, int start, int before, int count) {}
79 | @Override
80 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
81 |
82 | }
83 | @Override
84 | public void afterTextChanged(Editable s) {
85 | setDrawable();
86 | }
87 | });
88 | setDrawable();
89 | }
90 |
91 | //设置删除图片
92 | private void setDrawable() {
93 | setCompoundDrawablesWithIntrinsicBounds(null, null, isFocusable()&&length()>0?deleceImg:null, null);
94 | }
95 |
96 | @Override
97 | public boolean onTouchEvent(MotionEvent event) {
98 | if (deleceImg != null && event.getAction() == MotionEvent.ACTION_UP) {
99 | boolean isClean =(event.getX() > (getWidth() - getTotalPaddingRight()))&&
100 | (event.getX() < (getWidth() - getPaddingRight()));
101 | if(isClean) setText("");
102 | }
103 | if(onEditTextTouchListener!=null){
104 | onEditTextTouchListener.onEditTextTouch();
105 | }
106 | return super.onTouchEvent(event);
107 | }
108 |
109 | @SuppressWarnings({ "UnusedDeclaration" })
110 | @Override
111 | public void performFiltering(CharSequence text, int keyCode) {
112 | if(getFilter()==null) return;
113 | getFilter().filter(text, this);
114 | }
115 |
116 | @Override
117 | public boolean enoughToFilter() {
118 | return true;
119 | }
120 |
121 | @Override
122 | protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
123 | setDrawable();
124 | super.onFocusChanged(focused, direction, previouslyFocusedRect);
125 | }
126 |
127 | private onEditTextTouchListener onEditTextTouchListener;
128 |
129 | public void setOnEditTextTouchListener(CanClearEditText.onEditTextTouchListener onEditTextTouchListener) {
130 | this.onEditTextTouchListener = onEditTextTouchListener;
131 | }
132 |
133 | public interface onEditTextTouchListener{
134 | void onEditTextTouch();
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/View/UtilView/Grid_Yibi.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.View.UtilView;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.MotionEvent;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.GridView;
9 | import android.widget.Toast;
10 |
11 | import com.example.onelinetoend.Adapter.Adapter_Yibi;
12 | import com.example.onelinetoend.Model.Bean.Bean_Road;
13 | import com.example.onelinetoend.R;
14 | import com.example.onelinetoend.Util.OtherUtil;
15 | import com.example.onelinetoend.Util.ValueUtil;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | public class Grid_Yibi extends GridView implements View.OnTouchListener {
21 |
22 | public interface yibiListener{
23 | void initGirdRoad(final int initRows, final int initColums, final int initDifficulties);
24 | boolean stopGettingRoad();
25 | void saveYibi(Bean_Road road, List passedPositions);
26 | void passed(Bean_Road road);
27 | void setIsHelping(boolean isHelping);
28 | boolean isHelping();
29 | }
30 |
31 | private int firstChildPosition=-1;//初始位置
32 | private int lastChildPosition=-1;//记录上次有变更的子view的位置
33 | private int downChildPosition=-1;//记录每次的落点
34 | private int lastMoveChildPosition=-1;//记录当前滑动周期内最后一个被滑过的子view的位置
35 |
36 | Bean_Road road; //当前可以通关的路
37 | private final List passedPositions = new ArrayList<>();//当前走过的路径,不包含起始位置
38 |
39 | private yibiListener listener;
40 |
41 | public void refreshGrid(){
42 | if(road==null) return;
43 | initGrid(road,listener);
44 | }
45 |
46 | public void initGrid(Bean_Road road,yibiListener listener){
47 | if(listener!=null)
48 | listener.setIsHelping(false);
49 | if(road==null){
50 | showToat("获取的地图错误!");
51 | return;
52 | }
53 | this.road=road;
54 | this.passedPositions.clear();
55 | this.listener=listener;
56 | this.lastChildPosition=road.getRoadList().get(0);
57 | this.firstChildPosition=road.getRoadList().get(0);
58 | if(listener!=null)
59 | listener.stopGettingRoad();
60 | setNumColumns(road.getColumns());
61 | setWidth();
62 | setAdapter(new Adapter_Yibi(road));
63 | }
64 |
65 | private void showToat(String mes){
66 | post(()->Toast.makeText(getContext(),mes, Toast.LENGTH_SHORT).show());
67 | }
68 |
69 | public void initPassedGrid(final int rows, final int columns, final int difficulties, final String roadposition, final String passedPosition,final yibiListener listener){
70 | if(listener!=null)
71 | listener.setIsHelping(false);
72 | String[] roadpositions=roadposition.split("[,]");
73 | final List roadList= ValueUtil.getIntListFromStrs(roadpositions);
74 | if(ValueUtil.isListEmpty(roadList)){
75 | showToat("恢复上次游戏失败,正在重开一局。。。");
76 | if(listener!=null)
77 | listener.initGirdRoad(rows,columns,difficulties);
78 | return;
79 | }
80 | this.road= new Bean_Road(rows,columns,roadList);
81 | this.passedPositions.clear();
82 | this.listener=listener;
83 | this.lastChildPosition=roadList.get(0);
84 | this.firstChildPosition=roadList.get(0);
85 | setNumColumns(columns);
86 | setWidth();
87 | setAdapter(new Adapter_Yibi(road));
88 |
89 | if(passedPosition.length()==0) {
90 | if(listener!=null)
91 | listener.stopGettingRoad();
92 | return;
93 | }
94 |
95 | post(() -> {
96 | if(listener!=null)
97 | listener.stopGettingRoad();
98 | String[] passedPositions=passedPosition.split("[,]");
99 | for(String passP:passedPositions){
100 | try {
101 | int p= Integer.parseInt(passP);
102 | View childView=getChildAt(p);
103 | QianJin(childView,p);
104 | }catch (Exception e){
105 | OtherUtil.DebuggerLog(e);
106 | if(listener!=null)
107 | listener.initGirdRoad(rows,columns,difficulties);
108 | showToat("恢复上次游戏失败,正在重开一局。。。");
109 | return;
110 | }
111 | }
112 | if(passedPositions.length+1==roadList.size()){
113 | if(listener!=null)
114 | listener.passed(road);
115 | }
116 | });
117 | }
118 |
119 | private void setWidth(){
120 | ViewGroup.LayoutParams params=getLayoutParams();
121 | params.width=ValueUtil.DpToPx(60)*(road==null?0:road.getColumns());
122 | setLayoutParams(params);
123 | }
124 |
125 | public Grid_Yibi(Context context) {
126 | super(context);
127 | setOnTouchListener(this);
128 | }
129 |
130 | public Grid_Yibi(Context context, AttributeSet attrs) {
131 | super(context, attrs);
132 | setOnTouchListener(this);
133 | }
134 |
135 | public Grid_Yibi(Context context, AttributeSet attrs, int defStyleAttr) {
136 | super(context, attrs, defStyleAttr);
137 | setOnTouchListener(this);
138 | }
139 |
140 | private void QianJin(View curChildview, int currentChildPosition){
141 | if(curChildview==null) return;
142 | post(()->{
143 | if(lastChildPosition+1==currentChildPosition&¤tChildPosition%getNumColumns()!=0
144 | || lastChildPosition+getNumColumns()==currentChildPosition
145 | || lastChildPosition-getNumColumns()==currentChildPosition
146 | ||lastChildPosition-1==currentChildPosition&&lastChildPosition%getNumColumns()!=0){
147 |
148 | if(lastChildPosition=0){
149 | View lastchildview=getChildAt(lastChildPosition);
150 | View nowbase=curChildview.findViewById(R.id.baseyibi);
151 | if(lastChildPosition+1==currentChildPosition&¤tChildPosition%getNumColumns()!=0){
152 | //当当前位置处于上个被选中的位置的右边时,选中当前位置。其它前进情况和后退情况类似
153 | View lastright=lastchildview.findViewById(R.id.rightyibi)
154 | ,nowleft=curChildview.findViewById(R.id.leftyibi);
155 | lastright.setBackgroundResource(R.color.colorGray);
156 | nowleft.setBackgroundResource(R.color.colorGray);
157 | nowbase.setBackgroundResource(R.drawable.shape_gray_selected);
158 | }else if(lastChildPosition+getNumColumns()==currentChildPosition){
159 | View lastbottom=lastchildview.findViewById(R.id.bottomyibi)
160 | ,nowtop=curChildview.findViewById(R.id.topyibi);
161 | lastbottom.setBackgroundResource(R.color.colorGray);
162 | nowtop.setBackgroundResource(R.color.colorGray);
163 | nowbase.setBackgroundResource(R.drawable.shape_gray_selected);
164 | }else if(lastChildPosition-getNumColumns()==currentChildPosition){
165 | View lasttop=lastchildview.findViewById(R.id.topyibi)
166 | ,nowbottom=curChildview.findViewById(R.id.bottomyibi);
167 | lasttop.setBackgroundResource(R.color.colorGray);
168 | nowbottom.setBackgroundResource(R.color.colorGray);
169 | nowbase.setBackgroundResource(R.drawable.shape_gray_selected);
170 | }else if(lastChildPosition-1==currentChildPosition&&lastChildPosition%getNumColumns()!=0){
171 | View lastleft=lastchildview.findViewById(R.id.leftyibi)
172 | ,nowright=curChildview.findViewById(R.id.rightyibi);
173 | lastleft.setBackgroundResource(R.color.colorGray);
174 | nowright.setBackgroundResource(R.color.colorGray);
175 | nowbase.setBackgroundResource(R.drawable.shape_gray_selected);
176 | }
177 | passedPositions.add(currentChildPosition);
178 | if(listener!=null)
179 | listener.saveYibi(road,passedPositions);
180 | curChildview.setTag(lastChildPosition);
181 | lastChildPosition=currentChildPosition;
182 | }
183 | }
184 | });
185 | }
186 |
187 | //代码太多又简单所以我直接复制前进方法过来改
188 | public void getHelp(){
189 | refreshGrid();
190 | post(() -> {
191 | if(road==null) return;
192 | if(listener!=null)
193 | listener.setIsHelping(true);
194 | int lastChildPosition=road.getRoadList().get(0);
195 | for(int i = 1; i=0){
205 | View lastchildview=getChildAt(lastChildPosition);
206 | View nowbase=childview.findViewById(R.id.baseyibi);
207 | if(lastChildPosition+1==currentChildPosition&¤tChildPosition%getNumColumns()!=0){
208 | //当当前位置处于上个被选中的位置的右边时,选中当前位置。其它前进情况和后退情况类似
209 | View lastright=lastchildview.findViewById(R.id.rightyibi)
210 | ,nowleft=childview.findViewById(R.id.leftyibi);
211 | lastright.setBackgroundResource(R.color.colorGray_tran);
212 | nowleft.setBackgroundResource(R.color.colorGray_tran);
213 | nowbase.setBackgroundResource(R.drawable.shape_gray_tran_selected);
214 | }else if(lastChildPosition+getNumColumns()==currentChildPosition){
215 | View lastbottom=lastchildview.findViewById(R.id.bottomyibi)
216 | ,nowtop=childview.findViewById(R.id.topyibi);
217 | lastbottom.setBackgroundResource(R.color.colorGray_tran);
218 | nowtop.setBackgroundResource(R.color.colorGray_tran);
219 | nowbase.setBackgroundResource(R.drawable.shape_gray_tran_selected);
220 | }else if(lastChildPosition-getNumColumns()==currentChildPosition){
221 | View lasttop=lastchildview.findViewById(R.id.topyibi)
222 | ,nowbottom=childview.findViewById(R.id.bottomyibi);
223 | lasttop.setBackgroundResource(R.color.colorGray_tran);
224 | nowbottom.setBackgroundResource(R.color.colorGray_tran);
225 | nowbase.setBackgroundResource(R.drawable.shape_gray_tran_selected);
226 | }else if(lastChildPosition-1==currentChildPosition&&lastChildPosition%getNumColumns()!=0){
227 | View lastleft=lastchildview.findViewById(R.id.leftyibi)
228 | ,nowright=childview.findViewById(R.id.rightyibi);
229 | lastleft.setBackgroundResource(R.color.colorGray_tran);
230 | nowright.setBackgroundResource(R.color.colorGray_tran);
231 | nowbase.setBackgroundResource(R.drawable.shape_gray_tran_selected);
232 | }
233 | lastChildPosition=currentChildPosition;
234 | }
235 | }
236 | }
237 | });
238 | }
239 |
240 | //后退操作需谨慎,因为前进时后退或者直接后退不一样,需分别处理,不像前进只需要对比上一步位置和当前所在位置即可判断是否前进
241 | private void HouTui(View curChildview, int currentChildPosition){
242 | if(curChildview==null) return;
243 | post(()->{
244 | if(!(curChildview.getTag() instanceof Integer)) return;
245 | if(lastChildPosition=0){
246 | lastChildPosition=(Integer)curChildview.getTag();
247 | View lastchildview=getChildAt(lastChildPosition);
248 | if(lastChildPosition+1==currentChildPosition&¤tChildPosition%getNumColumns()!=0){//从右侧往左后退
249 | View lastright=lastchildview.findViewById(R.id.rightyibi);
250 | lastright.setBackgroundResource(R.color.colortransparency);
251 | }else if(lastChildPosition+getNumColumns()==currentChildPosition){//从下方往上后退
252 | View lastbottom=lastchildview.findViewById(R.id.bottomyibi);
253 | lastbottom.setBackgroundResource(R.color.colortransparency);
254 | }else if(lastChildPosition-getNumColumns()==currentChildPosition){//从上方往下后退
255 | View lasttop=lastchildview.findViewById(R.id.topyibi);
256 | lasttop.setBackgroundResource(R.color.colortransparency);
257 | }else if(lastChildPosition-1==currentChildPosition&&lastChildPosition%getNumColumns()!=0){//从左侧往右后退
258 | View lastleft=lastchildview.findViewById(R.id.leftyibi);
259 | lastleft.setBackgroundResource(R.color.colortransparency);
260 | }
261 | passedPositions.remove(passedPositions.size()-1);
262 | if(listener!=null)
263 | listener.saveYibi(road,passedPositions);
264 | curChildview.setTag(null);
265 | View right=curChildview.findViewById(R.id.rightyibi)
266 | ,left=curChildview.findViewById(R.id.leftyibi)
267 | ,top=curChildview.findViewById(R.id.topyibi)
268 | ,bottom=curChildview.findViewById(R.id.bottomyibi)
269 | ,nowbase=curChildview.findViewById(R.id.baseyibi);
270 | right.setBackgroundResource(R.color.colortransparency);
271 | left.setBackgroundResource(R.color.colortransparency);
272 | top.setBackgroundResource(R.color.colortransparency);
273 | bottom.setBackgroundResource(R.color.colortransparency);
274 | nowbase.setBackgroundResource(R.drawable.shape_gray_unselected);
275 | }
276 | });
277 | }
278 |
279 | @Override
280 | public boolean onTouch(View v, MotionEvent event) {
281 | if(listener!=null && listener.isHelping()){
282 | refreshGrid();
283 | listener.setIsHelping(false);
284 | }
285 | if(road==null) return false;
286 |
287 | final List roadList = road.getRoadList();
288 | if(listener!=null&&passedPositions.size()+1==roadList.size()){
289 | StringBuilder roadString=new StringBuilder();
290 |
291 | for(int p:roadList){
292 | roadString.append(p);
293 | if(roadList.lastIndexOf(p)!=roadList.size()-1){
294 | roadString.append(",");
295 | }
296 | }
297 | listener.passed(road);
298 | }
299 | float currentX=event.getX();
300 | float currentY=event.getY();
301 | int currentChildPosition=pointToPosition((int)currentX,(int)currentY);
302 | if(currentChildPosition>=getChildCount() || currentChildPosition<0){
303 | return false;
304 | }
305 | final View childview=getChildAt(currentChildPosition);
306 | if(childview.getTag()!=null&&childview.getTag().toString().equals("forbidden")){
307 | return false;
308 | }
309 | try {
310 | switch (event.getAction()){
311 | case MotionEvent.ACTION_DOWN:
312 | //记录落点
313 | downChildPosition=currentChildPosition;
314 |
315 | if(lastChildPosition==downChildPosition || downChildPosition==firstChildPosition || passedPositions.contains(downChildPosition)){
316 | if(lastChildPosition==downChildPosition){
317 | //当上次操作的view位置和当前所落的位置一样时执行后退
318 | HouTui(childview,downChildPosition);
319 | }else{
320 | //当 落点在起点位置 或者 已通过的点包含当前落点时,回退到落点
321 | for(int i=passedPositions.size()-1;i>=0;i--){
322 | final int curP = passedPositions.get(i);
323 | if(curP==downChildPosition) break;
324 | View curView = getChildAt(curP);
325 | if(curView.getTag()==null) break;
326 | HouTui(curView,curP);
327 | }
328 | }
329 | }else{
330 | //其它情况尝试前进
331 | QianJin(childview,downChildPosition);
332 | }
333 | break;
334 | case MotionEvent.ACTION_MOVE:
335 | //当前位置为初始位置时需做特殊处理
336 | if(currentChildPosition==firstChildPosition&&lastChildPosition!=firstChildPosition){
337 | View lastChildView=getChildAt(lastChildPosition);
338 | if(lastChildView.getTag() instanceof Integer &&(Integer)lastChildView.getTag()==firstChildPosition){
339 | //当前位置为初始位置并且当“笔尾”的前一个位置为初始位置时后退
340 | HouTui(lastChildView,lastChildPosition);
341 | }
342 | break;
343 | }
344 | if(currentChildPosition!=lastMoveChildPosition){//仅在一个子view内来回滑动时状态不改变
345 | if(currentChildPosition!=downChildPosition){
346 | if(childview.getTag()!=null){
347 | //记录不为空时分两种情况执行后退,直接后退和前进时突然后退
348 | if(lastChildPosition==currentChildPosition){
349 | //直接后退:当上次操作点等于当前移动点时执行后退
350 | HouTui(childview,currentChildPosition);
351 | }else{
352 | View lastchildview=getChildAt(lastChildPosition);
353 | if(lastchildview.getTag() instanceof Integer && (Integer)lastchildview.getTag()==currentChildPosition){
354 | //前进时突然后退:退的是笔尾,此时currentChildPosition为“笔尾”的上一步位置
355 | HouTui(lastchildview,lastChildPosition);
356 | }
357 | }
358 | }else {
359 | //当记录为空时尝试前进
360 | QianJin(childview,currentChildPosition);
361 | }
362 | }else if(childview.getTag()==null){
363 | //当当前点为落点并且记录为空时尝试前进
364 | QianJin(childview,currentChildPosition);
365 | }
366 | }
367 | lastMoveChildPosition=currentChildPosition;
368 | break;
369 | }
370 | }catch (Exception e){
371 | OtherUtil.DebuggerLog(e);
372 | showToat("出错了,有一步没执行");
373 | }
374 | return false;
375 | }
376 |
377 | }
378 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/onelinetoend/View/UtilView/MyNumPicker.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend.View.UtilView;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.EditText;
7 | import android.widget.NumberPicker;
8 |
9 |
10 | import com.example.onelinetoend.R;
11 |
12 | import androidx.core.content.ContextCompat;
13 |
14 | public class MyNumPicker extends NumberPicker {
15 | public MyNumPicker(Context context) {
16 | super(context);
17 | }
18 |
19 | public MyNumPicker(Context context, AttributeSet attrs) {
20 | super(context, attrs);
21 | }
22 |
23 | public MyNumPicker(Context context, AttributeSet attrs, int defStyleAttr) {
24 | super(context, attrs, defStyleAttr);
25 | }
26 |
27 | @Override
28 | public void addView(View child) {
29 | super.addView(child);
30 | updateView(child);
31 | }
32 |
33 | @Override
34 | public void addView(View child, int index,
35 | android.view.ViewGroup.LayoutParams params) {
36 | super.addView(child, index, params);
37 | updateView(child);
38 | }
39 |
40 | @Override
41 | public void addView(View child, android.view.ViewGroup.LayoutParams params) {
42 | super.addView(child, params);
43 | updateView(child);
44 | }
45 |
46 | private void updateView(View view) {
47 | if (view instanceof EditText) {
48 | //这里修改显示字体的属性,主要修改颜色
49 | ((EditText) view).setTextColor(ContextCompat.getColor(getContext(), R.color.colorGray));
50 | ((EditText) view).setTextSize(30);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/accelerate_quart.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/bottomin.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/bottomout.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/decelerate_quart.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/decelerate_quint.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/decelerate_terz.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/magnify.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/rotate.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/scale.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/scene_close_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 |
21 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/scene_close_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
21 |
22 |
31 |
32 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/scene_open_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
21 |
22 |
30 |
31 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/scene_open_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 |
21 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/app_icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/drawable/app_icon.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_shape_white.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 | -
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_collection.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_delete.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_help.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_helping.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_refresh.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_return.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_right.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_ellipse_gray.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_ellipse_tran.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_gray_deep_selected.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_gray_selected.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_gray_tran_selected.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_gray_unselected.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_red.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_difficulty.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_difficulty_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_yibi.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
22 |
29 |
36 |
37 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_yibi_collection.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
20 |
29 |
38 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_collection.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
26 |
36 |
46 |
56 |
57 |
58 |
66 |
67 |
77 |
78 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_difficulty.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
21 |
22 |
34 |
35 |
36 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_edittext_number.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_index.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
28 |
29 |
39 |
40 |
50 |
51 |
61 |
62 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_road.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
18 |
19 |
25 |
26 |
34 |
35 |
43 |
44 |
52 |
53 |
61 |
62 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_road_random.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
26 |
39 |
40 |
48 |
49 |
55 |
56 |
65 |
66 |
74 |
75 |
76 |
77 |
85 |
86 |
94 |
95 |
103 |
115 |
123 |
124 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_selector_yibi.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
18 |
23 |
28 |
29 |
34 |
39 |
40 |
45 |
50 |
51 |
52 |
53 |
62 |
63 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_setting.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
20 |
21 |
26 |
34 |
38 |
39 |
40 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_waiting.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
24 |
25 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/raw/bgm.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/app/src/main/res/raw/bgm.mp3
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #66CDAA
4 | #00574B
5 | #D81B60
6 | #121111
7 | #FBFBFB
8 | #80FBFBFB
9 | #CCFBFBFB
10 | #686868
11 | #CC686868
12 | #458B74
13 | #778899
14 | #61b499
15 | #0F000000
16 | #FFBF00
17 | #80FFBF00
18 | #0000
19 | #BF094E
20 | #5CB9FF
21 | #805CB9FF
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | OneLineToEnd
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
17 |
18 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/onelinetoend/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.example.onelinetoend;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 |
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.5.3'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 |
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/demo.gif
--------------------------------------------------------------------------------
/demo1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/demo1.gif
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eaken/OneLineFinishedDrawing/550663e7bdc108648ae0f451cfaacae44ecd7f2e/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Feb 17 12:17:41 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name='OneLineToEnd'
3 |
--------------------------------------------------------------------------------