Compare commits

..

1 Commits

Author SHA1 Message Date
prnmvbwqp fd8fa214f4 ADD file via upload
2 years ago

@ -1,25 +1,2 @@
# FruitandVegetableGuide
## 系统简介
这是一个专为安卓用户设计的蔬果选购和食用指南。此应用程序旨在帮助您更好地挑选各种蔬果,同时提供一个平台,让您可以分享和获取有关蔬果的各种经验。
<img src="src\FruitandVegetableGuide\app\src\main\res\drawable/screen_shot.png" width = "300" height = "700" alt="screen_shot" align=center />
### 主要功能
1. 提供蔬果挑选方法:我们为您提供了科学的挑选方法,帮助您根据新鲜度、营养成分和口感等因素来挑选最适合您的蔬果。
2. 拍照识别:通过我们的拍照识别功能,您只需对准任何蔬果,我们的系统就能自动识别并给出相关建议。
3. 经验分享:我们鼓励用户在平台上分享自己的蔬果选购和食用经验。您可以发布文章,与其他用户分享您的知识和技巧。
4. 搜索:在我们的数据库中,您可以找到有关蔬果的指南文章。我们提供了搜索功能,帮助您快速找到您需要的信息。
## 配置环境
### 开发环境
1. Android Studio版本2022.2.1 Patch2及以上
2. Gradle Version 8.0,AGP Version 8.0.2
3. compileSdk 34
4. 阿里云SQL Server类型云数据库支持
5. 百度AI图像识别API支持
### 使用环境
1. 安卓系统应用支持Android操作系统Android版本需要为7.0及以上(minSDK = 24)。
2. 网络环境:用户需要有良好的网络连接并提供网络权限,以便访问和下载系统内的各种资源。
3. 硬件要求:设备应具有摄像头并提供图片文件存取权限。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

@ -1,15 +0,0 @@
*.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

@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>

@ -1,414 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DBNavigator.Project.DatabaseBrowserManager">
<autoscroll-to-editor value="false" />
<autoscroll-from-editor value="true" />
<show-object-properties value="true" />
<loaded-nodes />
</component>
<component name="DBNavigator.Project.DatabaseFileManager">
<open-files />
</component>
<component name="DBNavigator.Project.ExecutionManager">
<retain-sticky-names value="false" />
</component>
<component name="DBNavigator.Project.Settings">
<connections />
<browser-settings>
<general>
<display-mode value="TABBED" />
<navigation-history-size value="100" />
<show-object-details value="false" />
</general>
<filters>
<object-type-filter>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="true" />
<object-type name="ROLE" enabled="true" />
<object-type name="PRIVILEGE" enabled="true" />
<object-type name="CHARSET" enabled="true" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED_VIEW" enabled="true" />
<object-type name="NESTED_TABLE" enabled="true" />
<object-type name="COLUMN" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET_TRIGGER" enabled="true" />
<object-type name="DATABASE_TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="true" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="TYPE_ATTRIBUTE" enabled="true" />
<object-type name="ARGUMENT" enabled="true" />
<object-type name="DIMENSION" enabled="true" />
<object-type name="CLUSTER" enabled="true" />
<object-type name="DBLINK" enabled="true" />
</object-type-filter>
</filters>
<sorting>
<object-type name="COLUMN" sorting-type="NAME" />
<object-type name="FUNCTION" sorting-type="NAME" />
<object-type name="PROCEDURE" sorting-type="NAME" />
<object-type name="ARGUMENT" sorting-type="POSITION" />
<object-type name="TYPE ATTRIBUTE" sorting-type="POSITION" />
</sorting>
<default-editors>
<object-type name="VIEW" editor-type="SELECTION" />
<object-type name="PACKAGE" editor-type="SELECTION" />
<object-type name="TYPE" editor-type="SELECTION" />
</default-editors>
</browser-settings>
<navigation-settings>
<lookup-filters>
<lookup-objects>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="false" />
<object-type name="ROLE" enabled="false" />
<object-type name="PRIVILEGE" enabled="false" />
<object-type name="CHARSET" enabled="false" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED VIEW" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET TRIGGER" enabled="true" />
<object-type name="DATABASE TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="false" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="DIMENSION" enabled="false" />
<object-type name="CLUSTER" enabled="false" />
<object-type name="DBLINK" enabled="true" />
</lookup-objects>
<force-database-load value="false" />
<prompt-connection-selection value="true" />
<prompt-schema-selection value="true" />
</lookup-filters>
</navigation-settings>
<dataset-grid-settings>
<general>
<enable-zooming value="true" />
<enable-column-tooltip value="true" />
</general>
<sorting>
<nulls-first value="true" />
<max-sorting-columns value="4" />
</sorting>
<audit-columns>
<column-names value="" />
<visible value="true" />
<editable value="false" />
</audit-columns>
</dataset-grid-settings>
<dataset-editor-settings>
<text-editor-popup>
<active value="false" />
<active-if-empty value="false" />
<data-length-threshold value="100" />
<popup-delay value="1000" />
</text-editor-popup>
<values-actions-popup>
<show-popup-button value="true" />
<element-count-threshold value="1000" />
<data-length-threshold value="250" />
</values-actions-popup>
<general>
<fetch-block-size value="100" />
<fetch-timeout value="30" />
<trim-whitespaces value="true" />
<convert-empty-strings-to-null value="true" />
<select-content-on-cell-edit value="true" />
<large-value-preview-active value="true" />
</general>
<filters>
<prompt-filter-dialog value="true" />
<default-filter-type value="BASIC" />
</filters>
<qualified-text-editor text-length-threshold="300">
<content-types>
<content-type name="Text" enabled="true" />
<content-type name="Properties" enabled="true" />
<content-type name="XML" enabled="true" />
<content-type name="DTD" enabled="true" />
<content-type name="HTML" enabled="true" />
<content-type name="XHTML" enabled="true" />
<content-type name="Java" enabled="true" />
<content-type name="SQL" enabled="true" />
<content-type name="PL/SQL" enabled="true" />
<content-type name="JSON" enabled="true" />
<content-type name="JSON5" enabled="true" />
<content-type name="Groovy" enabled="true" />
<content-type name="AIDL" enabled="true" />
<content-type name="YAML" enabled="true" />
<content-type name="Manifest" enabled="true" />
</content-types>
</qualified-text-editor>
<record-navigation>
<navigation-target value="VIEWER" />
</record-navigation>
</dataset-editor-settings>
<code-editor-settings>
<general>
<show-object-navigation-gutter value="false" />
<show-spec-declaration-navigation-gutter value="true" />
<enable-spellchecking value="true" />
<enable-reference-spellchecking value="false" />
</general>
<confirmations>
<save-changes value="false" />
<revert-changes value="true" />
</confirmations>
</code-editor-settings>
<code-completion-settings>
<filters>
<basic-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="false" />
<filter-element type="OBJECT" id="view" selected="false" />
<filter-element type="OBJECT" id="materialized view" selected="false" />
<filter-element type="OBJECT" id="index" selected="false" />
<filter-element type="OBJECT" id="constraint" selected="false" />
<filter-element type="OBJECT" id="trigger" selected="false" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="false" />
<filter-element type="OBJECT" id="procedure" selected="false" />
<filter-element type="OBJECT" id="function" selected="false" />
<filter-element type="OBJECT" id="package" selected="false" />
<filter-element type="OBJECT" id="type" selected="false" />
<filter-element type="OBJECT" id="dimension" selected="false" />
<filter-element type="OBJECT" id="cluster" selected="false" />
<filter-element type="OBJECT" id="dblink" selected="false" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</basic-filter>
<extended-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</extended-filter>
</filters>
<sorting enabled="true">
<sorting-element type="RESERVED_WORD" id="keyword" />
<sorting-element type="RESERVED_WORD" id="datatype" />
<sorting-element type="OBJECT" id="column" />
<sorting-element type="OBJECT" id="table" />
<sorting-element type="OBJECT" id="view" />
<sorting-element type="OBJECT" id="materialized view" />
<sorting-element type="OBJECT" id="index" />
<sorting-element type="OBJECT" id="constraint" />
<sorting-element type="OBJECT" id="trigger" />
<sorting-element type="OBJECT" id="synonym" />
<sorting-element type="OBJECT" id="sequence" />
<sorting-element type="OBJECT" id="procedure" />
<sorting-element type="OBJECT" id="function" />
<sorting-element type="OBJECT" id="package" />
<sorting-element type="OBJECT" id="type" />
<sorting-element type="OBJECT" id="dimension" />
<sorting-element type="OBJECT" id="cluster" />
<sorting-element type="OBJECT" id="dblink" />
<sorting-element type="OBJECT" id="schema" />
<sorting-element type="OBJECT" id="role" />
<sorting-element type="OBJECT" id="user" />
<sorting-element type="RESERVED_WORD" id="function" />
<sorting-element type="RESERVED_WORD" id="parameter" />
</sorting>
<format>
<enforce-code-style-case value="true" />
</format>
</code-completion-settings>
<execution-engine-settings>
<statement-execution>
<fetch-block-size value="100" />
<execution-timeout value="20" />
<debug-execution-timeout value="600" />
<focus-result value="false" />
<prompt-execution value="false" />
</statement-execution>
<script-execution>
<command-line-interfaces />
<execution-timeout value="300" />
</script-execution>
<method-execution>
<execution-timeout value="30" />
<debug-execution-timeout value="600" />
<parameter-history-size value="10" />
</method-execution>
</execution-engine-settings>
<operation-settings>
<transactions>
<uncommitted-changes>
<on-project-close value="ASK" />
<on-disconnect value="ASK" />
<on-autocommit-toggle value="ASK" />
</uncommitted-changes>
<multiple-uncommitted-changes>
<on-commit value="ASK" />
<on-rollback value="ASK" />
</multiple-uncommitted-changes>
</transactions>
<session-browser>
<disconnect-session value="ASK" />
<kill-session value="ASK" />
<reload-on-filter-change value="false" />
</session-browser>
<compiler>
<compile-type value="KEEP" />
<compile-dependencies value="ASK" />
<always-show-controls value="false" />
</compiler>
<debugger>
<debugger-type value="ASK" />
</debugger>
</operation-settings>
<ddl-file-settings>
<extensions>
<mapping file-type-id="VIEW" extensions="vw" />
<mapping file-type-id="TRIGGER" extensions="trg" />
<mapping file-type-id="PROCEDURE" extensions="prc" />
<mapping file-type-id="FUNCTION" extensions="fnc" />
<mapping file-type-id="PACKAGE" extensions="pkg" />
<mapping file-type-id="PACKAGE_SPEC" extensions="pks" />
<mapping file-type-id="PACKAGE_BODY" extensions="pkb" />
<mapping file-type-id="TYPE" extensions="tpe" />
<mapping file-type-id="TYPE_SPEC" extensions="tps" />
<mapping file-type-id="TYPE_BODY" extensions="tpb" />
</extensions>
<general>
<lookup-ddl-files value="true" />
<create-ddl-files value="false" />
<synchronize-ddl-files value="true" />
<use-qualified-names value="false" />
<make-scripts-rerunnable value="true" />
</general>
</ddl-file-settings>
<general-settings>
<regional-settings>
<date-format value="MEDIUM" />
<number-format value="UNGROUPED" />
<locale value="SYSTEM_DEFAULT" />
<use-custom-formats value="false" />
</regional-settings>
<environment>
<environment-types>
<environment-type id="development" name="Development" description="Development environment" color="-2430209/-12296320" readonly-code="false" readonly-data="false" />
<environment-type id="integration" name="Integration" description="Integration environment" color="-2621494/-12163514" readonly-code="true" readonly-data="false" />
<environment-type id="production" name="Production" description="Productive environment" color="-11574/-10271420" readonly-code="true" readonly-data="true" />
<environment-type id="other" name="Other" description="" color="-1576/-10724543" readonly-code="false" readonly-data="false" />
</environment-types>
<visibility-settings>
<connection-tabs value="true" />
<dialog-headers value="true" />
<object-editor-tabs value="true" />
<script-editor-tabs value="false" />
<execution-result-tabs value="true" />
</visibility-settings>
</environment>
</general-settings>
</component>
</project>

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

@ -1,41 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
</profile>
</component>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.22" />
</component>
</project>

@ -1,9 +0,0 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

@ -1,92 +0,0 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.example.fruitandvegetableguide'
compileSdk 34
defaultConfig {
applicationId "com.example.fruitandvegetableguide"
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion '1.4.8'
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation "androidx.compose.foundation:foundation:1.0.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07"
//compose使
implementation("com.google.accompanist:accompanist-permissions:0.31.0-alpha")
//
implementation("com.google.accompanist:accompanist-placeholder-material:0.31.0-alpha")
implementation("io.coil-kt:coil-compose:2.2.2")
//gson
implementation 'com.google.code.gson:gson:2.10.1'
implementation("androidx.navigation:navigation-compose:2.5.3")
implementation "androidx.appcompat:appcompat:1.6.1"
implementation "androidx.core:core-ktx:1.10.1"
implementation "com.google.android.material:material:1.9.0"
implementation 'androidx.core:core-ktx:+'
implementation 'androidx.core:core-ktx:+'
implementation 'androidx.core:core-ktx:+'
implementation 'androidx.core:core-ktx:+'
implementation files('libs\\jtds-1.3.1.jar')
def composeBom = platform('androidx.compose:compose-bom:2023.08.00')
implementation(composeBom)
androidTestImplementation(composeBom)
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.material:material-icons-extended")
implementation 'androidx.compose.ui:ui-tooling-preview'
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation("androidx.compose.ui:ui-test-manifest")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1"
implementation("androidx.activity:activity-compose:1.7.2")
implementation 'androidx.core:core-ktx:1.10.1'
implementation "androidx.window:window:1.1.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

@ -1,21 +0,0 @@
# 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

@ -1,20 +0,0 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.example.fruitandvegetableguide",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 1,
"versionName": "1.0",
"outputFile": "app-release.apk"
}
],
"elementType": "File"
}

@ -1,24 +0,0 @@
package com.example.fruitandvegetableguide
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.fruitandvegetableguide", appContext.packageName)
}
}

@ -1,58 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- 权限清单-->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="31" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<!-- 权限清单-->
<application
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.FruitandVegetableGuide"
tools:targetApi="31">
<!-- provider-->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.image.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths" />
</provider>
<!-- provider-->
<!--android:exported=“false”当前Activity是否可以被另一个Application的组件启动true允许被启动false不允许被启动-->
<activity
android:name=".MyActivity"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

@ -1,62 +0,0 @@
package com.example.fruitandvegetableguide
// 每个界面路由接口
interface Destinations {
val route: String
}
//接口实现
object Login : Destinations {
override val route: String = "login"
}
object Register : Destinations {
override val route: String = "register"
}
object Main : Destinations {
override val route: String = "main"
}
object Search : Destinations {
override val route: String = "search"
}
object PostDetail : Destinations {
override val route: String = "postDetail"
}
object GuideDetail : Destinations {
override val route: String = "guideDetail"
}
object User : Destinations {
override val route: String = "user"
}
object MyPost : Destinations {
override val route: String = "myPost"
}
object Photograph : Destinations {
override val route: String = "photograph"
}
object RecognizeResult : Destinations {
override val route: String = "recognizeResult"
}
object Guide : Destinations {
override val route: String = "guide"
}
object Community : Destinations {
override val route: String = "community"
}
object PostEdit : Destinations {
override val route: String = "postEdit"
}
//val Screens = listOf(Login, Register, Main)

@ -1,50 +0,0 @@
package com.example.fruitandvegetableguide
import android.os.Bundle
import android.os.StrictMode
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.compose.rememberNavController
import com.example.fruitandvegetableguide.ui.theme.FruitandVegetableGuideTheme
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy)
setContent {
FruitandVegetableGuideTheme() {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MyApp()
}
}
}
}
}
@Composable
fun MyApp() {
//使用 rememberNavController() 获取 NavController因为它是整个应用的根可组合项和入口点
val navController = rememberNavController()
// val currentBackStack by navController.currentBackStackEntryAsState()
// val currentDestination = currentBackStack?.destination
Scaffold { innerPadding ->
NavHost(
navController = navController,
modifier = Modifier.padding(innerPadding)
)
}
}

