211540882-10.26 #3

Merged
p5gy6sexf merged 3 commits from djm_branch into master 2 years ago

@ -1,8 +1,17 @@
<?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>

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<runningDeviceTargetSelectedWithDropDown>
<Target>
<type value="RUNNING_DEVICE_TARGET" />
<deviceKey>
<Key>
<type value="SERIAL_NUMBER" />
<value value="30467862410075A" />
</Key>
</deviceKey>
</Target>
</runningDeviceTargetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-10-26T14:23:55.395630500Z" />
</component>
</project>

@ -9,7 +9,7 @@ android {
defaultConfig {
applicationId "com.example.fruitandvegetableguide"
minSdk 21
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
@ -47,6 +47,14 @@ android {
}
dependencies {
//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"
@ -54,6 +62,8 @@ dependencies {
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:+'
def composeBom = platform('androidx.compose:compose-bom:2023.08.00')
implementation(composeBom)
@ -78,16 +88,3 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
// implementation("androidx.compose.material:material")
// implementation("androidx.compose.foundation:foundation")
// implementation("androidx.compose.ui:ui")
// implementation("androidx.compose.material:material-icons-core")
// implementation("androidx.compose.material3:material3-window-size-class")
// implementation("androidx.compose.runtime:runtime-livedata")
// implementation("androidx.compose.runtime:runtime-rxjava2")
// implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0')
// implementation platform('androidx.compose:compose-bom:2022.10.00')
// implementation 'androidx.compose.ui:ui-graphics'
// implementation 'androidx.appcompat:appcompat:1.4.1'
// implementation 'com.google.android.material:material:1.5.0'
// androidTestImplementation platform('androidx.compose:compose-bom:2022.10.00')

@ -0,0 +1,20 @@
{
"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"
}

@ -2,7 +2,24 @@
<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"
@ -12,6 +29,19 @@
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"

@ -9,11 +9,8 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.example.fruitandvegetableguide.ui.login.LoginScreen
import com.example.fruitandvegetableguide.ui.theme.FruitandVegetableGuideTheme
class MyActivity : AppCompatActivity() {

@ -57,75 +57,156 @@ fun NavHost(
val userid = it.arguments?.getInt("userid")//接收userid
MainScreen(
userid = userid,
onClickToSearch = { navController.navigateSingleTopTo(Search.route) },
onClickToUser = { navController.navigateSingleTopTo(User.route) },
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) },
onClickToGuideDetail = { navController.navigateSingleTopTo(GuideDetail.route) },
onClickToPostDetail = { navController.navigateSingleTopTo(PostDetail.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) {
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 = { navController.navigateSingleTopTo(GuideDetail.route) },
onClickToPostDetail = { navController.navigateSingleTopTo(PostDetail.route) },
onClickToGuideDetail = { guideId -> navController.navigateSingleTopTo("${GuideDetail.route}/${guideId}") },
onClickToPostDetail = { postId -> navController.navigateSingleTopTo("${PostDetail.route}/${postId}") },
)
}
//指南详情页路由
composable(route = GuideDetail.route) {
GuideDetailScreen(onClickBack = { navController.popBackStack() })
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) {
PostDetailScreen(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) {
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) },
onClickToMyPost = { navController.navigateSingleTopTo("${MyPost.route}/${userid}") },
onClickToPhotograph = { navController.navigateSingleTopTo(Photograph.route) }
)
}
//我的帖子页面路由
composable(route = MyPost.route) {
MyPostScreen(onClickBack = { navController.popBackStack() })
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(onClickToRecognizeResult = {
navController.navigateSingleTopTo(
RecognizeResult.route
)
})
PhotographScreen(
onClickBack = { navController.popBackStack() },
onClickToRecognizeResult = { localImgPath ->
navController.navigateSingleTopTo(
"${RecognizeResult.route}/${localImgPath}"
)
})
}
//识别结果页路由
composable(route = RecognizeResult.route) {
RecognizeResultScreen(onClickBack = { navController.popBackStack() })
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 = { navController.navigateSingleTopTo(Search.route) },
onClickToGuideDetail = { navController.navigateSingleTopTo(GuideDetail.route) },
onClickToSearch = { search -> navController.navigateSingleTopTo("${Search.route}/${search}") },
onClickToGuideDetail = { guideId -> navController.navigateSingleTopTo("${GuideDetail.route}/${guideId}") },
onClickBack = { navController.popBackStack() }
)
}
//社区页路由
composable(route = Community.route) {
composable(
route = "${Community.route}/{userid}",
arguments = listOf(
navArgument("userid") {
type = NavType.IntType
nullable = false
}
)
) {
val userid = it.arguments?.getInt("userid")//接收userid
CommunityScreen(
onClickToSearch = { navController.navigateSingleTopTo(Search.route) },
onClickToPostDetail = { navController.navigateSingleTopTo(PostDetail.route) },
onClickToPostEdit = { navController.navigateSingleTopTo(PostEdit.route) },
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) {
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() }
)
}

@ -0,0 +1,15 @@
# 要做的事还有很多
![TODO](../../../../res/drawable/todo_list.png)
运行前,先放一张西红柿图片在相册中,方便测试
Android Studio无法预览Markdown文件
```
In the Android Studio:
1. Find action (ctrl + shift + A / command + shift + A)
2. Search for Choose Boot Java Runtime for the IDE
3. Select the latest version in the "New:" dropdown - e.g. 11.0.12+7-b1504.27 JetBrains Runtime with JCEF // 这里我选择的最新版本即可。
4. OK
5. Restart
```

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

@ -1,13 +1,15 @@
package com.example.fruitandvegetableguide.data
import com.example.fruitandvegetableguide.R
//指南数据类
data class Guide(
val id: Long,
val imgId: Int,
val kind: String,
val imgUrl: String,
val suitable: String,
val taboo: String,
val nutritiveValue: String,
val recommendedMenu: String
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,11 +1,13 @@
package com.example.fruitandvegetableguide.data
import com.example.fruitandvegetableguide.R
//帖子数据类
data class Post(
val id: Long,
val userId: Long,
val title: String,
val imgId: Int,
val labels: ArrayList<String>,
val content: String
val id: Int, //id自增
val userId: Int, //用户id
val title: String, //标题
val imgId: Int = R.drawable.loading,
val labels: String, //标签
val content: String //内容
)

@ -0,0 +1,3 @@
package com.example.fruitandvegetableguide.data.dbhelper
// TODO 这里放数据库工具方法,这些方法可以去网上找现成的

@ -1,3 +0,0 @@
package com.example.fruitandvegetableguide.data.dbutils
// TODO 这里放数据库工具方法

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

@ -9,19 +9,17 @@ object LocalPostsDataProvider {
Post(
1,
1,
"白菜怎么挑选",
"标题-默认",
R.drawable.cabbage,
labels = arrayListOf("挑选方法", "白菜"),
"大白菜一般菜心是黄色的,越往外菜叶绿色逐渐增多,黄色菜叶越多,大白菜越好吃。\n" +
"黄色菜叶多说明生长周期长,主要是大白菜生长时间越长,黄色菜叶越多,说明长势很好,包的也瓷实。\n" +
"黄色菜叶多说明农家肥用得多。一般来说,农家肥种的白菜个头大,菜瓷实,黄色菜叶比较多,吃起来口感也要好一些。颜色发绿的白菜叶片厚实,吃起来口感比较粗,特别耐储存。\n"
labels = "标签-默认",
"内容-默认"
),
Post(
2,
2,
"关于西瓜的挑选经验",
R.drawable.watermelon,
labels = arrayListOf("西瓜", "经验"),
labels = "西瓜,经验",
"1、看脐圈 西瓜底部的脐圈,小、圆而且略微凹陷的西瓜,一般生长时间充分,果实会更甜。\n" +
"2、辨颜色 颜色最好挑青绿色,不要雾雾白白的。\n" +
"3、看纹路 西瓜纹路整齐的,就是好瓜。\n" +
@ -33,15 +31,15 @@ object LocalPostsDataProvider {
3,
"生姜怎么挑选?",
R.drawable.ginger,
labels = arrayListOf("生姜", "提问"),
"有没有好心人教教如何挑选生姜,大的和小的有什么区别"
labels = "生姜,经验",
"测试数据"
),
Post(
4,
1,
"白菜怎么挑选",
R.drawable.cabbage,
labels = arrayListOf("挑选方法", "白菜"),
labels = "挑选方法,白菜",
"大白菜一般菜心是黄色的,越往外菜叶绿色逐渐增多,黄色菜叶越多,大白菜越好吃。\n" +
"黄色菜叶多说明生长周期长,主要是大白菜生长时间越长,黄色菜叶越多,说明长势很好,包的也瓷实。\n" +
"黄色菜叶多说明农家肥用得多。一般来说,农家肥种的白菜个头大,菜瓷实,黄色菜叶比较多,吃起来口感也要好一些。颜色发绿的白菜叶片厚实,吃起来口感比较粗,特别耐储存。\n"
@ -51,7 +49,7 @@ object LocalPostsDataProvider {
2,
"关于西瓜的挑选经验",
R.drawable.watermelon,
labels = arrayListOf("西瓜", "经验"),
labels = "西瓜,经验",
"1、看脐圈 西瓜底部的脐圈,小、圆而且略微凹陷的西瓜,一般生长时间充分,果实会更甜。\n" +
"2、辨颜色 颜色最好挑青绿色,不要雾雾白白的。\n" +
"3、看纹路 西瓜纹路整齐的,就是好瓜。\n" +
@ -63,15 +61,15 @@ object LocalPostsDataProvider {
3,
"生姜怎么挑选?",
R.drawable.ginger,
labels = arrayListOf("生姜", "提问"),
"有没有好心人教教如何挑选生姜,大的和小的有什么区别"
labels = "生姜,经验",
"测试数据"
),
Post(
7,
1,
"白菜怎么挑选",
R.drawable.cabbage,
labels = arrayListOf("挑选方法", "白菜"),
labels = "挑选方法,白菜",
"大白菜一般菜心是黄色的,越往外菜叶绿色逐渐增多,黄色菜叶越多,大白菜越好吃。\n" +
"黄色菜叶多说明生长周期长,主要是大白菜生长时间越长,黄色菜叶越多,说明长势很好,包的也瓷实。\n" +
"黄色菜叶多说明农家肥用得多。一般来说,农家肥种的白菜个头大,菜瓷实,黄色菜叶比较多,吃起来口感也要好一些。颜色发绿的白菜叶片厚实,吃起来口感比较粗,特别耐储存。\n"
@ -81,7 +79,7 @@ object LocalPostsDataProvider {
2,
"关于西瓜的挑选经验",
R.drawable.watermelon,
labels = arrayListOf("西瓜", "经验"),
labels = "西瓜,经验",
"1、看脐圈 西瓜底部的脐圈,小、圆而且略微凹陷的西瓜,一般生长时间充分,果实会更甜。\n" +
"2、辨颜色 颜色最好挑青绿色,不要雾雾白白的。\n" +
"3、看纹路 西瓜纹路整齐的,就是好瓜。\n" +
@ -93,15 +91,15 @@ object LocalPostsDataProvider {
3,
"生姜怎么挑选?",
R.drawable.ginger,
labels = arrayListOf("生姜", "提问"),
"有没有好心人教教如何挑选生姜,大的和小的有什么区别"
labels = "生姜,经验",
"测试数据"
),
Post(
10,
1,
"白菜怎么挑选",
R.drawable.cabbage,
labels = arrayListOf("挑选方法", "白菜"),
labels = "挑选方法,白菜",
"大白菜一般菜心是黄色的,越往外菜叶绿色逐渐增多,黄色菜叶越多,大白菜越好吃。\n" +
"黄色菜叶多说明生长周期长,主要是大白菜生长时间越长,黄色菜叶越多,说明长势很好,包的也瓷实。\n" +
"黄色菜叶多说明农家肥用得多。一般来说,农家肥种的白菜个头大,菜瓷实,黄色菜叶比较多,吃起来口感也要好一些。颜色发绿的白菜叶片厚实,吃起来口感比较粗,特别耐储存。\n"
@ -111,7 +109,7 @@ object LocalPostsDataProvider {
2,
"关于西瓜的挑选经验",
R.drawable.watermelon,
labels = arrayListOf("西瓜", "经验"),
labels = "西瓜,经验",
"1、看脐圈 西瓜底部的脐圈,小、圆而且略微凹陷的西瓜,一般生长时间充分,果实会更甜。\n" +
"2、辨颜色 颜色最好挑青绿色,不要雾雾白白的。\n" +
"3、看纹路 西瓜纹路整齐的,就是好瓜。\n" +
@ -123,15 +121,15 @@ object LocalPostsDataProvider {
3,
"生姜怎么挑选?",
R.drawable.ginger,
labels = arrayListOf("生姜", "提问"),
"有没有好心人教教如何挑选生姜,大的和小的有什么区别"
labels = "生姜,经验",
"测试数据"
),
Post(
14,
1,
"白菜怎么挑选",
R.drawable.cabbage,
labels = arrayListOf("挑选方法", "白菜"),
labels = "挑选方法,白菜",
"大白菜一般菜心是黄色的,越往外菜叶绿色逐渐增多,黄色菜叶越多,大白菜越好吃。\n" +
"黄色菜叶多说明生长周期长,主要是大白菜生长时间越长,黄色菜叶越多,说明长势很好,包的也瓷实。\n" +
"黄色菜叶多说明农家肥用得多。一般来说,农家肥种的白菜个头大,菜瓷实,黄色菜叶比较多,吃起来口感也要好一些。颜色发绿的白菜叶片厚实,吃起来口感比较粗,特别耐储存。\n"
@ -141,7 +139,7 @@ object LocalPostsDataProvider {
2,
"关于西瓜的挑选经验",
R.drawable.watermelon,
labels = arrayListOf("西瓜", "经验"),
labels = "西瓜,经验",
"1、看脐圈 西瓜底部的脐圈,小、圆而且略微凹陷的西瓜,一般生长时间充分,果实会更甜。\n" +
"2、辨颜色 颜色最好挑青绿色,不要雾雾白白的。\n" +
"3、看纹路 西瓜纹路整齐的,就是好瓜。\n" +
@ -153,15 +151,15 @@ object LocalPostsDataProvider {
3,
"生姜怎么挑选?",
R.drawable.ginger,
labels = arrayListOf("生姜", "提问"),
"有没有好心人教教如何挑选生姜,大的和小的有什么区别"
labels = "生姜,经验",
"测试数据"
),
Post(
17,
1,
"白菜怎么挑选",
R.drawable.cabbage,
labels = arrayListOf("挑选方法", "白菜"),
labels = "挑选方法,白菜",
"大白菜一般菜心是黄色的,越往外菜叶绿色逐渐增多,黄色菜叶越多,大白菜越好吃。\n" +
"黄色菜叶多说明生长周期长,主要是大白菜生长时间越长,黄色菜叶越多,说明长势很好,包的也瓷实。\n" +
"黄色菜叶多说明农家肥用得多。一般来说,农家肥种的白菜个头大,菜瓷实,黄色菜叶比较多,吃起来口感也要好一些。颜色发绿的白菜叶片厚实,吃起来口感比较粗,特别耐储存。\n"
@ -171,7 +169,7 @@ object LocalPostsDataProvider {
2,
"关于西瓜的挑选经验",
R.drawable.watermelon,
labels = arrayListOf("西瓜", "经验"),
labels = "西瓜,经验",
"1、看脐圈 西瓜底部的脐圈,小、圆而且略微凹陷的西瓜,一般生长时间充分,果实会更甜。\n" +
"2、辨颜色 颜色最好挑青绿色,不要雾雾白白的。\n" +
"3、看纹路 西瓜纹路整齐的,就是好瓜。\n" +
@ -179,12 +177,20 @@ object LocalPostsDataProvider {
"5、听声音 将西瓜托在手里,手指轻轻敲,如果发出“咚、咚”的清脆声,并且托瓜的手有震动的感觉就是熟透的瓜,一般瓜味也会比较甜。"
),
Post(
19,
12,
3,
"生姜怎么挑选?",
R.drawable.ginger,
labels = arrayListOf("生姜", "提问"),
"有没有好心人教教如何挑选生姜,大的和小的有什么区别"
labels = "生姜,经验",
"测试数据"
),
Post(
13,
3,
"西红柿怎么挑选?",
R.drawable.ginger,
labels = "西红柿,经验",
"测试数据"
)
)
}

@ -1,76 +1,206 @@
@file:OptIn(ExperimentalMaterial3Api::class)
@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(
onClickToSearch: () -> Unit = {},
onClickToPostDetail: () -> Unit = {},
onClickToPostEdit: () -> Unit = {},
userid: Int? = 1,
onClickToSearch: (search: String) -> Unit = {},
onClickToPostDetail: (postId: Int) -> Unit = {},
onClickToPostEdit: (userid: 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
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))
}
})
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(text = "社区页", modifier = Modifier.align(alignment = Alignment.CenterHorizontally))
Spacer(modifier = Modifier.requiredHeight(10.dp))
Button(
onClick = onClickToSearch,
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
) {
Text(text = "搜索页")
}
}) {
Column {
Spacer(
modifier = Modifier
.requiredHeight(60.dp)
)
SearchBar(onClickToSearch = onClickToSearch)
Spacer(
modifier = Modifier
.requiredHeight(10.dp)
.padding(it)
)
Posts(postsList = postList, onClickToPostDetail = onClickToPostDetail)
}
Spacer(modifier = Modifier.requiredHeight(10.dp))
Button(
onClick = onClickToPostDetail,
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
) {
Text(text = "帖子详情页")
}
}
//搜索框
@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)
}
Spacer(modifier = Modifier.requiredHeight(10.dp))
Button(
onClick = onClickToPostEdit,
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
}
}
//单个帖子的显示
@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
)
)
) {
Text(text = "帖子编辑页")
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)
}
}
}
}

