├── .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 | 14 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OneLineFinishedDrawing 2 | Android版一笔画完小游戏 3 | 4 | 采用单个activity多个fragment构造场景。 具有随机模式,随机模式采用深度优先遍历算法和全组合算法构图。 5 | 6 | 项目为本人大学时期的Android试验作品,有许多不足之处,但如需将本项目源码用于商业发布,请提前知悉本人。 7 | 8 | 交流联系:eaken1063874640@gmail.com,标题写Android,有空回 9 | 10 | ![image](https://github.com/Eaken/OneLineFinishedDrawing/blob/master/demo1.gif) 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 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 |