@ -1,218 +0,0 @@
package com.example.fruitandvegetableguide
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import com.example.fruitandvegetableguide.ui.community.CommunityScreen
import com.example.fruitandvegetableguide.ui.community.PostDetailScreen
import com.example.fruitandvegetableguide.ui.community.PostEditScreen
import com.example.fruitandvegetableguide.ui.guide.GuideDetailScreen
import com.example.fruitandvegetableguide.ui.guide.GuideScreen
import com.example.fruitandvegetableguide.ui.imgrecognition.PhotographScreen
import com.example.fruitandvegetableguide.ui.imgrecognition.RecognizeResultScreen
import com.example.fruitandvegetableguide.ui.login.LoginScreen
import com.example.fruitandvegetableguide.ui.main.MainScreen
import com.example.fruitandvegetableguide.ui.register.RegisterScreen
import com.example.fruitandvegetableguide.ui.search.SearchScreen
import com.example.fruitandvegetableguide.ui.user.MyPostScreen
import com.example.fruitandvegetableguide.ui.user.UserScreen
@Composable
fun NavHost(
navController: NavHostController,
modifier: Modifier = Modifier
) {
NavHost(
navController = navController,
startDestination = Login.route,
modifier = modifier
) {
//登录页路由
composable(route = Login.route) {
LoginScreen(
onClickToRegister = { navController.navigateSingleTopTo(Register.route) },
onClickToMain = { userid -> navController.navigateSingleTopTo("${Main.route}/${userid}") }//传出userid
)
}
//注册页路由
composable(route = Register.route) {
RegisterScreen(
onClickBack = { navController.popBackStack() }
)
}
//首页路由
composable(
route = "${Main.route}/{userid}",
arguments = listOf(
navArgument("userid") {
type = NavType.IntType
nullable = false
}
)
) {
val userid = it.arguments?.getInt("userid")//接收userid
MainScreen(
userid = userid,
onClickToSearch = { search -> navController.navigateSingleTopTo("${Search.route}/${search}") },
onClickToUser = { navController.navigateSingleTopTo("${User.route}/${userid}") },
onClickToPhotograph = { navController.navigateSingleTopTo(Photograph.route) },
onClickToGuide = { navController.navigateSingleTopTo(Guide.route) },
onClickToCommunity = { navController.navigateSingleTopTo("${Community.route}/${userid}") },
onClickToGuideDetail = { guideId -> navController.navigateSingleTopTo("${GuideDetail.route}/${guideId}") },
onClickToPostDetail = { postId -> navController.navigateSingleTopTo("${PostDetail.route}/${postId}") }
)
}
//搜索页路由
composable(
route = "${Search.route}/{search}",
arguments = listOf(
navArgument("search") {
type = NavType.StringType
nullable = false
}
)
) {
val search = it.arguments?.getString("search")
SearchScreen(
search = search,
onClickBack = { navController.popBackStack() },
onClickToGuideDetail = { guideId -> navController.navigateSingleTopTo("${GuideDetail.route}/${guideId}") },
onClickToPostDetail = { postId -> navController.navigateSingleTopTo("${PostDetail.route}/${postId}") },
)
}
//指南详情页路由
composable(
route = "${GuideDetail.route}/{guideId}",
arguments = listOf(
navArgument("guideId") {
type = NavType.IntType
nullable = false
}
)
) {
val guideId = it.arguments?.getInt("guideId")
GuideDetailScreen(
guideId = guideId,
onClickBack = { navController.popBackStack() })
}
//帖子详情页路由
composable(
route = "${PostDetail.route}/{postId}",
arguments = listOf(
navArgument("postId") {
type = NavType.IntType
nullable = false
}
)
) {
val postId = it.arguments?.getInt("postId") //接收post的属性列表
PostDetailScreen(
postId = postId,
onClickBack = { navController.popBackStack() }
)
}
//用户页路由
composable(
route = "${User.route}/{userid}",
arguments = listOf(
navArgument("userid") {
type = NavType.IntType
nullable = false
}
)) {
val userid = it.arguments?.getInt("userid")//接收userid
UserScreen(
userid = userid,
onClickToMain = { navController.popBackStack() },
onClickToMyPost = { navController.navigateSingleTopTo("${MyPost.route}/${userid}") },
onClickToPhotograph = { navController.navigateSingleTopTo(Photograph.route) }
)
}
//我的帖子页面路由
composable(route = "${MyPost.route}/{userid}",
arguments = listOf(
navArgument("userid") {
type = NavType.IntType
nullable = false
}
)) {
val userid = it.arguments?.getInt("userid")//接收userid
MyPostScreen(userid = userid, onClickBack = { navController.popBackStack() })
}
//拍照页路由
composable(route = Photograph.route) {
PhotographScreen(
onClickBack = { navController.popBackStack() },
onClickToRecognizeResult = { localImgPath ->
navController.navigateSingleTopTo(
"${RecognizeResult.route}/${localImgPath}"
)
})
}
//识别结果页路由
composable(
route = "${RecognizeResult.route}/{localImgPath}",
arguments = listOf(
navArgument("localImgPath") {
type = NavType.StringType
nullable = false
})
) {
val localImgPath = it.arguments?.getString("localImgPath")
RecognizeResultScreen(
transPath = localImgPath,
onClickBack = { navController.popBackStack() })
}
//指南页路由
composable(route = Guide.route) {
GuideScreen(
onClickToSearch = { search -> navController.navigateSingleTopTo("${Search.route}/${search}") },
onClickToGuideDetail = { guideId -> navController.navigateSingleTopTo("${GuideDetail.route}/${guideId}") },
onClickBack = { navController.popBackStack() }
)
}
//社区页路由
composable(
route = "${Community.route}/{userid}",
arguments = listOf(
navArgument("userid") {
type = NavType.IntType
nullable = false
}
)
) {
val userid = it.arguments?.getInt("userid")//接收userid
CommunityScreen(
userid = userid,
onClickToSearch = { search -> navController.navigateSingleTopTo("${Search.route}/${search}") },
onClickToPostDetail = { postId -> navController.navigateSingleTopTo("${PostDetail.route}/${postId}") },
onClickToPostEdit = { navController.navigateSingleTopTo("${PostEdit.route}/${userid}") },
onClickBack = { navController.popBackStack() }
)
}
//帖子编辑页路由
composable(
route = "${PostEdit.route}/{userid}",
arguments = listOf(
navArgument("userid") {
type = NavType.IntType
nullable = false
}
)
) {
val userid = it.arguments?.getInt("userid")//接收userid
PostEditScreen(
userid = userid,
onClickBack = { navController.popBackStack() }
)
}
}
}
//如果栈中已经包含了指定要跳转的界面,那么只会保留一个
fun NavHostController.navigateSingleTopTo(route: String) =
this.navigate(route) { launchSingleTop = true }

@ -1,23 +0,0 @@
# 系统简介
这是一个专为安卓用户设计的蔬果选购和食用指南。此应用程序旨在帮助您更好地挑选各种蔬果,同时提供一个平台,让您可以分享和获取有关蔬果的各种经验。
<img src="../../../../res/drawable/screen_shot.png" width = "300" height = "700" alt="screen_shot" align=center />
## 主要功能
1. 提供蔬果挑选方法:我们为您提供了科学的挑选方法,帮助您根据新鲜度、营养成分和口感等因素来挑选最适合您的蔬果。
2. 拍照识别:通过我们的拍照识别功能,您只需对准任何蔬果,我们的系统就能自动识别并给出相关建议。
3. 经验分享:我们鼓励用户在平台上分享自己的蔬果选购和食用经验。您可以发布文章,与其他用户分享您的知识和技巧。
4. 搜索:在我们的数据库中,您可以找到有关蔬果的指南文章。我们提供了搜索功能,帮助您快速找到您需要的信息。
# 配置环境
## 开发环境
1. Android Studio版本2022.2.1 Patch2及以上
2. Gradle Version 8.0,AGP Version 8.0.2
3. compileSdk 34
4. 阿里云SQL Server类型云数据库支持
5. 百度AI图像识别API支持
## 使用环境
1. 安卓系统应用支持Android操作系统Android版本需要为7.0及以上(minSDK = 24)。
2. 网络环境:用户需要有良好的网络连接并提供网络权限,以便访问和下载系统内的各种资源。
3. 硬件要求:设备应具有摄像头并提供图片文件存取权限。

@ -1,8 +0,0 @@
package com.example.fruitandvegetableguide.data
//用户数据类
data class Account(
val id:Int, //id自增
val username: String, //用户名
val password:String //密码
)

@ -1,15 +0,0 @@
package com.example.fruitandvegetableguide.data
import com.example.fruitandvegetableguide.R
//指南数据类
data class Guide(
val id: Int, //id自增
val imgId: Int = R.drawable.loading,
val kind: String, //种类
val identify :String, //辨识方法
val suitable: String, //适宜人群
val taboo: String, //禁忌
val nutritiveValue: String, //营养价值
val recommendedMenu: String //推荐菜
)

@ -1,13 +0,0 @@
package com.example.fruitandvegetableguide.data
import com.example.fruitandvegetableguide.R
//帖子数据类
data class Post(
val id: Int, //id自增
val userId: Int, //用户id
val title: String, //标题
val imgId: Int = R.drawable.loading,
val labels: String, //标签
val content: String //内容
)

@ -1,421 +0,0 @@
package com.example.fruitandvegetableguide.data.dbhelper
import android.util.Log
import androidx.compose.runtime.rememberCompositionContext
import com.example.fruitandvegetableguide.R
import com.example.fruitandvegetableguide.data.Guide
import com.example.fruitandvegetableguide.data.Post
import com.example.fruitandvegetableguide.data.local.LocalGuidesDataProvider
import java.sql.Connection
import java.sql.DriverManager
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.SQLException
import kotlinx.coroutines.*
object DbHelper {
//private const val DB_URL = "jdbc:sqlserver://rm-2ze1sd060o140gu067o.sqlserver.rds.aliyuncs.com:3433;databaseName=FruitandVegetableGuide"
private const val DB_URL =
"jdbc:jtds:sqlserver://rm-2ze1sd060o140gu067o.sqlserver.rds.aliyuncs.com:3433/FruitandVegetableGuide"
private const val USER = "aruan"
private const val PASS = "WRLXwrlx465434"
fun connect(): Connection? {
var conn: Connection? = null
try {
Class.forName("net.sourceforge.jtds.jdbc.Driver")
conn = DriverManager.getConnection(DB_URL, USER, PASS)
} catch (e: SQLException) {
e.printStackTrace()
} catch (e: ClassNotFoundException) {
e.printStackTrace()
}
return conn
}
fun closeConnection(conn: Connection?) {
try {
conn?.close()
} catch (e: SQLException) {
e.printStackTrace()
}
}
fun login(username: String, password: String): Int {
val conn = connect()
var userId = -1 // 默认为 -1表示未找到用户或不匹配
if (conn != null) {
try {
val selectQuery = "SELECT id, password FROM Account WHERE username = ?"
val preparedStatement = conn.prepareStatement(selectQuery)
preparedStatement.setString(1, username) // 使用用户名作为参数
val resultSet = preparedStatement.executeQuery()
if (resultSet.next()) {
val storedPassword = resultSet.getString("password")
val userIdFromDB = resultSet.getInt("id")
// 检查提供的密码与数据库中存储的密码是否匹配
if (storedPassword == password) {
userId = userIdFromDB // 密码匹配,将用户 ID 设置为查询到的 ID
}
}
resultSet.close()
preparedStatement.close()
} catch (e: SQLException) {
e.printStackTrace()
} finally {
closeConnection(conn)
}
}
return userId
}
fun register(username: String, password: String): Boolean {
val conn = connect()
var rowsAffected = 0
if (conn != null) {
try {
val insertQuery = "INSERT INTO Account (username, password) VALUES (?, ?)"
val preparedStatement = conn.prepareStatement(insertQuery)
preparedStatement.setString(1, username)
preparedStatement.setString(2, password)
rowsAffected = preparedStatement.executeUpdate()
preparedStatement.close()
} catch (e: SQLException) {
e.printStackTrace()
} finally {
closeConnection(conn)
}
}
return rowsAffected > 0
}
//
fun postListInit(): MutableList<Post> {
val conn = connect()
val posts = mutableListOf<Post>()
if (conn != null) {
try {
val postQuery = "SELECT TOP 100 * FROM Post" // 假设post表是你的帖子表
val preparedStatement = conn.prepareStatement(postQuery)
val resultSet = preparedStatement.executeQuery()
while (resultSet.next()) {
val id = resultSet.getInt("id")
val userId = resultSet.getInt("userId")
val title = resultSet.getString("title")
val labels = resultSet.getString("labels")
val content = resultSet.getString("content")
val post = Post(id, userId, title, labels = labels, content = content)
posts.add(post)
}
preparedStatement.close()
} catch (e: SQLException) {
e.printStackTrace()
} finally {
closeConnection(conn)
}
}
println("贴子初始化成功")
return posts
}
fun getPostByPostId(postId: Int): Post {
val conn = connect()
var post = Post(0, 0, "", labels = "", content = "")
if (conn != null) {
try {
val postQuery = "select * from Post where id = ?"
val preparedStatement = conn.prepareStatement(postQuery)
preparedStatement.setInt(1, postId)
val resultSet = preparedStatement.executeQuery()
resultSet.next()
val id = resultSet.getInt("id")
val userId = resultSet.getInt("userId")
val title = resultSet.getString("title")
val labels = resultSet.getString("labels")
val content = resultSet.getString("content")
post = Post(
id = id,
userId = userId,
title = title,
labels = labels,
content = content
)
preparedStatement.close()
} catch (e: SQLException) {
e.printStackTrace()
} finally {
closeConnection(conn)
}
}
return post
}
fun getGuideByKind(kind: String): List<Guide> {
var guides = mutableListOf<Guide>()
var coon = connect()
if (coon != null) {
try {
val guideQuery = "select * from Guide where kind = ?"
val preparedStatement = coon.prepareStatement(guideQuery)
preparedStatement.setString(1, kind)
val resultSet = preparedStatement.executeQuery()
while (resultSet.next()) {
val id = resultSet.getInt("id")
val kind = resultSet.getString("kind")
val identify = resultSet.getString("identify")
val suitable = resultSet.getString("suitable")
val taboo = resultSet.getString("taboo")
val nutritiveValue = resultSet.getString("nutritiveValue")
val recommendedMenu = resultSet.getString("recommendedMenu")
val guide = Guide(
id,
kind = kind,
identify = identify,
suitable = suitable,
taboo = taboo,
nutritiveValue = nutritiveValue,
recommendedMenu = recommendedMenu
)
guides.add(guide)
}
preparedStatement.close()
} catch (e: SQLException) {
e.printStackTrace()
} finally {
closeConnection(coon)
}
}
return guides
}
fun getPostByLabels(labels: String): List<Post> {
var posts = mutableListOf<Post>()
var coon = connect()
if (coon != null) {
try {
val postQuery = "select * from Post where labels LIKE ?"
val preparedStatement = coon.prepareStatement(postQuery)
val temp = "%$labels%"
preparedStatement.setString(1, temp)
val resultSet = preparedStatement.executeQuery()
while (resultSet.next()) {
val id = resultSet.getInt("id")
val userid = resultSet.getInt("userid")
val title = resultSet.getString("title")
val labels = resultSet.getString("labels")
val content = resultSet.getString("content")
val post = Post(id, userid, title, labels = labels, content = content)
posts.add(post)
}
preparedStatement.close()
} catch (e: SQLException) {
//e.printStackTrace()
} finally {
closeConnection(coon)
}
}
return posts
}
fun getGuideByGuideId(guideId: Int): Guide {
val conn = connect()
var guide = Guide(
0,
kind = "",
identify = "",
suitable = "",
taboo = "",
nutritiveValue = "",
recommendedMenu = ""
)
if (conn != null) {
try {
val guideQuery = "select * from Guide where id = ?"
val preparedStatement = conn.prepareStatement(guideQuery)
preparedStatement.setInt(1, guideId)
val resultSet = preparedStatement.executeQuery()
resultSet.next()
val id = resultSet.getInt("id")
val kind = resultSet.getString("kind")
val identify = resultSet.getString("identify")
val suitable = resultSet.getString("suitable")
val taboo = resultSet.getString("taboo")
val nutritiveValue = resultSet.getString("nutritiveValue")
val recommendedMenu = resultSet.getString("recommendedMenu")
guide = Guide(
id = guideId,
kind = kind,
identify = identify,
suitable = suitable,
taboo = taboo,
nutritiveValue = nutritiveValue,
recommendedMenu = recommendedMenu
)
preparedStatement.close()
} catch (e: SQLException) {
e.printStackTrace()
} finally {
closeConnection(conn)
}
}
return guide
}
//
fun guideListInit(): List<Guide> {
val conn = connect()
val guides = LocalGuidesDataProvider.allGuides
if (conn != null) {
try {
val guideQuery = "SELECT TOP 100 * FROM Guide"
Log.d("guides", "guideListInit")
val preparedStatement = conn.prepareStatement(guideQuery)
val resultSet = preparedStatement.executeQuery()
while (resultSet.next()) {
val id = resultSet.getInt("id")
val kind = resultSet.getString("kind")
val identify = resultSet.getString("identify")
val suitable = resultSet.getString("suitable")
val taboo = resultSet.getString("taboo")
val nutritiveValue = resultSet.getString("nutritiveValue")
val recommendedMenu = resultSet.getString("recommendedMenu")
val guide = Guide(
id,
kind = kind,
identify = identify,
suitable = suitable,
taboo = taboo,
nutritiveValue = nutritiveValue,
recommendedMenu = recommendedMenu
)
guides.add(guide)
}
preparedStatement.close()
} catch (e: SQLException) {
e.printStackTrace()
} finally {
closeConnection(conn)
}
}
for (guide in guides) {
Log.d("guides", "dbHelper,guideListInit: $guide")
}
return guides
}
fun postInsert(
userid: Int,
title: String,
imgId: Int = R.drawable.loading,
labels: String,
content: String
): Boolean {
val coon = connect()
var rowsAffected = 0
if (coon != null) {
try {
val postInsert =
"insert into Post (userId, title, imgId, labels, content) values (?, ?, ?, ?, ?)"
val preparedStatement = coon.prepareStatement(postInsert)
preparedStatement.setInt(1, userid);
preparedStatement.setString(2, title)
preparedStatement.setInt(3, imgId)
preparedStatement.setString(4, labels)
preparedStatement.setString(5, content)
rowsAffected = preparedStatement.executeUpdate()
preparedStatement.close()
} catch (e: SQLException) {
e.printStackTrace()
} finally {
closeConnection(coon)
}
}
println("postInsert成功")
return rowsAffected > 0
}
fun getUsernameByUserid(userid: Int): String {
val coon = connect()
var username = ""
if (coon != null) {
try {
val accountQuery = "select * from Account where id = ?"
val preparedStatement = coon.prepareStatement(accountQuery)
preparedStatement.setInt(1, userid)
val resultSet = preparedStatement.executeQuery()
resultSet.next()
username = resultSet.getString("username")
preparedStatement.close()
} catch (e: SQLException) {
e.printStackTrace()
} finally {
closeConnection(coon)
}
}
println("getUsernameByUserid成功")
return username
}
fun getPostByUserid(userid: Int): List<Post> {
var posts = mutableListOf<Post>()
var coon = connect()
if (coon != null) {
try {
val postQuery = "select * from Post where userID = ?"
val preparedStatement = coon.prepareStatement(postQuery)
preparedStatement.setInt(1, userid)
val resultSet = preparedStatement.executeQuery()
while (resultSet.next()) {
val id = resultSet.getInt("id")
val userid = resultSet.getInt("userid")
val title = resultSet.getString("title")
val labels = resultSet.getString("labels")
val content = resultSet.getString("content")
val post = Post(id, userid, title, labels = labels, content = content)
posts.add(post)
}
preparedStatement.close()
} catch (e: SQLException) {
e.printStackTrace()
} finally {
closeConnection(coon)
}
}
return posts
}
//
fun DeletePost(postToDeleteList: List<Post>): Boolean {
var coon = connect()
var rowsAffected = 0
if (coon != null) {
try {
val postDelete = "delete from Post where id = ?"
val preparedStatement = coon.prepareStatement(postDelete)
for (post in postToDeleteList) {
preparedStatement.setInt(1, post.id)
val resultSet = preparedStatement.executeUpdate()
if (resultSet > 0) rowsAffected++
}
preparedStatement.close()
} catch (e: SQLException) {
e.printStackTrace()
} finally {
closeConnection(coon)
}
}
if (rowsAffected == postToDeleteList.size) {
println("DeletePost成功")
return true
}
//println("DeletePost失败")
return false
}
}