@ -8,27 +8,39 @@ 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.tooling.preview.Preview
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(onClickBack: () -> Unit) {
Column(modifier = Modifier.fillMaxSize()) {
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 = "PostTitle",
text = post.title,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
@ -42,7 +54,23 @@ fun PostDetailScreen(onClickBack: () -> Unit) {
)
}
})
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 = "帖子详情页", modifier = Modifier.align(alignment = Alignment.CenterHorizontally))
Text(
text = "内容",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.ExtraBold)
)
Text(
text = post.content
)
}
}

@ -2,30 +2,45 @@
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(),
@ -46,6 +61,67 @@ fun PostEditScreen(
}
})
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(text = "帖子编辑页", modifier = Modifier.align(alignment = Alignment.CenterHorizontally))
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("发布")
}
}
}
}

@ -8,27 +8,42 @@ 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(onClickBack: () -> Unit) {
Column(modifier = Modifier.fillMaxSize()) {
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 = "GuideTitle",
text = guide.kind,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
@ -43,6 +58,47 @@ fun GuideDetailScreen(onClickBack: () -> Unit) {
}
})
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(text = "指南详情页", modifier = Modifier.align(alignment = Alignment.CenterHorizontally))
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,
)
}
}

@ -2,31 +2,53 @@
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: () -> Unit = {},
onClickToGuideDetail: () -> Unit = {},
onClickToSearch: (search: String) -> Unit = {},
onClickToGuideDetail: (guideId: Int) -> Unit = {},
onClickBack: () -> Unit = {},
) {
Column(modifier = Modifier.fillMaxSize()) {
@ -48,21 +70,97 @@ fun GuideScreen(
)
}
})
SearchBar(onClickToSearch = onClickToSearch)
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(text = "指南页", modifier = Modifier.align(alignment = Alignment.CenterHorizontally))
Spacer(modifier = Modifier.requiredHeight(10.dp))
Button(
onClick = onClickToSearch,
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
) {
Text(text = "搜索页")
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)
}
Spacer(modifier = Modifier.requiredHeight(10.dp))
Button(
onClick = onClickToGuideDetail,
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
}
}
//单个指南的显示
@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
)
)
) {
Text(text = "指南详情页")
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,46 +1,134 @@
@file:OptIn(ExperimentalMaterial3Api::class)
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.fillMaxWidth
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.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
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.android.example.camerx2.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: () -> Unit = {}) {
Column(modifier = Modifier.fillMaxSize()) {
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
text = "拍照",
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
})
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(text = "拍照页", modifier = Modifier.align(alignment = Alignment.CenterHorizontally))
Spacer(modifier = Modifier.requiredHeight(10.dp))
Button(onClick = onClickToRecognizeResult,modifier = Modifier.align(alignment = Alignment.CenterHorizontally)) {
Text(text = "识别")
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()
}
)
}

