diff --git a/CODE/RommteStory/.gitignore b/CODE/RommteStory/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/CODE/RommteStory/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/CODE/RommteStory/.idea/.gitignore b/CODE/RommteStory/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/CODE/RommteStory/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/CODE/RommteStory/.idea/compiler.xml b/CODE/RommteStory/.idea/compiler.xml
new file mode 100644
index 0000000..fb7f4a8
--- /dev/null
+++ b/CODE/RommteStory/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/.idea/dbnavigator.xml b/CODE/RommteStory/.idea/dbnavigator.xml
new file mode 100644
index 0000000..210ee48
--- /dev/null
+++ b/CODE/RommteStory/.idea/dbnavigator.xml
@@ -0,0 +1,407 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/.idea/deploymentTargetDropDown.xml b/CODE/RommteStory/.idea/deploymentTargetDropDown.xml
new file mode 100644
index 0000000..ccacf5f
--- /dev/null
+++ b/CODE/RommteStory/.idea/deploymentTargetDropDown.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/.idea/gradle.xml b/CODE/RommteStory/.idea/gradle.xml
new file mode 100644
index 0000000..0625cbe
--- /dev/null
+++ b/CODE/RommteStory/.idea/gradle.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/.idea/misc.xml b/CODE/RommteStory/.idea/misc.xml
new file mode 100644
index 0000000..c34d6b7
--- /dev/null
+++ b/CODE/RommteStory/.idea/misc.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/.gitignore b/CODE/RommteStory/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/CODE/RommteStory/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/CODE/RommteStory/app/build.gradle b/CODE/RommteStory/app/build.gradle
new file mode 100644
index 0000000..45a406a
--- /dev/null
+++ b/CODE/RommteStory/app/build.gradle
@@ -0,0 +1,58 @@
+plugins {
+ id 'com.android.application'
+}
+
+android {
+ compileSdk 32
+
+ defaultConfig {
+ applicationId "com.lh.app"
+ minSdk 21
+ targetSdk 32
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ buildFeatures {
+ viewBinding true
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.appcompat:appcompat:1.3.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+ implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
+ implementation 'androidx.navigation:navigation-fragment:2.3.5'
+ implementation 'androidx.navigation:navigation-ui:2.3.5'
+ implementation 'com.google.android.gms:play-services-location:18.0.0'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+
+ //noinspection GradleCompatible
+ implementation 'androidx.recyclerview:recyclerview:1.2.1'//添加RecycleView库依赖
+ //图像类库
+ implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
+
+ implementation 'com.google.android.gms:play-services-location:21.0.1'
+ //图片加载
+ implementation 'com.github.bumptech.glide:glide:4.15.1' // 使用最新版本
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
+ //高德地图
+ implementation 'com.amap.api:3dmap-location-search:latest.integration'
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/app/proguard-rules.pro b/CODE/RommteStory/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/CODE/RommteStory/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/androidTest/java/com/lh/app/ExampleInstrumentedTest.java b/CODE/RommteStory/app/src/androidTest/java/com/lh/app/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..23ee024
--- /dev/null
+++ b/CODE/RommteStory/app/src/androidTest/java/com/lh/app/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.lh.app;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("com.lh.app", appContext.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/AndroidManifest.xml b/CODE/RommteStory/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4c57875
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/AndroidManifest.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/ic_launcher-playstore.png b/CODE/RommteStory/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000..17cefac
Binary files /dev/null and b/CODE/RommteStory/app/src/main/ic_launcher-playstore.png differ
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/MainActivity.java b/CODE/RommteStory/app/src/main/java/com/lh/app/MainActivity.java
new file mode 100644
index 0000000..b69808c
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/MainActivity.java
@@ -0,0 +1,182 @@
+package com.lh.app;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import com.lh.app.ui.honor.HonorFragment;
+import com.lh.app.ui.detail.DetailFragment;
+import com.lh.app.ui.memo.MemoFragment;
+import com.lh.app.ui.mine.MineFragment;
+import com.google.android.material.bottomnavigation.BottomNavigationView;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentPagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import com.lh.app.databinding.ActivityMainBinding;
+
+import java.util.ArrayList;
+
+public class MainActivity extends AppCompatActivity {
+
+ private ActivityMainBinding binding;
+ BottomNavigationView navView;
+ private ViewPager mViewPager;
+
+ //设置顶部菜单栏
+ ActionBar actionBar;
+ //
+ public static boolean isShowViewPage_memo = false;
+ public static boolean isShowViewPage_honor = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d("MainActivity","onCreate");
+
+ actionBar = getSupportActionBar();
+ assert actionBar != null;
+ actionBar.setTitle(R.string.title_detail);
+
+ binding = ActivityMainBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+
+ addUi();
+ addUiListener();
+ initPageAdapter();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if(isShowViewPage_memo){
+ mViewPager.setCurrentItem(1);
+ isShowViewPage_memo = false;
+ }
+ if(isShowViewPage_honor){
+ mViewPager.setCurrentItem(2);
+ isShowViewPage_honor = false;
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ Log.d("MainActivity","onDestroy");
+
+ }
+
+ //添加组件及变量声明
+ private void addUi() {
+ navView = findViewById(R.id.nav_view);
+ mViewPager = findViewById(R.id.mViewPager);
+ }
+
+ //初始化页面监听器
+ private void initPageAdapter() {
+ //Fragment列表,将fragment放入列表中,放入mPagerAdapter
+ final ArrayList fgLists = new ArrayList<>(4);
+ fgLists.add(new DetailFragment());
+ fgLists.add(new MemoFragment());
+ fgLists.add(new HonorFragment());
+ fgLists.add(new MineFragment());
+ FragmentPagerAdapter mPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
+ @Override
+ public Fragment getItem(int position) {
+ return fgLists.get(position);
+ }
+
+ @Override
+ public int getCount() {
+ return fgLists.size();
+ }
+ };
+ mViewPager.setAdapter(mPagerAdapter);
+ //设置预加载页面数量的方法
+ mViewPager.setOffscreenPageLimit(1);
+ }
+
+ //添加组件监听器
+ private void addUiListener() {
+ navView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
+ mViewPager.addOnPageChangeListener(onPageChangeListener);
+ }
+
+ //底部导航栏监听器
+ @SuppressLint("NonConstantResourceId")
+ private final BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() {
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ // 清除之前的选中状态
+ Menu menu = navView.getMenu();
+ for (int i = 0; i < menu.size(); i++) {
+ MenuItem menuItem = menu.getItem(i);
+ menuItem.setChecked(false);
+ }
+ switch (item.getItemId()) {
+ case R.id.navigation_detail:
+ mViewPager.setCurrentItem(0);
+ item.setChecked(true);
+ break;
+ case R.id.navigation_memo:
+ mViewPager.setCurrentItem(1);
+ item.setChecked(true);
+ break;
+ case R.id.navigation_communicate:
+ mViewPager.setCurrentItem(2);
+ item.setChecked(true);
+ break;
+ case R.id.navigation_mine:
+ mViewPager.setCurrentItem(3);
+ item.setChecked(true);
+ break;
+ }
+ return false;
+ }
+ };
+ //页面改变监听器
+ private final ViewPager.OnPageChangeListener onPageChangeListener = new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ //在滑动中
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ actionBar.setDisplayHomeAsUpEnabled(false);
+ //写滑动页面后做的事,使每一个fragment与一个page相对应
+ switch (position) {
+ case 0:
+ navView.setSelectedItemId(R.id.navigation_detail);
+ actionBar.setTitle(R.string.title_detail);
+ break;
+ case 1:
+ navView.setSelectedItemId(R.id.navigation_memo);
+ actionBar.setTitle(R.string.title_memo);
+ break;
+ case 2:
+ navView.setSelectedItemId(R.id.navigation_communicate);
+ actionBar.setTitle(R.string.title_honor);
+ break;
+ case 3:
+ navView.setSelectedItemId(R.id.navigation_mine);
+ actionBar.setTitle(R.string.title_mine);
+ break;
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+
+ };
+
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/MyApplication.java b/CODE/RommteStory/app/src/main/java/com/lh/app/MyApplication.java
new file mode 100644
index 0000000..b3a745f
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/MyApplication.java
@@ -0,0 +1,53 @@
+package com.lh.app;
+
+import android.app.Application;
+import android.content.res.Configuration;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.util.HashMap;
+
+/**
+ * Created by on .
+ */
+public class MyApplication extends Application {
+ private static MyApplication mApp;
+ //声明一个公共的信息映射对象,可当全局变量使用
+ public HashMap infoMap = new HashMap<>();
+ public static RoommateDBHelper mHelper;
+ //得到Application唯一实例
+ public static MyApplication getInstance(){
+ return mApp;
+ }
+
+ //在App启用时调用
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mApp = this;
+ Log.d("ning","MyApplication onCreate");
+
+ mHelper = RoommateDBHelper.getInstance(this);
+ //打开数据库帮助器读写连接
+ mHelper.openWriteLink();
+ mHelper.openReadLink();
+
+ }
+
+ //在App终止时调用
+ @Override
+ public void onTerminate() {
+ super.onTerminate();
+ Log.d("ning","MyApplication onTerminate");
+ //关闭数据库连接
+ mHelper.closeLink();
+ }
+
+ //配置改变时被调用,如:横屏变竖屏
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ Log.d("ning","MyApplication onConfigurationChanged");
+ }
+}
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/RoommateDBHelper.java b/CODE/RommteStory/app/src/main/java/com/lh/app/RoommateDBHelper.java
new file mode 100644
index 0000000..63f6b69
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/RoommateDBHelper.java
@@ -0,0 +1,360 @@
+package com.lh.app;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+import com.lh.app.ui.detail.Roommate;
+import com.lh.app.ui.honor.Honor;
+import com.lh.app.ui.memo.Memo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RoommateDBHelper extends SQLiteOpenHelper {
+ private static final String DB_NAME = "roommate.db";
+ private static final int DB_VERSION = 1;
+ private static final String TABLE_NAME_ROOMMATE_INFO = "roommate_info";//舍友信息表
+ private static final String TABLE_NAME_ROOMMATE_MEMO = "roommate_memo";//舍友备忘录表
+ private static final String TABLE_NAME_ROOMMATE_HONOR = "roommate_honor";//舍友大事记表
+ private static RoommateDBHelper mHelper = null;
+ private SQLiteDatabase mRDB = null;//钥匙,用来读
+ private SQLiteDatabase mWDB = null;//钥匙,用来写
+
+ private RoommateDBHelper(Context context){
+ //向父类方法传参,上下文、数据库名、游标、版本号
+ super(context,DB_NAME,null,DB_VERSION);
+ }
+
+ //利用单例模式去获取数据库帮助库的唯一实例
+ public static RoommateDBHelper getInstance(Context context){
+ if (mHelper==null){
+ mHelper = new RoommateDBHelper(context);
+ }
+ return mHelper;
+ }
+
+ public static String getTableNameRoommateInfo() {
+ return TABLE_NAME_ROOMMATE_INFO;
+ }
+
+ public static String getTableNameRoommateMemo() {
+ return TABLE_NAME_ROOMMATE_MEMO;
+ }
+
+ //打开数据库的读连接
+ public SQLiteDatabase openReadLink(){
+ if (mRDB==null||!mRDB.isOpen()){
+ mRDB = mHelper.getReadableDatabase();
+ }
+ return mRDB;
+ }
+
+ //打开数据库的写连接
+ public SQLiteDatabase openWriteLink(){
+ if (mWDB==null||!mWDB.isOpen()){
+ mWDB = mHelper.getWritableDatabase();
+ }
+ return mWDB;
+ }
+
+ //关闭数据库连接
+ public void closeLink(){
+ //关闭读链接
+ if (mRDB!=null&&mRDB.isOpen()){
+ mRDB.close();
+ mRDB = null;
+ }
+ //关闭写连接
+ if (mWDB!=null&&mWDB.isOpen()){
+ mWDB.close();
+ mWDB = null;
+ }
+ }
+
+ //创建数据库,执行建表语句
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ //建表语句
+ String sql_01 = "create table if not exists "+ TABLE_NAME_ROOMMATE_INFO +" (" +
+ "_id integer primary key autoincrement not null," +
+ "name varchar not null," +
+ "id varchar not null,"+
+ "height long not null," +
+ "weight float not null," +
+ "married integer not null,"+
+ "birthday varchar not null,"+
+ "hobby varchar not null,"+
+ "position integer not null);";//记录在屏幕上位置
+
+ String sql_02 = "create table if not exists "+ TABLE_NAME_ROOMMATE_MEMO +" (" +
+ "_id integer primary key autoincrement not null," +
+ "name varchar not null," +
+ "title varchar not null," +
+ "content varchar not null," +
+ "date varchar not null,"+
+ "isDelete int not null,"+//是否是被删除记录(是1,否0)
+ "isRevise int not null);";//是否是被修改记录(是1,否0)
+
+ String sql_03 = "create table if not exists "+ TABLE_NAME_ROOMMATE_HONOR +" (" +
+ "_id integer primary key autoincrement not null," +
+ "imagePath varchar not null," +
+ "content varchar not null);";//是否是被修改记录(是1,否0)
+ //建表
+ db.execSQL(sql_01);
+ db.execSQL(sql_02);
+ db.execSQL(sql_03);
+ }
+
+ //数据库版本变化时执行
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ /* String sql = "alter table "+ TABLE_NAME_ROOMMATE_INFO +" add column phone varchar;";
+ db.execSQL(sql);*/
+ }
+
+ /**
+ * 添加
+ * */
+ //向指定表中添加一行数据
+ public long insert(String table_name,ContentValues values){
+ if (table_name.equals(TABLE_NAME_ROOMMATE_INFO) || table_name.equals(TABLE_NAME_ROOMMATE_MEMO))
+ //若第三个参数values不为null,并且元素个数大于0,可以吧第二个参数置为null
+ //返回行号,若为-1,表示添加失败
+ return mWDB.insert(table_name,null,values);
+ else return -1;
+ }
+
+ /**
+ * 删除
+ * */
+ //通过名字删除舍友信息表数据
+ public long deleteByName(String name){
+ //返回删除的行数
+ return mWDB.delete(TABLE_NAME_ROOMMATE_INFO,"name=?",new String[]{name});
+ //mWDB.delete(TABLE_NAME,"name=? and age=?",new String[]{name,age});
+ }
+ //清空指定表内容
+ public long deleteAll(String table_name){
+ //删除所有,返回删除的行数
+ return mWDB.delete(table_name,"1=1",null);
+ }
+ //删除舍友备忘录表中指定数据
+ public long deleteMemoByType(Memo memo, boolean isDelete, boolean isRevise){
+ String name = memo.getName();
+ String title = memo.getTitle();
+ String content = memo.getContent();
+ String date = memo.getDate();
+ String INT_isDelete = isDelete?"1":"0";
+ String INT_isRevise = isRevise?"1":"0";
+ //返回删除的行数
+ return mWDB.delete(TABLE_NAME_ROOMMATE_MEMO,
+ "name=? and title=? and content=? and date=? and isDelete=? and isRevise=?",
+ new String[]{name,title,content,date,INT_isDelete,INT_isRevise});
+ }
+ //删除舍友备忘录中指定类型全部数据
+ public long deleteMemoAllType(boolean isDelete,boolean isRevise){
+ String INT_isDelete = isDelete?"1":"0";
+ String INT_isRevise = isRevise?"1":"0";
+ //返回删除的行数
+ return mWDB.delete(TABLE_NAME_ROOMMATE_MEMO, "isDelete=? and isRevise=?", new String[]{INT_isDelete,INT_isRevise});
+ }
+
+ /**
+ * 更新
+ * */
+ //更新室友表中数据
+ public long update_roommateInfo(ContentValues values,String name){
+ //返回更改的行数
+ return mWDB.update(TABLE_NAME_ROOMMATE_INFO,values,"name=?",new String[]{name});
+ }
+ //更新室友备忘录中显示数据的状态
+ public long update_roommateMemo(Memo memo,boolean isDelete,boolean isRevise){
+ ContentValues values = new ContentValues();
+ values.put("name",memo.getName());
+ values.put("title",memo.getTitle());
+ values.put("content",memo.getContent());
+ values.put("date",memo.getDate());
+ values.put("isDelete", isDelete);
+ values.put("isRevise", isRevise);
+ //返回更改的行数
+ return mWDB.update(TABLE_NAME_ROOMMATE_MEMO,values,
+ "name=? and title=? and content=? and date=? and isDelete=? and isRevise=?",
+ new String[]{memo.getName(),memo.getTitle(),memo.getContent(),memo.getDate(),"0","0"});
+ }
+ //更新室友备忘录中显示数据的状态为被删除
+ public void update_roommateAllMemoToDelete(){
+ //先查询备忘录中显示的数据
+ List memoList = query_roommateMemoByType(false,false);
+ //循环遍历列表,将其类型改为删除
+ for (Memo memo:memoList){
+ long row = update_roommateMemo(memo,true,false);
+ Log.d("UpdateMemoTable","删除备忘录"+row+"条");
+ }
+ }
+
+ /**
+ * 查询
+ * */
+ //查询室友信息全部数据
+ public List query_roommateInfo_All(){
+ List list = new ArrayList<>();
+ //返回结果集的游标
+ Cursor cursor = mRDB.query(TABLE_NAME_ROOMMATE_INFO,null,null,null,null,null,null);
+ //循环取出游标指向的每条数据
+ while (cursor.moveToNext()){
+ Roommate roommate = new Roommate();
+ roommate.setName(cursor.getString(1));
+ roommate.setId(cursor.getString(2));
+ roommate.setHeight(cursor.getLong(3));
+ roommate.setWeight(cursor.getFloat(4));
+ //Sqlite没有布尔类型,用0表示false,用1表示true
+ roommate.setMarried(cursor.getInt(5) != 0);
+ roommate.setBirthday(cursor.getString(6));
+ roommate.setHobby(cursor.getString(7));
+ list.add(roommate);
+ }
+ cursor.close();
+ return list;
+ }
+ //返回舍友信息表中所有舍友姓名列表
+ public List query_roommateName(){
+ List list = new ArrayList<>();
+ //返回结果集的游标
+ Cursor cursor = mRDB.query(TABLE_NAME_ROOMMATE_INFO, new String[]{"name"},null,null,null,null,null);
+ //循环取出游标指向的每条数据
+ while (cursor.moveToNext()){
+ String name;
+ name = cursor.getString(0);
+ list.add(name);
+ }
+ cursor.close();
+ return list;
+ }
+ //通过名字查询指定室友信息数据
+ public Roommate query_roommateInfo_ByName(String name){
+ Roommate roommate = new Roommate();
+ //返回结果集的游标
+ Cursor cursor = mRDB.query(TABLE_NAME_ROOMMATE_INFO,null,
+ "name=?",new String[]{name},
+ null,null,null);
+ //循环取出游标指向的每条数据
+ while (cursor.moveToNext()){
+ roommate.setName(cursor.getString(1));
+ roommate.setId(cursor.getString(2));
+ roommate.setHeight(cursor.getLong(3));
+ roommate.setWeight(cursor.getFloat(4));
+ //Sqlite没有布尔类型,用0表示false,用1表示true
+ roommate.setMarried(cursor.getInt(5) != 0);
+ roommate.setBirthday(cursor.getString(6));
+ roommate.setHobby(cursor.getString(7));
+ }
+ cursor.close();
+ return roommate;
+ }
+ //返回室友信息表是否为空
+ public Boolean query_roommateInfo_isEmpty(){
+ List list = new ArrayList<>();
+ //返回结果集的游标
+ Cursor cursor = mRDB.query(TABLE_NAME_ROOMMATE_INFO,null,null,null,null,null,null);
+ if (cursor.getCount()==0) return true;
+
+ cursor.close();
+ return false;
+ }
+ //查询室友名在屏幕上位置
+ public int query_roommate_position(String name){
+ //返回结果集的游标
+ Cursor cursor = mRDB.query(TABLE_NAME_ROOMMATE_INFO,new String[]{"position"},
+ "name=?",new String[]{name}, null,null,null);
+ //使游标移到第一个位置
+ cursor.moveToFirst();
+ return cursor.getInt(0);
+ }
+
+ //查询室友备忘录中指定类型数据
+ //参数(1,1)表示:查询被修改过的数据;(1,0)表示:查询被删除数据:(0,0)表示:查询要显示的数据;(0,1)表示:查询增加数据
+ public List query_roommateMemoByType(boolean isDelete,boolean isRevise){
+ List list = new ArrayList<>();
+ String INT_isDelete = isDelete?"1":"0";
+ String INT_isRevise = isRevise?"1":"0";
+ //返回结果集的游标
+ Cursor cursor = mRDB.query(TABLE_NAME_ROOMMATE_MEMO,null,"isDelete=? and isRevise=?",new String[]{INT_isDelete,INT_isRevise},null,null,null);
+ //循环取出游标指向的每条数据
+ while (cursor.moveToNext()){
+ Memo memo = new Memo(cursor.getString(1),cursor.getString(2),
+ cursor.getString(3),cursor.getString(4));
+ list.add(memo);
+ }
+ cursor.close();
+ return list;
+ }
+ //查询室友备忘录指定数据
+ public Memo query_roommateMemo_ByDate(String name,String title,String content,String date){
+ Memo memo = null;
+ //返回结果集的游标
+ Cursor cursor = mRDB.query(TABLE_NAME_ROOMMATE_MEMO,null,
+ "name=? title=? and content=? and date=?",new String[]{name,title,content,date},
+ null,null,null);
+ //循环取出游标指向的每条数据
+ while (cursor.moveToNext()){
+ memo = new Memo(cursor.getString(1),cursor.getString(2),
+ cursor.getString(3),cursor.getString(4));
+ }
+ cursor.close();
+ return memo;
+ }
+ //查询备忘录指定类型行数
+ //参数(1,1)表示:查询被修改过的数据;(1,0)表示:查询被删除数据:(0,0)表示:查询要显示的数据;(0,1)表示:查询增加数据
+ public int query_roommateMemo_rowsNumByType(boolean isDelete, boolean isRevise){
+ List list = new ArrayList<>();
+ String INT_isDelete = isDelete?"1":"0";
+ String INT_isRevise = isRevise?"1":"0";
+ //返回结果集的游标
+ Cursor cursor = mRDB.query(TABLE_NAME_ROOMMATE_MEMO,null,"isDelete=? and isRevise=?",new String[]{INT_isDelete,INT_isRevise},null,null,null);
+ //循环取出游标指向的每条数据
+ while (cursor.moveToNext()){
+ Memo memo = new Memo(cursor.getString(1),cursor.getString(2),
+ cursor.getString(3),cursor.getString(4));
+ list.add(memo);
+ }
+ cursor.close();
+ return list.size();
+ }
+
+ /**
+ * 大事记表的操作
+ * */
+ //插入大事记表数据
+ public long insert_honor(Honor honor){
+ ContentValues values = new ContentValues();
+ values.put("imagePath",honor.getImageLocation());
+ values.put("content",honor.getContent());
+ //若第三个参数values不为null,并且元素个数大于0,可以吧第二个参数置为null
+ //返回行号,若为-1,表示添加失败
+ return mWDB.insert(TABLE_NAME_ROOMMATE_HONOR,null,values);
+ }
+ //查询大事记全部数据
+ public List query_honor_All(){
+ List honorList = new ArrayList<>();
+ //返回结果集的游标
+ Cursor cursor = mRDB.query(TABLE_NAME_ROOMMATE_HONOR,null,null,null,null,null,null);
+
+ //循环取出游标指向的每条数据
+ while (cursor.moveToNext()){
+ Honor honor = new Honor();
+ honor.setImageLocation(cursor.getString(1));
+ honor.setContent(cursor.getString(2));
+ honorList.add(honor);
+ }
+ cursor.close();
+ return honorList;
+ }
+ //删除大事记指定数据
+ public long delete_honorByLocation(String imagePath){
+ return mWDB.delete(TABLE_NAME_ROOMMATE_HONOR, "imagePath=?", new String[]{imagePath});
+ }
+}
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/tool/CircleImageView.java b/CODE/RommteStory/app/src/main/java/com/lh/app/tool/CircleImageView.java
new file mode 100644
index 0000000..8428181
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/tool/CircleImageView.java
@@ -0,0 +1,75 @@
+package com.lh.app.tool;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatImageView;
+
+public class CircleImageView extends AppCompatImageView {
+
+ //画笔
+ private Paint mPaint;
+ //圆形图片的半径
+ private int mRadius;
+ //图片的宿放比例
+ private float mScale;
+
+ public CircleImageView(Context context) {
+ super(context);
+ }
+
+ public CircleImageView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CircleImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ //由于是圆形,宽高应保持一致
+ int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
+ mRadius = size / 2;
+ setMeasuredDimension(size, size);
+ }
+
+ @SuppressLint("DrawAllocation")
+ @Override
+ protected void onDraw(Canvas canvas) {
+
+ mPaint = new Paint();
+
+ Drawable drawable = getDrawable();
+
+ if (null != drawable) {
+ Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+
+ //初始化BitmapShader,传入bitmap对象
+ BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+ //计算缩放比例
+ mScale = (mRadius * 2.0f) / Math.min(bitmap.getHeight(), bitmap.getWidth());
+
+ Matrix matrix = new Matrix();
+ matrix.setScale(mScale, mScale);
+ bitmapShader.setLocalMatrix(matrix);
+ mPaint.setShader(bitmapShader);
+ //画圆形,指定好坐标,半径,画笔
+ canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
+ } else {
+ super.onDraw(canvas);
+ }
+ }
+
+}
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/tool/ImageUtils.java b/CODE/RommteStory/app/src/main/java/com/lh/app/tool/ImageUtils.java
new file mode 100644
index 0000000..da83cf3
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/tool/ImageUtils.java
@@ -0,0 +1,112 @@
+package com.lh.app.tool;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.MediaStore;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+public class ImageUtils {
+ //uri转bitmap
+ public static Bitmap uriToBitmap(Context context, Uri uri){
+ if (uri == null) return null;
+ Bitmap bitmap = null;
+ try {
+ // decodeStream()可以将output_image.jpg解析成Bitmap对象。
+ bitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri));
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ return bitmap;
+ }
+
+ public static String getRealPathFromUri(Context context, Uri uri) {
+ if (uri == null) {
+ return null;
+ }
+
+ // 检查Uri是否是文件类型的Uri
+ if ("file".equalsIgnoreCase(uri.getScheme())) {
+ return uri.getPath();
+ }
+
+ // 检查Uri是否是Content类型的Uri
+ if ("content".equalsIgnoreCase(uri.getScheme())) {
+ String[] projection = { MediaStore.Images.Media.DATA };
+ Cursor cursor = null;
+ try {
+ cursor = context.getContentResolver().query(uri, projection, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
+ return cursor.getString(columnIndex);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ return null;
+ }
+ public static Bitmap stringToBitmap(String filePath) {
+ if (filePath == null || filePath.isEmpty()) {
+ return null;
+ }
+ File file = new File(filePath);
+ if (!file.exists()) {
+ return null;
+ }
+ return BitmapFactory.decodeFile(filePath);
+ }
+ /***
+ * 通过文件路径加载 Bitmap 图片
+ * @param filePath 路径
+ * @return Bitmap 图片
+ */
+ public static Bitmap loadBitmapFromPath(String filePath) {
+ // 创建一个文件对象
+ File imageFile = new File(filePath);
+
+ // 检查文件是否存在
+ if (imageFile.exists()) {
+ // 从文件路径加载 Bitmap
+ return BitmapFactory.decodeFile(imageFile.getAbsolutePath());
+ } else {
+ // 文件不存在,返回 null 或者处理错误
+ return null;
+ }
+ }
+ // 临时创建图片文件
+ public static File createImageFile(Context context) {
+ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
+ String fileName = "JPEG_" + timeStamp + ".jpg";
+ File storageDir = context.getExternalCacheDir(); // 使用缓存目录,避免申请存储权限
+ File photoFile = new File(storageDir, fileName);
+
+ try {
+ if (photoFile.createNewFile()) {
+ return photoFile;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+ //根据路径读取图片
+ public static Bitmap getBitmapFromPath(String path){
+ Bitmap bitmap = BitmapFactory.decodeFile(path);
+ return bitmap;
+ }
+}
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/tool/LineChartManager.java b/CODE/RommteStory/app/src/main/java/com/lh/app/tool/LineChartManager.java
new file mode 100644
index 0000000..1e5fc51
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/tool/LineChartManager.java
@@ -0,0 +1,263 @@
+package com.lh.app.tool;
+
+import com.github.mikephil.charting.charts.LineChart;
+import com.github.mikephil.charting.components.Description;
+import com.github.mikephil.charting.components.Legend;
+import com.github.mikephil.charting.components.LimitLine;
+import com.github.mikephil.charting.components.XAxis;
+import com.github.mikephil.charting.components.YAxis;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.data.LineData;
+import com.github.mikephil.charting.data.LineDataSet;
+import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *图像管理类
+ */
+public class LineChartManager {
+ private LineChart lineChart;
+ private YAxis leftAxis;
+ private YAxis rightAxis;
+ private XAxis xAxis;
+ private LineData lineData;
+ private LineDataSet lineDataSet;
+ private List lineDataSets = new ArrayList<>();
+ private SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");//设置日期格式
+ private List timeList = new ArrayList<>(); //存储x轴的时间
+
+ //一条曲线
+ public LineChartManager(LineChart mLineChart, String name, int color) {
+ this.lineChart = mLineChart;
+ leftAxis = lineChart.getAxisLeft();
+ rightAxis = lineChart.getAxisRight();
+ xAxis = lineChart.getXAxis();
+ initLineChart();
+ initLineDataSet(name, color);
+ }
+
+ //多条曲线
+ public LineChartManager(LineChart mLineChart, List names, List colors) {
+ this.lineChart = mLineChart;
+ leftAxis = lineChart.getAxisLeft();
+ rightAxis = lineChart.getAxisRight();
+ xAxis = lineChart.getXAxis();
+ initLineChart();
+ initLineDataSet(names, colors);
+ }
+
+ /**
+ * 初始化LineChar
+ */
+ private void initLineChart() {
+
+ lineChart.setDrawGridBackground(false);
+ //显示边界
+ lineChart.setDrawBorders(true);
+ //折线图例 标签 设置
+ Legend legend = lineChart.getLegend();
+ legend.setForm(Legend.LegendForm.LINE);
+ legend.setTextSize(11f);
+ //显示位置
+ legend.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
+ legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT);
+ legend.setOrientation(Legend.LegendOrientation.HORIZONTAL);
+ legend.setDrawInside(false);
+
+ //X轴设置显示位置在底部
+ xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
+ xAxis.setGranularity(1f);
+ xAxis.setLabelCount(10);
+
+
+ //保证Y轴从0开始,不然会上移一点
+ leftAxis.setAxisMinimum(0f);
+ rightAxis.setAxisMinimum(0f);
+ }
+
+ /**
+ * 初始化折线(一条线)
+ *
+ * @param name
+ * @param color
+ */
+ private void initLineDataSet(String name, int color) {
+
+ lineDataSet = new LineDataSet(null, name);
+ lineDataSet.setLineWidth(1.5f);
+ lineDataSet.setCircleRadius(1.5f);
+ lineDataSet.setColor(color);
+ lineDataSet.setCircleColor(color);
+ lineDataSet.setHighLightColor(color);
+ //设置曲线填充
+ lineDataSet.setDrawFilled(false);
+ lineDataSet.setDrawValues(false);
+ lineDataSet.setAxisDependency(YAxis.AxisDependency.LEFT);
+ lineDataSet.setValueTextSize(10f);
+ lineDataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);
+ //添加一个空的 LineData
+ lineData = new LineData();
+ lineChart.setData(lineData);
+ lineChart.invalidate();
+
+ }
+
+ /**
+ * 初始化折线(多条线)
+ *
+ * @param names
+ * @param colors
+ */
+ private void initLineDataSet(List names, List colors) {
+
+ for (int i = 0; i < names.size(); i++) {
+ lineDataSet = new LineDataSet(null, names.get(i));
+ lineDataSet.setColor(colors.get(i));
+ lineDataSet.setLineWidth(1.5f);
+ lineDataSet.setCircleRadius(1.5f);
+ lineDataSet.setColor(colors.get(i));
+
+ lineDataSet.setDrawFilled(false);
+ lineDataSet.setDrawValues(false);
+ lineDataSet.setCircleColor(colors.get(i));
+ lineDataSet.setHighLightColor(colors.get(i));
+ lineDataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);
+ lineDataSet.setAxisDependency(YAxis.AxisDependency.LEFT);
+ lineDataSet.setValueTextSize(10f);
+ lineDataSets.add(lineDataSet);
+
+ }
+ //添加一个空的 LineData
+ lineData = new LineData();
+ lineChart.setData(lineData);
+ lineChart.invalidate();
+ }
+
+ /**
+ * 动态添加数据(一条折线图)
+ *
+ * @param number
+ */
+ public void addEntry(int number) {
+
+ //最开始的时候才添加 lineDataSet(一个lineDataSet 代表一条线)
+ if (lineDataSet.getEntryCount() == 0) {
+ lineData.addDataSet(lineDataSet);
+ }
+ lineChart.setData(lineData);
+ //避免集合数据过多,及时清空(做这样的处理,并不知道有没有用,但还是这样做了)
+ if (timeList.size() > 11) {
+ timeList.clear();
+ }
+
+ timeList.add(df.format(System.currentTimeMillis()));
+
+ Entry entry = new Entry(lineDataSet.getEntryCount(), number);
+ lineData.addEntry(entry, 0);
+ //通知数据已经改变
+ lineData.notifyDataChanged();
+ lineChart.notifyDataSetChanged();
+ //设置在曲线图中显示的最大数量
+ lineChart.setVisibleXRangeMaximum(10);
+ //移到某个位置
+ lineChart.moveViewToX(lineData.getEntryCount() - 5);
+ }
+
+ /**
+ * 动态添加数据(多条折线图)
+ *
+ * @param numbers
+ */
+ public void addEntry(List numbers) {
+
+ if (lineDataSets.get(0).getEntryCount() == 0) {
+ lineData = new LineData(lineDataSets);
+ lineChart.setData(lineData);
+ }
+ if (timeList.size() > 100) {
+ timeList.clear();
+ }
+ timeList.add(df.format(System.currentTimeMillis()));
+ for (int i = 0; i < numbers.size(); i++) {
+ Entry entry = new Entry(lineDataSet.getEntryCount(), numbers.get(i));
+ lineData.addEntry(entry, i);
+ lineData.notifyDataChanged();
+ lineChart.notifyDataSetChanged();
+ lineChart.setVisibleXRangeMaximum(6);
+ lineChart.moveViewToX(lineData.getEntryCount() - 5);
+ }
+ }
+
+ /**
+ * 设置Y轴值
+ *
+ * @param max
+ * @param min
+ * @param labelCount
+ */
+ public void setYAxis(float max, float min, int labelCount) {
+ if (max < min) {
+ return;
+ }
+ leftAxis.setAxisMaximum(max);
+ leftAxis.setAxisMinimum(min);
+ leftAxis.setLabelCount(labelCount, false);
+
+ rightAxis.setAxisMaximum(max);
+ rightAxis.setAxisMinimum(min);
+ rightAxis.setLabelCount(labelCount, false);
+ lineChart.invalidate();
+ }
+
+ /**
+ * 设置高限制线
+ *
+ * @param high
+ * @param name
+ */
+ public void setHightLimitLine(float high, String name, int color) {
+ if (name == null) {
+ name = "高限制线";
+ }
+ LimitLine hightLimit = new LimitLine(high, name);
+ hightLimit.setLineWidth(4f);
+ hightLimit.setTextSize(10f);
+ hightLimit.setLineColor(color);
+ hightLimit.setTextColor(color);
+ leftAxis.addLimitLine(hightLimit);
+ lineChart.invalidate();
+ }
+
+ /**
+ * 设置低限制线
+ *
+ * @param low
+ * @param name
+ */
+ public void setLowLimitLine(int low, String name) {
+ if (name == null) {
+ name = "低限制线";
+ }
+ LimitLine hightLimit = new LimitLine(low, name);
+ hightLimit.setLineWidth(4f);
+ hightLimit.setTextSize(10f);
+ leftAxis.addLimitLine(hightLimit);
+ lineChart.invalidate();
+ }
+
+ /**
+ * 设置描述信息
+ *
+ * @param str
+ */
+ public void setDescription(String str) {
+ Description description = new Description();
+ description.setText(str);
+ lineChart.setDescription(description);
+ lineChart.invalidate();
+ }
+}
+
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/tool/NotificationUtil.java b/CODE/RommteStory/app/src/main/java/com/lh/app/tool/NotificationUtil.java
new file mode 100644
index 0000000..5a554fc
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/tool/NotificationUtil.java
@@ -0,0 +1,51 @@
+package com.lh.app.tool;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Build;
+
+import androidx.core.app.NotificationCompat;
+
+/**
+ * Created by LiShiyao on 2022.11.12.
+ */
+public class NotificationUtil {
+ private Context context;
+ private String contentTitle;
+ private String contentText;
+ private int smallIcon;
+ private Bitmap largeIcon;
+
+ public NotificationUtil(Context context, String contentTitle, String contentText, int smallIcon, Bitmap largeIcon) {
+ this.context = context;
+ this.contentTitle = contentTitle;
+ this.contentText = contentText;
+ this.smallIcon = smallIcon;
+ this.largeIcon = largeIcon;
+ }
+
+ //通知提醒
+ public void notification(){
+ //
+ NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
+ String channelId = "whatever";
+ String channelName = "whatever content";
+ int important = NotificationManager.IMPORTANCE_HIGH;
+ manager.createNotificationChannel(new NotificationChannel(channelId,channelName,important));
+ }
+ Notification notification;
+ notification = new NotificationCompat.Builder(context,"whatever")
+ .setContentTitle(contentTitle)
+ .setContentText(contentText)
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(smallIcon)
+ .setLargeIcon(largeIcon)
+ .setAutoCancel(false)
+ .build();
+ manager.notify(1,notification);
+ }
+}
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/tool/ToastUtil.java b/CODE/RommteStory/app/src/main/java/com/lh/app/tool/ToastUtil.java
new file mode 100644
index 0000000..f08cb9c
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/tool/ToastUtil.java
@@ -0,0 +1,10 @@
+package com.lh.app.tool;
+
+import android.content.Context;
+import android.widget.Toast;
+
+public class ToastUtil {
+ public static void show(Context context,String desc){
+ Toast.makeText(context, desc, Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/detail/DashboardViewModel.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/detail/DashboardViewModel.java
new file mode 100644
index 0000000..6c2c735
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/detail/DashboardViewModel.java
@@ -0,0 +1,19 @@
+package com.lh.app.ui.detail;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+
+public class DashboardViewModel extends ViewModel {
+
+ private final MutableLiveData mText;
+
+ public DashboardViewModel() {
+ mText = new MutableLiveData<>();
+ mText.setValue("This is dashboard fragment");
+ }
+
+ public LiveData getText() {
+ return mText;
+ }
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/detail/DetailFragment.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/detail/DetailFragment.java
new file mode 100644
index 0000000..e9907e1
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/detail/DetailFragment.java
@@ -0,0 +1,730 @@
+package com.lh.app.ui.detail;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.text.Html;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.TranslateAnimation;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.github.mikephil.charting.charts.LineChart;
+import com.lh.app.MyApplication;
+import com.lh.app.R;
+import com.lh.app.RoommateDBHelper;
+import com.lh.app.tool.LineChartManager;
+import com.lh.app.tool.ToastUtil;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+public class DetailFragment extends Fragment {
+ //室友名显示控件
+ private TextView tv_roommate1;
+ private TextView tv_roommate2;
+ private TextView tv_roommate3;
+ //室友名默认值
+ private final String name1_default="mate1";
+ private final String name2_default="mate2";
+ private final String name3_default="mate3";
+ //室友图片显示控件
+ private ImageView img_roma1;
+ private ImageView img_roma2;
+ private ImageView img_roma3;
+ //信息输入控件
+ private ScrollView scr_enterInfo;//输入信息布局
+ private EditText ed_name;
+ private EditText ed_sno;
+ private EditText ed_weight;
+ private EditText ed_height;
+ private EditText ed_birthday;
+ private EditText ed_hobby;
+ private CheckBox ckb_isMarried;
+ //其它控件
+ private TextView tv_showText;
+ private Button but_toSource;
+ private Button but_sure;
+ private Button but_clear;
+ private int select_mate=0;//选择的室友(1,2,3)
+
+ //控件双击控制
+ private long lastClickTime = 0;
+ //图像控件
+ private LineChart mLineChart;
+ //数据库帮助器对象实例
+ private final RoommateDBHelper mHelper = MyApplication.mHelper;
+ //活动视图
+ private View view;
+ //上下文
+ private Context context;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ //Fragment被创建时,被调用
+ super.onCreate(savedInstanceState);
+ Log.d("fragment_detail"," onCreate");
+
+
+ }
+
+ public View onCreateView(@NonNull LayoutInflater inflater,
+ ViewGroup container, Bundle savedInstanceState) {
+ Log.d("fragment_detail"," onCreateView");
+
+ //创建Fragment视图
+ view = inflater.inflate(R.layout.fragment_detail,container,false);
+
+ //获取上下文
+ context = getActivity();
+ //引入组件
+ inductUI();
+ //向组件中填充文本
+ initUI();
+ //设置组件监听器
+ addListenerToUI();
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ Log.d("fragment_detail","onActivityCreated");
+ }
+
+ @Override
+ public void onStart() {
+ //无焦点但可见时,调用此方法
+ super.onStart();
+ Log.d("fragment_detail"," onStart");
+ }
+
+ @Override
+ public void onPause() {
+ //Fragment失去焦点但可见时,调用此方法.
+ super.onPause();
+ Log.d("fragment_detail"," onPause");
+ }
+
+ @Override
+ public void onStop() {
+ //Fragment不可见时,调用此方法.
+ super.onStop();
+ Log.d("fragment_detail"," onStop");
+ }
+
+ @Override
+ public void onDestroyView() {
+ //当Fragment被移除时被调用
+ super.onDestroyView();
+ Log.d("fragment_detail"," onDestroyView");
+
+ }
+
+ //引入组件
+ private void inductUI(){
+ //引入标签
+ tv_roommate1 = view.findViewById(R.id.roommate1);
+ tv_roommate2 = view.findViewById(R.id.roommate2);
+ tv_roommate3 = view.findViewById(R.id.roommate3);
+ tv_showText = view.findViewById(R.id.text);
+
+ img_roma1 = view.findViewById(R.id.roommate1_img);
+ img_roma2 = view.findViewById(R.id.roommate2_img);
+ img_roma3 = view.findViewById(R.id.roommate3_img);
+
+ ed_name = view.findViewById(R.id.ed_name) ;
+ ed_sno = view.findViewById(R.id.ed_sno) ;
+ ed_weight = view.findViewById(R.id.ed_weight);
+ ed_height = view.findViewById(R.id.ed_height);
+ ed_birthday = view.findViewById(R.id.ed_birthday) ;
+ ed_hobby = view.findViewById(R.id.ed_hobby) ;
+ ckb_isMarried = view.findViewById(R.id.ckb_isMarried);
+
+ scr_enterInfo = view.findViewById(R.id.scr_enterIfo) ;
+
+ but_toSource = view.findViewById(R.id.source_button);
+ but_sure = view.findViewById(R.id.sure_but);
+ but_clear = view.findViewById(R.id.but_clear);
+
+ mLineChart = view.findViewById(R.id.show_img_curve);
+ }
+ /*初始化可见组件的样式*/
+ private void initUI() {
+ //判断舍友信息是否为空
+ if (!mHelper.query_roommateInfo_isEmpty()){
+ int pos1,pos2,pos3;
+ //查询数据库中舍友信息表中全部名字
+ List list_roommateName = mHelper.query_roommateName();
+ //将已录入人数的姓名显示在对应TextView上
+ switch (list_roommateName.size()){
+ case 1:
+ pos1 = mHelper.query_roommate_position(list_roommateName.get(0));
+ setName(pos1,list_roommateName.get(0));
+ break;
+ case 2:
+ pos1 = mHelper.query_roommate_position(list_roommateName.get(0));
+ pos2 = mHelper.query_roommate_position(list_roommateName.get(1));
+ setName(pos1,list_roommateName.get(0));
+ setName(pos2,list_roommateName.get(1));
+ break;
+ case 3:
+ pos1 = mHelper.query_roommate_position(list_roommateName.get(0));
+ pos2 = mHelper.query_roommate_position(list_roommateName.get(1));
+ pos3 = mHelper.query_roommate_position(list_roommateName.get(2));
+ setName(pos1,list_roommateName.get(0));
+ setName(pos2,list_roommateName.get(1));
+ setName(pos3,list_roommateName.get(2));
+ break;
+ }
+
+ }
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ private void addListenerToUI(){
+ //舍友姓名单击效果
+ tv_roommate1.setOnClickListener(this::onClick);
+ tv_roommate2.setOnClickListener(this::onClick);
+ tv_roommate3.setOnClickListener(this::onClick);
+ //舍友图片双击效果
+ img_roma1.setOnClickListener(this::onClick);
+ img_roma2.setOnClickListener(this::onClick);
+ img_roma3.setOnClickListener(this::onClick);
+ //添加图片长按效果
+ registerForContextMenu(img_roma1);
+ registerForContextMenu(img_roma2);
+ registerForContextMenu(img_roma3);
+ //"打回原型"按钮长按效果
+ but_toSource.setOnTouchListener(this::onTouch);
+ //信息录入“确认”按钮单击效果
+ but_sure.setOnClickListener(this::onClick);
+ //“清空数据”按钮单击效果
+ but_clear.setOnClickListener(this::onClick);
+ }
+ //组件点击
+ @SuppressLint("NonConstantResourceId")
+ private void onClick(View v){
+ switch (v.getId()){
+ case R.id.roommate1:
+ TextView_show(1, (String) tv_roommate1.getText());
+ break;
+ case R.id.roommate2:
+ TextView_show(2, (String) tv_roommate2.getText());
+ break;
+ case R.id.roommate3:
+ TextView_show(3, (String) tv_roommate3.getText());
+ break;
+
+ case R.id.roommate1_img:
+ clickImg(1);
+ break;
+ case R.id.roommate2_img:
+ clickImg(2);
+ break;
+ case R.id.roommate3_img:
+ clickImg(3);
+ break;
+ case R.id.sure_but:
+ sure_but_show();
+ break;
+ case R.id.but_clear:
+ //弹出弹框提示
+ showAlertDialog();
+ break;
+ }
+
+ }
+ //显示对话框提示
+ @SuppressLint("DiscouragedPrivateApi")
+ private void showAlertDialog(){
+ AlertDialog dialog = new AlertDialog.Builder(context)
+ .setIcon(R.drawable.img_delete)
+ .setTitle(Html.fromHtml("信息提示"))
+ .setMessage(Html.fromHtml("确认全删除?"))
+ .setPositiveButton("是", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int i) {//确认按钮监听事件
+ mHelper.deleteAll(RoommateDBHelper.getTableNameRoommateInfo());
+ ToastUtil.show(context,"删除完成");
+ switch (select_mate){
+ case 1:
+ tv_roommate1.setText(name1_default);
+ break;
+ case 2:
+ tv_roommate2.setText(name2_default);
+ break;
+ case 3:
+ tv_roommate3.setText(name3_default);
+ break;
+ }
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton("否", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int i) {//返回按钮监听事件
+ dialog.dismiss();
+ }
+ })
+ .create();
+ dialog.show();
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(Color.BLACK);
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextSize(25);
+ dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setTextColor(Color.BLUE);
+ dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setTextSize(25);
+ }
+ //"打回原型"按钮被按下
+ @SuppressLint("UseCompatLoadingForDrawables")
+ private boolean onTouch(View view, MotionEvent event) {
+ if(view.getId() == R.id.source_button){
+ if (event.getAction() == MotionEvent.ACTION_DOWN){
+ if(select_mate==1) img_roma1.setImageDrawable(context.getDrawable(R.drawable.img_dog1));
+ else if(select_mate==2) img_roma2.setImageDrawable(context.getDrawable(R.drawable.img_dog3));
+ else if(select_mate==3) img_roma3.setImageDrawable(context.getDrawable(R.drawable.img_dog2));
+ }
+ else if(event.getAction() == MotionEvent.ACTION_UP){
+ if(select_mate==1) img_roma1.setImageDrawable(context.getDrawable(R.drawable.hyt));
+ else if(select_mate==2) img_roma2.setImageDrawable(context.getDrawable(R.drawable.wyz1));
+ else if(select_mate==3) img_roma3.setImageDrawable(context.getDrawable(R.drawable.wz));
+ }
+
+ }
+ return false;
+ }
+
+ //信息录入"确认"按钮点击事件
+ private void sure_but_show() {
+ //判断输入是否有空
+ if(TextUtils.isEmpty(ed_name.getText()) || TextUtils.isEmpty(ed_sno.getText()) ||
+ TextUtils.isEmpty(ed_height.getText()) || TextUtils.isEmpty(ed_weight.getText())||
+ TextUtils.isEmpty(ed_birthday.getText()) || TextUtils.isEmpty(ed_hobby.getText()))
+ {
+ Toast.makeText(context,"输入不能有空!",Toast.LENGTH_SHORT).show();
+ }
+ else{
+ //设置舍友信息
+ Roommate roommate = new Roommate();
+ roommate.setName(String.valueOf(ed_name.getText()));
+ roommate.setId(String.valueOf(ed_sno.getText()));
+ roommate.setHeight(Integer.parseInt(String.valueOf(ed_height.getText())));
+ roommate.setWeight(Float.parseFloat(String.valueOf(ed_weight.getText())));
+ roommate.setBirthday(String.valueOf(ed_birthday.getText()));
+ roommate.setMarried(ckb_isMarried.isChecked());
+ roommate.setHobby(String.valueOf(ed_hobby.getText()));
+ //保存舍友信息到键值对中
+ ContentValues values = new ContentValues();
+ values.put("name",roommate.getName());
+ values.put("id",roommate.getId());
+ values.put("height",roommate.getHeight());
+ values.put("weight",roommate.getWeight());
+ values.put("married",roommate.isMarried());
+ values.put("birthday",roommate.getBirthday());
+ values.put("hobby",roommate.getHobby());
+ values.put("position",select_mate);
+ //若是第一次录入内容,添加数据
+ if(isFirstInput()) {
+ //保存键值对到数据库中的舍友信息表中
+ mHelper.insert(RoommateDBHelper.getTableNameRoommateInfo(),values);
+ }
+ //若非第一次录入内容,修改数据
+ else {
+ String name = getName();
+ long row = mHelper.update_roommateInfo(values,name);
+ Log.d("UpdateMemoTable","修改备忘录"+row+"条");
+ }
+ //设置指定textview的text
+ setTextToTV(roommate.getName());
+ //隐藏输入框
+ scr_enterInfo.setVisibility(View.GONE);
+ }
+ }
+
+ //舍友名字点击事件
+ @SuppressLint("SetTextI18n")
+ private void TextView_show(int i, String name) {
+ //选择室友序号
+ select_mate = i;
+ //设置字体颜色
+ changeTextColor();
+ //隐藏信息录入
+ scr_enterInfo.setVisibility(View.GONE);
+ //曲线显示隐藏
+ mLineChart.setVisibility(View.GONE);
+ //清空文本域
+ tv_showText.setText("");
+ //室友信息弹出动画
+ startAnim();
+ //判断舍友姓名是否为空
+ if (name.equals("mate"+i)) return;
+ //读取数据库信息
+ Roommate roommate = mHelper.query_roommateInfo_ByName(name);
+ //写入具体内容
+ int[][] bodyRhythm = body_rhythm(roommate.getBirthday());
+ tv_showText.setText("姓名:"+roommate.getName()+"\n学号:"+roommate.getId()+"身高:"+roommate.getHeight()+
+ "\n体重:"+roommate.getWeight()+"\n生日:"+ roommate.getBirthday()+"\n爱好:"+roommate.getHobby()+
+ "\n\n人体生物节律\n 已运行周期数 新周期开始天数"+
+ "\n智力: "+bodyRhythm[0][0]+" "+bodyRhythm[1][0]+
+ "\n情绪: "+bodyRhythm[0][1]+" "+bodyRhythm[1][1]+
+ "\n体力: "+bodyRhythm[0][2]+" "+bodyRhythm[1][2], TextView.BufferType.EDITABLE);
+ }
+ //改变文本颜色
+ public void changeTextColor() {
+ //设置字体变换颜色
+ int[] color = new int[2];
+ color[0] = Color.parseColor("#64E14B");
+ color[1] = Color.parseColor("#2B2B2B");
+ //改变字体颜色
+ if(select_mate == 1){
+ tv_roommate1.setTextColor(color[0]);
+ tv_roommate2.setTextColor(color[1]);
+ tv_roommate3.setTextColor(color[1]);
+ }else if(select_mate == 2){
+ tv_roommate1.setTextColor(color[1]);
+ tv_roommate2.setTextColor(color[0]);
+ tv_roommate3.setTextColor(color[1]);
+ }else{
+ tv_roommate1.setTextColor(color[1]);
+ tv_roommate2.setTextColor(color[1]);
+ tv_roommate3.setTextColor(color[0]);
+ }
+ }
+ //文本框弹出效果
+ private void startAnim() {
+ //设置动画,从自身位置的最下端向上滑动了自身的高度,持续时间为500ms
+ final TranslateAnimation ctrlAnimation = new TranslateAnimation(
+ TranslateAnimation.RELATIVE_TO_SELF, 0, TranslateAnimation.RELATIVE_TO_SELF, 0,
+ TranslateAnimation.RELATIVE_TO_SELF, 1, TranslateAnimation.RELATIVE_TO_SELF, 0);
+ ctrlAnimation.setDuration(500l); //设置动画的过渡时间
+ tv_showText.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ tv_showText.setVisibility(View.VISIBLE);
+ tv_showText.startAnimation(ctrlAnimation);
+ }
+ }, 000);
+ }
+ //点击图片事件
+ private void clickImg(int i) {
+ //获取当前系统当前毫秒数
+ long currentTimeMills = SystemClock.uptimeMillis();
+ //两次间隔小于300ms代表双击
+ if(currentTimeMills-lastClickTime<300){
+ //改变选择的照片
+ select_mate = i;
+ //改变文字颜色
+ changeTextColor();
+ //舍友录入信息清空
+ setInputTextEmpty();
+ //隐藏曲线显示、文本显示
+ tv_showText.setVisibility(View.GONE);
+ mLineChart.setVisibility(View.GONE);
+ //显示输入框
+ scr_enterInfo.setVisibility(View.VISIBLE);
+ textShow();
+ return;
+ }
+ //更新最后一次点击时间
+ lastClickTime = currentTimeMills;
+ }
+ //清空输入文本和显示文本域
+ private void setInputTextEmpty() {
+ tv_showText.setText("");
+ ed_name.setText("");
+ ed_sno.setText("");
+ ed_weight.setText("");
+ ed_height.setText("");
+ ed_hobby.setText("");
+ ed_birthday.setText("");
+ ckb_isMarried.setChecked(false);
+ }
+
+ //设置指定textView的text
+ private void setTextToTV(String name) {
+ switch (select_mate){
+ case 1:
+ tv_roommate1.setText(name);
+ break;
+ case 2:
+ tv_roommate2.setText(name);
+ break;
+ case 3:
+ tv_roommate3.setText(name);
+ break;
+ }
+ }
+ //向信息录入中填充文本信息
+ private void textShow() {
+ //若未给室友录入信息,弹出清空后的输入界面
+ if (isFirstInput()) {
+ setInputTextEmpty();
+ return;
+ }
+ Roommate roommate = new Roommate();
+ //判断点击室友名,从数据库中取出数据
+ switch (select_mate){
+ case 1:
+ if ((tv_roommate1.getText()).equals(name1_default)) return;
+ roommate = mHelper.query_roommateInfo_ByName((String) tv_roommate1.getText());
+ break;
+ case 2:
+ if ((tv_roommate2.getText()).equals(name2_default)) return;
+ roommate = mHelper.query_roommateInfo_ByName((String) tv_roommate2.getText());
+ break;
+ case 3:
+ if ((tv_roommate3.getText()).equals(name3_default)) return;
+ roommate = mHelper.query_roommateInfo_ByName((String) tv_roommate3.getText());
+ break;
+ }
+ //填充数据
+ ed_name.setText(roommate.getName());
+ ed_sno.setText(roommate.getId());
+ ed_weight.setText(String.valueOf(roommate.getWeight()));
+ ed_height.setText(String.valueOf(roommate.getHeight()));
+ ed_birthday.setText(roommate.getBirthday());
+ ed_hobby.setText(roommate.getHobby());
+ ckb_isMarried.setChecked(roommate.isMarried());
+ }
+ //是否是第一次为室友录入信息或是否为该室友录入信息
+ public boolean isFirstInput() {
+ switch (select_mate){
+ case 1:
+ if (name1_default.contentEquals(tv_roommate1.getText())) return true;
+ break;
+ case 2:
+ if (name2_default.contentEquals(tv_roommate2.getText())) return true;
+ break;
+ case 3:
+ if (name3_default.contentEquals(tv_roommate3.getText())) return true;
+ break;
+ }
+ return false;
+ }
+ //得到指定室友在textView上的名字
+ public String getName() {
+ switch (select_mate){
+ case 1:
+ return (String) tv_roommate1.getText();
+ case 2:
+ return (String) tv_roommate2.getText();
+ case 3:
+ return (String) tv_roommate3.getText();
+ }
+ return "mate";
+ }
+ //填充文本框文本
+ private void setName(int pos,String name) {
+ switch (pos){
+ case 1:
+ tv_roommate1.setText(name);
+ break;
+ case 2:
+ tv_roommate2.setText(name);
+ break;
+ case 3:
+ tv_roommate3.setText(name);
+ break;
+ }
+ }
+
+ //长按显示菜单
+ @SuppressLint("NonConstantResourceId")
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ //子菜单部分
+ MenuInflater inflater = new MenuInflater(context);
+ inflater.inflate(R.menu.menu_sub,menu);
+ switch (v.getId()){
+ case R.id.roommate1_img:
+ select_mate = 1;
+ changeTextColor();
+ break;
+ case R.id.roommate2_img:
+ select_mate = 2;
+ changeTextColor();
+ break;
+ case R.id.roommate3_img:
+ select_mate = 3;
+ changeTextColor();
+ break;
+ default:
+ break;
+ }
+
+ super.onCreateContextMenu(menu, v, menuInfo);
+ }
+ //上下文菜单被点击时触发该方法
+ @SuppressLint("NonConstantResourceId")
+ @Override
+ public boolean onContextItemSelected(@NonNull MenuItem item) {
+ switch (item.getItemId()){
+ case R.id.item_revise_introduce:
+ //填充内容,显示输入
+ textShow();
+ mLineChart.setVisibility(View.GONE);
+ tv_showText.setVisibility(View.GONE);
+ scr_enterInfo.setVisibility(View.VISIBLE);
+ break;
+ case R.id.item_show_curve:
+ //判断是否给舍友录入信息
+ if(isFirstInput()) {
+ ToastUtil.show(context, "没有信息!");
+ return true;
+ }
+ show_body_rhythm_curve();
+ mLineChart.setVisibility(View.VISIBLE);
+ scr_enterInfo.setVisibility(View.GONE);
+ tv_showText.setVisibility(View.GONE);
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+ //显示曲线图
+ private void show_body_rhythm_curve() {
+ Roommate roommate = mHelper.query_roommateInfo_ByName(getName());
+ String birthday = roommate.getBirthday();
+ int[][] body_rhythm_data = body_rhythm_month(birthday);
+ List list_name = new ArrayList<>();
+ List list_color = new ArrayList<>();
+ list_name.add("智力");
+ list_name.add("情绪");
+ list_name.add("体力");
+ list_color.add(Color.BLACK);
+ list_color.add(Color.GREEN);
+ list_color.add(Color.BLUE);
+ LineChartManager manager = new LineChartManager(mLineChart,list_name,list_color);
+ //设置图像显示内容
+ manager.setDescription("月份");
+ manager.setYAxis(60,-60,30);
+ manager.setHightLimitLine(30,"最高",Color.BLACK);
+ List list_numbers = new ArrayList<>();
+ for(int i=0;i<31;i++){
+ list_numbers.add((float) body_rhythm_data[1][i]);
+ list_numbers.add((float) body_rhythm_data[2][i]);
+ list_numbers.add((float) body_rhythm_data[3][i]);
+ manager.addEntry(list_numbers);
+ list_numbers.clear();
+ }
+ }
+
+ /**
+ * 根据生日计算人体生物节律
+ * @param birthday 生日 格式: 2000.08.04
+ * @return 人体生命节律值 body_rhythm【【已运行周期天数(智力、情绪、体力)】【开始周期运行天数(智力、情绪、体力)】】
+ * */
+ private int[][] body_rhythm(String birthday){
+ int[][] body_rhythm = new int[2][3];
+ String bornYear,bornMonth,bornDay;
+ //计算生日年-月-日 统一格式
+ String[] bornData = birthday.split("\\.+");
+ bornYear = bornData[0];
+ bornMonth = bornData[1];
+ bornDay = bornData[2];
+ String endTime = bornYear+"-"+bornMonth+"-"+bornDay;
+ //计算当前年-月-日 统一格式
+ Calendar calendar = Calendar.getInstance();
+ int nowYear = calendar.get(Calendar.YEAR); //年
+ int nowMonth = calendar.get(Calendar.MONTH)+1; //月
+ int nowDay = calendar.get(Calendar.DAY_OF_MONTH); //日
+ String startTime = nowYear+"-"+nowMonth+"-"+nowDay;
+ //计算当前时间与出生相隔天数
+ SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd");
+ long nd = 1000*24*60*60;//一天的毫秒数
+ long diff = 0;
+ try {
+ //getTime()返回毫秒数
+ diff = sd.parse(startTime).getTime() - sd.parse(endTime).getTime();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ long days = diff/nd;//计算相差多少天
+
+ //使用公式计算智力(IT)、情绪(mood)、体力(HP)节律周期(cycle)天数
+ body_rhythm[0][0] = (int) (days/32);
+ body_rhythm[0][1] = (int) (days/28);
+ body_rhythm[0][2] = (int) (days/23);
+
+ //使用公式计算智力(IT)、情绪(mood)、体力(HP)节律新周期运行天数
+ body_rhythm[1][0] = (int) (days%32);
+ body_rhythm[1][1] = (int) (days%28);
+ body_rhythm[1][2] = (int) (days%23);
+
+ return body_rhythm;
+ }
+
+ /**
+ * 根据生日计算到当月的人体生物节律
+ * @param birthday 生日 格式: 2000.08.04
+ * @return 人体生命节律值 body_rhythm【【已运行周期天数(智力、情绪、体力)】【开始周期运行天数(智力、情绪、体力)】】
+ * */
+ private int[][] body_rhythm_month(String birthday){
+ int[][] body_rhythm = new int[4][32];
+ String bornYear,bornMonth,bornDay;
+ //计算生日年-月-日 统一格式
+ String[] bornData = birthday.split("\\.+");
+ bornYear = bornData[0];
+ bornMonth = bornData[1];
+ bornDay = bornData[2];
+ String endTime = bornYear+"-"+bornMonth+"-"+bornDay;
+ //计算当前年-月-日 统一格式
+ Calendar calendar = Calendar.getInstance();
+ int nowYear = calendar.get(Calendar.YEAR); //年
+ int nowMonth = calendar.get(Calendar.MONTH)+1; //月
+ int nowDay = 1;
+ String startTime;
+ while (nowDay<30){
+ startTime = nowYear+"-"+nowMonth+"-"+nowDay;
+ //计算当前时间与出生相隔天数
+ SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd");
+ long nd = 1000*24*60*60;//一天的毫秒数
+ long diff = 0;
+ try {
+ //getTime()返回毫秒数
+ diff = sd.parse(startTime).getTime() - sd.parse(endTime).getTime();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ long days = diff/nd;//计算相差多少天
+
+ //使用公式计算智力(IT)、情绪(mood)、体力(HP)节律周期(cycle)天数
+ body_rhythm[0][0] = (int) (days/32);
+ body_rhythm[0][1] = (int) (days/28);
+ body_rhythm[0][2] = (int) (days/23);
+
+ //使用公式计算智力(IT)、情绪(mood)、体力(HP)节律新周期运行天数
+ body_rhythm[1][nowDay-1] = (int) (days%32);
+ body_rhythm[2][nowDay-1] = (int) (days%28);
+ body_rhythm[3][nowDay-1] = (int) (days%23);
+
+ nowDay++;
+ }
+ return body_rhythm;
+ }
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/detail/Roommate.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/detail/Roommate.java
new file mode 100644
index 0000000..2426fdf
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/detail/Roommate.java
@@ -0,0 +1,78 @@
+package com.lh.app.ui.detail;
+
+
+public class Roommate {
+ private String name;
+ private String id;
+ private long height;//单位cm
+ private float weight;//单位kg
+ private boolean married;
+ private String birthday;
+ private String hobby;
+ public Roommate(){ }
+
+ public Roommate(String name, String id, long height, float weight, boolean married, String birthday, String hobby) {
+ this.name = name;
+ this.id = id;
+ this.height = height;
+ this.weight = weight;
+ this.married = married;
+ this.birthday = birthday;
+ this.hobby = hobby;
+ }
+
+ public String getName() {
+ return name;
+ }
+ public String getId() {
+ return id;
+ }
+ public long getHeight() {
+ return height;
+ }
+ public float getWeight() {
+ return weight;
+ }
+ public boolean isMarried() {
+ return married;
+ }
+ public String getHobby() {
+ return hobby;
+ }
+ public String getBirthday() {
+ return birthday;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ public void setId(String id) {
+ this.id = id;
+ }
+ public void setHeight(long height) {
+ this.height = height;
+ }
+ public void setWeight(float weight) {
+ this.weight = weight;
+ }
+ public void setMarried(boolean married) {
+ this.married = married;
+ }
+ public void setHobby(String hobby) {
+ this.hobby = hobby;
+ }
+ public void setBirthday(String birthday) {
+ this.birthday = birthday;
+ }
+
+ @Override
+ public String toString() {
+ return "User{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", height=" + height +
+ ", weight=" + weight +
+ ", married=" + married +
+ '}';
+ }
+}
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/Honor.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/Honor.java
new file mode 100644
index 0000000..c5939f7
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/Honor.java
@@ -0,0 +1,40 @@
+package com.lh.app.ui.honor;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+
+import androidx.core.content.FileProvider;
+
+import java.io.FileNotFoundException;
+
+/**
+ * Created by LiShiyao on 2022.11.17
+ */
+public class Honor {
+ private String content;
+ private String imageLocation;
+
+ public Honor(){}
+
+ public Honor(String content, String imageLocation) {
+ this.content = content;
+ this.imageLocation = imageLocation;
+ }
+ public String getContent() {
+ return content;
+ }
+
+ public String getImageLocation() {
+ return imageLocation;
+ }
+
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public void setImageLocation(String imageLocation) {
+ this.imageLocation = imageLocation;
+ }
+}
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/HonorAdapter.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/HonorAdapter.java
new file mode 100644
index 0000000..3ed3ee1
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/HonorAdapter.java
@@ -0,0 +1,99 @@
+package com.lh.app.ui.honor;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.core.content.FileProvider;
+import androidx.recyclerview.widget.RecyclerView;
+
+
+import com.bumptech.glide.Glide;
+import com.lh.app.R;
+import com.lh.app.tool.ImageUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class HonorAdapter extends RecyclerView.Adapter {
+ private List mHonorList;
+ public static List selectHonor = new ArrayList<>();
+ private Context mContext;
+
+ static class ViewHolder extends RecyclerView.ViewHolder{
+ View view;
+ ImageView imageImage;
+ TextView tv_describe;
+ CheckBox ckb_honorItem;
+ public ViewHolder(View view){
+ super(view);
+ this.view = view;
+ imageImage = (ImageView) view.findViewById(R.id.img_honor);
+ tv_describe = (TextView) view.findViewById(R.id.tv_describe);
+ ckb_honorItem = view.findViewById(R.id.ckb_honorItem);
+ }
+ }
+ public HonorAdapter(List honorList,Context context){
+ mHonorList = honorList;
+ mContext = context;
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view;
+ view = LayoutInflater.from(parent.getContext()).inflate(R.layout.honor_item,parent,false);
+ final ViewHolder holder = new ViewHolder(view);
+
+ //list的点击事件
+ holder.view.setOnClickListener(view1 -> {
+ int position = holder.getAdapterPosition();
+ Honor honor = mHonorList.get(position);
+ Toast.makeText(view.getContext(),"you clicked view " +position,Toast.LENGTH_SHORT).show();
+ });
+
+ return holder;
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ Honor honor = mHonorList.get(position);
+ Uri uri = Uri.parse(honor.getImageLocation());
+ Glide.with(mContext)
+ .load(uri)
+ .error(R.drawable.ic_no_picture)
+ .into(holder.imageImage);
+
+ holder.tv_describe.setText(honor.getContent());
+
+ //设置单选框监听器
+ holder.ckb_honorItem.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if(isChecked) selectHonor.add(honor);
+ else selectHonor.remove(honor);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return mHonorList.size();
+ }
+
+}
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/HonorFragment.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/HonorFragment.java
new file mode 100644
index 0000000..103329f
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/HonorFragment.java
@@ -0,0 +1,266 @@
+package com.lh.app.ui.honor;
+
+import static com.lh.app.tool.ImageUtils.uriToBitmap;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.InputType;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.activity.result.ActivityResult;
+import androidx.activity.result.ActivityResultCallback;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.NonNull;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.lh.app.MyApplication;
+import com.lh.app.R;
+import com.lh.app.RoommateDBHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+;
+
+public class HonorFragment extends Fragment {
+ private static final int REQUEST_CODE_PERMISSION = 100;
+ private List mHonorList = new ArrayList<>();
+ private RecyclerView recyclerView;
+ private View view;
+ private Context context;
+ private Button but_addHonor;
+ private ImageView img_imageHonor;
+ private EditText edt_describe;
+ private LinearLayout ll_inputHonor;
+ private Button but_backHonor;
+ private Button but_sureHonor;
+ private Button but_deleteHonor;
+ private TextView tv_emptyHonor;
+ private ActivityResultLauncher mFromAlumn;
+
+ private Image checkImage = null;
+
+ private HonorAdapter adapter;
+ //数据库帮助器对象实例
+ private RoommateDBHelper mHelper = MyApplication.mHelper;
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Log.d("ning","Communicate Fragment onCreate");
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater,
+ ViewGroup container, Bundle savedInstanceState) {
+ view = inflater.inflate(R.layout.fragment_honor,container,false);
+ context = getActivity();
+ addUI_Listener();
+ text_Control();
+ initList();
+ setRecycleView();
+ return view;
+ }
+
+
+
+ @Override
+ public void onStart() {
+ //无焦点但可见时,调用此方法
+ super.onStart();
+ Log.d("HonorActivity","onStart");
+ }
+
+ @Override
+ public void onPause() {
+ //Fragment失去焦点但可见时,调用此方法.
+ super.onPause();
+ }
+
+ @Override
+ public void onStop() {
+ //Fragment不可见时,调用此方法.
+ super.onStop();
+ }
+
+ @Override
+ public void onDestroyView() {
+ //当Fragment被移除时被调用
+ super.onDestroyView();
+ }
+
+ //对文本进行控制
+ private void text_Control(){
+ edt_describe.setInputType(InputType.TYPE_TEXT_FLAG_MULTI_LINE);//多行文本输入
+ edt_describe.setGravity(Gravity.TOP);//文本显示在最上方
+ edt_describe.setSingleLine(false);//改变默认单行模式
+ edt_describe.setHorizontallyScrolling(false);//取消水平滚动
+ }
+
+ @SuppressLint("CutPasteId")
+ private void addUI_Listener() {
+ but_addHonor = view.findViewById(R.id.but_addHonor);
+ img_imageHonor = view.findViewById(R.id.img_imgHonor);
+ edt_describe = view.findViewById(R.id.edt_contentHonor);
+ ll_inputHonor = view.findViewById(R.id.ll_inputHonor);
+ but_backHonor = view.findViewById(R.id.but_backHonor);
+ but_sureHonor = view.findViewById(R.id.but_sureHonor);
+ but_deleteHonor = view.findViewById(R.id.but_removeHonor);
+ tv_emptyHonor = view.findViewById(R.id.tv_emptyHonor);
+
+ but_addHonor.setOnClickListener(this::onClick);
+ img_imageHonor.setOnClickListener(this::onClick);
+ but_backHonor.setOnClickListener(this::onClick);
+ but_sureHonor.setOnClickListener(this::onClick);
+ but_deleteHonor.setOnClickListener(this::onClick);
+
+ //选择图片
+ mFromAlumn = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback() {
+ @Override
+ public void onActivityResult(ActivityResult result) {
+ //用户取消选择图片
+ if (result.getResultCode() == Activity.RESULT_OK) {
+ //用户选择了图片
+ assert result.getData() != null;
+ Uri uri = result.getData().getData();
+ checkImage = new Image(uriToBitmap(context,uri),uri.toString());
+ img_imageHonor.setImageBitmap(checkImage.getImage());
+ // 获取持久化权限
+ context.getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ }
+ }
+ });
+ }
+
+ @SuppressLint("NonConstantResourceId")
+ private void onClick(View view) {
+ switch (view.getId()){
+ case R.id.but_backHonor:
+ recyclerView.setVisibility(View.VISIBLE);
+ ll_inputHonor.setVisibility(View.GONE);
+ break;
+ case R.id.but_addHonor:
+ recyclerView.setVisibility(View.GONE);
+ ll_inputHonor.setVisibility(View.VISIBLE);
+ img_imageHonor.setImageResource(R.drawable.img_add);
+ break;
+ case R.id.but_removeHonor:
+ remove();
+ break;
+ case R.id.img_imgHonor:
+ //检查权限
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(this.getActivity(),
+ new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ REQUEST_CODE_PERMISSION);
+ }
+ else {
+ //选择图片
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ // 指定显示图片
+ intent.setType("image/*");
+ mFromAlumn.launch(intent);
+ }
+ break;
+ case R.id.but_sureHonor:
+ if (edt_describe.getText().toString().trim().equals("")){
+ Toast.makeText(context,"描述不能有空!",Toast.LENGTH_SHORT).show();
+ }
+ else {
+ Honor honor = new Honor(String.valueOf(edt_describe.getText()),checkImage.getLocation());
+ //保存到数据库中
+ if (mHelper.insert_honor(honor) != -1){
+ Toast.makeText(context,"添加成功!",Toast.LENGTH_SHORT).show();
+ }
+ else Toast.makeText(context,"添加失败!",Toast.LENGTH_SHORT).show();
+
+ ll_inputHonor.setVisibility(View.GONE);
+ recyclerView.setVisibility(View.VISIBLE);
+ add(0, honor);
+ }
+ break;
+ }
+ }
+
+ /*初始化列表*/
+ private void initList() {
+ mHonorList.clear();
+ List honorList = mHelper.query_honor_All();
+ if (!honorList.isEmpty()){
+ mHonorList.addAll(honorList);
+ //隐藏空提示
+ tv_emptyHonor.setVisibility(View.GONE);
+ }
+ }
+
+ private void setRecycleView() {
+ adapter = new HonorAdapter(mHonorList,context);
+ recyclerView = view.findViewById(R.id.rv_honor);
+ LinearLayoutManager layoutManager = new LinearLayoutManager(context,LinearLayoutManager.VERTICAL,false);
+ recyclerView.setLayoutManager(layoutManager);
+
+ recyclerView.setAdapter(adapter);
+ }
+ /*添加子项*/
+ private void add(int position, Honor honor){
+ Log.d("Roommate_activity","添加子项!");
+
+ //tv_empty.setVisibility(View.GONE);
+ mHonorList.add(position,honor);
+ adapter.notifyItemInserted(position);
+ //隐藏空提示
+ tv_emptyHonor.setVisibility(View.GONE);
+ }
+ /*删除子项*/
+ @SuppressLint("NotifyDataSetChanged")
+ private void remove(){
+ for(Honor honor:HonorAdapter.selectHonor){
+ mHonorList.remove(honor);
+ mHelper.delete_honorByLocation(honor.getImageLocation());
+ }
+ //更新RecycleView
+ adapter.notifyDataSetChanged();
+ //将选中子项列表清空
+ HonorAdapter.selectHonor.clear();
+ //若子项全被清空,则出现“没有数据”提示
+ if(mHonorList.isEmpty()) tv_emptyHonor.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == REQUEST_CODE_PERMISSION) {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ // 权限已授予
+ onClick(img_imageHonor);
+ } else {
+ // 权限被拒绝
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/Image.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/Image.java
new file mode 100644
index 0000000..7a1ae20
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/Image.java
@@ -0,0 +1,31 @@
+package com.lh.app.ui.honor;
+
+import android.graphics.Bitmap;
+
+/** 图像信息类 */
+public class Image {
+
+ private Bitmap image;
+ private String location;
+
+ public Image(Bitmap image, String location) {
+ this.image = image;
+ this.location = location;
+ }
+
+ public Bitmap getImage() {
+ return image;
+ }
+
+ public void setImage(Bitmap image) {
+ this.image = image;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ public void setLocation(String location) {
+ this.location = location;
+ }
+}
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/ImageTools.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/ImageTools.java
new file mode 100644
index 0000000..d1eb666
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/ImageTools.java
@@ -0,0 +1,82 @@
+package com.lh.app.ui.honor;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ImageTools {
+
+ /**
+ * 通过uri获取图片并进行压缩
+ *
+ * @param uri
+ * @param activity
+ * @return
+ * @throws IOException
+ */
+ public static Bitmap getBitmapFromUri(Uri uri, Activity activity) throws IOException {
+ InputStream inputStream = activity.getContentResolver().openInputStream(uri);
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ options.inDither = true;
+ options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ BitmapFactory.decodeStream(inputStream, null, options);
+ inputStream.close();
+
+ int originalWidth = options.outWidth;
+ int originalHeight = options.outHeight;
+ if (originalWidth == -1 || originalHeight == -1) {
+ return null;
+ }
+
+ float height = 800f;
+ float width = 480f;
+ int be = 1; //be=1表示不缩放
+ if (originalWidth > originalHeight && originalWidth > width) {
+ be = (int) (originalWidth / width);
+ } else if (originalWidth < originalHeight && originalHeight > height) {
+ be = (int) (originalHeight / height);
+ }
+
+ if (be <= 0) {
+ be = 1;
+ }
+ BitmapFactory.Options bitmapOptinos = new BitmapFactory.Options();
+ bitmapOptinos.inSampleSize = be;
+ bitmapOptinos.inDither = true;
+ bitmapOptinos.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ inputStream = activity.getContentResolver().openInputStream(uri);
+
+ Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, bitmapOptinos);
+ inputStream.close();
+
+ return compressImage(bitmap);
+ }
+
+ /**
+ * 质量压缩方法
+ *
+ * @param bitmap
+ * @return
+ */
+ public static Bitmap compressImage(Bitmap bitmap) {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream);
+ int options = 100;
+ while (byteArrayOutputStream.toByteArray().length / 1024 > 100) {
+ byteArrayOutputStream.reset();
+ //第一个参数 :图片格式 ,第二个参数: 图片质量,100为最高,0为最差 ,第三个参数:保存压缩后的数据的流
+ bitmap.compress(Bitmap.CompressFormat.JPEG, options, byteArrayOutputStream);
+ options -= 10;
+ }
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
+ Bitmap bitmapImage = BitmapFactory.decodeStream(byteArrayInputStream, null, null);
+ return bitmapImage;
+ }
+}
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/NotificationsViewModel.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/NotificationsViewModel.java
new file mode 100644
index 0000000..cbb3ac1
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/honor/NotificationsViewModel.java
@@ -0,0 +1,19 @@
+package com.lh.app.ui.honor;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+
+public class NotificationsViewModel extends ViewModel {
+
+ private final MutableLiveData mText;
+
+ public NotificationsViewModel() {
+ mText = new MutableLiveData<>();
+ mText.setValue("This is notifications fragment");
+ }
+
+ public LiveData getText() {
+ return mText;
+ }
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/AddMemo_activity.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/AddMemo_activity.java
new file mode 100644
index 0000000..a0b7cca
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/AddMemo_activity.java
@@ -0,0 +1,228 @@
+package com.lh.app.ui.memo;
+
+import android.annotation.SuppressLint;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.text.InputType;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.TextClock;
+import android.widget.Toast;
+
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.lh.app.MainActivity;
+import com.lh.app.MyApplication;
+import com.lh.app.R;
+import com.lh.app.RoommateDBHelper;
+import com.lh.app.tool.NotificationUtil;
+
+public class AddMemo_activity extends AppCompatActivity {
+
+ private TextClock clock;
+ private EditText title_edt;
+ private EditText content_edt;
+ private Button back_button;
+ private Button success_button;
+ private RadioGroup group;
+ private String select_roommateName = "全部";//选择室友
+
+ public static boolean is_backData = false;//是否带回数据
+ public static Memo jumpMemo = null; //修改前对应的Memo
+ public static boolean is_revise = false;//是否修改数据
+ //数据库帮助器对象实例
+ private RoommateDBHelper mHelper = MyApplication.mHelper;
+
+ @SuppressLint("NonConstantResourceId")
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.add_memo_activity_layout);
+
+ addUI();
+ //判断点击打开页面来源,以下来自子项的点击,即修改内容
+ if(is_revise){
+ //获取传入数据
+ jumpMemo = Roommate_memo_adapter.jumpMemo;
+ //对控件文本及状态进行填充
+ title_edt.setText(jumpMemo.getTitle());
+ content_edt.setText(jumpMemo.getContent());
+ switch (jumpMemo.getName()){
+ case "全部":
+ group.check(R.id.btn_all);
+ break;
+ case "刘治浩":
+ group.check(R.id.btn_lzh);
+ break;
+ case "谭宗辉":
+ group.check(R.id.btn_tzh);
+ break;
+ case "樊彪":
+ group.check(R.id.btn_fb);
+ break;
+ case "李石尧":
+ group.check(R.id.btn_lsy);
+ break;
+ }
+ }
+ else {
+ title_edt.setText("");
+ content_edt.setText("");
+ }
+ text_Control();
+ Menu_part();
+ }
+
+ /*添加组件*/
+ @SuppressLint("NonConstantResourceId")
+ private void addUI(){
+ back_button = (Button) findViewById(R.id.back_button);
+ success_button = (Button)findViewById(R.id.success_button) ;
+ title_edt = (EditText)findViewById(R.id.title_edt) ;
+ content_edt = (EditText) findViewById(R.id.content_edt);
+ clock = (TextClock) findViewById(R.id.clock);
+ group = (RadioGroup)findViewById(R.id.radioGroup) ;
+
+ /*对菜单栏按钮点击事件监听*/
+ back_button.setOnClickListener(this::onclick);
+ success_button.setOnClickListener(this::onclick);
+ group.setOnCheckedChangeListener(radioChangeListener);
+ }
+ //RadioGroup监听器
+ @SuppressLint("NonConstantResourceId")
+ private final RadioGroup.OnCheckedChangeListener radioChangeListener = new RadioGroup.OnCheckedChangeListener(){
+ @Override
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ switch (checkedId) {
+ case R.id.btn_all:
+ RadioButton radioButton_01 = AddMemo_activity.this.findViewById(R.id.btn_all);
+ select_roommateName = radioButton_01.getText().toString();
+ break;
+ case R.id.btn_lzh:
+ RadioButton radioButton_02 = AddMemo_activity.this.findViewById(R.id.btn_lzh);
+ select_roommateName = radioButton_02.getText().toString();
+ break;
+ case R.id.btn_tzh:
+ RadioButton radioButton_03 = AddMemo_activity.this.findViewById(R.id.btn_tzh);
+ select_roommateName = radioButton_03.getText().toString();
+ break;
+ case R.id.btn_fb:
+ RadioButton radioButton_04 = AddMemo_activity.this.findViewById(R.id.btn_fb);
+ select_roommateName = radioButton_04.getText().toString();
+ break;
+ case R.id.btn_lsy:
+ RadioButton radioButton_05 = AddMemo_activity.this.findViewById(R.id.btn_lsy);
+ select_roommateName = radioButton_05.getText().toString();
+ break;
+ }
+ Toast.makeText(AddMemo_activity.this, "name:" + select_roommateName, Toast.LENGTH_SHORT).show();
+ }
+ };
+ //按钮点击事件
+ @SuppressLint("NonConstantResourceId")
+ private void onclick(View view){
+ switch (view.getId()){
+ case R.id.back_button:
+ is_backData = false;
+ is_revise = false;
+
+ MainActivity.isShowViewPage_memo = true;
+ Intent intent = new Intent(AddMemo_activity.this,MainActivity.class);
+ startActivity(intent);
+ finish();//销毁当前活动
+ break;
+ case R.id.success_button:
+ //将数据存入内存
+ String name = select_roommateName;
+ String title = String.valueOf(title_edt.getText());
+ String content = String.valueOf(content_edt.getText());
+ String now_time = (String)clock.getText();
+ //对输入进行判断
+ if (!title.equals("") && !content.equals("")) {
+ //打印存入数据
+ String date =String.format(title+" "+content+" "+now_time+" %s", name);
+ Log.d("Roommate_activity","存入数据:"+date);
+ Memo memo = new Memo(name,title,content,now_time);
+ //将增加数据存入数据库
+ ContentValues values01 = new ContentValues();
+ values01.put("name", select_roommateName);
+ values01.put("title",title);
+ values01.put("content",content);
+ values01.put("date",now_time);
+ values01.put("isDelete",false);
+ values01.put("isRevise",false);
+ mHelper.insert(RoommateDBHelper.getTableNameRoommateMemo(),values01);
+ if (is_revise){
+ mHelper.update_roommateMemo(jumpMemo,true,true);
+ }
+ else {
+ //将增加数据存入数据库
+ ContentValues values02 = new ContentValues();
+ values02.put("name", select_roommateName);
+ values02.put("title",title);
+ values02.put("content",content);
+ values02.put("date",now_time);
+ values02.put("isDelete",false);
+ values02.put("isRevise",true);
+ mHelper.insert(RoommateDBHelper.getTableNameRoommateMemo(),values02);
+ }
+ //通知显示
+ notification(memo);
+ //返回界面
+ is_backData = true;
+ jumpMemo = null;
+ is_revise = false;
+ Roommate_memo_adapter.jumpMemo = null;
+
+ Toast.makeText(this, "完成", Toast.LENGTH_SHORT).show();
+ Log.d("Roommate_activity","返回!");
+ //重新转跳到首界面
+ MainActivity.isShowViewPage_memo = true;
+ intent = new Intent(AddMemo_activity.this,MainActivity.class);
+ startActivity(intent);
+ finish();
+ }
+ else Toast.makeText(this, "标题和内容不能为空", Toast.LENGTH_SHORT).show();
+ break;
+ }
+
+ }
+ //通知提醒
+ private void notification(Memo memo){
+ //
+ NotificationUtil notificationUtil;
+ if(is_revise){
+ notificationUtil = new NotificationUtil(this,"修改通知","您在备忘录中修改了一条通知!!!",
+ R.drawable.add_small_icon, BitmapFactory.decodeResource(getResources(),memo.getImgID() ));
+ }
+ else {
+ notificationUtil = new NotificationUtil(this,"添加通知","您在备忘录中添加了一条通知!!!",
+ R.drawable.add_small_icon, BitmapFactory.decodeResource(getResources(), memo.getImgID()));
+ }
+ notificationUtil.notification();
+ }
+
+ //对文本进行控制
+ private void text_Control(){
+ content_edt.setInputType(InputType.TYPE_TEXT_FLAG_MULTI_LINE);//多行文本输入
+ content_edt.setGravity(Gravity.TOP);//文本显示在最上方
+ content_edt.setSingleLine(false);//改变默认单行模式
+ content_edt.setHorizontallyScrolling(false);//取消水平滚动
+ }
+ /*菜单栏编写*/
+ private void Menu_part(){
+ /*隐藏菜单栏*/
+ ActionBar actionBar = getSupportActionBar();
+ if(actionBar != null){
+ actionBar.hide();
+ }
+ }
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/HomeViewModel.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/HomeViewModel.java
new file mode 100644
index 0000000..34651d8
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/HomeViewModel.java
@@ -0,0 +1,19 @@
+package com.lh.app.ui.memo;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+
+public class HomeViewModel extends ViewModel {
+
+ private final MutableLiveData mText;
+
+ public HomeViewModel() {
+ mText = new MutableLiveData<>();
+ mText.setValue("This is home fragment");
+ }
+
+ public LiveData getText() {
+ return mText;
+ }
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/Memo.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/Memo.java
new file mode 100644
index 0000000..b643532
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/Memo.java
@@ -0,0 +1,45 @@
+package com.lh.app.ui.memo;
+
+import com.lh.app.R;
+
+/**
+ * Created by LiShiyao on 2022/10/5.
+ */
+public class Memo {
+ private String name;
+ private String title;
+ private String content;
+ private String date;
+
+ public Memo(){}
+ public Memo(String name, String title, String content, String time){
+ this.name = name;
+ this.title = title;
+ this.content = content;
+ this.date = time;
+ }
+ public Memo(String title, String content){
+ this.title = title;
+ this.content = content;
+ }
+
+ public String getName() {
+ return name;
+ }
+ public String getTitle(){
+ return title;
+ }
+ public String getContent(){
+ return content;
+ }
+ public String getDate(){
+ return date;
+ }
+ public int getImgID(){
+ if ("全部".equals(name)) return R.drawable.img_dog1;
+ else if ("刘治浩".equals(name)) return R.drawable.hyt;
+ else if ("谭宗辉".equals(name)) return R.drawable.pyy;
+ else if ("樊彪".equals(name)) return R.drawable.yyqx;
+ else return R.drawable.dc;
+ }
+}
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/MemoFragment.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/MemoFragment.java
new file mode 100644
index 0000000..1f09730
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/MemoFragment.java
@@ -0,0 +1,307 @@
+package com.lh.app.ui.memo;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.lh.app.MyApplication;
+import com.lh.app.R;
+import com.lh.app.RoommateDBHelper;
+import com.lh.app.tool.NotificationUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MemoFragment extends Fragment {
+ //数据库帮助器对象实例
+ private RoommateDBHelper mHelper = MyApplication.mHelper;
+ //recycleView中子项列表
+ private List memoList = new ArrayList<>();
+ private Roommate_memo_adapter adapter;
+ //空数据文本
+ private TextView tv_empty;
+ //备忘录子项数量
+ private int memoNum = 0;
+ //定义菜单项标识
+ private final int REMOVE = 110;
+ private final int ADD = 111;
+ private final int REVISE = 112;
+ //活动视图
+ private View view;
+ //上下文
+ private Context context;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ Log.d("fragment_memo","onCreate");
+ //Fragment被创建时,被调用
+ super.onCreate(savedInstanceState);
+ //指出fragment愿意添加item到选项菜单
+ setHasOptionsMenu(true);
+
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater,
+ ViewGroup container, Bundle savedInstanceState) {
+ Log.d("fragment_memo","onCreateView");
+ //获取活动视图和活动上下文
+ view = inflater.inflate(R.layout.fragment_memo,container,false);
+ context = getActivity();
+ //将选中子项列表清空
+ Roommate_memo_adapter.checkList.clear();
+ //设置recycleView配置
+ setRecycleView();
+ addUI_and_Listener();
+ //初始化备忘录列表和备忘录行数
+ initList();
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ Log.d("fragment_memo","onActivityCreated");
+ }
+
+ @Override
+ public void onStart() {
+ //无焦点但可见时,调用此方法
+ super.onStart();
+ Log.d("fragment_memo","onStart");
+
+ }
+
+ @Override
+ public void onPause() {
+ //Fragment失去焦点但可见时,调用此方法.
+ super.onPause();
+ Log.d("fragment_memo","onPause");
+ }
+
+ @Override
+ public void onStop() {
+ //Fragment不可见时,调用此方法.
+ super.onStop();
+ Log.d("fragment_memo","onStop");
+ }
+
+ @Override
+ public void onDestroyView() {
+ //当Fragment被移除时被调用
+ super.onDestroyView();
+ Log.d("fragment_memo","onDestroyView");
+ }
+ /*添加组件及其监听器*/
+ private void addUI_and_Listener(){
+ Button but_add = view.findViewById(R.id.add_but);
+ Button remove_btn = view.findViewById(R.id.remove_btn);
+ Button clear_btn = view.findViewById(R.id.clear_btn);
+ tv_empty = view.findViewById(R.id.empty_tv);
+
+ but_add.setOnClickListener(this::onClick);
+ remove_btn.setOnClickListener(this::onClick);
+ clear_btn.setOnClickListener(this::onClick);
+ }
+ /*设置RecycleView样式*/
+ private void setRecycleView() {
+ //竖直流布局
+ RecyclerView recyclerView = view.findViewById(R.id.recycleView_horizontal);
+ LinearLayoutManager LayoutManager2 = new LinearLayoutManager(context);
+ //LayoutManager2.setOrientation(LinearLayoutManager.VERTICAL);
+ recyclerView.setLayoutManager(LayoutManager2);
+ adapter = new Roommate_memo_adapter(memoList);
+ recyclerView.setAdapter(adapter);
+ }
+ /*初始化recycleView列表*/
+ private void initList() {
+ //更新文件行数
+ memoNum = mHelper.query_roommateMemo_rowsNumByType(false,false);
+ Log.d("Roommate_activity","文件行数:"+ memoNum);
+ //若备忘录子项不为0,则添加到recycleView中
+ if (memoNum !=0){
+ //先清空序列
+ memoList.clear();
+ //取备忘录中被显示数据
+ List memoList = mHelper.query_roommateMemoByType(false,false);
+ for (Memo memo:memoList){
+ add(0,memo);
+ }
+ }
+ }
+
+ //点击事件
+ @SuppressLint("NonConstantResourceId")
+ private void onClick(View view) {
+ switch (view.getId()){
+ case R.id.add_but:
+ Intent intent = new Intent(context, AddMemo_activity.class);
+ startActivity(intent);
+
+ getActivity().finish();
+
+ break;
+ case R.id.remove_btn:
+ remove();
+ break;
+ case R.id.clear_btn:
+ //弹出弹框提示
+ new AlertDialog.Builder(context)
+ .setTitle("信息提示")
+ .setMessage("确认全删除?")
+ .setPositiveButton("是", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int i) {//确认按钮监听事件
+ //请空子项、清空备忘录表中数据、弹出通知、更新备忘录子项数
+ clear();
+ //弹框消失
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton("否", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int i) {//返回按钮监听事件
+ dialog.dismiss();
+ }
+ }).show();//显示弹框
+ break;
+ default:
+ throw new IllegalStateException("Unexpected value: " + view);
+ }
+ }
+
+ /*添加子项*/
+ private void add(int position,Memo memo){
+ Log.d("Roommate_activity","添加子项!");
+ tv_empty.setVisibility(View.GONE);
+ memoList.add(position,memo);
+ adapter.notifyItemInserted(position);
+ }
+
+ /*删除子项*/
+ @SuppressLint("NotifyDataSetChanged")
+ private void remove(){
+ //获取要被删除子项列表
+ List checklist = Roommate_memo_adapter.checkList;
+ int remove_num = checklist.size();
+ //判断是否有要被删除的子项
+ if (remove_num == 0) {
+ Toast.makeText(context, "请选择数据!!!", Toast.LENGTH_SHORT).show();
+ }
+ else {
+ //循环删除被选中子项
+ while(checklist.size()!=0){
+ Memo memo = checklist.get(0);
+ //删除在子项列表中位置
+ memoList.remove(memo);
+ //修改备忘录表中被删除子项type为删除状态(1,0)
+ mHelper.update_roommateMemo(memo,true,false);
+ //删除被选中子项列表位置
+ Roommate_memo_adapter.checkList.remove(0);
+ }
+ //更新文件行数
+ memoNum = mHelper.query_roommateMemo_rowsNumByType(false,false);
+ //更新RecycleView
+ adapter.notifyDataSetChanged();
+ //将选中子项列表清空
+ Roommate_memo_adapter.checkList.clear();
+ //若子项全被清空,则出现“没有数据”提示
+ if(memoList.size()==0) tv_empty.setVisibility(View.VISIBLE);
+ //显示删除通知
+ show_TypeOfDeleteNotification(remove_num);
+ }
+ }
+
+
+ /*清空子项*/
+ @SuppressLint("NotifyDataSetChanged")
+ private void clear(){
+ if (memoList.size()!=0){
+ //清空recycleView中子项
+ memoList.clear();
+ adapter.notifyDataSetChanged();
+ //出现空数据提示
+ tv_empty.setVisibility(View.VISIBLE);
+
+ //修改备忘录表中被删除子项type
+ mHelper.update_roommateAllMemoToDelete();
+
+ //显示删除通知
+ show_TypeOfDeleteNotification(memoNum);
+ //更新备忘录子项数为0
+ memoNum = 0;
+ }
+ else Toast.makeText(context, "子项已被清空", Toast.LENGTH_SHORT).show();
+
+ }
+
+ //显示删除通知
+ private void show_TypeOfDeleteNotification(int remove_num){
+ NotificationUtil notificationUtil = new NotificationUtil(context,"删除通知",
+ "您在备忘录中删除了"+remove_num+"条通知!!!",R.drawable.add_small_icon,
+ BitmapFactory.decodeResource(getResources(),R.drawable.remove_large_icon));
+ notificationUtil.notification();
+ }
+
+ /*重写顶部菜单栏菜单键*/
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ int id = item.getItemId();
+ Intent intent = new Intent(context,Story_activity.class);
+ switch (id){
+ case REMOVE:
+ Toast.makeText(context, "删除记录", Toast.LENGTH_SHORT).show();
+ Story_activity.open_type = 1;
+ startActivity(intent);
+ break;
+ case ADD:
+ Toast.makeText(context, "添加记录", Toast.LENGTH_SHORT).show();
+ Story_activity.open_type = 2;
+ startActivity(intent);
+ break;
+ case REVISE:
+ Toast.makeText(context, "修改记录", Toast.LENGTH_SHORT).show();
+ Story_activity.open_type = 3;
+ startActivity(intent);
+ break;
+ case android.R.id.home:
+ //no
+ return true;
+ default:
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * 重写菜单栏中选项,完成菜单初始化
+ * 与在activity中添加有区别
+ * */
+ @Override
+ public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ menu.add(0,REMOVE,1,"删除记录");
+ menu.add(0,ADD,2,"增加记录");
+ menu.add(0,REVISE,3,"修改记录");
+ }
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/Roommate_memo_adapter.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/Roommate_memo_adapter.java
new file mode 100644
index 0000000..f1cadf8
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/Roommate_memo_adapter.java
@@ -0,0 +1,166 @@
+package com.lh.app.ui.memo;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.lh.app.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by LiShiyao on 2022.10.5
+ */
+public class Roommate_memo_adapter extends RecyclerView.Adapter {
+ private List mMemoList;
+ //选中的数据
+ public static List checkList = new ArrayList<>();
+ //跳转至AddMemo界面携带的Memo
+ public static Memo jumpMemo = null;
+ public static int click_type = 1;//默认打开编辑界面
+
+ /**
+ * 定义一个内部类,继承自RecyclerView.ViewHolder
+ * */
+ static class ViewHolder extends RecyclerView.ViewHolder{
+ View memoView;
+ ImageView memoImage;
+ CheckBox isSelect;
+ RelativeLayout body_rel;
+ TextView memoTitle;
+ TextView memoContent;
+ TextView memoDate;
+ //修改
+ LinearLayout lil_showInfo;
+ ImageView img_back_showInfo;
+ TextView tv_title_showInfo;
+ TextView tv_date_showInfo;
+ TextView tv_content_showInfo;
+ TextView tv_roommate_showInfo;
+
+ // 作用:获取布局中的组件实例
+ // view参数 最外层布局(小方块)
+ public ViewHolder(View view){
+ super(view);
+ memoView = view;
+ memoImage = view.findViewById(R.id.memo_image);
+ isSelect = view.findViewById(R.id.ck_select);
+ body_rel = view.findViewById(R.id.body_rel);
+ memoTitle = view.findViewById(R.id.title_tev);
+ memoContent = view.findViewById(R.id.content_tev);
+ memoDate = view.findViewById(R.id.date_tv);
+ lil_showInfo = view.findViewById(R.id.lil_showInfo);
+ img_back_showInfo = view.findViewById(R.id.img_back_showInfo);
+ tv_title_showInfo = view.findViewById(R.id.tv_title_showInfo);
+ tv_date_showInfo = view.findViewById(R.id.tv_date_showInfo);
+ tv_content_showInfo = view.findViewById(R.id.tv_content_showInfo);
+ tv_roommate_showInfo = view.findViewById(R.id.tv_roommate_showInfo);
+ }
+ }
+
+ /**
+ * 构造函数
+ * 作用:把要展示的数据源传进来
+ * */
+ public Roommate_memo_adapter(List memoList){
+ mMemoList = memoList;
+ }
+
+
+ //因为继承自RecyclerView.Adapter,必须重写onCreateViewHolder()、onBindViewHolder()、getItemCount()
+ /**
+ * onCreateViewHolder()用于创建ViewHolder实例方法
+ * onBindViewHolder() 用于对recycleView子项的数据进行赋值(在子项滚入屏幕时执行)
+ * getItemCount() 告诉recycleView有多少个子项
+ * */
+ @SuppressLint("NonConstantResourceId")
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view;
+ //1.加载布局
+ view = LayoutInflater.from(parent.getContext()).inflate(R.layout.memo_item,parent,false);
+ //2.创建ViewHolder实例
+ final ViewHolder holder = new ViewHolder(view);
+ //3.创建点击事件
+ //为view (小方块)创建监听事件
+
+ holder.body_rel.setOnClickListener(view1 -> {
+ if (click_type==1){//在备忘录界面点击子项
+ int position = holder.getAdapterPosition();
+ Memo memo = mMemoList.get(position);
+ jumpMemo = memo;//携带的Memo跳转
+ AddMemo_activity.is_revise = true;
+ //跳转界面
+ Intent intent = new Intent(view.getContext(), AddMemo_activity.class);
+ view.getContext().startActivity(intent);
+ }
+
+ if (click_type==2){//在增、删、改界面上点击
+ holder.lil_showInfo.setVisibility(View.VISIBLE);
+ int position = holder.getAdapterPosition();
+ Memo memo = mMemoList.get(position);
+ holder.tv_title_showInfo.setText(memo.getTitle());
+ holder.tv_date_showInfo.setText(memo.getDate());
+ holder.tv_content_showInfo.setText(memo.getContent());
+ holder.tv_roommate_showInfo.setText(memo.getName());
+ //禁用单选按钮,若被选中,取消选中
+ if (holder.isSelect.isChecked()) holder.isSelect.setChecked(false);
+ holder.isSelect.setEnabled(false);
+ }
+ //Toast.makeText(view.getContext(),"you clicked view " +memo.getImageId(),Toast.LENGTH_SHORT).show();
+ });
+ holder.img_back_showInfo.setOnClickListener(view1 -> {
+ holder.lil_showInfo.setVisibility(View.GONE);
+ //启用单选按钮
+ holder.isSelect.setEnabled(true);
+ });
+
+ //4.返回ViewHolder实例
+ return holder;
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ //通过position参数 获得当前 Memo 实例
+ Memo memo = mMemoList.get(position);
+ //获得子项数据
+ holder.memoImage.setImageResource(memo.getImgID());
+ holder.memoTitle.setText(memo.getTitle());
+ //防止文本过长
+ String content = memo.getContent();
+ if (content.length()>14){
+ holder.memoContent.setText(content.substring(0,14)+"...");
+ }
+ else holder.memoContent.setText(content);
+
+ holder.memoDate.setText(memo.getDate());
+ //为checkBox创建监听事件
+ holder.isSelect.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ //单选按钮被选中,将memo加入队列,否则退出队列
+ if(isChecked) checkList.add(memo);
+ if (!isChecked) checkList.remove(memo);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return mMemoList.size();
+ }
+}
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/Story_activity.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/Story_activity.java
new file mode 100644
index 0000000..483d47e
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/memo/Story_activity.java
@@ -0,0 +1,290 @@
+package com.lh.app.ui.memo;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.lh.app.MyApplication;
+import com.lh.app.R;
+import com.lh.app.RoommateDBHelper;
+import com.lh.app.tool.NotificationUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Story_activity extends AppCompatActivity {
+ private RecyclerView recyclerView_story;
+ private Roommate_memo_adapter adapter;
+ private List memoList = new ArrayList<>();
+
+ private int memoNum = 0;
+
+ private Button btn_clear_story;
+ private TextView tv_empty;
+ private TextView tv_story_type;
+ private Button btn_back;
+ private Button but_remove_story;
+ private LinearLayout lil_showInfo;
+
+ public static int open_type = 0;//1:删除记录、2:增加记录、3:修改记录
+
+ //数据库帮助器对象实例
+ private RoommateDBHelper mHelper = MyApplication.mHelper;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_story);
+ //将选中子项列表清空
+ Roommate_memo_adapter.checkList.clear();
+ Roommate_memo_adapter.click_type = 2;//点击子项出现详情1->2
+
+ addUI_and_listener();//添加UI和监听器
+ setTopPart();//顶部菜单隐藏
+ setRecycleView();
+ initList();
+
+ }
+ //添加UI和监听器
+ private void addUI_and_listener() {
+ btn_clear_story = (Button) findViewById(R.id.btn_clear_story);
+ tv_empty = (TextView) findViewById(R.id.empty_tv_story);
+ btn_back = (Button) findViewById(R.id.but_back_04) ;
+ tv_story_type = (TextView) findViewById(R.id.tv_title_04);
+ but_remove_story = (Button)findViewById(R.id.but_remove_story) ;
+
+
+ btn_clear_story.setOnClickListener(this::onclick);
+ btn_back.setOnClickListener(this::onclick);
+ but_remove_story.setOnClickListener(this::onclick);
+ }
+ @SuppressLint("NonConstantResourceId")
+ private void onclick(View view){
+ switch (view.getId()){
+ case R.id.but_back_04:
+ //将选中子项列表清空
+ Roommate_memo_adapter.checkList.clear();
+ //恢复点击子项显示编辑界面2->1
+ Roommate_memo_adapter.click_type = 1;
+ finish();
+ break;
+ case R.id.but_remove_story:
+ remove();
+ break;
+ case R.id.btn_clear_story:
+ //弹出弹框提示
+ new AlertDialog.Builder(Story_activity.this)
+ .setTitle("信息提示")
+ .setMessage("确认全删除?")
+ .setPositiveButton("是", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int i) {//确认按钮监听事件
+ //请空子项
+ clear();
+ //弹框消失
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton("否", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int i) {//返回按钮监听事件
+ dialog.dismiss();
+ }
+ }).show();//显示弹框
+ break;
+ }
+ }
+ /*初始化recycleView列表*/
+ private void initList() {
+ //更新文件行数
+ updateMemoNum();
+ Log.d("Roommate_activity","文件行数:"+ memoNum);
+ //若备忘录子项不为0,则添加到recycleView中
+ if (memoNum !=0){
+ //先清空序列
+ memoList.clear();
+ //取备忘录中被显示数据
+ List memoList = getMemoList();
+ for (Memo memo:memoList){
+ add(0,memo);//向recycleView开头插入子项
+ }
+ }
+ }
+ /*添加子项*/
+ private void add(int position,Memo memo){
+ Log.d("Roommate_activity","添加子项!");
+ tv_empty.setVisibility(View.GONE);
+ memoList.add(position,memo);
+ adapter.notifyItemInserted(position);
+ }
+
+ /*删除子项*/
+ @SuppressLint("NotifyDataSetChanged")
+ private void remove(){
+ //获取要被删除子项列表
+ List checklist = Roommate_memo_adapter.checkList;
+ int remove_num = checklist.size();
+ //判断是否有要被删除的子项
+ if (remove_num == 0) {
+ Toast.makeText(this, "请选择数据!!!", Toast.LENGTH_SHORT).show();
+ }
+ else {
+ //循环删除被选中子项
+ while(checklist.size()!=0){
+ Memo memo = checklist.get(0);
+ //删除在子项列表中位置
+ memoList.remove(memo);
+ //删除在数据库中位置
+ deleteMemo(memo);
+ //删除被选中子项列表位置
+ Roommate_memo_adapter.checkList.remove(0);
+
+ }
+ //更新文件行数
+ memoNum = mHelper.query_roommateMemo_rowsNumByType(false,false);
+ //更新RecycleView
+ adapter.notifyDataSetChanged();
+ //将选中子项列表清空
+ Roommate_memo_adapter.checkList.clear();
+ //若子项全被清空,则出现“没有数据”提示
+ if(memoList.size()==0) tv_empty.setVisibility(View.VISIBLE);
+ //显示删除通知
+ show_TypeOfDeleteNotification(remove_num);
+ }
+ }
+
+
+
+ /*清空子项*/
+ @SuppressLint("NotifyDataSetChanged")
+ private void clear(){
+ if (memoList.size()!=0){
+ //清空recycleView中子项
+ memoList.clear();
+ adapter.notifyDataSetChanged();
+ //出现空数据提示
+ tv_empty.setVisibility(View.VISIBLE);
+ //清空文件数据
+ deleteAllTypeMemo();
+ //显示删除通知
+ show_TypeOfDeleteNotification(memoNum);
+ //更新备忘录子项数为0
+ memoNum = 0;
+ }
+ else Toast.makeText(this, "子项已被清空", Toast.LENGTH_SHORT).show();
+
+ }
+
+
+ //显示删除通知
+ private void show_TypeOfDeleteNotification(int remove_num){
+ NotificationUtil notificationUtil = new NotificationUtil(this,"删除通知",
+ "您在备忘录中删除了"+remove_num+"条通知!!!",R.drawable.add_small_icon,
+ BitmapFactory.decodeResource(getResources(),R.drawable.remove_large_icon));
+ notificationUtil.notification();
+ }
+
+ /*设置RecycleView样式*/
+ private void setRecycleView() {
+ //竖直流布局
+ recyclerView_story = (RecyclerView) findViewById(R.id.recycleView_story);
+ LinearLayoutManager LayoutManager2 = new LinearLayoutManager(this);
+ //LayoutManager2.setOrientation(LinearLayoutManager.VERTICAL);
+ recyclerView_story.setLayoutManager(LayoutManager2);
+ adapter = new Roommate_memo_adapter(memoList);
+ recyclerView_story.setAdapter(adapter);
+ }
+
+ /**
+ * 隐藏菜单栏和设置菜单标题
+ * */
+ private void setTopPart(){
+ //隐藏菜单栏
+ ActionBar actionBar = getSupportActionBar();
+ if(actionBar != null){
+ actionBar.hide();
+ }
+ //设置菜单标题
+ switch (open_type) {
+ case 1:
+ tv_story_type.setText("删除记录");
+ break;
+ case 2:
+ tv_story_type.setText("增加记录");
+ break;
+ case 3:
+ tv_story_type.setText("修改记录");
+ break;
+ }
+ }
+ /*更新文件行数*/
+ private void updateMemoNum(){
+ switch (open_type) {
+ case 1:
+ memoNum = mHelper.query_roommateMemo_rowsNumByType(true,false);
+ break;
+ case 2:
+ memoNum = mHelper.query_roommateMemo_rowsNumByType(false,true);
+ break;
+ case 3:
+ memoNum = mHelper.query_roommateMemo_rowsNumByType(true,true);
+ break;
+ }
+ }
+ //取备忘录中指定数据
+ private List getMemoList(){
+ switch (open_type) {
+ case 1:
+ return mHelper.query_roommateMemoByType(true,false);
+ case 2:
+ return mHelper.query_roommateMemoByType(false,true);
+ case 3:
+ return mHelper.query_roommateMemoByType(true,true);
+ }
+ return null;
+ }
+ //删除数据库备忘录表中指定类型数据
+ private void deleteMemo(Memo memo) {
+ long row = 0;
+ switch (open_type) {
+ case 1:
+ //删除删除子项(1,0)
+ row = mHelper.deleteMemoByType(memo,true,false);
+ break;
+ case 2:
+ //删除增加子项(1,0)
+ row = mHelper.deleteMemoByType(memo,false,true);
+ break;
+ case 3:
+ //删除修改子项(1,0)
+ row = mHelper.deleteMemoByType(memo,true,true);
+ break;
+ }
+ Log.d("delete"+open_type+"型","删除第"+row+"行");
+ }
+ //删除数据库备忘录表中指定类型全部数据
+ private void deleteAllTypeMemo() {
+ switch (open_type) {
+ case 1:
+ mHelper.deleteMemoAllType(true,false);
+ case 2:
+ mHelper.deleteMemoAllType(false,true);
+ case 3:
+ mHelper.deleteMemoAllType(true,true);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/java/com/lh/app/ui/mine/MineFragment.java b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/mine/MineFragment.java
new file mode 100644
index 0000000..0f2e7d6
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/java/com/lh/app/ui/mine/MineFragment.java
@@ -0,0 +1,459 @@
+package com.lh.app.ui.mine;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.location.Address;
+import android.location.Geocoder;
+import android.location.Location;
+import android.location.LocationManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.Looper;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.core.app.ActivityCompat;
+import androidx.fragment.app.Fragment;
+
+import com.amap.api.location.AMapLocation;
+import com.amap.api.location.AMapLocationClient;
+import com.amap.api.location.AMapLocationClientOption;
+import com.amap.api.location.AMapLocationListener;
+import com.amap.api.maps.AMap;
+import com.amap.api.maps.CameraUpdateFactory;
+import com.amap.api.maps.LocationSource;
+import com.amap.api.maps.MapView;
+import com.amap.api.maps.model.LatLng;
+import com.amap.api.maps.model.MyLocationStyle;
+import com.google.android.gms.location.FusedLocationProviderClient;
+import com.google.android.gms.location.LocationCallback;
+import com.google.android.gms.location.LocationRequest;
+import com.google.android.gms.location.LocationResult;
+import com.google.android.gms.location.LocationServices;
+import com.google.android.gms.tasks.OnSuccessListener;
+import com.lh.app.R;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * 高德地图定位Fragment
+ * 需要权限:
+ * - android.permission.ACCESS_FINE_LOCATION
+ * - android.permission.ACCESS_COARSE_LOCATION
+ * - android.permission.INTERNET
+ * - android.permission.ACCESS_NETWORK_STATE
+ */
+public class MineFragment extends Fragment implements AMapLocationListener, LocationSource {
+
+ private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
+ private static final int REQUEST_ENABLE_GPS = 2;
+
+ private FusedLocationProviderClient fusedLocationClient;
+
+ private MapView mapView;
+ private AMap aMap;
+ private TextView tvLocation;
+ private Button btnLocation;
+
+ // 高德定位相关
+ private AMapLocationClient locationClient;
+ private AMapLocationClientOption locationOption;
+ private OnLocationChangedListener mapLocationListener;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // 设置隐私合规接口
+ AMapLocationClient.updatePrivacyShow(getContext(), true, true);
+ AMapLocationClient.updatePrivacyAgree(getContext(), true);
+ fusedLocationClient = LocationServices.getFusedLocationProviderClient(getContext());
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater,
+ ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_mine, container, false);
+ initViews(view);
+ initMap(savedInstanceState, view);
+ return view;
+ }
+
+ private void initViews(View view) {
+ tvLocation = view.findViewById(R.id.text_location);
+ btnLocation = view.findViewById(R.id.btn_getLocation);
+ btnLocation.setOnClickListener(v -> checkLocationAndPermission());
+ }
+
+ private void initMap(Bundle savedInstanceState, View view) {
+ mapView = view.findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+
+ if (aMap == null) {
+ aMap = mapView.getMap();
+ setupMap();
+ }
+ }
+
+ private void setupMap() {
+ // 设置地图参数
+ aMap.getUiSettings().setZoomControlsEnabled(true);
+ aMap.getUiSettings().setMyLocationButtonEnabled(false); // 使用自定义按钮
+
+ // 设置定位蓝点样式
+ MyLocationStyle myLocationStyle = new MyLocationStyle();
+ myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);
+ myLocationStyle.interval(5000); // 定位间隔
+ myLocationStyle.strokeColor(android.R.color.transparent);
+ myLocationStyle.radiusFillColor(0x500000FF);
+ aMap.setMyLocationStyle(myLocationStyle);
+
+ // 设置定位源
+ aMap.setLocationSource(this);
+ aMap.setMyLocationEnabled(true);
+ }
+
+ private void checkLocationAndPermission() {
+ if (!isNetworkConnected()) {
+ showNetworkDisconnectedAlert();
+ return;
+ }
+ if (!isGpsEnabled()) {
+ showGpsDisabledAlert();
+ return;
+ }
+
+ if (hasLocationPermission()) {
+ startLocation();
+ } else {
+ requestLocationPermission();
+ }
+ }
+
+ private boolean isGpsEnabled() {
+ LocationManager manager = (LocationManager) requireContext()
+ .getSystemService(Context.LOCATION_SERVICE);
+ return manager.isProviderEnabled(LocationManager.GPS_PROVIDER);
+ }
+
+ private void showGpsDisabledAlert() {
+ new AlertDialog.Builder(requireContext())
+ .setTitle("GPS未开启")
+ .setMessage("需要开启GPS才能获取精确位置")
+ .setPositiveButton("去设置", (dialog, which) -> {
+ Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
+ startActivityForResult(intent, REQUEST_ENABLE_GPS);
+ })
+ .setNegativeButton("取消", null)
+ .show();
+ }
+
+ private boolean hasLocationPermission() {
+ return ActivityCompat.checkSelfPermission(requireContext(),
+ Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
+ ActivityCompat.checkSelfPermission(requireContext(),
+ Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private void requestLocationPermission() {
+ if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) ||
+ shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION)) {
+ new AlertDialog.Builder(requireContext())
+ .setTitle("需要位置权限")
+ .setMessage("应用需要位置权限来提供定位服务")
+ .setPositiveButton("确定", (dialog, which) -> {
+ requestPermissions(
+ new String[]{
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ },
+ LOCATION_PERMISSION_REQUEST_CODE);
+ })
+ .setNegativeButton("取消", null)
+ .show();
+ } else {
+ requestPermissions(
+ new String[]{
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ },
+ LOCATION_PERMISSION_REQUEST_CODE);
+ }
+ }
+
+ private void startLocation() {
+ requestLocationUpdates();
+ if (locationClient == null) {
+ initLocationClient();
+ }
+
+ if (!locationClient.isStarted()) {
+ locationClient.startLocation();
+ btnLocation.setText("定位中...");
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ private void getLastLocation() {
+ fusedLocationClient.getLastLocation()
+ .addOnSuccessListener(requireActivity(), new OnSuccessListener() {
+ @Override
+ public void onSuccess(Location location) {
+ if (location != null) {
+ // 获取到位置信息
+ double latitude = location.getLatitude();
+ double longitude = location.getLongitude();
+ Log.d("Location", "Latitude: " + latitude + ", Longitude: " + longitude);
+ } else {
+ // 位置信息为空
+ Log.d("Location", "No location found");
+ }
+ }
+ });
+ }
+ private void requestLocationUpdates() {
+ LocationRequest locationRequest = LocationRequest.create();
+ locationRequest.setInterval(10000); // 更新间隔
+ locationRequest.setFastestInterval(5000); // 最快更新间隔
+ locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); // 高精度模式
+
+ LocationCallback locationCallback = new LocationCallback() {
+ @Override
+ public void onLocationResult(LocationResult locationResult) {
+ if (locationResult == null) {
+ return;
+ }
+ for (Location location : locationResult.getLocations()) {
+ Log.d("Location", "Latitude: " + location.getLatitude() + ", Longitude: " + location.getLongitude());
+ getAddressFromLocation(location.getLatitude(),location.getLongitude());
+ }
+ }
+ };
+
+ if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper());
+ }
+
+ private void getAddressFromLocation(double latitude, double longitude) {
+ Geocoder geocoder = new Geocoder(getContext(), Locale.getDefault());
+ try {
+ List addresses = geocoder.getFromLocation(latitude, longitude, 1);
+ if (addresses != null && addresses.size() > 0) {
+ Address address = addresses.get(0);
+ String province = address.getAdminArea(); // 省
+ String city = address.getLocality(); // 市
+ String district = address.getSubLocality(); // 区
+ String fullAddress = String.format("%s,%s,%s", province, city, district);
+ Log.d("Location", "Full Address: " + fullAddress);
+ // 更新 UI 或其他逻辑
+ getActivity().runOnUiThread(() -> tvLocation.setText(fullAddress));
+ }
+ } catch (IOException e) {
+ Log.e("Location", "Error getting address", e);
+ }
+ }
+ private void initLocationClient() {
+ try {
+ locationClient = new AMapLocationClient(requireContext());
+ locationOption = new AMapLocationClientOption();
+
+ // 修改定位参数
+ locationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
+ locationOption.setNeedAddress(true); // 确保这个设置为true
+ locationOption.setOnceLocation(false);//false表示连续定位
+ locationOption.setWifiActiveScan(true);
+ locationOption.setHttpTimeOut(20000); // 设置超时时间
+ locationOption.setLocationCacheEnable(false); // 关闭缓存
+ locationOption.setInterval(10000);
+
+ locationClient.setLocationOption(locationOption);
+ locationClient.setLocationListener(this);
+ } catch (Exception e) {
+ Log.e("Location", "初始化定位失败", e);
+ Toast.makeText(requireContext(), "定位初始化失败", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public void onLocationChanged(AMapLocation location) {
+ if (location == null) {
+ Log.e("Location", "定位结果为空");
+ return;
+ }
+
+ // 打印完整的定位信息用于调试
+ Log.d("Location", "完整定位信息: " + location.toString());
+
+ if (location.getErrorCode() == 0) {
+ updateLocationUI(location);
+ } else {
+ showLocationError(location);
+ // 尝试重新启动定位
+ if (locationClient != null) {
+ locationClient.stopLocation();
+ locationClient.startLocation();
+ }
+ }
+ }
+
+ private void updateLocationUI(AMapLocation location) {
+ // 移动地图到当前位置
+ LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
+ aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16));
+
+ /* // 获取省、市、区信息
+ String province = location.getProvince();
+ String city = location.getCity();
+ String district = location.getDistrict();
+
+ // 检查是否为空
+ Log.d("Location", "Province: " + province + ", City: " + city + ", District: " + district);
+
+ // 构建显示信息
+ StringBuilder locationInfoBuilder = new StringBuilder();
+ if (province != null && !province.isEmpty()) {
+ locationInfoBuilder.append(province).append("省");
+ }
+ if (city != null && !city.isEmpty()) {
+ locationInfoBuilder.append(city).append("市");
+ }
+ if (district != null && !district.isEmpty()) {
+ locationInfoBuilder.append(district).append("区");
+ }
+ String locationInfo = locationInfoBuilder.toString();
+ if (locationInfo.isEmpty()) {
+ locationInfo = "未获取到详细地址信息";
+ }
+
+ // 更新 UI
+ if (tvLocation != null) {
+ String finalLocationInfo = locationInfo;
+ getActivity().runOnUiThread(() -> tvLocation.setText(finalLocationInfo));
+ } else {
+ Log.e("Location", "tvLocation is null");
+ }*/
+
+ // 通知地图更新蓝点位置
+ if (mapLocationListener != null) {
+ mapLocationListener.onLocationChanged(location);
+ }
+ }
+
+ private void showLocationError(AMapLocation location) {
+ String errorInfo = String.format("定位失败\n错误码: %d\n错误信息: %s",
+ location.getErrorCode(),
+ location.getErrorInfo());
+
+ tvLocation.setText(errorInfo);
+ Toast.makeText(requireContext(), errorInfo, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
+ if (grantResults.length >= 2 &&
+ grantResults[0] == PackageManager.PERMISSION_GRANTED &&
+ grantResults[1] == PackageManager.PERMISSION_GRANTED) {
+ startLocation();
+ } else {
+ Toast.makeText(requireContext(), "定位权限被拒绝", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ if (requestCode == REQUEST_ENABLE_GPS && isGpsEnabled()) {
+ startLocation();
+ }
+ }
+
+ // ========== LocationSource 接口实现 ==========
+ @Override
+ public void activate(OnLocationChangedListener listener) {
+ mapLocationListener = listener;
+ }
+
+ @Override
+ public void deactivate() {
+ mapLocationListener = null;
+ }
+
+ // ========== 生命周期管理 ==========
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ stopLocation();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ releaseLocation();
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ private void stopLocation() {
+ if (locationClient != null && locationClient.isStarted()) {
+ locationClient.stopLocation();
+ btnLocation.setText("开始定位");
+ }
+ }
+
+ private void releaseLocation() {
+ if (locationClient != null) {
+ locationClient.stopLocation();
+ locationClient.onDestroy();
+ locationClient = null;
+ }
+ }
+
+ private boolean isNetworkConnected() {
+ Context context = requireContext();
+ ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = null;
+ if (connectivityManager != null) {
+ networkInfo = connectivityManager.getActiveNetworkInfo();
+ }
+ return networkInfo != null && networkInfo.isConnected();
+ }
+
+ private void showNetworkDisconnectedAlert() {
+ new AlertDialog.Builder(requireContext())
+ .setTitle("网络连接断开")
+ .setMessage("请检查网络连接后重试")
+ .setPositiveButton("确定", null)
+ .show();
+ }
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/add_small_icon.png b/CODE/RommteStory/app/src/main/res/drawable-v24/add_small_icon.png
new file mode 100644
index 0000000..18fb98e
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/add_small_icon.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/back.png b/CODE/RommteStory/app/src/main/res/drawable-v24/back.png
new file mode 100644
index 0000000..f7c2676
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/back.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/dc.jpg b/CODE/RommteStory/app/src/main/res/drawable-v24/dc.jpg
new file mode 100644
index 0000000..55e20ba
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/dc.jpg differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/hyt.jpg b/CODE/RommteStory/app/src/main/res/drawable-v24/hyt.jpg
new file mode 100644
index 0000000..0a932e0
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/hyt.jpg differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/ic_launcher.webp b/CODE/RommteStory/app/src/main/res/drawable-v24/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/ic_launcher.webp differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/CODE/RommteStory/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/ic_launcher_round.webp b/CODE/RommteStory/app/src/main/res/drawable-v24/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/ic_launcher_round.webp differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/ic_my_location.png b/CODE/RommteStory/app/src/main/res/drawable-v24/ic_my_location.png
new file mode 100644
index 0000000..f1e1be3
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/ic_my_location.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/ic_no_picture.png b/CODE/RommteStory/app/src/main/res/drawable-v24/ic_no_picture.png
new file mode 100644
index 0000000..be8316c
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/ic_no_picture.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/img_add.png b/CODE/RommteStory/app/src/main/res/drawable-v24/img_add.png
new file mode 100644
index 0000000..35766dd
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/img_add.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/img_cat.png b/CODE/RommteStory/app/src/main/res/drawable-v24/img_cat.png
new file mode 100644
index 0000000..eb6085d
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/img_cat.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/img_delete.png b/CODE/RommteStory/app/src/main/res/drawable-v24/img_delete.png
new file mode 100644
index 0000000..7ec3fce
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/img_delete.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/img_detail.png b/CODE/RommteStory/app/src/main/res/drawable-v24/img_detail.png
new file mode 100644
index 0000000..0b9bd54
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/img_detail.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/img_dog1.png b/CODE/RommteStory/app/src/main/res/drawable-v24/img_dog1.png
new file mode 100644
index 0000000..4a1f6a3
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/img_dog1.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/img_dog2.png b/CODE/RommteStory/app/src/main/res/drawable-v24/img_dog2.png
new file mode 100644
index 0000000..2a29c7b
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/img_dog2.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/img_dog3.png b/CODE/RommteStory/app/src/main/res/drawable-v24/img_dog3.png
new file mode 100644
index 0000000..ad66b82
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/img_dog3.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/img_honor.png b/CODE/RommteStory/app/src/main/res/drawable-v24/img_honor.png
new file mode 100644
index 0000000..4d58538
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/img_honor.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/img_memo.png b/CODE/RommteStory/app/src/main/res/drawable-v24/img_memo.png
new file mode 100644
index 0000000..41eff7f
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/img_memo.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/img_memo1.png b/CODE/RommteStory/app/src/main/res/drawable-v24/img_memo1.png
new file mode 100644
index 0000000..99c33b9
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/img_memo1.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/img_mine.png b/CODE/RommteStory/app/src/main/res/drawable-v24/img_mine.png
new file mode 100644
index 0000000..9517f6c
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/img_mine.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/img_qq.png b/CODE/RommteStory/app/src/main/res/drawable-v24/img_qq.png
new file mode 100644
index 0000000..07fd25a
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/img_qq.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/img_test.jpg b/CODE/RommteStory/app/src/main/res/drawable-v24/img_test.jpg
new file mode 100644
index 0000000..4e9d280
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/img_test.jpg differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/pyy.png b/CODE/RommteStory/app/src/main/res/drawable-v24/pyy.png
new file mode 100644
index 0000000..782f8f1
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/pyy.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/remove_large_icon.png b/CODE/RommteStory/app/src/main/res/drawable-v24/remove_large_icon.png
new file mode 100644
index 0000000..7ec3fce
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/remove_large_icon.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/success.png b/CODE/RommteStory/app/src/main/res/drawable-v24/success.png
new file mode 100644
index 0000000..24e07a2
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/success.png differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/wyz1.jpg b/CODE/RommteStory/app/src/main/res/drawable-v24/wyz1.jpg
new file mode 100644
index 0000000..d2eb45e
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/wyz1.jpg differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/wz.jpg b/CODE/RommteStory/app/src/main/res/drawable-v24/wz.jpg
new file mode 100644
index 0000000..a41c6b7
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/wz.jpg differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable-v24/yyqx.jpg b/CODE/RommteStory/app/src/main/res/drawable-v24/yyqx.jpg
new file mode 100644
index 0000000..15717c7
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/drawable-v24/yyqx.jpg differ
diff --git a/CODE/RommteStory/app/src/main/res/drawable/bg_boarder.xml b/CODE/RommteStory/app/src/main/res/drawable/bg_boarder.xml
new file mode 100644
index 0000000..8d9459c
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/drawable/bg_boarder.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CODE/RommteStory/app/src/main/res/drawable/button_style.xml b/CODE/RommteStory/app/src/main/res/drawable/button_style.xml
new file mode 100644
index 0000000..f3e0934
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/drawable/button_style.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/drawable/cursor_style.xml b/CODE/RommteStory/app/src/main/res/drawable/cursor_style.xml
new file mode 100644
index 0000000..07c4f67
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/drawable/cursor_style.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/drawable/ic_dashboard_black_24dp.xml b/CODE/RommteStory/app/src/main/res/drawable/ic_dashboard_black_24dp.xml
new file mode 100644
index 0000000..46fc8de
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/drawable/ic_dashboard_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/CODE/RommteStory/app/src/main/res/drawable/ic_home_black_24dp.xml b/CODE/RommteStory/app/src/main/res/drawable/ic_home_black_24dp.xml
new file mode 100644
index 0000000..a5b4070
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/drawable/ic_home_black_24dp.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/CODE/RommteStory/app/src/main/res/drawable/ic_launcher_background.xml b/CODE/RommteStory/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..ca3826a
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CODE/RommteStory/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/CODE/RommteStory/app/src/main/res/drawable/ic_notifications_black_24dp.xml
new file mode 100644
index 0000000..78b75c3
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/drawable/ic_notifications_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/CODE/RommteStory/app/src/main/res/drawable/img_honor_style.xml b/CODE/RommteStory/app/src/main/res/drawable/img_honor_style.xml
new file mode 100644
index 0000000..1630664
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/drawable/img_honor_style.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/drawable/sl_tab_color.xml b/CODE/RommteStory/app/src/main/res/drawable/sl_tab_color.xml
new file mode 100644
index 0000000..e4e9a8b
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/drawable/sl_tab_color.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/CODE/RommteStory/app/src/main/res/font/you_light.ttf b/CODE/RommteStory/app/src/main/res/font/you_light.ttf
new file mode 100644
index 0000000..a74529c
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/font/you_light.ttf differ
diff --git a/CODE/RommteStory/app/src/main/res/layout/activity_main.xml b/CODE/RommteStory/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..702e609
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/layout/activity_story.xml b/CODE/RommteStory/app/src/main/res/layout/activity_story.xml
new file mode 100644
index 0000000..b8ba492
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/layout/activity_story.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/layout/add_memo_activity_layout.xml b/CODE/RommteStory/app/src/main/res/layout/add_memo_activity_layout.xml
new file mode 100644
index 0000000..2366aed
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/layout/add_memo_activity_layout.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/layout/fragment_detail.xml b/CODE/RommteStory/app/src/main/res/layout/fragment_detail.xml
new file mode 100644
index 0000000..9d68c6c
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/layout/fragment_detail.xml
@@ -0,0 +1,267 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/layout/fragment_honor.xml b/CODE/RommteStory/app/src/main/res/layout/fragment_honor.xml
new file mode 100644
index 0000000..a0206db
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/layout/fragment_honor.xml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/layout/fragment_memo.xml b/CODE/RommteStory/app/src/main/res/layout/fragment_memo.xml
new file mode 100644
index 0000000..40cf4b3
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/layout/fragment_memo.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/layout/fragment_mine.xml b/CODE/RommteStory/app/src/main/res/layout/fragment_mine.xml
new file mode 100644
index 0000000..5214f4b
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/layout/fragment_mine.xml
@@ -0,0 +1,268 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/layout/honor_img.xml b/CODE/RommteStory/app/src/main/res/layout/honor_img.xml
new file mode 100644
index 0000000..0d4058a
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/layout/honor_img.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/layout/honor_item.xml b/CODE/RommteStory/app/src/main/res/layout/honor_item.xml
new file mode 100644
index 0000000..1e22c9a
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/layout/honor_item.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/layout/item_listview.xml b/CODE/RommteStory/app/src/main/res/layout/item_listview.xml
new file mode 100644
index 0000000..bbd2a36
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/layout/item_listview.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/layout/memo_item.xml b/CODE/RommteStory/app/src/main/res/layout/memo_item.xml
new file mode 100644
index 0000000..23e867c
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/layout/memo_item.xml
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/layout/top_part_04.xml b/CODE/RommteStory/app/src/main/res/layout/top_part_04.xml
new file mode 100644
index 0000000..e6f69cc
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/layout/top_part_04.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/layout/top_part_2.xml b/CODE/RommteStory/app/src/main/res/layout/top_part_2.xml
new file mode 100644
index 0000000..b405921
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/layout/top_part_2.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/menu/bottom_nav_menu_main.xml b/CODE/RommteStory/app/src/main/res/menu/bottom_nav_menu_main.xml
new file mode 100644
index 0000000..847e229
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/menu/bottom_nav_menu_main.xml
@@ -0,0 +1,28 @@
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/menu/menu_sub.xml b/CODE/RommteStory/app/src/main/res/menu/menu_sub.xml
new file mode 100644
index 0000000..05a0789
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/menu/menu_sub.xml
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/CODE/RommteStory/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..c4a603d
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/CODE/RommteStory/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..c4a603d
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-hdpi/ic_launcher.png b/CODE/RommteStory/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..1fd3c0d
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/CODE/RommteStory/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..c2ca87e
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/CODE/RommteStory/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..df91ddb
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-mdpi/ic_launcher.png b/CODE/RommteStory/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..8d60bb1
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/CODE/RommteStory/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..efc8630
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/CODE/RommteStory/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..2e4ddfa
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/CODE/RommteStory/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..d65a954
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/CODE/RommteStory/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..27120db
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/CODE/RommteStory/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..9e6b57b
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/CODE/RommteStory/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..6fb17b1
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/CODE/RommteStory/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..b16bce2
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/CODE/RommteStory/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..957e663
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/CODE/RommteStory/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4004c2f
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/CODE/RommteStory/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..bf66730
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/CODE/RommteStory/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/CODE/RommteStory/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b4d76f3
Binary files /dev/null and b/CODE/RommteStory/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/CODE/RommteStory/app/src/main/res/navigation/mobile_navigation.xml b/CODE/RommteStory/app/src/main/res/navigation/mobile_navigation.xml
new file mode 100644
index 0000000..905e145
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/navigation/mobile_navigation.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/values-night/themes.xml b/CODE/RommteStory/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..4537b83
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/values/colors.xml b/CODE/RommteStory/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..fb1bb92
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/values/colors.xml
@@ -0,0 +1,11 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+ #97C0D4
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/values/dimens.xml b/CODE/RommteStory/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..e00c2dd
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/values/strings.xml b/CODE/RommteStory/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..638fc50
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/values/strings.xml
@@ -0,0 +1,14 @@
+
+ 舍友记
+ Home
+ Dashboard
+ Notifications
+ 舍友信息
+ 备忘录
+ 大事记
+ 我的
+
+ Hello blank fragment
+
+ 我的室友
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/main/res/values/themes.xml b/CODE/RommteStory/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..043588e
--- /dev/null
+++ b/CODE/RommteStory/app/src/main/res/values/themes.xml
@@ -0,0 +1,34 @@
+
+
+
+
\ No newline at end of file
diff --git a/CODE/RommteStory/app/src/test/java/com/lh/app/ExampleUnitTest.java b/CODE/RommteStory/app/src/test/java/com/lh/app/ExampleUnitTest.java
new file mode 100644
index 0000000..9875efd
--- /dev/null
+++ b/CODE/RommteStory/app/src/test/java/com/lh/app/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.lh.app;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/build.gradle b/CODE/RommteStory/build.gradle
new file mode 100644
index 0000000..905d3bd
--- /dev/null
+++ b/CODE/RommteStory/build.gradle
@@ -0,0 +1,9 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ id 'com.android.application' version '7.1.3' apply false
+ id 'com.android.library' version '7.1.3' apply false
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/CODE/RommteStory/gradle.properties b/CODE/RommteStory/gradle.properties
new file mode 100644
index 0000000..dab7c28
--- /dev/null
+++ b/CODE/RommteStory/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/CODE/RommteStory/gradle/wrapper/gradle-wrapper.jar b/CODE/RommteStory/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/CODE/RommteStory/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/CODE/RommteStory/gradle/wrapper/gradle-wrapper.properties b/CODE/RommteStory/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..275d969
--- /dev/null
+++ b/CODE/RommteStory/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun Mar 23 15:03:12 CST 2025
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/CODE/RommteStory/gradlew b/CODE/RommteStory/gradlew
new file mode 100644
index 0000000..4f906e0
--- /dev/null
+++ b/CODE/RommteStory/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/CODE/RommteStory/gradlew.bat b/CODE/RommteStory/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/CODE/RommteStory/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/CODE/RommteStory/settings.gradle b/CODE/RommteStory/settings.gradle
new file mode 100644
index 0000000..122eea8
--- /dev/null
+++ b/CODE/RommteStory/settings.gradle
@@ -0,0 +1,24 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ maven { url 'https://maven.aliyun.com/repository/google' }
+ maven { url 'https://maven.aliyun.com/repository/public' }
+ maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
+ maven { url 'https://jitpack.io' }
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ maven { url 'https://maven.aliyun.com/repository/google' }
+ maven { url 'https://maven.aliyun.com/repository/public' }
+ maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
+ maven { url 'https://jitpack.io' }
+ }
+}
+rootProject.name = "RommteStory"
+include ':app'
diff --git a/CODE/MyApplication_release_v1.0_2025_04_13_17_45_28.apk b/CODE/base.apk
similarity index 77%
rename from CODE/MyApplication_release_v1.0_2025_04_13_17_45_28.apk
rename to CODE/base.apk
index d727398..4e85496 100644
Binary files a/CODE/MyApplication_release_v1.0_2025_04_13_17_45_28.apk and b/CODE/base.apk differ
diff --git a/需求分析/软件设计文档0421.doc b/需求分析/软件设计文档0421.doc
new file mode 100644
index 0000000..c087086
Binary files /dev/null and b/需求分析/软件设计文档0421.doc differ