@ -1,12 +0,0 @@
package com.example.fruitandvegetableguide.data.local
import com.example.fruitandvegetableguide.data.Account
//本地用户测试数据
object LocalAccountsDataProvider {
val allUserAccounts = mutableListOf(
Account(1,"aaa","123456"),
Account(2,"bbb","123456"),
Account(3,"ccc","123456")
)
}

@ -1,132 +0,0 @@
package com.example.fruitandvegetableguide.data.local
import com.example.fruitandvegetableguide.data.Guide
import com.example.fruitandvegetableguide.R
//本地指南测试数据
object LocalGuidesDataProvider {
val allGuides = mutableListOf(
Guide(
100000,
R.drawable.carrots,
"种类-默认",
"1、首先就要观察外表看表皮有没有破损甚至坑坑洼洼掉皮的胡萝卜会让其水分蒸发肯定会影响口感并且不容易存放。\n" +
"2、把两个相同大小的胡萝卜拿在手里掂一下如果有一个轻得多的就不要买了说明放置的时间太长了里面的水分减少要买那种沉甸甸的胡萝卜水分更充足更新鲜好吃。所以大家买胡萝卜的时候最好用手感受一下重量。\n" +
"3、个头偏大的长得比较老水分肯定少口感不鲜嫩而个头偏小的也不行虽然很脆嫩但是还没有完全长成熟营养价值不高。挑胡萝卜尽量选中等个头粗细均匀带有根须的。",
"适宜-默认",
"禁忌-默认",
"营养价值-默认",
"推荐菜-默认"
),
Guide(
29,
R.drawable.cucumbers,
"黄瓜",
"颜色:黄瓜的颜色应该是均匀的绿色,没有任何瑕疵。\n手感:轻轻掐一下黄瓜,如果手感觉进入黄瓜时很脆嫩,而且有水分出来的黄瓜比较鲜嫩。\n刺:黄瓜的刺特别嫩才是质量口感好的黄瓜。看到黄瓜表皮的刺小而密,而且轻轻一摸就会碎断,这种黄瓜十分新鲜润滑。\n纵陵:选择黄瓜的时候要选择黄瓜的瓜体呈棒状带有纵陵的一类黄瓜。\n顶花:好的黄瓜,顶花会在黄瓜成熟后自然脱落,留下个小黄点。\n大小:太大的黄瓜一般是催熟的黄瓜,相对来说个头小的黄瓜比较好吃。",
"一般人群均可食用。尤适宜[热病、肥胖、高血脂、水肿、癌症、嗜酒者、糖尿病 ]患者食用。",
"[腰胃虚弱、腹痛腹泻、肺寒咳嗽、 肝病心血管病、肠胃病]患者慎食。",
"蛋白质: 0.8克, 维生素A: 15微克, 胡萝卜素: 90微克",
"黄瓜玉子烧"
),
Guide(
25,
R.drawable.tomatoes,
"西红柿",
"1. 选择正常大小的西红柿,不要买畸形的西红柿,因为畸形的很有可能是打了一些对人体有害的药在里面,有助于西红柿快速的生长.\n2. 选择能从表皮面能看到西红柿内部的大概结构,这也能说明西红柿是很健康的.\n3. 选择西红柿顶端是有裂缝的,意思就是有一些小小的裂缝痕迹,这样也能说明西红柿没有打催生剂.\n4. 选择西红柿里面不是绿色的,因为现场也不能做好实验,可以先买一个回去试一试,切开看西红柿里面是不是绿色的,如果是绿色的,西红柿外表是红色的,这只能说明西红柿并未成熟,而是打了催生剂.\n5. 选择比较硬一点的西红柿,这样就比较新鲜,同时也容易放置,这样也就不容易坏掉了¹.",
"一般人群均可食用。尤适宜于[热性病发热、口渴、食欲不振、习惯性牙龈出血、贫血、头晕心悸、高血压、急慢性肝炎、急慢性肾炎、夜盲症、近视眼]者食用",
"[急性肠炎、菌痢、溃疡活动期]病人不宜食",
"热量: 19千卡,蛋白质: 0.9 克, 碳水化合物: 4克",
"番茄乌冬面"
),
Guide(
27,
R.drawable.carrots,
"胡萝卜",
"1、首先就要观察外表看表皮有没有破损甚至坑坑洼洼掉皮的胡萝卜会让其水分蒸发肯定会影响口感并且不容易存放。\n2、把两个相同大小的胡萝卜拿在手里掂一下如果有一个轻得多的就不要买了说明放置的时间太长了里面的水分减少要买那种沉甸甸的胡萝卜水分更充足更新鲜好吃。所以大家买胡萝卜的时候最好用手感受一下重量。\n3、个头偏大的长得比较老水分肯定少口感不鲜嫩而个头偏小的也不行虽然很脆嫩但是还没有完全长成熟营养价值不高。挑胡萝卜尽量选中等个头粗细均匀带有根须的。\n",
"一般人皆可食用,尤以[癌症、高血压、夜盲症干眼症、营养不良、皮肤粗糙]者为最。",
"[饮酒过量]者不宜食用",
"热量: 37千卡, 蛋白质: 1克, 碳水化合物: 8.8克, 膳食纤维3.2克, 胡萝卜素: 4130微克",
"炝腌萝卜"
),
Guide(
29,
R.drawable.cucumbers,
"黄瓜",
"颜色:黄瓜的颜色应该是均匀的绿色,没有任何瑕疵。\n手感:轻轻掐一下黄瓜,如果手感觉进入黄瓜时很脆嫩,而且有水分出来的黄瓜比较鲜嫩。\n刺:黄瓜的刺特别嫩才是质量口感好的黄瓜。看到黄瓜表皮的刺小而密,而且轻轻一摸就会碎断,这种黄瓜十分新鲜润滑。\n纵陵:选择黄瓜的时候要选择黄瓜的瓜体呈棒状带有纵陵的一类黄瓜。\n顶花:好的黄瓜,顶花会在黄瓜成熟后自然脱落,留下个小黄点。\n大小:太大的黄瓜一般是催熟的黄瓜,相对来说个头小的黄瓜比较好吃。",
"一般人群均可食用。尤适宜[热病、肥胖、高血脂、水肿、癌症、嗜酒者、糖尿病 ]患者食用。",
"[腰胃虚弱、腹痛腹泻、肺寒咳嗽、 肝病心血管病、肠胃病]患者慎食。",
"蛋白质: 0.8克, 维生素A: 15微克, 胡萝卜素: 90微克",
"黄瓜玉子烧"
),
Guide(
25,
R.drawable.tomatoes,
"西红柿",
"1. 选择正常大小的西红柿,不要买畸形的西红柿,因为畸形的很有可能是打了一些对人体有害的药在里面,有助于西红柿快速的生长.\n2. 选择能从表皮面能看到西红柿内部的大概结构,这也能说明西红柿是很健康的.\n3. 选择西红柿顶端是有裂缝的,意思就是有一些小小的裂缝痕迹,这样也能说明西红柿没有打催生剂.\n4. 选择西红柿里面不是绿色的,因为现场也不能做好实验,可以先买一个回去试一试,切开看西红柿里面是不是绿色的,如果是绿色的,西红柿外表是红色的,这只能说明西红柿并未成熟,而是打了催生剂.\n5. 选择比较硬一点的西红柿,这样就比较新鲜,同时也容易放置,这样也就不容易坏掉了¹.",
"一般人群均可食用。尤适宜于[热性病发热、口渴、食欲不振、习惯性牙龈出血、贫血、头晕心悸、高血压、急慢性肝炎、急慢性肾炎、夜盲症、近视眼]者食用",
"[急性肠炎、菌痢、溃疡活动期]病人不宜食",
"热量: 19千卡,蛋白质: 0.9 克, 碳水化合物: 4克",
"番茄乌冬面"
),
Guide(
27,
R.drawable.carrots,
"胡萝卜",
"1、首先就要观察外表看表皮有没有破损甚至坑坑洼洼掉皮的胡萝卜会让其水分蒸发肯定会影响口感并且不容易存放。\n2、把两个相同大小的胡萝卜拿在手里掂一下如果有一个轻得多的就不要买了说明放置的时间太长了里面的水分减少要买那种沉甸甸的胡萝卜水分更充足更新鲜好吃。所以大家买胡萝卜的时候最好用手感受一下重量。\n3、个头偏大的长得比较老水分肯定少口感不鲜嫩而个头偏小的也不行虽然很脆嫩但是还没有完全长成熟营养价值不高。挑胡萝卜尽量选中等个头粗细均匀带有根须的。\n",
"一般人皆可食用,尤以[癌症、高血压、夜盲症干眼症、营养不良、皮肤粗糙]者为最。",
"[饮酒过量]者不宜食用",
"热量: 37千卡, 蛋白质: 1克, 碳水化合物: 8.8克, 膳食纤维3.2克, 胡萝卜素: 4130微克",
"炝腌萝卜"
),
Guide(
29,
R.drawable.cucumbers,
"黄瓜",
"颜色:黄瓜的颜色应该是均匀的绿色,没有任何瑕疵。\n手感:轻轻掐一下黄瓜,如果手感觉进入黄瓜时很脆嫩,而且有水分出来的黄瓜比较鲜嫩。\n刺:黄瓜的刺特别嫩才是质量口感好的黄瓜。看到黄瓜表皮的刺小而密,而且轻轻一摸就会碎断,这种黄瓜十分新鲜润滑。\n纵陵:选择黄瓜的时候要选择黄瓜的瓜体呈棒状带有纵陵的一类黄瓜。\n顶花:好的黄瓜,顶花会在黄瓜成熟后自然脱落,留下个小黄点。\n大小:太大的黄瓜一般是催熟的黄瓜,相对来说个头小的黄瓜比较好吃。",
"一般人群均可食用。尤适宜[热病、肥胖、高血脂、水肿、癌症、嗜酒者、糖尿病 ]患者食用。",
"[腰胃虚弱、腹痛腹泻、肺寒咳嗽、 肝病心血管病、肠胃病]患者慎食。",
"蛋白质: 0.8克, 维生素A: 15微克, 胡萝卜素: 90微克",
"黄瓜玉子烧"
),
Guide(
25,
R.drawable.tomatoes,
"西红柿",
"1. 选择正常大小的西红柿,不要买畸形的西红柿,因为畸形的很有可能是打了一些对人体有害的药在里面,有助于西红柿快速的生长.\n2. 选择能从表皮面能看到西红柿内部的大概结构,这也能说明西红柿是很健康的.\n3. 选择西红柿顶端是有裂缝的,意思就是有一些小小的裂缝痕迹,这样也能说明西红柿没有打催生剂.\n4. 选择西红柿里面不是绿色的,因为现场也不能做好实验,可以先买一个回去试一试,切开看西红柿里面是不是绿色的,如果是绿色的,西红柿外表是红色的,这只能说明西红柿并未成熟,而是打了催生剂.\n5. 选择比较硬一点的西红柿,这样就比较新鲜,同时也容易放置,这样也就不容易坏掉了¹.",
"一般人群均可食用。尤适宜于[热性病发热、口渴、食欲不振、习惯性牙龈出血、贫血、头晕心悸、高血压、急慢性肝炎、急慢性肾炎、夜盲症、近视眼]者食用",
"[急性肠炎、菌痢、溃疡活动期]病人不宜食",
"热量: 19千卡,蛋白质: 0.9 克, 碳水化合物: 4克",
"番茄乌冬面"
),
Guide(
27,
R.drawable.carrots,
"胡萝卜",
"1、首先就要观察外表看表皮有没有破损甚至坑坑洼洼掉皮的胡萝卜会让其水分蒸发肯定会影响口感并且不容易存放。\n2、把两个相同大小的胡萝卜拿在手里掂一下如果有一个轻得多的就不要买了说明放置的时间太长了里面的水分减少要买那种沉甸甸的胡萝卜水分更充足更新鲜好吃。所以大家买胡萝卜的时候最好用手感受一下重量。\n3、个头偏大的长得比较老水分肯定少口感不鲜嫩而个头偏小的也不行虽然很脆嫩但是还没有完全长成熟营养价值不高。挑胡萝卜尽量选中等个头粗细均匀带有根须的。\n",
"一般人皆可食用,尤以[癌症、高血压、夜盲症干眼症、营养不良、皮肤粗糙]者为最。",
"[饮酒过量]者不宜食用",
"热量: 37千卡, 蛋白质: 1克, 碳水化合物: 8.8克, 膳食纤维3.2克, 胡萝卜素: 4130微克",
"炝腌萝卜"
),
Guide(
29,
R.drawable.cucumbers,
"黄瓜",
"颜色:黄瓜的颜色应该是均匀的绿色,没有任何瑕疵。\n手感:轻轻掐一下黄瓜,如果手感觉进入黄瓜时很脆嫩,而且有水分出来的黄瓜比较鲜嫩。\n刺:黄瓜的刺特别嫩才是质量口感好的黄瓜。看到黄瓜表皮的刺小而密,而且轻轻一摸就会碎断,这种黄瓜十分新鲜润滑。\n纵陵:选择黄瓜的时候要选择黄瓜的瓜体呈棒状带有纵陵的一类黄瓜。\n顶花:好的黄瓜,顶花会在黄瓜成熟后自然脱落,留下个小黄点。\n大小:太大的黄瓜一般是催熟的黄瓜,相对来说个头小的黄瓜比较好吃。",
"一般人群均可食用。尤适宜[热病、肥胖、高血脂、水肿、癌症、嗜酒者、糖尿病 ]患者食用。",
"[腰胃虚弱、腹痛腹泻、肺寒咳嗽、 肝病心血管病、肠胃病]患者慎食。",
"蛋白质: 0.8克, 维生素A: 15微克, 胡萝卜素: 90微克",
"黄瓜玉子烧"
),
Guide(
25,
R.drawable.tomatoes,
"西红柿",
"1. 选择正常大小的西红柿,不要买畸形的西红柿,因为畸形的很有可能是打了一些对人体有害的药在里面,有助于西红柿快速的生长.\n2. 选择能从表皮面能看到西红柿内部的大概结构,这也能说明西红柿是很健康的.\n3. 选择西红柿顶端是有裂缝的,意思就是有一些小小的裂缝痕迹,这样也能说明西红柿没有打催生剂.\n4. 选择西红柿里面不是绿色的,因为现场也不能做好实验,可以先买一个回去试一试,切开看西红柿里面是不是绿色的,如果是绿色的,西红柿外表是红色的,这只能说明西红柿并未成熟,而是打了催生剂.\n5. 选择比较硬一点的西红柿,这样就比较新鲜,同时也容易放置,这样也就不容易坏掉了¹.",
"一般人群均可食用。尤适宜于[热性病发热、口渴、食欲不振、习惯性牙龈出血、贫血、头晕心悸、高血压、急慢性肝炎、急慢性肾炎、夜盲症、近视眼]者食用",
"[急性肠炎、菌痢、溃疡活动期]病人不宜食",
"热量: 19千卡,蛋白质: 0.9 克, 碳水化合物: 4克",
"番茄乌冬面"
)
)
}