@ -2,27 +2,57 @@
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.tooling.preview.Preview
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
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(onClickBack: () -> Unit = {}) {
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(),
@ -43,6 +73,128 @@ fun RecognizeResultScreen(onClickBack: () -> Unit = {}) {
}
})
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(text = "识别结果页", modifier = Modifier.align(alignment = Alignment.CenterHorizontally))
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
}
}

@ -0,0 +1,65 @@
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();
}
}

@ -0,0 +1,72 @@
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();
}
}
}
}

@ -0,0 +1,29 @@
/*
* 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);
}
}

@ -0,0 +1,77 @@
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;
}
}

@ -0,0 +1,30 @@
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";
}
}

@ -0,0 +1,151 @@
package com.android.example.camerx2.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)
}
}

@ -0,0 +1,24 @@
package com.android.example.camerx2.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)

@ -0,0 +1,55 @@
package com.android.example.camerx2.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)
}
}

@ -0,0 +1,27 @@
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;
}
}

@ -49,7 +49,7 @@ import com.example.fruitandvegetableguide.utils.loginVerification
@Composable
fun LoginScreen(
onClickToRegister: () -> Unit = {},
onClickToMain: (Int) -> Unit = {}
onClickToMain: (userid: Int) -> Unit = {}
) {
var userid by remember { mutableIntStateOf(-1) }
var username by remember { mutableStateOf("") }

@ -1,9 +1,7 @@
package com.example.fruitandvegetableguide.ui.main
import android.widget.ImageButton
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@ -41,6 +39,10 @@ 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
@ -49,30 +51,33 @@ 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.data.local.LocalGuidesDataProvider.allGuides
import com.example.fruitandvegetableguide.data.local.LocalPostsDataProvider.allPosts
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?,
onClickToSearch: () -> Unit = {},
onClickToUser: () -> Unit = {},
userid: Int? = 1,
onClickToSearch: (search: String) -> Unit = {},
onClickToUser: (userid: Int) -> Unit = {},
onClickToPhotograph: () -> Unit = {},
onClickToGuide: () -> Unit = {},
onClickToCommunity: () -> Unit = {},
onClickToGuideDetail: () -> Unit = {},
onClickToPostDetail: () -> Unit = {},
onClickToCommunity: (userid: Int) -> Unit = {},
onClickToGuideDetail: (guideId: Int) -> Unit = {},
onClickToPostDetail: (postId: Int) -> Unit = {},
) {
FruitandVegetableGuideTheme() {
FruitandVegetableGuideTheme {
Scaffold(bottomBar = {
MyBottomNavigation(
userid = userid ?: 1,
onClickToUser = onClickToUser,
onClickToPhotograph = onClickToPhotograph
)
}) { padding ->
HomeScreen(
userid = userid ?: 1,
modifier = Modifier.padding(padding),
onClickToSearch = onClickToSearch,
onClickToGuide = onClickToGuide,
@ -87,12 +92,13 @@ fun MainScreen(
//主屏幕
@Composable
fun HomeScreen(
userid: Int,
modifier: Modifier = Modifier,
onClickToSearch: () -> Unit,
onClickToSearch: (search: String) -> Unit,
onClickToGuide: () -> Unit = {},
onClickToCommunity: () -> Unit = {},
onClickToGuideDetail: () -> Unit = {},
onClickToPostDetail: () -> Unit = {},
onClickToCommunity: (userid: Int) -> Unit = {},
onClickToGuideDetail: (guideId: Int) -> Unit = {},
onClickToPostDetail: (postId: Int) -> Unit = {},
) {
Column(modifier.verticalScroll(rememberScrollState())) {
Spacer(modifier = Modifier.height(16.dp))
@ -112,11 +118,9 @@ fun HomeScreen(
}
GuideGrid(onClickToGuideDetail = onClickToGuideDetail)
}
// HomeSection(title = "指南") {
// GuideGrid()
// }
Column {
TextButton(onClick = onClickToCommunity) {
TextButton(onClick = { onClickToCommunity(userid) }) {
Text(
text = "社区",
style = MaterialTheme.typography.titleMedium,
@ -127,9 +131,7 @@ fun HomeScreen(
}
PostGrid(onClickToPostDetail = onClickToPostDetail)
}
// HomeSection(title = "社区") {
// PostGrid()
// }
Spacer(modifier = Modifier.height(16.dp))
}
}
@ -138,12 +140,21 @@ fun HomeScreen(
@Composable
fun SearchBar(
modifier: Modifier = Modifier,
onClickToSearch: () -> Unit
onClickToSearch: (search: String) -> Unit
) {
var search by remember {
mutableStateOf("")
}
TextField(
value = "", onValueChange = {},
value = search, onValueChange = { str -> search = str },
leadingIcon = {
IconButton(onClick = onClickToSearch) {
IconButton(onClick = {
//路由不能传递空字符串
if (search == "") {
search = " "
}
onClickToSearch(search)
}) {
Icon(imageVector = Icons.Default.Search, contentDescription = null)
}
},
@ -163,14 +174,14 @@ fun SearchBar(
fun GuideElement(
modifier: Modifier = Modifier,
@DrawableRes drawable: Int,
guideId: Int,
kind: String,
onClickToGuideDetail: () -> Unit = {},
onClickToGuideDetail: (guideId: Int) -> Unit = {},
) {
Button(
onClick = onClickToGuideDetail,
onClick = { onClickToGuideDetail(guideId) },
colors = ButtonDefaults.buttonColors(
containerColor = Color.White,
// contentColor = Color.Black
),
contentPadding = PaddingValues(1.dp)
) {
@ -200,10 +211,11 @@ fun GuideElement(
//帖子元素
@Composable
fun PostCard(
postId: Int,
modifier: Modifier = Modifier,
@DrawableRes drawable: Int,
title: String,
onClickToPostDetail: () -> Unit = {},
onClickToPostDetail: (postId: Int) -> Unit = {},
) {
Surface(
shape = MaterialTheme.shapes.medium,
@ -215,7 +227,7 @@ fun PostCard(
modifier = Modifier.width(255.dp)
) {
Button(
onClick = onClickToPostDetail,
onClick = { onClickToPostDetail(postId) },
colors = ButtonDefaults.buttonColors(
containerColor = Color.LightGray,
)
@ -231,7 +243,6 @@ fun PostCard(
modifier = Modifier.padding(horizontal = 16.dp)
)
}
}
}
}
@ -241,7 +252,7 @@ fun PostCard(
@Composable
fun GuideGrid(
modifier: Modifier = Modifier,
onClickToGuideDetail: () -> Unit = {},
onClickToGuideDetail: (guideId: Int) -> Unit = {},
) {
LazyHorizontalGrid(
rows = GridCells.Fixed(2),
@ -250,8 +261,9 @@ fun GuideGrid(
verticalArrangement = Arrangement.spacedBy(2.dp),
modifier = modifier.height(300.dp)
) {
items(allGuides) { item ->
items(guideListInit()) { item ->
GuideElement(
guideId = item.id,
drawable = item.imgId,
kind = item.kind,
onClickToGuideDetail = onClickToGuideDetail
@ -264,7 +276,7 @@ fun GuideGrid(
@Composable
fun PostGrid(
modifier: Modifier = Modifier,
onClickToPostDetail: () -> Unit = {},
onClickToPostDetail: (postId: Int) -> Unit = {},
) {
LazyHorizontalGrid(
rows = GridCells.Fixed(4),
@ -273,8 +285,9 @@ fun PostGrid(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier.height(300.dp)
) {
items(allPosts) { item ->
items(postListInit()) { item ->
PostCard(
postId = item.id,
drawable = item.imgId,
title = item.title,
onClickToPostDetail = onClickToPostDetail
@ -283,26 +296,12 @@ fun PostGrid(
}
}
//小组件加上标题
//@Composable
//fun HomeSection(title: String, modifier: Modifier = Modifier, content: @Composable () -> Unit) {
// Column(modifier) {
// Text(
// text = title,
// style = MaterialTheme.typography.titleMedium,
// modifier = Modifier
// .paddingFromBaseline(top = 40.dp, bottom = 16.dp)
// .padding(horizontal = 16.dp)
// )
// content()
// }
//}
//底部导航栏
@Composable
fun MyBottomNavigation(
userid: Int,
modifier: Modifier = Modifier,
onClickToUser: () -> Unit = {},
onClickToUser: (userid: Int) -> Unit = {},
onClickToPhotograph: () -> Unit = {},
) {
NavigationBar(containerColor = MaterialTheme.colorScheme.surfaceVariant, modifier = modifier) {
@ -333,14 +332,8 @@ fun MyBottomNavigation(
)
}, label = { Text(text = "用户") },
selected = false,
onClick = onClickToUser
onClick = { onClickToUser(userid) }
)
}
}
//预览
//@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
//@Composable
//fun PostPrev() {
// MainScreen()
//}

@ -1,34 +1,91 @@
@file:OptIn(ExperimentalMaterial3Api::class)
@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: () -> Unit = {},
onClickToPostDetail: () -> 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(),
@ -48,24 +105,221 @@ fun SearchScreen(
)
}
})
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "搜索结果页",
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
//搜索框
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))
Button(
onClick = onClickToGuideDetail,
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Text(text = "指南详情页")
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))
Button(
onClick = onClickToPostDetail,
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
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
)
)
) {
Text(text = "帖子详情页")
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
}
}
}
}

@ -0,0 +1,16 @@
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,35 +1,86 @@
@file:OptIn(ExperimentalMaterial3Api::class)
@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.material3.Button
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(onClickBack: () -> Unit = {}) {
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 = "MyPost",
text = "我的帖子",
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
@ -42,11 +93,102 @@ fun MyPostScreen(onClickBack: () -> Unit = {}) {
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))
Text(
text = "我的帖子页",
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
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
}

@ -2,7 +2,9 @@
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
@ -11,33 +13,39 @@ 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.ArrowBack
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.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.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.tooling.preview.Preview
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: () -> Unit = {},
onClickToMyPost: (userid: Int) -> Unit = {},
onClickToPhotograph: () -> Unit = {},
) {
val username: String = getUsernameByUserid(userid ?: 1)
Scaffold(bottomBar = {
MyBottomNavigation(
userid = userid ?: 1,
onClickToMain = onClickToMain,
onClickToPhotograph = onClickToPhotograph
)
@ -58,24 +66,50 @@ fun UserScreen(
)
})
Spacer(modifier = Modifier.requiredHeight(10.dp))
Text(
text = "用户页",
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
)
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))
Button(
onClick = onClickToMyPost,
modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
) {
Text(text = "我的帖子")
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 = "修改密码")
}
}
}
}
}
//Unable to match the desired swap behavior.
//底部导航栏
@Composable
fun MyBottomNavigation(
userid: Int,
modifier: Modifier = Modifier,
onClickToMain: () -> Unit = {},
onClickToPhotograph: () -> Unit = {},

@ -1,14 +1,27 @@
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.local.LocalAccountsDataProvider
import com.example.fruitandvegetableguide.data.local.LocalGuidesDataProvider
import com.example.fruitandvegetableguide.data.local.LocalPostsDataProvider
// TODO 这里放页面逻辑相关代码,要自己写,通过调用数据库工具方法实现
// 输入参数和返回参数不要改
// TODO 这里放页面逻辑相关代码
fun loginVerification(username: String, password: String): Int {
// TODO 1.登录验证
// 输入username:用户名, password:密码
// 逻辑查询用户表用户名和密码正确返回id;用户名和密码不匹配返回-1;用户表中没有该用户名返回-1
// 输出id或-1
// 将下面的测试代码清空完成需要的代码之后的TODO同此
return 1
// 将下面的测试代码删除完成需要的代码之后的TODO同此
if (password == "123456") {
if (username == "aaa") return 1
if (username == "bbb") return 2
if (username == "ccc") return 3
}
return -1
}
fun register(username: String, password: String): Boolean {
@ -16,6 +29,168 @@ fun register(username: String, password: String): Boolean {
// 输入username:用户名, password:密码
// 逻辑:插入用户表;插入成功返回true;插入失败返回false
// 输出true或false
// 将下面的测试代码清空完成需要的代码之后的TODO同此
// 将下面的测试代码删除,完成需要的代码
return true
}
fun postListInit(): List<Post> {
// TODO 3.帖子列表初始化
// 输入:无
// 逻辑查询post表获取前100个帖子信息用于最初显示。
// 查询的每一行帖子数据封装成Post数据类实例即每一行数据信息都可以用来声明一个Post数据类对象然后将这些对象封装成一个列表使用mutableListOf
// 输出:列表
// 将下面的测试代码删除,完成需要的代码
return LocalPostsDataProvider.allPosts
}
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]
}
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
}
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
}
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]
}
fun guideListInit(): List<Guide> {
// TODO 8.指南列表初始化
// 输入:无
// 逻辑查询guide表获取前100个指南信息用于最初显示。
// 查询的每一行指南数据封装成Guide数据类实例即每一行数据信息都可以用来声明一个Guide数据类对象然后将这些对象封装成一个列表使用mutableListOf
// 输出:列表
// 将下面的测试代码删除,完成需要的代码
return LocalGuidesDataProvider.allGuides
}
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
}
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
}
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
}
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
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="img_files"
path="." />
<cache-path
name="cache"
path="." />
</paths>
Loading…
Cancel
Save