@ -1,196 +0,0 @@
package com.example.fruitandvegetableguide.data.local
import com.example.fruitandvegetableguide.R
import com.example.fruitandvegetableguide.data.Post
//本地帖子测试数据
object LocalPostsDataProvider {
val allPosts = mutableListOf(
Post(
1,
1,
"标题-默认",
R.drawable.cabbage,
labels = "标签-默认",
"内容-默认"
),
Post(
2,
2,
"关于西瓜的挑选经验",
R.drawable.watermelon,
labels = "西瓜,经验",
"1、看脐圈 西瓜底部的脐圈,小、圆而且略微凹陷的西瓜,一般生长时间充分,果实会更甜。\n" +
"2、辨颜色 颜色最好挑青绿色,不要雾雾白白的。\n" +
"3、看纹路 西瓜纹路整齐的,就是好瓜。\n" +
"4、看瓜蒂 西瓜头,就是所谓的瓜蒂头,从蒂部的弯曲程度,一般可判断出是第一茬瓜还是第二茬瓜。 ...\n" +
"5、听声音 将西瓜托在手里,手指轻轻敲,如果发出“咚、咚”的清脆声,并且托瓜的手有震动的感觉就是熟透的瓜,一般瓜味也会比较甜。"
),
Post(
3,
3,
"生姜怎么挑选?",
R.drawable.ginger,
labels = "生姜,经验",
"测试数据"
),
Post(
4,
1,
"白菜怎么挑选",
R.drawable.cabbage,
labels = "挑选方法,白菜",
"大白菜一般菜心是黄色的,越往外菜叶绿色逐渐增多,黄色菜叶越多,大白菜越好吃。\n" +
"黄色菜叶多说明生长周期长,主要是大白菜生长时间越长,黄色菜叶越多,说明长势很好,包的也瓷实。\n" +
"黄色菜叶多说明农家肥用得多。一般来说,农家肥种的白菜个头大,菜瓷实,黄色菜叶比较多,吃起来口感也要好一些。颜色发绿的白菜叶片厚实,吃起来口感比较粗,特别耐储存。\n"
),
Post(
5,
2,
"关于西瓜的挑选经验",
R.drawable.watermelon,
labels = "西瓜,经验",
"1、看脐圈 西瓜底部的脐圈,小、圆而且略微凹陷的西瓜,一般生长时间充分,果实会更甜。\n" +
"2、辨颜色 颜色最好挑青绿色,不要雾雾白白的。\n" +
"3、看纹路 西瓜纹路整齐的,就是好瓜。\n" +
"4、看瓜蒂 西瓜头,就是所谓的瓜蒂头,从蒂部的弯曲程度,一般可判断出是第一茬瓜还是第二茬瓜。 ...\n" +
"5、听声音 将西瓜托在手里,手指轻轻敲,如果发出“咚、咚”的清脆声,并且托瓜的手有震动的感觉就是熟透的瓜,一般瓜味也会比较甜。"
),
Post(
6,
3,
"生姜怎么挑选?",
R.drawable.ginger,
labels = "生姜,经验",
"测试数据"
),
Post(
7,
1,
"白菜怎么挑选",
R.drawable.cabbage,
labels = "挑选方法,白菜",
"大白菜一般菜心是黄色的,越往外菜叶绿色逐渐增多,黄色菜叶越多,大白菜越好吃。\n" +
"黄色菜叶多说明生长周期长,主要是大白菜生长时间越长,黄色菜叶越多,说明长势很好,包的也瓷实。\n" +
"黄色菜叶多说明农家肥用得多。一般来说,农家肥种的白菜个头大,菜瓷实,黄色菜叶比较多,吃起来口感也要好一些。颜色发绿的白菜叶片厚实,吃起来口感比较粗,特别耐储存。\n"
),
Post(
8,
2,
"关于西瓜的挑选经验",
R.drawable.watermelon,
labels = "西瓜,经验",
"1、看脐圈 西瓜底部的脐圈,小、圆而且略微凹陷的西瓜,一般生长时间充分,果实会更甜。\n" +
"2、辨颜色 颜色最好挑青绿色,不要雾雾白白的。\n" +
"3、看纹路 西瓜纹路整齐的,就是好瓜。\n" +
"4、看瓜蒂 西瓜头,就是所谓的瓜蒂头,从蒂部的弯曲程度,一般可判断出是第一茬瓜还是第二茬瓜。 ...\n" +
"5、听声音 将西瓜托在手里,手指轻轻敲,如果发出“咚、咚”的清脆声,并且托瓜的手有震动的感觉就是熟透的瓜,一般瓜味也会比较甜。"
),
Post(
9,
3,
"生姜怎么挑选?",
R.drawable.ginger,
labels = "生姜,经验",
"测试数据"
),
Post(
10,
1,
"白菜怎么挑选",
R.drawable.cabbage,
labels = "挑选方法,白菜",
"大白菜一般菜心是黄色的,越往外菜叶绿色逐渐增多,黄色菜叶越多,大白菜越好吃。\n" +
"黄色菜叶多说明生长周期长,主要是大白菜生长时间越长,黄色菜叶越多,说明长势很好,包的也瓷实。\n" +
"黄色菜叶多说明农家肥用得多。一般来说,农家肥种的白菜个头大,菜瓷实,黄色菜叶比较多,吃起来口感也要好一些。颜色发绿的白菜叶片厚实,吃起来口感比较粗,特别耐储存。\n"
),
Post(
11,
2,
"关于西瓜的挑选经验",
R.drawable.watermelon,
labels = "西瓜,经验",
"1、看脐圈 西瓜底部的脐圈,小、圆而且略微凹陷的西瓜,一般生长时间充分,果实会更甜。\n" +
"2、辨颜色 颜色最好挑青绿色,不要雾雾白白的。\n" +
"3、看纹路 西瓜纹路整齐的,就是好瓜。\n" +
"4、看瓜蒂 西瓜头,就是所谓的瓜蒂头,从蒂部的弯曲程度,一般可判断出是第一茬瓜还是第二茬瓜。 ...\n" +
"5、听声音 将西瓜托在手里,手指轻轻敲,如果发出“咚、咚”的清脆声,并且托瓜的手有震动的感觉就是熟透的瓜,一般瓜味也会比较甜。"
),
Post(
13,
3,
"生姜怎么挑选?",
R.drawable.ginger,
labels = "生姜,经验",
"测试数据"
),
Post(
14,
1,
"白菜怎么挑选",
R.drawable.cabbage,
labels = "挑选方法,白菜",
"大白菜一般菜心是黄色的,越往外菜叶绿色逐渐增多,黄色菜叶越多,大白菜越好吃。\n" +
"黄色菜叶多说明生长周期长,主要是大白菜生长时间越长,黄色菜叶越多,说明长势很好,包的也瓷实。\n" +
"黄色菜叶多说明农家肥用得多。一般来说,农家肥种的白菜个头大,菜瓷实,黄色菜叶比较多,吃起来口感也要好一些。颜色发绿的白菜叶片厚实,吃起来口感比较粗,特别耐储存。\n"
),
Post(
15,
2,
"关于西瓜的挑选经验",
R.drawable.watermelon,
labels = "西瓜,经验",
"1、看脐圈 西瓜底部的脐圈,小、圆而且略微凹陷的西瓜,一般生长时间充分,果实会更甜。\n" +
"2、辨颜色 颜色最好挑青绿色,不要雾雾白白的。\n" +
"3、看纹路 西瓜纹路整齐的,就是好瓜。\n" +
"4、看瓜蒂 西瓜头,就是所谓的瓜蒂头,从蒂部的弯曲程度,一般可判断出是第一茬瓜还是第二茬瓜。 ...\n" +
"5、听声音 将西瓜托在手里,手指轻轻敲,如果发出“咚、咚”的清脆声,并且托瓜的手有震动的感觉就是熟透的瓜,一般瓜味也会比较甜。"
),
Post(
16,
3,
"生姜怎么挑选?",
R.drawable.ginger,
labels = "生姜,经验",
"测试数据"
),
Post(
17,
1,
"白菜怎么挑选",
R.drawable.cabbage,
labels = "挑选方法,白菜",
"大白菜一般菜心是黄色的,越往外菜叶绿色逐渐增多,黄色菜叶越多,大白菜越好吃。\n" +
"黄色菜叶多说明生长周期长,主要是大白菜生长时间越长,黄色菜叶越多,说明长势很好,包的也瓷实。\n" +
"黄色菜叶多说明农家肥用得多。一般来说,农家肥种的白菜个头大,菜瓷实,黄色菜叶比较多,吃起来口感也要好一些。颜色发绿的白菜叶片厚实,吃起来口感比较粗,特别耐储存。\n"
),
Post(
18,
2,
"关于西瓜的挑选经验",
R.drawable.watermelon,
labels = "西瓜,经验",
"1、看脐圈 西瓜底部的脐圈,小、圆而且略微凹陷的西瓜,一般生长时间充分,果实会更甜。\n" +
"2、辨颜色 颜色最好挑青绿色,不要雾雾白白的。\n" +
"3、看纹路 西瓜纹路整齐的,就是好瓜。\n" +
"4、看瓜蒂 西瓜头,就是所谓的瓜蒂头,从蒂部的弯曲程度,一般可判断出是第一茬瓜还是第二茬瓜。 ...\n" +
"5、听声音 将西瓜托在手里,手指轻轻敲,如果发出“咚、咚”的清脆声,并且托瓜的手有震动的感觉就是熟透的瓜,一般瓜味也会比较甜。"
),
Post(
12,
3,
"生姜怎么挑选?",
R.drawable.ginger,
labels = "生姜,经验",
"测试数据"
),
Post(
13,
3,
"西红柿怎么挑选?",
R.drawable.ginger,
labels = "西红柿,经验",
"测试数据"
)
)
}

@ -1,206 +0,0 @@
@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3Api::class)
package com.example.fruitandvegetableguide.ui.community
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowForwardIos
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.fruitandvegetableguide.data.Post
import com.example.fruitandvegetableguide.utils.postListInit
@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
@Composable
fun CommunityScreen(
userid: Int? = 1,
onClickToSearch: (search: String) -> Unit = {},
onClickToPostDetail: (postId: Int) -> Unit = {},
onClickToPostEdit: (userid: Int) -> Unit = {},
onClickBack: () -> Unit = {},
) {
val postList = postListInit()
Scaffold(
topBar = {
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
text = "社区",
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
},
navigationIcon = {
IconButton(onClick = onClickBack) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null
)
}
})
},
bottomBar = {
BottomAppBar(
modifier = Modifier.fillMaxWidth()
) {
Row(
Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Spacer(modifier = Modifier.requiredWidth(30.dp))
Button(onClick = { onClickToPostEdit(userid ?: 1) }) {
Text(text = "分享我的经验")
}
Spacer(modifier = Modifier.requiredWidth(30.dp))
}
}
}) {
Column {
Spacer(
modifier = Modifier
.requiredHeight(60.dp)
)
SearchBar(onClickToSearch = onClickToSearch)
Spacer(
modifier = Modifier
.requiredHeight(10.dp)
.padding(it)
)
Posts(postsList = postList, onClickToPostDetail = onClickToPostDetail)
}
}
}
//搜索框
@Composable
fun SearchBar(
modifier: Modifier = Modifier,
onClickToSearch: (search: String) -> Unit
) {
var search by remember {
mutableStateOf("")
}
TextField(
value = search, onValueChange = { str -> search = str },
leadingIcon = {
IconButton(onClick = {
//路由不能传递空字符串
if (search == "") {
search = " "
}
onClickToSearch(search)
}) {
Icon(imageVector = Icons.Default.Search, contentDescription = null)
}
},
colors = TextFieldDefaults.colors(
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
focusedContainerColor = MaterialTheme.colorScheme.surface
),
placeholder = { Text(text = "搜索") },
modifier = modifier
.fillMaxWidth()
.heightIn(56.dp)
)
}
//帖子列表的显示
@Composable
fun Posts(
modifier: Modifier = Modifier,
onClickToPostDetail: (postId: Int) -> Unit = {},
postsList: List<Post>
) {
LazyColumn(
modifier = modifier
.padding(vertical = 4.dp)
) {
items(items = postsList) { post ->
Post(post = post, onClickToPostDetail = onClickToPostDetail)
}
}
}
//单个帖子的显示
@Composable
fun Post(
post: Post,
onClickToPostDetail: (postId: Int) -> Unit = {},
) {
Card(
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primary),
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Row(
modifier = Modifier
.padding(12.dp)
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
)
)
) {
Column(
modifier = Modifier
.weight(1f)
.padding(12.dp)
) {
Text(
text = post.title,
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(text = post.labels)
}
Button(onClick = {
onClickToPostDetail(post.id)
}) {
Icon(imageVector = Icons.Filled.ArrowForwardIos, contentDescription = null)
}
}
}
}

@ -1,76 +0,0 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package com.example.fruitandvegetableguide.ui.community
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.example.fruitandvegetableguide.data.Post
import com.example.fruitandvegetableguide.utils.getPostByPostId
@Composable
fun PostDetailScreen(
postId: Int? = 1,
onClickBack: () -> Unit
) {
val scrollState = rememberScrollState()
val post: Post = getPostByPostId(postId ?: 1)
Column(modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)) {
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
text = post.title,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
},
navigationIcon = {
IconButton(onClick = onClickBack) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null
)
}
})
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "标签",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(
text = post.labels,
)
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "内容",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(
text = post.content
)
}
}

@ -1,127 +0,0 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package com.example.fruitandvegetableguide.ui.community
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.fruitandvegetableguide.utils.postInsert
@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
@Composable
fun PostEditScreen(
userid: Int? = 1,
onClickBack: () -> Unit = {},
) {
val context = LocalContext.current
Column(modifier = Modifier.fillMaxSize()) {
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
text = "编辑",
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
},
navigationIcon = {
IconButton(onClick = onClickBack) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null
)
}
})
Spacer(modifier = Modifier.requiredHeight(10.dp))
var title by remember { mutableStateOf("") }
var labels by remember { mutableStateOf("") }
var content by remember { mutableStateOf("") }
Text(
text = "标题",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
TextField(
value = title,
onValueChange = { str -> title = str },
modifier = Modifier.fillMaxWidth(1f)
)
Text(
text = "标签",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
TextField(
value = labels,
onValueChange = { str -> labels = str },
modifier = Modifier.fillMaxWidth(1f)
)
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "内容",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
TextField(
value = content,
onValueChange = { str -> content = str },
modifier = Modifier.fillMaxWidth(1f)
)
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(
modifier = Modifier.padding(vertical = 24.dp),
onClick = {
if (
title != "" && labels != "" && content != "" && postInsert(
userid = userid ?: 1,
title = title,
labels = labels,
content = content
)
) {
Toast.makeText(context, "发布成功", Toast.LENGTH_SHORT).show()
onClickBack()
} else {
Toast.makeText(context, "发布失败", Toast.LENGTH_SHORT).show()
}
}
) {
Text("发布")
}
}
}
}

@ -1,104 +0,0 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package com.example.fruitandvegetableguide.ui.guide
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.fruitandvegetableguide.data.Guide
import com.example.fruitandvegetableguide.utils.getGuideByGuideId
//@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
@Composable
fun GuideDetailScreen(
guideId: Int? = 1,
onClickBack: () -> Unit
) {
val guide: Guide = getGuideByGuideId(guideId ?: 1)
val scrollState = rememberScrollState()
Column(modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)) {
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
text = guide.kind,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
},
navigationIcon = {
IconButton(onClick = onClickBack) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null
)
}
})
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "挑选方法",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(
text = guide.identify,
)
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "适宜人群",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(
text = guide.suitable,
)
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "食用禁忌",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(
text = guide.taboo,
)
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "营养价值",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(
text = guide.nutritiveValue
)
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "推荐菜品",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(
text = guide.recommendedMenu,
)
}
}

@ -1,166 +0,0 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package com.example.fruitandvegetableguide.ui.guide
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowForwardIos
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.fruitandvegetableguide.data.Guide
import com.example.fruitandvegetableguide.utils.guideListInit
@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
@Composable
fun GuideScreen(
onClickToSearch: (search: String) -> Unit = {},
onClickToGuideDetail: (guideId: Int) -> Unit = {},
onClickBack: () -> Unit = {},
) {
Column(modifier = Modifier.fillMaxSize()) {
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
text = "指南",
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
},
navigationIcon = {
IconButton(onClick = onClickBack) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null
)
}
})
SearchBar(onClickToSearch = onClickToSearch)
Spacer(modifier = Modifier.requiredHeight(10.dp))
Guides(onClickToGuideDetail = onClickToGuideDetail)
}
}
//搜索框
@Composable
fun SearchBar(
modifier: Modifier = Modifier,
onClickToSearch: (search: String) -> Unit
) {
var search by remember {
mutableStateOf("")
}
TextField(
value = search, onValueChange = { str -> search = str },
leadingIcon = {
IconButton(onClick = {
//路由不能传递空字符串
if (search == "") {
search = " "
}
onClickToSearch(search)
}) {
Icon(imageVector = Icons.Default.Search, contentDescription = null)
}
},
colors = TextFieldDefaults.colors(
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
focusedContainerColor = MaterialTheme.colorScheme.surface
),
placeholder = { Text(text = "搜索") },
modifier = modifier
.fillMaxWidth()
.heightIn(56.dp)
)
}
//指南列表的显示
@Composable
fun Guides(
modifier: Modifier = Modifier,
onClickToGuideDetail: (guideId: Int) -> Unit = {}
) {
LazyColumn(
modifier = modifier
.padding(vertical = 4.dp)
) {
items(guideListInit()) { guide ->
Guide(guide = guide, onClickToGuideDetail = onClickToGuideDetail)
}
}
}
//单个指南的显示
@Composable
fun Guide(
guide: Guide,
onClickToGuideDetail: (guideId: Int) -> Unit = {}
) {
Card(
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primary),
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Row(
modifier = Modifier
.padding(12.dp)
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
)
)
) {
Column(
modifier = Modifier
.weight(1f)
.padding(12.dp)
) {
Text(
text = guide.kind,
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(text = guide.suitable)
}
Button(onClick = {
onClickToGuideDetail(guide.id)
}) {
Icon(imageVector = Icons.Filled.ArrowForwardIos, contentDescription = null)
}
}
}
}

@ -1,134 +0,0 @@
package com.example.fruitandvegetableguide.ui.imgrecognition
import android.net.Uri
import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Button
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.example.fruitandvegetableguide.ui.imgrecognition.camerax.PhotoComponent
import com.example.fruitandvegetableguide.ui.imgrecognition.path.Path
import com.google.accompanist.placeholder.PlaceholderHighlight
import com.google.accompanist.placeholder.material.placeholder
import com.google.accompanist.placeholder.material.shimmer
@RequiresApi(Build.VERSION_CODES.N)
@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
@Composable
fun PhotographScreen(
onClickToRecognizeResult: (localImgPath: String) -> Unit = {},
onClickBack: () -> Unit = {}
) {
val mediaAction by lazy { PhotoComponent.instance }
var localImgPath by remember {
mutableStateOf(Uri.EMPTY)
}
//1代表拍照2代表相册选择
var pathFlag by remember {
mutableIntStateOf(0)
}
val context = LocalContext.current
Surface(modifier = Modifier.fillMaxSize()) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Spacer(modifier = Modifier.height(24.dp))
AsyncImage(
model = localImgPath, contentDescription = null,
modifier = Modifier
.width(200.dp)
.height(200.dp)
.clip(CircleShape)
.placeholder(
visible = localImgPath == Uri.EMPTY,
color = Color(231, 234, 239, 255),
highlight = PlaceholderHighlight.shimmer(),
),
contentScale = ContentScale.Crop,
)
Spacer(modifier = Modifier.height(24.dp))
TextButton(
onClick = {
mediaAction.takePhoto()
pathFlag = 1
},
) {
Text(text = "拍照")
}
Spacer(modifier = Modifier.height(12.dp))
TextButton(
onClick = {
mediaAction.selectImage()
pathFlag = 2
},
) {
Text(text = "相册")
}
Spacer(modifier = Modifier.height(12.dp))
Button(onClick = {
if (pathFlag == 0 || localImgPath == null || localImgPath.path == "") {
Toast.makeText(context, "请先选择图片", Toast.LENGTH_SHORT).show()
} else {
val path: String
if (pathFlag == 1) {
// /data/data/com.android.example.camerax2
// Do not hardcode "`/data/`"; use `Context.getFilesDir().getPath()` instead
path = "${context.dataDir}${localImgPath.path}"
// Log.d("photograph", "拍照Path:${context.dataDir}")
Log.d("photograph", "拍照Path:${path}")
} else {
path = Path.getPath(context, localImgPath) ?: "null"
Log.d("photograph", "相册Path:${path}")
}
val transPath = path.replace('/', '¥')
onClickToRecognizeResult(transPath)
}
}) {
Text(text = "识别")
}
}
}
mediaAction.Register(
galleryCallback = {
Log.d("photograph", "相册内容${it}")
if (it.isSuccess) {
localImgPath = it.uri
}
},
graphCallback = {
Log.d("photograph", "拍照内容${it}")
if (it.isSuccess) {
localImgPath = it.uri
}
},
permissionRationale = {
//权限拒绝的处理
Toast.makeText(context, "您拒绝了相机相册权限", Toast.LENGTH_SHORT).show()
onClickBack()
}
)
}

@ -1,199 +0,0 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package com.example.fruitandvegetableguide.ui.imgrecognition
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.example.fruitandvegetableguide.ui.imgrecognition.bauduai.Ingredient
import com.example.fruitandvegetableguide.utils.getGuideByKind
import org.json.JSONObject
import kotlin.concurrent.thread
@Composable
fun RecognizeResultScreen(
transPath: String? = "",
onClickBack: () -> Unit = {}
) {
var result by remember {
mutableStateOf("")
}
var alreadyRequested by remember {
mutableStateOf(false)
}
var waiting by remember {
mutableStateOf(true)
}
val messageCode = 2342343
Column(modifier = Modifier.fillMaxSize()) {
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
text = "结果",
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
},
navigationIcon = {
IconButton(onClick = onClickBack) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null
)
}
})
Spacer(modifier = Modifier.requiredHeight(10.dp))
var path = transPath ?: "null"
path = path.replace('¥', '/')
/* Text(
text = "path是$path",
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
)*/
if (path != "null") {
if (!alreadyRequested && path != "") {
val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if (msg.what == messageCode) {
result = msg.obj.toString()
waiting = false
}
}
}
thread {
val msg = Message()
msg.what = messageCode
msg.obj = Ingredient.ingredient(path)
handler.sendMessage(msg)
}
alreadyRequested = true
}
if (waiting) {
Text(text = "正在等待结果")
} else {
ShowResult(result)
}
}
Log.d("photograph", "RecognizeResultScreen: $result")
}
}
@Composable
fun ShowResult(
result: String
) {
val resultJSON = JSONObject(result)
val confidence: Double
val kind: String
if (getResult(result = result)) {
confidence = resultJSON.getJSONArray("result").getJSONObject(0).getDouble("score")
kind = resultJSON.getJSONArray("result").getJSONObject(0).getString("name")
val guideList = getGuideByKind(kind)
if (guideList.isEmpty()) {
Text(text = result)
} else {
val guide = guideList[0]
val scrollState = rememberScrollState()
Column(modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)) {
Row(
modifier = Modifier.fillMaxWidth()
) {
Text(
text = kind,
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(text = "置信度:${confidence}")
}
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "挑选方法",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(
text = guide.identify,
)
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "适宜人群",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(
text = guide.suitable,
)
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "食用禁忌",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(
text = guide.taboo,
)
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "营养价值",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(
text = guide.nutritiveValue
)
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "推荐菜品",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(
text = guide.recommendedMenu,
)
}
}
} else {
Text(text = result)
}
}
private fun getResult(result: String): Boolean {
val resultJSON = JSONObject(result)
return try {
resultJSON.getJSONArray("result").getJSONObject(0).getDouble("score")
resultJSON.getJSONArray("result").getJSONObject(0).getString("name")
true
} catch (e: RuntimeException) {
false
}
}

@ -1,65 +0,0 @@
package com.example.fruitandvegetableguide.ui.imgrecognition.bauduai;
/**
* Base64
*/
public class Base64Util {
private static final char last2byte = (char) Integer.parseInt("00000011", 2);
private static final char last4byte = (char) Integer.parseInt("00001111", 2);
private static final char last6byte = (char) Integer.parseInt("00111111", 2);
private static final char lead6byte = (char) Integer.parseInt("11111100", 2);
private static final char lead4byte = (char) Integer.parseInt("11110000", 2);
private static final char lead2byte = (char) Integer.parseInt("11000000", 2);
private static final char[] encodeTable = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
public Base64Util() {
}
public static String encode(byte[] from) {
StringBuilder to = new StringBuilder((int) ((double) from.length * 1.34D) + 3);
int num = 0;
char currentByte = 0;
int i;
for (i = 0; i < from.length; ++i) {
for (num %= 8; num < 8; num += 6) {
switch (num) {
case 0:
currentByte = (char) (from[i] & lead6byte);
currentByte = (char) (currentByte >>> 2);
case 1:
case 3:
case 5:
default:
break;
case 2:
currentByte = (char) (from[i] & last6byte);
break;
case 4:
currentByte = (char) (from[i] & last4byte);
currentByte = (char) (currentByte << 2);
if (i + 1 < from.length) {
currentByte = (char) (currentByte | (from[i + 1] & lead2byte) >>> 6);
}
break;
case 6:
currentByte = (char) (from[i] & last2byte);
currentByte = (char) (currentByte << 4);
if (i + 1 < from.length) {
currentByte = (char) (currentByte | (from[i + 1] & lead4byte) >>> 4);
}
}
to.append(encodeTable[currentByte]);
}
}
if (to.length() % 4 != 0) {
for (i = 4 - to.length() % 4; i > 0; --i) {
to.append("=");
}
}
return to.toString();
}
}

@ -1,72 +0,0 @@
package com.example.fruitandvegetableguide.ui.imgrecognition.bauduai;
import java.io.*;
/**
*
*/
public class FileUtil {
/**
*
*/
public static String readFileAsString(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException(filePath);
}
if (file.length() > 1024 * 1024 * 1024) {
throw new IOException("File is too large");
}
StringBuilder sb = new StringBuilder((int) (file.length()));
// 创建字节输入流
FileInputStream fis = new FileInputStream(filePath);
// 创建一个长度为10240的Buffer
byte[] bbuf = new byte[10240];
// 用于保存实际读取的字节数
int hasRead = 0;
while ( (hasRead = fis.read(bbuf)) > 0 ) {
sb.append(new String(bbuf, 0, hasRead));
}
fis.close();
return sb.toString();
}
/**
* byte[]
*/
public static byte[] readFileByBytes(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException(filePath);
} else {
ByteArrayOutputStream bos = new ByteArrayOutputStream((int) file.length());
BufferedInputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(file));
short bufSize = 1024;
byte[] buffer = new byte[bufSize];
int len1;
while (-1 != (len1 = in.read(buffer, 0, bufSize))) {
bos.write(buffer, 0, len1);
}
byte[] var7 = bos.toByteArray();
return var7;
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException var14) {
var14.printStackTrace();
}
bos.close();
}
}
}
}

@ -1,29 +0,0 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.example.fruitandvegetableguide.ui.imgrecognition.bauduai;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
/**
* Json.
*/
public class GsonUtils {
private static Gson gson = new GsonBuilder().create();
public static String toJson(Object value) {
return gson.toJson(value);
}
public static <T> T fromJson(String json, Class<T> classOfT) throws JsonParseException {
return gson.fromJson(json, classOfT);
}
public static <T> T fromJson(String json, Type typeOfT) throws JsonParseException {
return (T) gson.fromJson(json, typeOfT);
}
}

@ -1,77 +0,0 @@
package com.example.fruitandvegetableguide.ui.imgrecognition.bauduai;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;
/**
* http
*/
public class HttpUtil {
public static String post(String requestUrl, String accessToken, String params)
throws Exception {
String contentType = "application/x-www-form-urlencoded";
return HttpUtil.post(requestUrl, accessToken, contentType, params);
}
public static String post(String requestUrl, String accessToken, String contentType, String params)
throws Exception {
String encoding = "UTF-8";
if (requestUrl.contains("nlp")) {
encoding = "GBK";
}
return HttpUtil.post(requestUrl, accessToken, contentType, params, encoding);
}
public static String post(String requestUrl, String accessToken, String contentType, String params, String encoding)
throws Exception {
String url = requestUrl + "?access_token=" + accessToken;
return HttpUtil.postGeneralUrl(url, contentType, params, encoding);
}
public static String postGeneralUrl(String generalUrl, String contentType, String params, String encoding)
throws Exception {
URL url = new URL(generalUrl);
// 打开和URL之间的连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
// 设置通用的请求属性
connection.setRequestProperty("Content-Type", contentType);
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setUseCaches(false);
connection.setDoOutput(true);
connection.setDoInput(true);
// 得到请求的输出流对象
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(params.getBytes(encoding));
out.flush();
out.close();
// 建立实际的连接
connection.connect();
// 获取所有响应头字段
Map<String, List<String>> headers = connection.getHeaderFields();
// 遍历所有的响应头字段
for (String key : headers.keySet()) {
System.err.println(key + "--->" + headers.get(key));
}
// 定义 BufferedReader输入流来读取URL的响应
BufferedReader in = null;
in = new BufferedReader(
new InputStreamReader(connection.getInputStream(), encoding));
String result = "";
String getLine;
while ((getLine = in.readLine()) != null) {
result += getLine;
}
in.close();
System.err.println("result:" + result);
return result;
}
}

@ -1,30 +0,0 @@
package com.example.fruitandvegetableguide.ui.imgrecognition.bauduai;
import android.annotation.SuppressLint;
import android.util.Log;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
public class Ingredient {
private static final String requestUrl = "https://aip.baidubce.com/rest/2.0/image-classify/v1/classify/ingredient";
private static final String accessToken = "24.f92314cba46ffeee34854093fa71966c.2592000.1700631460.282335-38996773";
public static String ingredient(String path) throws Exception {
try {
byte[] imgData = FileUtil.readFileByBytes(path);
String imgStr = Base64Util.encode(imgData);
String imgParam = URLEncoder.encode(imgStr, "UTF-8");
String param = "image=" + imgParam;
String result = HttpUtil.post(requestUrl, accessToken, param);
Log.d("ingredient", "ingredient: " + result);
return result;
} catch (Exception e) {
e.printStackTrace();
}
return "can not get result";
}
}

@ -1,151 +0,0 @@
package com.example.fruitandvegetableguide.ui.imgrecognition.camerax
import android.os.Build
import android.Manifest
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.runtime.getValue
import androidx.compose.runtime.saveable.rememberSaveable
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberMultiplePermissionsState
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
class PhotoComponent {
private var openGalleryLauncher: ManagedActivityResultLauncher<Unit?, PictureResult>? = null
private var takePhotoLauncher: ManagedActivityResultLauncher<Unit?, PictureResult>? = null
private val scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
companion object {
val instance get() = Helper.obj
}
private object Helper {
val obj = PhotoComponent()
}
//监听拍照权限flow
private val checkCameraPermission =
MutableSharedFlow<Boolean?>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
private fun setCheckCameraPermissionState(value: Boolean?) {
scope.launch {
checkCameraPermission.emit(value)
}
}
//相册权限flow
private val checkGalleryImagePermission =
MutableSharedFlow<Boolean?>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
private fun setCheckGalleryPermissionState(value: Boolean?) {
scope.launch {
checkGalleryImagePermission.emit(value)
}
}
/**
* @param galleryCallback 相册结果回调
* @param graphCallback 拍照结果回调
* @param permissionRationale 权限拒绝状态回调
**/
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun Register(
galleryCallback: (selectResult: PictureResult) -> Unit,
graphCallback: (graphResult: PictureResult) -> Unit,
permissionRationale: ((gallery: Boolean) -> Unit)? = null,
) {
val rememberGraphCallback = rememberUpdatedState(newValue = graphCallback)
val rememberGalleryCallback = rememberUpdatedState(newValue = galleryCallback)
openGalleryLauncher = rememberLauncherForActivityResult(contract = SelectPicture()) {
rememberGalleryCallback.value.invoke(it)
}
takePhotoLauncher = rememberLauncherForActivityResult(contract = TakePhoto.instance) {
rememberGraphCallback.value.invoke(it)
}
val readGalleryPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
//13以上的权限申请
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
var permissionCameraState by rememberSaveable { mutableStateOf(false) }
var permissionGalleryState by rememberSaveable { mutableStateOf(false) }
val permissionList = arrayListOf(
Manifest.permission.CAMERA,
readGalleryPermission,
)
val galleryPermissionState = rememberPermissionState(readGalleryPermission)
val cameraPermissionState = rememberMultiplePermissionsState(permissionList)
LaunchedEffect(Unit) {
checkCameraPermission.collectLatest {
permissionCameraState = it == true
if (it == true) {
if (cameraPermissionState.allPermissionsGranted) {
setCheckCameraPermissionState(null)
takePhotoLauncher?.launch(null)
} else if (cameraPermissionState.shouldShowRationale) {
setCheckCameraPermissionState(null)
permissionRationale?.invoke(false)
} else {
cameraPermissionState.launchMultiplePermissionRequest()
}
}
}
}
LaunchedEffect(Unit) {
checkGalleryImagePermission.collectLatest {
permissionGalleryState = it == true
if (it == true) {
if (galleryPermissionState.status.isGranted) {
setCheckGalleryPermissionState(null)
openGalleryLauncher?.launch(null)
} else if (galleryPermissionState.status.shouldShowRationale) {
setCheckGalleryPermissionState(null)
permissionRationale?.invoke(true)
} else {
galleryPermissionState.launchPermissionRequest()
}
}
}
}
LaunchedEffect(cameraPermissionState.allPermissionsGranted) {
if (cameraPermissionState.allPermissionsGranted && permissionCameraState) {
setCheckCameraPermissionState(null)
takePhotoLauncher?.launch(null)
}
}
LaunchedEffect(galleryPermissionState.status.isGranted) {
if (galleryPermissionState.status.isGranted && permissionGalleryState) {
setCheckGalleryPermissionState(null)
openGalleryLauncher?.launch(null)
}
}
}
//调用选择图片功能
fun selectImage() {
setCheckGalleryPermissionState(true)
}
//调用拍照
fun takePhoto() {
setCheckCameraPermissionState(true)
}
}

@ -1,24 +0,0 @@
package com.example.fruitandvegetableguide.ui.imgrecognition.camerax
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.activity.result.contract.ActivityResultContract
class SelectPicture : ActivityResultContract<Unit?, PictureResult>() {
private var context: Context? = null
override fun createIntent(context: Context, input: Unit?): Intent {
this.context = context
return Intent(Intent.ACTION_PICK).setType("image/*")
}
override fun parseResult(resultCode: Int, intent: Intent?): PictureResult {
return PictureResult(intent?.data, resultCode == Activity.RESULT_OK)
}
}
//图片结果
class PictureResult(val uri: Uri?, val isSuccess: Boolean)

@ -1,55 +0,0 @@
package com.example.fruitandvegetableguide.ui.imgrecognition.camerax
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.provider.MediaStore
import androidx.activity.result.contract.ActivityResultContract
import androidx.core.content.FileProvider
import java.io.File
import java.util.UUID
class TakePhoto : ActivityResultContract<Unit?, PictureResult>() {
var outUri: Uri? = null
private var imageName: String? = null
companion object {
//定义单例的原因是因为拍照返回的时候页面会重新实例takePhoto导致拍照的uri始终为空
val instance get() = Helper.obj
}
private object Helper {
val obj = TakePhoto()
}
override fun createIntent(context: Context, input: Unit?): Intent =
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { intent ->
getFileDirectory(context)?.let {
outUri = it
intent.putExtra(MediaStore.EXTRA_OUTPUT, it).apply {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
}
}
override fun parseResult(resultCode: Int, intent: Intent?): PictureResult {
return PictureResult(outUri, resultCode == Activity.RESULT_OK)
}
private fun getFileDirectory(context: Context): Uri? {//获取app内部文件夹
imageName = "img_${UUID.randomUUID().toString().substring(0, 7)}"
val fileFolder = File(context.cacheDir, "test_imgs")
if (!fileFolder.exists()) {
fileFolder.mkdirs()
}
val file = File(fileFolder, "${imageName}.jpg")
if (!file.exists()) {
file.createNewFile()
}
return FileProvider.getUriForFile(context, "${context.packageName}.image.provider", file)
}
}

@ -1,27 +0,0 @@
package com.example.fruitandvegetableguide.ui.imgrecognition.path;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.text.TextUtils;
public class Path {
public static String getPath(Context context, Uri uri) {
String path = null;
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
if (cursor == null) {
return null;
}
if (cursor.moveToFirst()) {
try {
path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
} catch (Exception e) {
e.printStackTrace();
}
}
cursor.close();
return path;
}
}

@ -1,218 +0,0 @@
package com.example.fruitandvegetableguide.ui.login
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountBox
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.fruitandvegetableguide.R
import com.example.fruitandvegetableguide.utils.loginVerification
@Composable
fun LoginScreen(
onClickToRegister: () -> Unit = {},
onClickToMain: (userid: Int) -> Unit = {}
) {
var userid by remember { mutableIntStateOf(-1) }
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
val pwdVisualTransformation = PasswordVisualTransformation()
var showPwd by remember { mutableStateOf(true) }
val transformation = if (showPwd) pwdVisualTransformation else VisualTransformation.None
val context = LocalContext.current
Box(modifier = Modifier.fillMaxSize()) {
TextButton(onClick = onClickToRegister) {
Text(
text = "注册",
fontSize = 20.sp,
textAlign = TextAlign.End,
modifier = Modifier
.fillMaxWidth()
.padding(20.dp)
)
}
Column {
Spacer(modifier = Modifier.weight(1f))
Column(
modifier = Modifier
.weight(3f)
.background(Color.White)
.padding(40.dp)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Column {
TextField(
modifier = Modifier.fillMaxWidth(),
value = username,
onValueChange = { str -> username = str },
placeholder = {
Text("账号")
},
colors = TextFieldDefaults.colors(focusedTextColor = Color.Black),
leadingIcon = {
Icon(
imageVector = Icons.Default.AccountBox,
contentDescription = null
)
}
)
TextField(
value = password,
onValueChange = { str -> password = str },
modifier = Modifier.fillMaxWidth(),
placeholder = {
Text("密码")
},
visualTransformation = transformation,
colors = TextFieldDefaults.colors(focusedTextColor = Color.Black),
leadingIcon = {
Icon(
imageVector = Icons.Default.Lock,
contentDescription = null
)
},
trailingIcon = {
if (showPwd) {
IconButton(onClick = { showPwd = !showPwd }) {
Icon(
painter = painterResource(id = R.drawable.sleep),
contentDescription = null,
Modifier.size(30.dp)
)
}
} else {
IconButton(onClick = { showPwd = !showPwd }) {
Icon(
painter = painterResource(id = R.drawable.eye),
contentDescription = null,
Modifier.size(30.dp)
)
}
}
}
)
}
Spacer(modifier = Modifier.height(20.dp))
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Text(text = "快捷登录", fontSize = 16.sp, color = Color.Gray)
Text(text = "忘记密码", fontSize = 16.sp, color = Color.Gray)
}
Spacer(modifier = Modifier.height(20.dp))
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Button(
onClick = {
userid = loginVerification(username, password)
if (username != "" && password != "" && userid != -1) {
Toast.makeText(context, "登陆成功", Toast.LENGTH_SHORT).show()
onClickToMain(userid)
} else {
Toast.makeText(context, "登陆失败", Toast.LENGTH_SHORT).show()
}
},
colors = ButtonDefaults.buttonColors(
containerColor = Color(
0xff5c59fe
)
),
contentPadding = PaddingValues(12.dp, 16.dp)
) {
Text("登录", color = Color.White, fontSize = 18.sp)
}
}
Spacer(modifier = Modifier.height(100.dp))
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Row(
Modifier
.background(Color(0xFFCFC5C5))
.padding(end = 10.dp)
) {}
Spacer(modifier = Modifier.height(20.dp))
Text(text = "第三方登录", fontSize = 16.sp, color = Color.Gray)
Row(
Modifier
.fillMaxWidth()
.weight(1f),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.Top
) {
repeat(3) {
Column(
Modifier.weight(1f),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
modifier = Modifier.size(50.dp),
painter = painterResource(id = R.drawable.qq),
contentDescription = null
)
Text(
"QQ",
color = Color(0xffcdcdcd),
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
}
}
}
}
}
}
}
}
@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
@Composable
fun Login() {
LoginScreen()
}

@ -1,359 +0,0 @@
package com.example.fruitandvegetableguide.ui.main
import android.annotation.SuppressLint
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.paddingFromBaseline
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Camera
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Spa
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.fruitandvegetableguide.R
import com.example.fruitandvegetableguide.ui.theme.FruitandVegetableGuideTheme
import com.example.fruitandvegetableguide.utils.guideListInit
import com.example.fruitandvegetableguide.utils.postListInit
//主页
@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
@Composable
fun MainScreen(
userid: Int? = 1,
onClickToSearch: (search: String) -> Unit = {},
onClickToUser: (userid: Int) -> Unit = {},
onClickToPhotograph: () -> Unit = {},
onClickToGuide: () -> Unit = {},
onClickToCommunity: (userid: Int) -> Unit = {},
onClickToGuideDetail: (guideId: Int) -> Unit = {},
onClickToPostDetail: (postId: Int) -> Unit = {},
) {
FruitandVegetableGuideTheme {
Scaffold(bottomBar = {
MyBottomNavigation(
userid = userid ?: 1,
onClickToUser = onClickToUser,
onClickToPhotograph = onClickToPhotograph
)
}) { padding ->
HomeScreen(
userid = userid ?: 1,
modifier = Modifier.padding(padding),
onClickToSearch = onClickToSearch,
onClickToGuide = onClickToGuide,
onClickToCommunity = onClickToCommunity,
onClickToGuideDetail = onClickToGuideDetail,
onClickToPostDetail = onClickToPostDetail
)
}
}
}
//主屏幕
@Composable
fun HomeScreen(
userid: Int,
modifier: Modifier = Modifier,
onClickToSearch: (search: String) -> Unit,
onClickToGuide: () -> Unit = {},
onClickToCommunity: (userid: Int) -> Unit = {},
onClickToGuideDetail: (guideId: Int) -> Unit = {},
onClickToPostDetail: (postId: Int) -> Unit = {},
) {
Column(modifier.verticalScroll(rememberScrollState())) {
Spacer(modifier = Modifier.height(16.dp))
SearchBar(
Modifier.padding(horizontal = 16.dp),
onClickToSearch = onClickToSearch
)
Column {
TextButton(onClick = onClickToGuide) {
Text(
text = "指南",
style = MaterialTheme.typography.titleMedium,
modifier = Modifier
.paddingFromBaseline(top = 40.dp, bottom = 16.dp)
.padding(horizontal = 16.dp)
)
}
GuideGrid(onClickToGuideDetail = onClickToGuideDetail)
}
Column {
TextButton(onClick = { onClickToCommunity(userid) }) {
Text(
text = "社区",
style = MaterialTheme.typography.titleMedium,
modifier = Modifier
.paddingFromBaseline(top = 40.dp, bottom = 16.dp)
.padding(horizontal = 16.dp)
)
}
PostGrid(onClickToPostDetail = onClickToPostDetail)
}
Spacer(modifier = Modifier.height(16.dp))
}
}
//搜索框
@Composable
fun SearchBar(
modifier: Modifier = Modifier,
onClickToSearch: (search: String) -> Unit
) {
var search by remember {
mutableStateOf("")
}
TextField(
value = search, onValueChange = { str -> search = str },
leadingIcon = {
IconButton(onClick = {
//路由不能传递空字符串
if (search == "") {
search = " "
}
onClickToSearch(search)
}) {
Icon(imageVector = Icons.Default.Search, contentDescription = null)
}
},
colors = TextFieldDefaults.colors(
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
focusedContainerColor = MaterialTheme.colorScheme.surface
),
placeholder = { Text(text = "搜索") },
modifier = modifier
.fillMaxWidth()
.heightIn(56.dp)
)
}
//指南元素
@Composable
fun GuideElement(
modifier: Modifier = Modifier,
@DrawableRes drawable: Int,
guideId: Int,
kind: String,
onClickToGuideDetail: (guideId: Int) -> Unit = {},
) {
Button(
onClick = { onClickToGuideDetail(guideId) },
colors = ButtonDefaults.buttonColors(
containerColor = Color.White,
),
contentPadding = PaddingValues(1.dp),
modifier = Modifier.width(90.dp)
) {
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(id = drawable),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = if (R.drawable.loading == drawable) {
Modifier
// .size(24.dp)
// .padding(32.dp)
.clip(CircleShape)
} else {
Modifier
.size(88.dp)
.clip(CircleShape)
}
)
Text(
text = kind,
modifier = Modifier.paddingFromBaseline(top = 24.dp, bottom = 8.dp),
style = MaterialTheme.typography.bodyMedium,
color = Color.Black
)
}
}
}
//帖子元素
@SuppressLint("InvalidColorHexValue")
@Composable
fun PostCard(
postId: Int,
modifier: Modifier = Modifier,
@DrawableRes drawable: Int,
title: String,
onClickToPostDetail: (postId: Int) -> Unit = {},
) {
Surface(
shape = MaterialTheme.shapes.medium,
modifier = modifier,
color = MaterialTheme.colorScheme.surfaceVariant,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.width(255.dp)
) {
Button(
onClick = { onClickToPostDetail(postId) },
colors = ButtonDefaults.buttonColors(
containerColor = Color(0x0000),
),
modifier = Modifier.width(251.dp)
) {
Image(
painter = painterResource(id = drawable),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = if (R.drawable.loading != drawable) {
Modifier.size(80.dp)
} else {
Modifier
}
)
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
color = Color.Black,
modifier = Modifier.padding(horizontal = 10.dp)
)
}
}
}
}
//指南的网格显示
@Preview
@Composable
fun GuideGrid(
modifier: Modifier = Modifier,
onClickToGuideDetail: (guideId: Int) -> Unit = {},
) {
val guideList = guideListInit()
LazyHorizontalGrid(
rows = GridCells.Fixed(2),
contentPadding = PaddingValues(horizontal = 8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(2.dp),
modifier = modifier.height(300.dp)
) {
items(guideList) { item ->
GuideElement(
guideId = item.id,
drawable = item.imgId,
kind = item.kind,
onClickToGuideDetail = onClickToGuideDetail
)
}
}
}
//帖子的网格显示
@Composable
fun PostGrid(
modifier: Modifier = Modifier,
onClickToPostDetail: (postId: Int) -> Unit = {},
) {
val postList = postListInit()
LazyHorizontalGrid(
rows = GridCells.Fixed(4),
contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier.height(300.dp)
) {
items(postList) { item ->
PostCard(
postId = item.id,
drawable = item.imgId,
title = item.title,
onClickToPostDetail = onClickToPostDetail
)
}
}
}
//底部导航栏
@Composable
fun MyBottomNavigation(
userid: Int,
modifier: Modifier = Modifier,
onClickToUser: (userid: Int) -> Unit = {},
onClickToPhotograph: () -> Unit = {},
) {
NavigationBar(containerColor = MaterialTheme.colorScheme.surfaceVariant, modifier = modifier) {
NavigationBarItem(icon = {
Icon(
imageVector = Icons.Default.Spa,
contentDescription = null
)
},
label = { Text(text = "首页") },
selected = true,
onClick = { })
NavigationBarItem(
icon = {
Icon(
imageVector = Icons.Default.Camera,
contentDescription = null
)
},
label = { Text(text = "识别") },
selected = false,
onClick = onClickToPhotograph
)
NavigationBarItem(icon = {
Icon(
imageVector = Icons.Default.AccountCircle,
contentDescription = null
)
}, label = { Text(text = "用户") },
selected = false,
onClick = { onClickToUser(userid) }
)
}
}

@ -1,205 +0,0 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package com.example.fruitandvegetableguide.ui.register
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountBox
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.fruitandvegetableguide.R
import com.example.fruitandvegetableguide.utils.register
@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
@Composable
fun RegisterScreen(onClickBack: () -> Unit = {}) {
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var rePassword by remember { mutableStateOf("") }
val pwdVisualTransformation = PasswordVisualTransformation()
var showPwd by remember { mutableStateOf(true) }
val transformation = if (showPwd) pwdVisualTransformation else VisualTransformation.None
val context = LocalContext.current
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
text = "注册",
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
},
navigationIcon = {
IconButton(onClick = onClickBack) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null
)
}
})
Column() {
Spacer(modifier = Modifier.weight(1f))
Column(
modifier = Modifier
.weight(3f)
.background(Color.White)
.padding(40.dp)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Column {
TextField(
modifier = Modifier.fillMaxWidth(),
value = username,
onValueChange = { str -> username = str },
placeholder = {
Text("账号")
},
colors = TextFieldDefaults.colors(focusedTextColor = Color.Black),
leadingIcon = {
Icon(
imageVector = Icons.Default.AccountBox,
contentDescription = null
)
}
)
TextField(
value = password,
onValueChange = { str -> password = str },
modifier = Modifier.fillMaxWidth(),
placeholder = {
Text("密码")
},
visualTransformation = transformation,
colors = TextFieldDefaults.colors(focusedTextColor = Color.Black),
leadingIcon = {
Icon(
imageVector = Icons.Default.Lock,
contentDescription = null
)
},
trailingIcon = {
if (showPwd) {
IconButton(onClick = { showPwd = !showPwd }) {
Icon(
painter = painterResource(id = R.drawable.sleep),
contentDescription = null,
Modifier.size(30.dp)
)
}
} else {
IconButton(onClick = { showPwd = !showPwd }) {
Icon(
painter = painterResource(id = R.drawable.eye),
contentDescription = null,
Modifier.size(30.dp)
)
}
}
}
)
TextField(
value = rePassword,
onValueChange = { str -> rePassword = str },
modifier = Modifier.fillMaxWidth(),
placeholder = {
Text("再次输入密码")
},
visualTransformation = transformation,
colors = TextFieldDefaults.colors(focusedTextColor = Color.Black),
leadingIcon = {
Icon(
imageVector = Icons.Default.Lock,
contentDescription = null
)
},
trailingIcon = {
if (showPwd) {
IconButton(onClick = { showPwd = !showPwd }) {
Icon(
painter = painterResource(id = R.drawable.sleep),
contentDescription = null,
Modifier.size(30.dp)
)
}
} else {
IconButton(onClick = { showPwd = !showPwd }) {
Icon(
painter = painterResource(id = R.drawable.eye),
contentDescription = null,
Modifier.size(30.dp)
)
}
}
}
)
}
Spacer(modifier = Modifier.height(20.dp))
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Button(
onClick = {
if (username != "" && password != "" && password == rePassword && register(
username,
password
)
) {
Toast.makeText(context, "注册成功", Toast.LENGTH_SHORT).show()
onClickBack()
} else {
Toast.makeText(context, "注册失败", Toast.LENGTH_SHORT).show()
}
},
colors = ButtonDefaults.buttonColors(
containerColor = Color(
0xff5c59fe
)
),
contentPadding = PaddingValues(12.dp, 16.dp)
) {
Text("注册", color = Color.White, fontSize = 18.sp)
}
}
}
Spacer(modifier = Modifier.height(120.dp))
}
}

@ -1,325 +0,0 @@
@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3Api::class)
package com.example.fruitandvegetableguide.ui.search
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowForwardIos
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.fruitandvegetableguide.data.Guide
import com.example.fruitandvegetableguide.data.Post
import com.example.fruitandvegetableguide.utils.getGuideByKind
import com.example.fruitandvegetableguide.utils.getPostByLabels
@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
@Composable
fun SearchScreen(
search: String? = "",
onClickBack: () -> Unit = {},
onClickToGuideDetail: (guideId: Int) -> Unit = {},
onClickToPostDetail: (postId: Int) -> Unit = {}
) {
var thisSearch by rememberSaveable {
mutableStateOf(search)
}
var guideClassify by remember {
mutableStateOf(true)
} //筛选指南
var postClassify by remember {
mutableStateOf(false)
} //筛选帖子
//刷新,当再次点击搜索的时候,该值改变,触发刷新
var refresh by remember {
mutableIntStateOf(1)
}
val guides = getGuideByKind(thisSearch ?: "") //搜索指南
val posts = getPostByLabels(thisSearch ?: "") //搜索帖子
Column(modifier = Modifier.fillMaxSize()) {
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
text = "搜索",
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
},
navigationIcon = {
IconButton(onClick = onClickBack) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null
)
}
})
//搜索框
TextField(
value = thisSearch ?: "", onValueChange = { str -> thisSearch = str },
leadingIcon = {
IconButton(onClick = { refresh += 1 }) {
Icon(imageVector = Icons.Default.Search, contentDescription = null)
}
},
colors = TextFieldDefaults.colors(
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
focusedContainerColor = MaterialTheme.colorScheme.surface
),
placeholder = { Text(text = "搜索") },
modifier = Modifier
.fillMaxWidth()
.heightIn(56.dp)
)
//筛选按钮
Spacer(modifier = Modifier.requiredHeight(10.dp))
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Spacer(modifier = Modifier.requiredWidth(15.dp))
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {
OutlinedButton(
onClick = {
postClassify = !postClassify
guideClassify = !guideClassify
},
shape = RoundedCornerShape(12.dp),
contentPadding = PaddingValues(horizontal = 30.dp, vertical = 16.dp),
modifier = Modifier.bounceClick(),
enabled = postClassify && !guideClassify
) {
Text(text = "筛选指南")
}
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {
OutlinedButton(
onClick = {
guideClassify = !guideClassify
postClassify = !postClassify
},
shape = RoundedCornerShape(12.dp),
contentPadding = PaddingValues(horizontal = 30.dp, vertical = 16.dp),
modifier = Modifier.bounceClick(),
enabled = guideClassify && !postClassify //不可用
) {
Text(text = "筛选帖子")
}
}
Spacer(modifier = Modifier.requiredWidth(15.dp))
}
//显示结果列表
Spacer(modifier = Modifier.requiredHeight(10.dp))
if (guideClassify) {
Guides(guidesList = guides, onClickToGuideDetail = onClickToGuideDetail)
}
if (postClassify) {
Posts(postsList = posts, onClickToPostDetail = onClickToPostDetail)
}
}
}
//帖子列表的显示
@Composable
fun Posts(
modifier: Modifier = Modifier,
onClickToPostDetail: (postId: Int) -> Unit = {},
postsList: List<Post>
) {
LazyColumn(modifier = modifier.padding(vertical = 4.dp)) {
items(items = postsList) { post ->
Post(
post = post,
onClickToPostDetail = onClickToPostDetail
)
}
}
}
//单个帖子的显示
@Composable
fun Post(
post: Post,
onClickToPostDetail: (postId: Int) -> Unit = {},
) {
Card(
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primary),
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Row(
modifier = Modifier
.padding(12.dp)
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
)
)
) {
Column(
modifier = Modifier
.weight(1f)
.padding(12.dp)
) {
Text(
text = post.title,
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(text = post.labels)
}
Button(onClick = {
onClickToPostDetail(post.id)
}) {
Icon(imageVector = Icons.Filled.ArrowForwardIos, contentDescription = null)
}
}
}
}
//指南列表的显示
@Composable
fun Guides(
modifier: Modifier = Modifier,
onClickToGuideDetail: (guideId: Int) -> Unit = {},
guidesList: List<Guide>
) {
LazyColumn(modifier = modifier.padding(vertical = 4.dp)) {
items(items = guidesList) { guide ->
Guide(
guide = guide,
onClickToGuideDetail = onClickToGuideDetail
)
}
}
}
//单个指南的显示
@Composable
fun Guide(
guide: Guide,
onClickToGuideDetail: (guideId: Int) -> Unit = {},
) {
Card(
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primary),
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Row(
modifier = Modifier
.padding(12.dp)
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
)
)
) {
Column(
modifier = Modifier
.weight(1f)
.padding(12.dp)
) {
Text(
text = guide.kind,
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(text = guide.suitable)
}
Button(onClick = {
onClickToGuideDetail(guide.id)
}) {
Icon(imageVector = Icons.Filled.ArrowForwardIos, contentDescription = null)
}
}
}
}
//实现按钮点击效果
enum class ButtonState { Pressed, Idle }
fun Modifier.bounceClick() = composed {
var buttonState by remember { mutableStateOf(ButtonState.Idle) }
val scale by animateFloatAsState(if (buttonState == ButtonState.Pressed) 0.70f else 1f)
this
.graphicsLayer {
scaleX = scale
scaleY = scale
}
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = { }
)
.pointerInput(buttonState) {
awaitPointerEventScope {
buttonState = if (buttonState == ButtonState.Pressed) {
waitForUpOrCancellation()
ButtonState.Idle
} else {
awaitFirstDown(false)
ButtonState.Pressed
}
}
}
}

@ -1,11 +0,0 @@
package com.example.fruitandvegetableguide.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

@ -1,70 +0,0 @@
package com.example.fruitandvegetableguide.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun FruitandVegetableGuideTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

@ -1,34 +0,0 @@
package com.example.fruitandvegetableguide.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)

@ -1,16 +0,0 @@
package com.example.fruitandvegetableguide.ui.user
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
//一个类,用来方便显示我的帖子
class MyPost(
val id: Int,
val title: String,
val content: String,
initialChecked: Boolean = false
) {
var checked: Boolean by mutableStateOf(initialChecked)
}

@ -1,194 +0,0 @@
@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3Api::class)
package com.example.fruitandvegetableguide.ui.user
import android.widget.Toast
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Checkbox
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.fruitandvegetableguide.data.Post
import com.example.fruitandvegetableguide.utils.DeletePost
import com.example.fruitandvegetableguide.utils.getPostByPostId
import com.example.fruitandvegetableguide.utils.getPostByUserid
@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
@Composable
fun MyPostScreen(userid: Int? = 1, onClickBack: () -> Unit = {}) {
val context = LocalContext.current
// 如果执行了删除操作,需要刷新页面
val deleteFlag1 by remember {
mutableIntStateOf(0)
}
var deleteFlag2 by remember {
mutableIntStateOf(0)
}
var myPosts = getPostByUserid(userid ?: 1)
/*var id1 = ""
for (myPost in myPosts) {
id1 += myPost.id
}
Log.d("MyPostScreen", "MyPostScreen1111,the one myPost: \n${id1}")*/
//新建一个列表用于显示
var postList = transMyPostList(myPosts)
/*var id2 = ""
for (post in postList) {
id2 += post.id
}
Log.d("MyPostScreen", "MyPostScreen22222,the two postList: \n${id2}")*/
//记录选中的帖子
var postSelected = mutableListOf<Post>().toMutableStateList()
Column(modifier = Modifier.fillMaxSize()) {
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
text = "我的帖子",
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
},
navigationIcon = {
IconButton(onClick = onClickBack) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null
)
}
},
actions = {
IconButton(onClick = {
if (DeletePost(postSelected)) {
postSelected = mutableListOf<Post>().toMutableStateList() //删除后,清空选中的帖子列表
Toast.makeText(context, "删除成功", Toast.LENGTH_SHORT).show()
deleteFlag2 += 1
} else {
Toast.makeText(context, "删除失败", Toast.LENGTH_SHORT).show()
}
}) {
Icon(imageVector = Icons.Default.Delete, contentDescription = null)
}
})
Spacer(modifier = Modifier.requiredHeight(10.dp))
if (postList.isEmpty()) {
Text(
text = "您还没分享过经验",
modifier = Modifier
.fillMaxSize(),
textAlign = TextAlign.Center
)
} else {
if (deleteFlag1 < deleteFlag2) {
myPosts = getPostByUserid(userid ?: 1)
postList = transMyPostList(myPosts)
deleteFlag2 = 0
}
/*var id3 = ""
for (myPost in myPosts) {
id3 += myPost.id
}
Log.d("MyPostScreen", "MyPostScreen22222 the three: \n${id3}")
var id4 = ""
for (post in postList) {
id4 += post.id
}
Log.d("MyPostScreen", "MyPostScreen22222,the four postList: \n${id4}")*/
LazyColumn {
items(items = postList) { post ->
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.border(1.dp, Color.Black, RoundedCornerShape(8.dp))
.padding(8.dp)
.fillMaxWidth()
) {
Column {
Text(
modifier = Modifier
.padding(start = 16.dp),
text = post.title,
style = MaterialTheme.typography.headlineSmall
)
Text(
modifier = Modifier
.padding(start = 16.dp),
text = post.content
)
}
Checkbox(
checked = post.checked,
onCheckedChange = { checked ->
if (post.checked) {
postSelected.remove(getPostByPostId(post.id))
} else {
postSelected.add(getPostByPostId(post.id))
}
post.checked = checked
})
}
Spacer(modifier = Modifier.requiredHeight(10.dp))
}
}
}
}
}
//转换列表,方便删除选择和删除
private fun transMyPostList(myPosts: List<Post>): List<MyPost> {
var newMyPosts = mutableListOf<MyPost>()
for (myPost in myPosts) {
newMyPosts.add(
MyPost(
myPost.id,
myPost.title,
if (myPost.content.length > 15) {
myPost.content.substring(0, 15) + "……"
} else {
myPost.content + "……"
}
)
)
}
return newMyPosts
}

@ -1,149 +0,0 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package com.example.fruitandvegetableguide.ui.user
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Camera
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Password
import androidx.compose.material.icons.filled.Spa
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.example.fruitandvegetableguide.R
import com.example.fruitandvegetableguide.utils.getUsernameByUserid
@Composable
fun UserScreen(
userid: Int? = 1,
onClickToMain: () -> Unit = {},
onClickToMyPost: (userid: Int) -> Unit = {},
onClickToPhotograph: () -> Unit = {},
) {
val username: String = getUsernameByUserid(userid ?: 1)
Scaffold(bottomBar = {
MyBottomNavigation(
userid = userid ?: 1,
onClickToMain = onClickToMain,
onClickToPhotograph = onClickToPhotograph
)
}) { padding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding)
) {
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
text = "用户",
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
})
Spacer(modifier = Modifier.requiredHeight(10.dp))
Row {
Image(
painter = painterResource(R.drawable.user_circle),
contentDescription = null,
Modifier.padding(16.dp)
)
Column {
Text(
text = "用户",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(text = "账号:$username")
}
}
Spacer(modifier = Modifier.requiredHeight(10.dp))
Row {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = null,
Modifier.padding(16.dp)
)
OutlinedButton(onClick = { onClickToMyPost(userid ?: 1) }) {
Text(text = "我的帖子")
}
}
Spacer(modifier = Modifier.requiredHeight(10.dp))
Row {
Icon(
imageVector = Icons.Default.Password,
contentDescription = null,
Modifier.padding(16.dp)
)
OutlinedButton(onClick = { /*TODO*/ }) {
Text(text = "修改密码")
}
}
}
}
}
//底部导航栏
@Composable
fun MyBottomNavigation(
userid: Int,
modifier: Modifier = Modifier,
onClickToMain: () -> Unit = {},
onClickToPhotograph: () -> Unit = {},
) {
NavigationBar(containerColor = MaterialTheme.colorScheme.surfaceVariant, modifier = modifier) {
NavigationBarItem(
icon = {
Icon(
imageVector = Icons.Default.Spa,
contentDescription = null
)
},
label = { Text(text = "首页") },
selected = false,
onClick = onClickToMain
)
NavigationBarItem(
icon = {
Icon(
imageVector = Icons.Default.Camera,
contentDescription = null
)
},
label = { Text(text = "识别") },
selected = false,
onClick = onClickToPhotograph
)
NavigationBarItem(icon = {
Icon(
imageVector = Icons.Default.AccountCircle,
contentDescription = null
)
}, label = { Text(text = "用户") },
selected = true,
onClick = { })
}
}

@ -1,209 +0,0 @@
package com.example.fruitandvegetableguide.utils
import com.example.fruitandvegetableguide.R
import com.example.fruitandvegetableguide.data.Guide
import com.example.fruitandvegetableguide.data.Post
import com.example.fruitandvegetableguide.data.dbhelper.DbHelper
import com.example.fruitandvegetableguide.data.local.LocalAccountsDataProvider
import com.example.fruitandvegetableguide.data.local.LocalGuidesDataProvider
import com.example.fruitandvegetableguide.data.local.LocalPostsDataProvider
import kotlinx.coroutines.*
// TODO 这里放页面逻辑相关代码
// 输入参数和返回参数不要改
fun loginVerification(username: String, password: String): Int {
// TODO 1.登录验证
// 输入username:用户名, password:密码
// 逻辑查询用户表用户名和密码正确返回id;用户名和密码不匹配返回-1;用户表中没有该用户名返回-1
// 输出id或-1
// 将下面的测试代码注释掉完成需要的代码之后的TODO同此
/* if (password == "123456") {
if (username == "aaa") return 1
if (username == "bbb") return 2
if (username == "ccc") return 3
}
return -1*/
return DbHelper.login(username, password)
}
fun register(username: String, password: String): Boolean {
// TODO 2.注册
// 输入username:用户名, password:密码
// 逻辑:插入用户表;插入成功返回true;插入失败返回false
// 输出true或false
// 将下面的测试代码注释掉,完成需要的代码
// return true
return DbHelper.register(username, password)
}
fun postListInit(): List<Post> {
// TODO 3.帖子列表初始化
// 输入:无
// 逻辑查询post表获取前100个帖子信息用于最初显示。
// 查询的每一行帖子数据封装成Post数据类实例即每一行数据信息都可以用来声明一个Post数据类对象然后将这些对象封装成一个列表使用mutableListOf
// 输出:列表
// 将下面的测试代码注释掉,完成需要的代码
// return LocalPostsDataProvider.allPosts
return DbHelper.postListInit()
}
fun getPostByPostId(postId: Int): Post {
// TODO 4.根据id在post表里查找帖子
// 输入postId要查找帖子的id
// 逻辑查询帖子表条件id==postId。
// 查询的数据信息封装成一个Post数据类对象
// 输出Post对象
// 将下面的测试代码注释掉,完成需要的代码
// for (post in LocalPostsDataProvider.allPosts) {
// if (post.id == postId) {
// return post
// }
// }
// return LocalPostsDataProvider.allPosts[0]
return DbHelper.getPostByPostId(postId)
}
fun getGuideByKind(kind: String): List<Guide> {
// TODO 5.根据kind在guide表里查找指南
// 输入kind要查找指南的kind
// 逻辑查询guide表条件 输入的kind in guide表的kind。
// 查询的每一行数据信息封装成一个Guide数据类对象
// 输出Guide的列表没有则返回空列表
// 将下面的测试代码注释掉,完成需要的代码
if (kind == "") {
return guideListInit()
}
// var guides = mutableListOf<Guide>()
// for (guide in LocalGuidesDataProvider.allGuides) {
// if (kind in guide.kind) {
// guides.add(guide)
// }
// }
// return guides
return DbHelper.getGuideByKind(kind)
}
fun getPostByLabels(labels: String): List<Post> {
// TODO 6.根据labels在post表里查找帖子
// 输入labels要查找帖子的labels
// 逻辑查询post表条件 输入的labels in post表的labels。
// 查询的每一行数据信息封装成一个Post数据类对象
// 输出Post的列表
// 将下面的测试代码注释掉,完成需要的代码
if (labels == "") {
return postListInit()
}
// var posts = mutableListOf<Post>()
// for (post in LocalPostsDataProvider.allPosts) {
// if (labels in post.labels) {
// posts.add(post)
// }
// }
// return posts
return DbHelper.getPostByLabels(labels)
}
fun getGuideByGuideId(guideId: Int): Guide {
// TODO 7.根据id在guide表里查找指南
// 输入guideId要查找指南的id
// 逻辑查询指南表条件id==guideId。
// 查询的数据信息封装成一个Guide数据类对象
// 输出Guide对象
// 将下面的测试代码注释掉,完成需要的代码
// for (guide in LocalGuidesDataProvider.allGuides) {
// if (guide.id == guideId) {
// return guide
// }
// }
// return LocalGuidesDataProvider.allGuides[0]
return DbHelper.getGuideByGuideId(guideId)
}
fun guideListInit(): List<Guide> {
// TODO 8.指南列表初始化
// 输入:无
// 逻辑查询guide表获取前100个指南信息用于最初显示。
// 查询的每一行指南数据封装成Guide数据类实例即每一行数据信息都可以用来声明一个Guide数据类对象然后将这些对象封装成一个列表使用mutableListOf
// 输出:列表
// 将下面的测试代码注释掉,完成需要的代码
return DbHelper.guideListInit()
}
fun postInsert(
userid: Int,
title: String,
imgId: Int = R.drawable.loading,
labels: String,
content: String
): Boolean {
// TODO 9.插入新建的帖子
// 输入userid用户id
// title标题
// imgId舍弃图片这里保持默认就行
// labels标签
// content内容
// 逻辑将传入的内容插入到post表。插入成功返回true否则返回false
// 输出true或false
// 将下面的测试代码注释掉,完成需要的代码
// val newPost =
// Post(LocalPostsDataProvider.allPosts.size + 1, userid, title, imgId, labels, content)
// try {
// LocalPostsDataProvider.allPosts.add(newPost)
// } catch (e: RuntimeException) {
// return false
// }
// return true
return DbHelper.postInsert(userid, title, imgId ,labels, content)
}
fun getUsernameByUserid(userid: Int): String {
// TODO 10.根据id在account表里查找用户用户名称
// 输入userid要查找的用户的id
// 逻辑查询账户表条件id==userid。
// 输出username
// 将下面的测试代码注释掉,完成需要的代码
// for (user in LocalAccountsDataProvider.allUserAccounts) {
// if (user.id == userid) {
// return user.username
// }
// }
// return LocalAccountsDataProvider.allUserAccounts[0].username
return DbHelper.getUsernameByUserid(userid)
}
fun getPostByUserid(userid: Int): List<Post> {
// TODO 11.根据userid在post表里查找帖子
// 输入userid要查找帖子的userid
// 逻辑查询post表条件 userid == userid。
// 查询的每一行数据信息封装成一个Post数据类对象
// 输出Post的列表
// 将下面的测试代码注释掉,完成需要的代码
// var postList = mutableListOf<Post>()
// for (post in LocalPostsDataProvider.allPosts) {
// if (post.userId == userid) {
// postList.add(post)
// }
// }
// return postList
return DbHelper.getPostByUserid(userid)
}
fun DeletePost(postToDeleteList: List<Post>): Boolean {
// TODO 12.根据传入的要删除的帖子的列表从数据库post表里删除帖子
// 输入postToDeleteList要删除帖子的列表
// 逻辑遍历postToDeleteList每次遍历从数据库post表里删除帖子sql语句条件可以为DELETE FROM post WHERE id==post.id;
// 删除成功返回true否则返回false
// 输出true或false
// 将下面的测试代码注释掉,完成需要的代码
// try {
// for (post in postToDeleteList) {
// LocalPostsDataProvider.allPosts.remove(post)
// }
// } catch (e: RuntimeException) {
// return false
// }
// return true
return DbHelper.DeletePost(postToDeleteList)
}

@ -1,30 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

@ -1,170 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="#FF000000"
android:pathData="M16,5.887C22.086,5.887 22.395,11.324 22.73,12.395C22.73,12.395 23.211,12.93 23.324,13.746C23.398,14.273 23.098,14.871 23.098,14.871C23.098,14.871 25.043,17.492 25.043,19.551C25.043,20.836 24.664,21.5 24.223,21.5C23.777,21.5 23.129,20.141 23.129,20.141C23.129,20.141 22.113,22.309 21.605,22.621C21.098,22.93 23.438,23.27 23.438,24.281C23.438,25.297 21.578,25.746 20.059,25.746C18.535,25.746 16.113,24.957 16.113,24.957L15.238,24.93C15.238,24.93 14.563,25.887 11.773,25.887C8.984,25.887 7.773,25.129 7.773,24.227C7.773,23.012 9.551,22.848 9.551,22.848C9.551,22.848 8.418,22.531 7.461,19.852C7.461,19.852 6.797,21.297 5.859,21.297C5.859,21.297 5.465,21.063 5.465,19.746C5.465,17.023 7.422,15.695 8.266,14.879C8.266,14.879 8.125,14.523 8.199,14.082C8.281,13.59 8.574,13.293 8.574,13.293C8.574,13.293 8.465,12.703 8.875,12.227C8.957,10.902 9.914,5.887 16,5.887M16,3.887C9.602,3.887 7.332,8.477 6.93,11.555C6.738,11.93 6.629,12.316 6.586,12.68C6.434,12.969 6.297,13.324 6.227,13.746C6.207,13.852 6.195,13.961 6.188,14.063C5.078,15.082 3.465,16.82 3.465,19.746C3.465,21.777 4.211,22.645 4.84,23.016L5.309,23.297L5.859,23.297C5.875,23.297 5.891,23.297 5.91,23.297C5.82,23.582 5.773,23.891 5.773,24.227C5.773,25.086 6.207,27.891 11.773,27.891C13.813,27.891 15.086,27.449 15.867,26.977C16.688,27.223 18.605,27.746 20.055,27.746C23.324,27.746 25.438,26.387 25.438,24.281C25.438,23.906 25.363,23.574 25.242,23.277C26.207,22.84 27.039,21.711 27.039,19.551C27.039,17.656 25.992,15.668 25.281,14.535C25.336,14.211 25.355,13.848 25.305,13.473C25.188,12.633 24.852,11.965 24.582,11.547C24.574,11.508 24.566,11.469 24.559,11.43C23.512,6.422 20.629,3.887 16,3.887Z"/>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 811 KiB

@ -1,24 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,15c-4.132,0 -7.98,-1.214 -10.294,-3.249a1,1 0,1 1,1.32 -1.502C4.986,11.972 8.34,13 12,13s7.014,-1.028 8.974,-2.751a1,1 0,1 1,1.32 1.502C19.98,13.786 16.132,15 12,15Z"
android:fillColor="#333"/>
<path
android:pathData="M12,18a1,1 0,0 1,-1 -1V14a1,1 0,0 1,2 0v3A1,1 0,0 1,12 18Z"
android:fillColor="#333"/>
<path
android:pathData="M7.749,17.667a0.964,0.964 0,0 1,-0.17 -0.015A0.999,0.999 0,0 1,6.762 16.498L7.267,13.563a1,1 0,1 1,1.971 0.339l-0.505,2.935A1,1 0,0 1,7.749 17.667Z"
android:fillColor="#333"/>
<path
android:pathData="M3.636,16.306a1.001,1.001 0,0 1,-0.942 -1.336l0.979,-2.745a1,1 0,1 1,1.884 0.672L4.578,15.642A1.001,1.001 0,0 1,3.636 16.306Z"
android:fillColor="#333"/>
<path
android:pathData="M16.251,17.667a1,1 0,0 1,-0.984 -0.831l-0.505,-2.935a1,1 0,0 1,1.971 -0.339l0.505,2.935a0.999,0.999 0,0 1,-0.816 1.155A0.964,0.964 0,0 1,16.251 17.667Z"
android:fillColor="#333"/>
<path
android:pathData="M20.364,16.306a1,1 0,0 1,-0.942 -0.665L18.444,12.897a1,1 0,1 1,1.884 -0.672l0.979,2.745a1.001,1.001 0,0 1,-0.942 1.336Z"
android:fillColor="#333"/>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

@ -1,3 +0,0 @@
<resources>
<string name="app_name">FruitandVegetableGuide</string>
</resources>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save