diff --git a/app/src/androidTest/java/com/gyf/csams/TestPreview.kt b/app/src/androidTest/java/com/gyf/csams/TestPreview.kt
new file mode 100644
index 0000000..aa63651
--- /dev/null
+++ b/app/src/androidTest/java/com/gyf/csams/TestPreview.kt
@@ -0,0 +1,192 @@
+package com.gyf.csams
+
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.rotate
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.drawscope.rotate
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+
+@Composable
+fun MyBox(modifier: Modifier, canvasSize:Dp, canvasD:Float, content: @Composable BoxScope.() -> Unit){
+ Box(modifier = modifier,contentAlignment = Alignment.Center){
+ Canvas(modifier = Modifier.size(canvasSize)) {
+ rotate(canvasD){
+ drawRect(color = Color.Cyan)
+ }
+ }
+ content()
+ }
+}
+
+@Composable
+fun BoxSetSize(degrees:Float=0F, content: @Composable BoxScope.() -> Unit){
+ MyBox(modifier = Modifier
+ .height(300.dp)
+ .fillMaxWidth()
+ .background(Color.Gray)
+ .rotate(degrees = degrees), canvasSize = 200.dp, canvasD = 45F,content = content
+ )
+}
+
+@Composable
+fun BoxFillSize(degrees:Float=0F, content: @Composable BoxScope.() -> Unit){
+ MyBox(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(Color.LightGray), canvasSize = 100.dp, canvasD = 80F,content = content
+ )
+}
+
+//@Preview
+@Composable
+fun TestPreview(){
+ Column(modifier = Modifier.fillMaxSize()){
+ BoxSetSize {
+ Text(buildAnnotatedString {
+ withStyle(style = SpanStyle(fontSize = 30.sp)){
+ append("前置子布局固定尺寸")
+ }
+ })
+ }
+
+ BoxFillSize {
+ Text(buildAnnotatedString {
+ withStyle(style = SpanStyle(fontSize = 30.sp)){
+ append("后置子布局使用")
+ }
+ withStyle(style = SpanStyle(color= Color.Red,fontSize = 30.sp)){
+ append("fillMaxSize修饰符")
+ }
+ withStyle(style = SpanStyle(fontSize = 30.sp)){
+ append("填充父项允许的所有可用空间")
+ }
+ })
+ }
+ }
+}
+
+//@Preview
+@Composable
+fun TestPreview2(){
+ Column(modifier = Modifier.fillMaxSize()){
+ Text(buildAnnotatedString {
+ withStyle(style = SpanStyle(fontSize = 30.sp)){
+ append("如果使用示例1方法是")
+ }
+ withStyle(style = SpanStyle(color= Color.Red,fontSize = 30.sp)){
+ append("无法实现前置子布局填充父项所有可用空间,后置子布局固定尺寸")
+ }
+ withStyle(style = SpanStyle(fontSize = 30.sp)){
+ append("因为前置布局已经填充父项允许的所有可用空间,后置子布局没有剩余空间可用")
+ }
+
+ })
+
+ BoxFillSize {
+ Text(buildAnnotatedString {
+ withStyle(style = SpanStyle(fontSize = 30.sp)){
+ append("前置子布局填充父项允许的所有可用空间")
+ }
+ })
+ }
+
+ BoxSetSize {
+ Text(buildAnnotatedString {
+ withStyle(style = SpanStyle(fontSize = 30.sp)){
+ append("后置子布局固定尺寸")
+ }
+ })
+ }
+ }
+}
+
+//@Preview
+@Composable
+fun TestPreview3(){
+ Column(modifier = Modifier
+ .fillMaxSize()) {
+ Text(buildAnnotatedString {
+ withStyle(style = SpanStyle(fontSize = 30.sp)){
+ append("折衷方案是通过父项旋转180°实现,子项分别旋转180°复位可")
+ }
+ withStyle(style = SpanStyle(color= Color.Red,fontSize = 30.sp)){
+ append("实现前置子布局填充父项所有可用空间,后置子布局固定尺寸")
+ }
+ withStyle(style = SpanStyle(fontSize = 30.sp)){
+ append(",除了旋转,应该有更好的实现方式?")
+ }
+ })
+ Column(modifier = Modifier.rotate(180F)){
+ BoxSetSize(degrees = 180F) {
+ Text(buildAnnotatedString {
+ withStyle(style = SpanStyle(fontSize = 30.sp)){
+ append("前置子布局固定尺寸,通过旋转和后置子布局对调位置")
+ }
+ })
+ }
+ BoxFillSize(degrees = 180F) {
+ Text(buildAnnotatedString {
+
+ withStyle(style = SpanStyle(fontSize = 30.sp)){
+ append("后置子布局使用")
+ }
+ withStyle(style = SpanStyle(color= Color.Red,fontSize = 30.sp)){
+ append("fillMaxSize修饰符")
+ }
+ withStyle(style = SpanStyle(fontSize = 30.sp)){
+ append("填充父项允许的所有可用空间,通过旋转和前置子布局对调位置")
+ }
+ })
+ }
+ }
+ }
+
+}
+
+//@Preview
+@Composable
+fun TestPreview4(){
+ Column(modifier = Modifier
+ .fillMaxSize()) {
+
+
+ MyBox(modifier = Modifier
+ .fillMaxWidth()
+ .fillMaxHeight()
+ .background(Color.Gray), canvasSize = 100.dp, canvasD = 80F){
+ Text(text = "Box1")
+ }
+
+ MyBox(modifier = Modifier
+ .height(300.dp)
+ .fillMaxWidth()
+ .background(Color.Gray), canvasSize = 200.dp, canvasD = 45F){
+ Text(text = "Box2")
+ }
+ }
+}
+
+@Preview
+@Composable
+fun TestPreview5(){
+
+ Column {
+ Row {
+
+ }
+
+ }
+}
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 34d063a..3af8a99 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,10 @@
package="com.gyf.csams">
+
+
+
+
+
+
diff --git a/app/src/main/java/com/gyf/csams/APP.kt b/app/src/main/java/com/gyf/csams/APP.kt
index 5c41d89..8f1b314 100644
--- a/app/src/main/java/com/gyf/csams/APP.kt
+++ b/app/src/main/java/com/gyf/csams/APP.kt
@@ -1,19 +1,100 @@
package com.gyf.csams
import android.app.Application
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.util.LruCache
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.asImageBitmap
+import com.gyf.csams.uikit.BackgroundImage
import com.orhanobut.logger.AndroidLogAdapter
import com.orhanobut.logger.DiskLogAdapter
import com.orhanobut.logger.Logger
class APP : Application() {
+ private lateinit var memoryCache: LruCache
+ // Get max available VM memory, exceeding this amount will throw an
+ // OutOfMemory exception. Stored in kilobytes as LruCache takes an
+ // int in its constructor.
+ private val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
+ // Use 1/8th of the available memory for this memory cache.
+ val cacheSize = maxMemory / 8
+
+ fun getImage(image: BackgroundImage, reqWidth: Int, reqHeight: Int): ImageBitmap {
+ val bitmap=memoryCache.get(image)
+ return if(bitmap==null){
+ Logger.i("reqWidth=$reqWidth,reqHeight=$reqHeight")
+ val cacheValue= decodeSampledBitmapFromResource(res = resources,image.id,reqWidth=reqWidth,reqHeight=reqHeight)
+ memoryCache.put(image, cacheValue)
+ Logger.i("添加缓存:${image}")
+ cacheValue.asImageBitmap()
+ }else{
+ Logger.i("从缓存读取:${image}")
+ bitmap.asImageBitmap()
+ }
+ }
+
+ private fun decodeSampledBitmapFromResource(
+ res: Resources,
+ resId: Int,
+ reqWidth: Int,
+ reqHeight: Int
+ ): Bitmap {
+ // First decode with inJustDecodeBounds=true to check dimensions
+ return BitmapFactory.Options().run {
+ inJustDecodeBounds = true
+ BitmapFactory.decodeResource(res, resId, this)
+
+ // Calculate inSampleSize
+ inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight)
+
+ // Decode bitmap with inSampleSize set
+ inJustDecodeBounds = false
+
+ BitmapFactory.decodeResource(res, resId, this)
+ }
+ }
+
+ private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
+ // Raw height and width of image
+ val (height: Int, width: Int) = options.run { outHeight to outWidth }
+ var inSampleSize = 1
+
+ if (height > reqHeight || width > reqWidth) {
+
+ val halfHeight: Int = height / 2
+ val halfWidth: Int = width / 2
+
+ // Calculate the largest inSampleSize value that is a power of 2 and keeps both
+ // height and width larger than the requested height and width.
+ while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
+ inSampleSize *= 2
+ }
+ }
+
+ return inSampleSize
+ }
override fun onCreate() {
super.onCreate()
+
+ memoryCache = object : LruCache(cacheSize) {
+
+ override fun sizeOf(key: BackgroundImage, bitmap: Bitmap): Int {
+ // The cache size will be measured in kilobytes rather than
+ // number of items.
+ return bitmap.byteCount / 1024
+ }
+ }
+
//初始化日志
Logger.addLogAdapter(AndroidLogAdapter())
Logger.addLogAdapter(DiskLogAdapter())
Logger.i("${BuildConfig.APP_NAME}启动")
+
+
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/gyf/csams/InitActivity.kt b/app/src/main/java/com/gyf/csams/InitActivity.kt
index e1899a5..11a85bb 100644
--- a/app/src/main/java/com/gyf/csams/InitActivity.kt
+++ b/app/src/main/java/com/gyf/csams/InitActivity.kt
@@ -10,8 +10,8 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.account.ui.AccountActivity
-import com.gyf.csams.ui.AnimationText
-import com.gyf.csams.ui.MainActivity
+import com.gyf.csams.main.ui.MainActivity
+import com.gyf.csams.uikit.AnimationText
import com.orhanobut.logger.Logger
class InitActivity : ComponentActivity() {
@@ -50,7 +50,7 @@ private fun Init(initViewModel:InitViewModel= viewModel()){
when (isValid) {
false -> context.startActivity(Intent(context, AccountActivity::class.java))
- true -> context.startActivity(Intent(context,MainActivity::class.java))
+ true -> context.startActivity(Intent(context, MainActivity::class.java))
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/gyf/csams/account/ui/AccountActivity.kt b/app/src/main/java/com/gyf/csams/account/ui/AccountActivity.kt
index 9804cd0..2775a8f 100644
--- a/app/src/main/java/com/gyf/csams/account/ui/AccountActivity.kt
+++ b/app/src/main/java/com/gyf/csams/account/ui/AccountActivity.kt
@@ -31,8 +31,8 @@ import androidx.navigation.compose.rememberNavController
import com.gyf.csams.BuildConfig
import com.gyf.csams.account.model.AccountViewModel
import com.gyf.csams.account.model.DialogMessage
-import com.gyf.csams.ui.AnimationText
-import com.gyf.csams.ui.theme.CSAMSTheme
+import com.gyf.csams.uikit.AnimationText
+import com.gyf.csams.uikit.theme.CSAMSTheme
enum class AccountRoute{
@@ -178,7 +178,7 @@ private fun Account(accountViewModel: AccountViewModel = viewModel(),
/**
* 学号
- *
+ *TODO 需要把逻辑封装到[com.gyf.csams.uikit.BaseTextField]
* @param accountViewModel
*/
@Composable
@@ -297,9 +297,10 @@ private fun PasswordDialog(accountViewModel: AccountViewModel = viewModel(), mes
}
+
/**
* 姓名文本框
- *
+ * TODO 需要把逻辑封装到[com.gyf.csams.uikit.BaseTextField]
* @param name 姓名
* @param accountViewModel
*/
@@ -328,7 +329,7 @@ private fun Name(name:String, accountViewModel: AccountViewModel = viewModel()){
/**
* 密码框
- *
+ *TODO 需要把逻辑封装到[com.gyf.csams.uikit.BaseTextField]
* @param accountViewModel
*/
@Composable
diff --git a/app/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt b/app/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt
new file mode 100644
index 0000000..87dd954
--- /dev/null
+++ b/app/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt
@@ -0,0 +1,45 @@
+package com.gyf.csams.association.model
+
+import android.net.Uri
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.gyf.csams.uikit.StringForm
+
+
+data class Image(val uri:Uri,val createTime:Long,val size:Long)
+
+class RegAssociationViewModel : ViewModel() {
+
+ val frameDesc="社团注册资料"
+
+ val name= StringForm(formDesc = "社团名称",textLength = 5)
+ val desc = StringForm(formDesc = "社团简介",textLength = 30)
+
+
+ val _picture=MutableLiveData()
+ val picture:LiveData =_picture
+
+ val piciurePlaceHolder="请上传图片"
+
+ val errorPicture="图片加载失败,请联系管理员"
+
+ val deninedPermission="拒绝授权"
+
+ val register="注册"
+ val back="返回"
+
+ fun setPicture(uri: Uri){
+ _picture.value=uri
+ }
+
+ /**
+ * TODO 注册社团
+ *
+ * @param callback
+ */
+ fun register(callback: (value: String) -> Unit){
+ callback("功能尚未实现,敬请期待")
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt b/app/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt
new file mode 100644
index 0000000..87bbccb
--- /dev/null
+++ b/app/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt
@@ -0,0 +1,258 @@
+package com.gyf.csams.association.ui
+
+import android.Manifest
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.graphics.BitmapFactory
+import android.net.Uri
+import android.os.Bundle
+import android.provider.MediaStore
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.compose.setContent
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.gyf.csams.R
+import com.gyf.csams.association.model.RegAssociationViewModel
+import com.gyf.csams.uikit.*
+import com.gyf.csams.uikit.theme.CSAMSTheme
+import com.orhanobut.logger.Logger
+
+
+/**
+ * 注册社团
+ *
+ */
+class RegAssociationActivity: ComponentActivity(){
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ CSAMSTheme {
+ Body()
+ }
+ }
+ }
+
+}
+
+@Composable
+fun Body(model:RegAssociationViewModel= viewModel()){
+ val scaffoldState = rememberScaffoldState()
+ Scaffold(scaffoldState = scaffoldState) {
+ Surface(color = MaterialTheme.colors.background) {
+ MainFrame(background = { Background(BackgroundImage.reg_association,alpha = 0.7F) }) {
+ Spacer(
+ modifier = Modifier
+ .weight(0.1F)
+ )
+ Title(model = model)
+ Name(model = model)
+ Desc(
+ model = model, modifier = Modifier
+ .weight(0.1F)
+ .fillMaxWidth()
+ )
+ Spacer(modifier = Modifier.weight(0.05F))
+ Logo(
+ model = model, modifier = Modifier
+ .weight(0.2F)
+ .fillMaxWidth()
+ )
+ Spacer(modifier = Modifier.weight(0.05F))
+ BottomButton(modifier = Modifier.fillMaxWidth())
+ Spacer(modifier = Modifier.weight(0.05F))
+
+ ShowSnackbar(scaffoldState = scaffoldState)
+ }
+ }
+ }
+}
+
+
+//@Composable
+fun PermissionHandle(context: Context, launcher: ActivityResultLauncher,onGranted:()->Unit){
+ // Check permission
+ when (PackageManager.PERMISSION_GRANTED) {
+ ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ ) -> {
+ // Some works that require permission
+ onGranted()
+ }
+ else -> {
+ // Asking for permission
+ launcher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
+ }
+ }
+}
+
+/**
+ * 社团Logo
+ *
+ * @param modifier
+ */
+@Composable
+fun Logo(model:RegAssociationViewModel= viewModel(),modifier: Modifier) {
+ val photoIntent=Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
+ photoIntent.type = "image/*"
+ val uri:Uri? by model.picture.observeAsState()
+
+ val resultLauncher=rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
+ when(it.resultCode){
+ Activity.RESULT_OK->{
+ Logger.i("uri=${it.data?.data}")
+ it.data?.data?.let { it1 -> model.setPicture(it1) }
+ }
+ }
+ }
+
+ val loadPicture={
+ //model.loadPicture(context)
+ resultLauncher.launch(photoIntent)
+ }
+
+ val launcher = rememberLauncherForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted: Boolean ->
+ if (isGranted) {
+ // Permission Accepted: Do something
+ loadPicture()
+ } else {
+ // Permission Denied: Do something
+ Logger.w(model.deninedPermission)
+ }
+ }
+
+
+
+ val context= LocalContext.current
+
+ Box(contentAlignment = Alignment.Center,modifier = modifier) {
+ Row(verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center,modifier = Modifier
+ .fillMaxSize()
+ .border(width = 1.dp, color = Color.Black)) {
+
+ if (uri == null) {
+ OutlinedButton(onClick = {
+ when (PackageManager.PERMISSION_GRANTED) {
+ ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ ) -> {
+ // Some works that require permission
+ loadPicture()
+ }
+ else -> {
+ // Asking for permission
+ launcher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
+ }
+ }
+ }) {
+ Text(text = model.piciurePlaceHolder)
+ }
+ } else {
+ uri.let {
+ if(it!=null){
+ Row {
+ Image(bitmap = BitmapFactory.decodeStream(context.contentResolver.openInputStream(it))
+ .asImageBitmap(), contentDescription = null)
+ IconButton(onClick = {
+ loadPicture()
+ }) {
+ Image(painter = painterResource(id = R.drawable.ic_exchange_rate), contentDescription = null)
+ }
+ }
+
+
+ }else{
+ Text(text = model.errorPicture)
+ }
+ }
+ }
+
+
+ }
+
+ }
+
+
+}
+
+@Composable
+fun BottomButton(modifier: Modifier=Modifier,scaffoldModel: ScaffoldModel= viewModel(),model:RegAssociationViewModel= viewModel()){
+ val context= LocalContext.current as RegAssociationActivity
+ Row(modifier = modifier,horizontalArrangement = Arrangement.Center) {
+ OutlinedButton(onClick = {
+ model.register { scaffoldModel.update(it) }
+ },modifier = Modifier.background(color = MaterialTheme.colors.primary)) {
+ Text(text = model.register)
+ }
+ Spacer(modifier = Modifier.width(10.dp))
+ OutlinedButton(onClick = {
+ context.onBackPressed()
+ },modifier = Modifier.background(color = MaterialTheme.colors.secondary)) {
+ Text(text = model.back)
+ }
+ }
+
+}
+
+/**
+ * 菜单标题
+ *
+ */
+@Composable
+fun Title(model:RegAssociationViewModel= viewModel()){
+ Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.Center) {
+ Text(text = model.frameDesc,style = MaterialTheme.typography.h4)
+ }
+}
+
+/**
+ * 社团名称
+ * @param model
+ */
+@Composable
+fun Name(model:RegAssociationViewModel= viewModel()){
+ BaseTextField(form = model.name,singeLine = true,modifier = Modifier.fillMaxWidth())
+}
+
+/**
+ * 社团简介
+ * @param model
+ */
+@Composable
+fun Desc(model:RegAssociationViewModel= viewModel(),modifier:Modifier){
+ BaseTextField(form = model.desc,modifier = modifier)
+}
+
+
+@Preview
+@Composable
+fun NamePreview(){
+ val model=RegAssociationViewModel()
+ Body(model=model)
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gyf/csams/ui/model/ViewModel.kt b/app/src/main/java/com/gyf/csams/main/model/MainViewModel.kt
similarity index 65%
rename from app/src/main/java/com/gyf/csams/ui/model/ViewModel.kt
rename to app/src/main/java/com/gyf/csams/main/model/MainViewModel.kt
index d8314e9..8174c8f 100644
--- a/app/src/main/java/com/gyf/csams/ui/model/ViewModel.kt
+++ b/app/src/main/java/com/gyf/csams/main/model/MainViewModel.kt
@@ -1,10 +1,11 @@
-package com.gyf.csams.ui.model
+package com.gyf.csams.main.model
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.gyf.csams.R
+import com.gyf.csams.uikit.StringForm
import com.orhanobut.logger.Logger
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
@@ -20,7 +21,7 @@ class MarqueeViewModel:ViewModel() {
private val _marqueeIndex=MutableLiveData(0)
var marqueeIndex:LiveData = _marqueeIndex
- var marqueeJob:Job? = null
+ private var marqueeJob:Job? = null
@@ -38,6 +39,10 @@ class MarqueeViewModel:ViewModel() {
}
}
+/**
+ * 海报轮播
+ *
+ */
class CarouselViewModel:ViewModel(){
val imageList= listOf(R.drawable.ic_launcher_foreground,R.drawable.ic_account_fill,R.drawable.ic_all_fill,R.drawable.ic_home_fill)
@@ -45,13 +50,13 @@ class CarouselViewModel:ViewModel(){
val index:LiveData = _index
- var job:Job? = null
+ private var job:Job? = null
init {
start()
}
- fun start(){
+ private fun start(){
job = viewModelScope.launch {
do{
_index.postValue(if (_index.value==imageList.size-1) 0 else _index.value?.plus(1))
@@ -60,50 +65,68 @@ class CarouselViewModel:ViewModel(){
}
}
- fun stop(){
- println("停止更新")
- job?.cancel()
- }
-
}
-
+/**
+ * 社团
+ *
+ * @property name 社团名称
+ */
data class AssociationDto(val name:String)
+/**
+ * 主页
+ *
+ */
+class MainViewModel:ViewModel(){
+ /**
+ * TODO 发送留言
+ *
+ */
+ fun sendMessage(callback: (value: String) -> Unit){
+ callback("功能尚未实现,敬请期待")
+ }
+
+ /**
+ * TODO 打开通知
+ *
+ * @param callback
+ */
+ fun openNotification(callback: (value: String) -> Unit){
+ callback("功能尚未实现,敬请期待")
+ }
+}
+
+/**
+ * 社团列表
+ *
+ */
class ListViewModel:ViewModel(){
- private val _name=MutableLiveData("")
- val name:LiveData = _name
- val nameDesc="社团名称"
- val namePlaceholder="请输入$nameDesc"
+ val name = StringForm(formDesc = "社团名称",textLength = 5)
- private val _desc=MutableLiveData("")
- val desc:LiveData = _desc
- val descDesc="社团简介"
- val descPlaceholder="请输入$descDesc"
+ val desc = StringForm(formDesc = "社团简介",textLength = 10)
- //注册请求响应信息
- private val _snackBarMsg=MutableLiveData()
- val snackBarMsg:LiveData = _snackBarMsg
+ //社团列表加载数量
+ val associationListSize=10
//社团列表
private val _associationList=MutableLiveData>(mutableListOf())
val associationDto:LiveData> = _associationList
+ val searchDesc="搜索"
+
init {
loadAssociation()
}
- fun onChangeName(name:String){
- _name.value=name
- }
-
- fun onChangeDesc(desc:String){
- _desc.value=desc
- }
-
- fun search(){
- Logger.i("使用社团名称:${_name.value},社团简介:${_desc.value} 搜索社团")
- _snackBarMsg.value="搜索失败,请联系管理员"
+ /**
+ * TODO 社团检索
+ *
+ * @param callback
+ */
+ fun search(callback: (value: String) -> Unit){
+ Logger.i("搜索条件[社团名称:${name.formValue.value},社团简介:${desc.formValue.value}]")
+ callback("功能尚未实现,敬请期待")
}
/**
@@ -131,7 +154,7 @@ class ListViewModel:ViewModel(){
* 加载更多社团列表
*
*/
- fun addMore(){
+ fun addMore(callback:(message:String) -> Unit){
viewModelScope.launch {
val c = _associationList.value
@@ -147,15 +170,26 @@ class ListViewModel:ViewModel(){
Logger.i("t.size=${t.size}")
_associationList.postValue(t)
Logger.i("加载更多社团size=${_associationList.value?.size}")
- _snackBarMsg.value="成功加载更多社团"
+ callback("成功加载更多社团")
}
-
-
}
}
+}
- fun reset(){
- _snackBarMsg.value=""
+/**
+ * 个人中心
+ *
+ */
+class CenterViewModel:ViewModel(){
+ val myAssociationDesc="我的社团"
+
+ /**
+ * TODO 打开我的社团
+ *
+ * @param callback
+ */
+ fun openMyAssociation(callback: (value: String) -> Unit){
+ callback("功能尚未实现,敬请期待")
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/gyf/csams/ui/MainActivity.kt b/app/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
similarity index 64%
rename from app/src/main/java/com/gyf/csams/ui/MainActivity.kt
rename to app/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
index 34b23e6..0d6127d 100644
--- a/app/src/main/java/com/gyf/csams/ui/MainActivity.kt
+++ b/app/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
@@ -1,28 +1,25 @@
-package com.gyf.csams.ui
+package com.gyf.csams.main.ui
+import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.text.KeyboardActions
-import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -32,13 +29,10 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.gyf.csams.R
-import com.gyf.csams.ui.model.AssociationDto
-import com.gyf.csams.ui.model.CarouselViewModel
-import com.gyf.csams.ui.model.ListViewModel
-import com.gyf.csams.ui.model.MarqueeViewModel
-import com.gyf.csams.ui.theme.CSAMSTheme
-import com.orhanobut.logger.Logger
-import kotlinx.coroutines.launch
+import com.gyf.csams.association.ui.RegAssociationActivity
+import com.gyf.csams.main.model.*
+import com.gyf.csams.uikit.*
+import com.gyf.csams.uikit.theme.CSAMSTheme
/**
@@ -48,11 +42,14 @@ import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+
setContent {
CSAMSTheme {
Body()
}
}
+
+
}
}
@@ -67,25 +64,15 @@ fun Body() {
NavHost(navController, startDestination = MainMenu.Main.name) {
composable(MainMenu.Main.name) {
Main(navController = navController)
+ ShowSnackbar(scaffoldState = scaffoldState)
}
composable(MainMenu.List.name) {
AssociationList(navController = navController)
-
- val model:ListViewModel= viewModel()
- val snackBarMsg:String by model.snackBarMsg.observeAsState("")
-
- if(snackBarMsg!=""){
- val scope= rememberCoroutineScope()
- scope.launch {
- scaffoldState.snackbarHostState.showSnackbar(
- message = snackBarMsg
- )
- model.reset()
- }
- }
+ ShowSnackbar(scaffoldState = scaffoldState)
}
composable(MainMenu.Center.name) {
Center(navController = navController)
+ ShowSnackbar(scaffoldState = scaffoldState)
}
}
}
@@ -98,16 +85,20 @@ fun Body() {
*
*/
@Composable
-fun Center(navController: NavController){
- MainFrame(background = { CenterBackground() }, mainMenu = MainMenu.Center, nav = navController) {
+fun Center(model:CenterViewModel= viewModel(), scaffoldModel: ScaffoldModel= viewModel(), navController: NavController){
+ MainFrame(background = { Background(image = BackgroundImage.center,alpha = 0.5F) }, mainMenu = MainMenu.Center, nav = navController) {
Column(modifier = Modifier
.weight(0.33F)
.fillMaxWidth(),verticalArrangement = Arrangement.Bottom) {
- Card(backgroundColor = Color.White) {
- Row(modifier = Modifier.fillMaxWidth(),verticalAlignment = Alignment.CenterVertically) {
+ Card(backgroundColor = MaterialTheme.colors.background) {
+ Row(modifier = Modifier
+ .fillMaxWidth()
+ .clickable(onClick = {
+ model.openMyAssociation { scaffoldModel.update(it) }
+ }),verticalAlignment = Alignment.CenterVertically) {
Spacer(modifier = Modifier.weight(0.33F))
Row(modifier = Modifier.weight(0.33F),horizontalArrangement = Arrangement.Center) {
- Text(text = "我的社团")
+ Text(text = model.myAssociationDesc)
}
Row(modifier = Modifier.weight(0.33F),horizontalArrangement = Arrangement.End) {
Icon(
@@ -126,27 +117,12 @@ fun Center(navController: NavController){
}
}
-/**
- * 个人中心背景
- *
- */
-@Composable
-fun CenterBackground(){
- Image(
- painter = painterResource(id = R.drawable.mb_bg_fb_28),
- contentDescription = null,
- contentScale = ContentScale.FillHeight,
- alpha = 0.5F,
- modifier = Modifier.fillMaxSize()
- )
-}
-
/**
* 主界面
*/
@Composable
fun Main(navController: NavController) {
- MainFrame(background = { MainBackground() }, mainMenu = MainMenu.Main, nav = navController) {
+ MainFrame(background = { Background(image = BackgroundImage.main) }, mainMenu = MainMenu.Main, nav = navController) {
Column(modifier = Modifier.weight(0.33F)) {
Notification()
MessageBoard()
@@ -160,19 +136,6 @@ fun Main(navController: NavController) {
}
}
-/**
- * 主界面背景
- *
- */
-@Composable
-fun MainBackground() {
- Image(
- painter = painterResource(id = R.drawable.mb_bg_fb_08),
- contentDescription = null,
- contentScale = ContentScale.FillHeight,
- modifier = Modifier.fillMaxSize()
- )
-}
/**
* 社团列表
@@ -182,9 +145,7 @@ fun MainBackground() {
@Composable
fun AssociationList(navController: NavController) {
MainFrame(
- background = {
- AssociationListBackground()
- },
+ background = { Background(image = BackgroundImage.list) },
mainMenu = MainMenu.List,
nav = navController
) {
@@ -195,45 +156,36 @@ fun AssociationList(navController: NavController) {
}
/**
- * 添加社团按钮
+ * 注册社团按钮
*
*/
@Composable
fun RegisterAssociation() {
+ val context= LocalContext.current
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
) {
- Icon(
- painter = painterResource(id = R.drawable.ic_add_fill),
- contentDescription = null,
- modifier = Modifier.size(50.dp)
- )
+ IconButton(onClick = {
+ context.startActivity(Intent(context, RegAssociationActivity::class.java))
+ }) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_add_fill),
+ contentDescription = null,
+ modifier = Modifier.size(50.dp),
+ )
+ }
}
}
-/**
- * 社团列表背景
- *
- */
-@Composable
-fun AssociationListBackground() {
- Image(
- painter = painterResource(id = R.drawable.mb_bg_fb_07),
- contentDescription = null,
- contentScale = ContentScale.FillHeight,
- modifier = Modifier.fillMaxSize()
- )
-}
-
/**
* 社团列表
*
*/
@Composable
-fun AssociationListBody(model: ListViewModel = viewModel()) {
+fun AssociationListBody(model: ListViewModel = viewModel(),scaffoldModel: ScaffoldModel= viewModel()) {
val associationList: MutableList? by model.associationDto.observeAsState()
val listState = rememberLazyListState()
@@ -253,7 +205,7 @@ fun AssociationListBody(model: ListViewModel = viewModel()) {
}else{
Box(modifier = Modifier
.weight(0.35F)
- .border(width = 1.dp, color = Color.Black))
+ .border(width = 1.dp, color = MaterialTheme.colors.onBackground))
}
Spacer(modifier = Modifier.weight(0.1F))
}
@@ -263,15 +215,9 @@ fun AssociationListBody(model: ListViewModel = viewModel()) {
}
}
}
- Logger.i("totalItemsCount=${listState.layoutInfo.totalItemsCount}" +
- ",firstVisibleItemIndex=${listState.firstVisibleItemIndex}," +
- "firstVisibleItemScrollOffset=${listState.firstVisibleItemScrollOffset}," +
- "viewportStartOffset=${listState.layoutInfo.viewportStartOffset}" +
- "viewportEndOffset=${listState.layoutInfo.viewportEndOffset}")
-
- if(listState.layoutInfo.totalItemsCount-listState.firstVisibleItemIndex==4){
- model.addMore()
+ if(listState.layoutInfo.totalItemsCount-listState.firstVisibleItemIndex==model.associationListSize/2-1){
+ model.addMore { scaffoldModel.update(it) }
}
}
@@ -296,48 +242,23 @@ fun Association(associationDto: AssociationDto) {
*
*/
@Composable
-fun AssociationSearch(model: ListViewModel = viewModel()) {
- val name: String by model.name.observeAsState("")
+fun AssociationSearch(model: ListViewModel = viewModel(),scaffoldModel: ScaffoldModel= viewModel()) {
+
Card(modifier = Modifier.padding(horizontal = 50.dp, vertical = 10.dp)) {
Column {
- Row {
- val focusManager = LocalFocusManager.current
- Spacer(modifier = Modifier.weight(0.05F))
- OutlinedTextField(
- modifier = Modifier.weight(0.4F), value = name,
- onValueChange = { model.onChangeName(it) },
- singleLine = true,
- label = { Text(text = model.nameDesc) },
- placeholder = { Text(text = model.namePlaceholder) },
- keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
- keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done)
- )
- Spacer(modifier = Modifier.weight(0.1F))
- OutlinedTextField(
- modifier = Modifier.weight(0.4F), value = name,
- onValueChange = { model.onChangeDesc(it) },
- singleLine = true,
- label = { Text(text = model.descDesc) },
- placeholder = { Text(text = model.descPlaceholder) },
- keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
- keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done)
- )
- Spacer(modifier = Modifier.weight(0.05F))
- }
+
+ BaseTextField(form = model.name,singeLine = true,modifier = Modifier.padding(horizontal = 10.dp))
+ BaseTextField(form = model.desc,singeLine = true,modifier = Modifier.padding(horizontal = 10.dp))
Spacer(
- modifier = Modifier
- .fillMaxWidth()
- .height(10.dp)
+ modifier = Modifier.height(10.dp)
)
Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth()) {
- OutlinedButton(onClick = { model.search() }, modifier = Modifier.width(100.dp)) {
- Text(text = "搜索")
+ OutlinedButton(onClick = { model.search { scaffoldModel.update(it) } }, modifier = Modifier.width(100.dp)) {
+ Text(text = model.searchDesc)
}
}
Spacer(
- modifier = Modifier
- .fillMaxWidth()
- .height(10.dp)
+ modifier = Modifier.height(10.dp)
)
}
@@ -350,18 +271,22 @@ fun AssociationSearch(model: ListViewModel = viewModel()) {
*
*/
@Composable
-fun Notification() {
+fun Notification(mainViewModel: MainViewModel= viewModel(),scaffoldModel: ScaffoldModel= viewModel()) {
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
) {
- Icon(
- painter = painterResource(id = R.drawable.ic_notification),
- contentDescription = null,
- modifier = Modifier.size(50.dp)
- )
+ IconButton(onClick = {
+ mainViewModel.openNotification { scaffoldModel.update(it) }
+ }) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_notification),
+ contentDescription = null
+ )
+ }
+
}
}
@@ -376,7 +301,7 @@ fun MyBorder(content: @Composable BoxScope.() -> Unit) {
modifier = Modifier
.border(
width = 1.dp,
- color = Color.Black,
+ color = MaterialTheme.colors.onBackground,
shape = RoundedCornerShape(size = 20.dp)
),
) {
@@ -390,18 +315,19 @@ fun MyBorder(content: @Composable BoxScope.() -> Unit) {
*
*/
@Composable
-fun MessageBoard(model: MarqueeViewModel = viewModel()) {
+fun MessageBoard(model: MarqueeViewModel = viewModel(),mainViewModel:MainViewModel= viewModel(),scaffoldModel: ScaffoldModel= viewModel()) {
MyBorder {
Row(
verticalAlignment = Alignment.CenterVertically
) {
- Icon(
- painter = painterResource(id = R.drawable.ic_comments),
- contentDescription = null,
- modifier = Modifier
- .size(50.dp)
- )
+
+ IconButton(onClick = { mainViewModel.sendMessage { scaffoldModel.update(it) } }) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_comments),
+ contentDescription = null,
+ )
+ }
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier
diff --git a/app/src/main/java/com/gyf/csams/ui/Base.kt b/app/src/main/java/com/gyf/csams/uikit/BaseView.kt
similarity index 63%
rename from app/src/main/java/com/gyf/csams/ui/Base.kt
rename to app/src/main/java/com/gyf/csams/uikit/BaseView.kt
index 4d662f5..68a6d4f 100644
--- a/app/src/main/java/com/gyf/csams/ui/Base.kt
+++ b/app/src/main/java/com/gyf/csams/uikit/BaseView.kt
@@ -1,4 +1,4 @@
-package com.gyf.csams.ui
+package com.gyf.csams.uikit
import androidx.annotation.DrawableRes
import androidx.compose.animation.Crossfade
@@ -6,14 +6,21 @@ import androidx.compose.animation.animateColor
import androidx.compose.animation.core.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.DefaultAlpha
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -21,10 +28,13 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.navigation.compose.navigate
import androidx.navigation.compose.rememberNavController
+import com.gyf.csams.APP
import com.gyf.csams.R
-import com.gyf.csams.ui.model.CarouselViewModel
-import com.gyf.csams.ui.model.MarqueeViewModel
-import com.gyf.csams.ui.theme.CSAMSTheme
+import com.gyf.csams.main.model.CarouselViewModel
+import com.gyf.csams.main.model.MarqueeViewModel
+import com.gyf.csams.uikit.theme.CSAMSTheme
+import com.orhanobut.logger.Logger
+import kotlinx.coroutines.launch
/**
* 淡入淡出并且颜色变化文本
@@ -35,8 +45,8 @@ import com.gyf.csams.ui.theme.CSAMSTheme
fun AnimationText(text:String){
val infiniteTransition = rememberInfiniteTransition()
val color by infiniteTransition.animateColor(
- initialValue = Color.Red,
- targetValue = Color.Green,
+ initialValue = MaterialTheme.colors.primary,
+ targetValue = MaterialTheme.colors.onPrimary,
animationSpec = infiniteRepeatable(
animation = tween(1000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse
@@ -94,7 +104,7 @@ fun MenuIconButton(_menu: MainMenu,menu: MainMenu,modifier: Modifier,onClick: ()
*/
@Composable
fun MainBottomAppBar(menu:MainMenu, nav: NavController, modifier: Modifier=Modifier){
- BottomAppBar(backgroundColor = Color.White,modifier=modifier) {
+ BottomAppBar(backgroundColor = MaterialTheme.colors.background,modifier=modifier) {
//图标宽度平等分
val weight=1/(MainMenu.values().size*1.0f)
@@ -146,7 +156,12 @@ fun Marquee(model: MarqueeViewModel = viewModel(), content: @Composable BoxScope
}
}
-
+/**
+ * 跑马灯布局
+ *
+ * @param model
+ * @param offset
+ */
@Composable
fun MarqueeText(model: MarqueeViewModel = viewModel(), offset: State) {
val poetryIndex: Int by model.marqueeIndex.observeAsState(0)
@@ -163,7 +178,7 @@ fun MarqueeText(model: MarqueeViewModel = viewModel(), offset: State) {
}
/**
- * 界面框架
+ * 导航界面框架
*
* @param background 背景
* @param mainMenu 菜单
@@ -184,6 +199,20 @@ fun MainFrame( background:@Composable ()->Unit,mainMenu: MainMenu,nav: NavContro
}
}
+/**
+ * 界面框架
+ *
+ * @param background
+ * @param body
+ */
+@Composable
+fun MainFrame(background:@Composable ()->Unit,body:@Composable ColumnScope.()->Unit){
+ Box(modifier = Modifier.fillMaxSize()) {
+ background()
+ Column(content = body)
+ }
+}
+
/**
* 图片轮播
*
@@ -200,34 +229,89 @@ fun Carousel(
}
}
-@Preview
+/**
+ * 通用文本输入框
+ *
+ * @param T
+ * @param modifier
+ * @param form
+ * @param singeLine
+ */
@Composable
-fun CarouselPreview() {
- val model = CarouselViewModel()
-// ClubActivitiesImage(model = model)
-
-
- Carousel(model = model) {
- Card(
- modifier = Modifier
- .height(300.dp)
- .fillMaxWidth()
-// .rotate(180F)
- ) {
- Image(
- painter = painterResource(id = R.drawable.hot_activity_background),
- contentDescription = null
- )
+fun BaseTextField(modifier:Modifier=Modifier,form:T, singeLine:Boolean=false){
+ val name: String by form.formValue.observeAsState("")
+ val focusManager = LocalFocusManager.current
+ OutlinedTextField(
+ modifier = modifier,
+ value = name,
+ onValueChange = {form.onChange(it)},
+ label={ Text(text = form.formDesc)},
+ placeholder = { Text(text = form.formPlaceholder)},
+ singleLine = singeLine,
+ keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
+ keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done),
+ trailingIcon={ Text(text = "${name.length}/${form.textLength}") })
+}
- Image(
- painter = painterResource(id = it),
- contentDescription = null
- )
+/**
+ * 底部提示
+ *
+ * @param model
+ * @param scaffoldState
+ */
+@Composable
+fun ShowSnackbar(model:ScaffoldModel= viewModel(),scaffoldState: ScaffoldState){
+ val message:String? by model.message.observeAsState()
+ message?.let {
+ Logger.i("message=$it")
+ LaunchedEffect(scaffoldState){
+ launch {
+ scaffoldState.snackbarHostState.showSnackbar(
+ message = it
+ )
+ model.update()
+ }
}
}
}
+/**
+ * 界面背景
+ *
+ * @property id 资源id
+ */
+enum class BackgroundImage(@DrawableRes val id:Int){
+ //主页
+ main(R.drawable.mb_bg_fb_08),
+ //社团列表
+ list(R.drawable.mb_bg_fb_07),
+ //个人中心
+ center(R.drawable.mb_bg_fb_28),
+ //注册社团
+ reg_association(R.drawable.mb_bg_fb_07)
+}
+
+/**
+ * 界面背景图
+ *
+ * @param image
+ * @param alpha
+ */
+@Composable
+fun Background(image: BackgroundImage, alpha:Float= DefaultAlpha){
+ val app= LocalContext.current.applicationContext as APP
+
+ BoxWithConstraints {
+ Image(
+ bitmap = app.getImage(image = image,reqHeight = maxHeight.value.toInt()/2,reqWidth = maxWidth.value.toInt()/2),
+ contentDescription = null,
+ contentScale = ContentScale.FillHeight,
+ alpha = alpha,
+ modifier = Modifier.fillMaxSize()
+ )
+ }
+}
@Preview
@Composable
@@ -235,7 +319,7 @@ fun AnimationTextPreview(){
AnimationText(text = "6666")
}
-@Preview
+//@Preview
@Composable
fun MyBottomAppBarPreview(){
val nav= rememberNavController()
diff --git a/app/src/main/java/com/gyf/csams/uikit/ViewModel.kt b/app/src/main/java/com/gyf/csams/uikit/ViewModel.kt
new file mode 100644
index 0000000..5a9843b
--- /dev/null
+++ b/app/src/main/java/com/gyf/csams/uikit/ViewModel.kt
@@ -0,0 +1,41 @@
+package com.gyf.csams.uikit
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.orhanobut.logger.Logger
+
+interface FormLength{
+ val nameLengthError:String
+}
+
+abstract class FormName(val formDesc:String){
+ protected val _formValue= MutableLiveData()
+ val formValue: LiveData = _formValue
+ val formPlaceholder="请输入$formDesc"
+
+ abstract fun onChange(value:T)
+}
+
+open class StringForm(formDesc: String, val textLength: Int) : FormName(formDesc = formDesc),
+ FormLength {
+ override val nameLengthError="${formDesc}不能超过最大长度$textLength"
+
+ override fun onChange(value: String) {
+ if(value.length>textLength){
+ _formValue.value=value.slice(IntRange(0,textLength-1))
+ }else{
+ _formValue.value=value
+ }
+ Logger.i("${formDesc}更新值:${_formValue.value}")
+ }
+}
+
+class ScaffoldModel:ViewModel(){
+ private val _message=MutableLiveData()
+ val message:LiveData = _message
+
+ fun update(message:String?=null){
+ _message.value=message
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gyf/csams/ui/theme/Color.kt b/app/src/main/java/com/gyf/csams/uikit/theme/Color.kt
similarity index 69%
rename from app/src/main/java/com/gyf/csams/ui/theme/Color.kt
rename to app/src/main/java/com/gyf/csams/uikit/theme/Color.kt
index 1ea6684..cbec799 100644
--- a/app/src/main/java/com/gyf/csams/ui/theme/Color.kt
+++ b/app/src/main/java/com/gyf/csams/uikit/theme/Color.kt
@@ -1,4 +1,4 @@
-package com.gyf.csams.ui.theme
+package com.gyf.csams.uikit.theme
import androidx.compose.ui.graphics.Color
diff --git a/app/src/main/java/com/gyf/csams/ui/theme/Shape.kt b/app/src/main/java/com/gyf/csams/uikit/theme/Shape.kt
similarity index 88%
rename from app/src/main/java/com/gyf/csams/ui/theme/Shape.kt
rename to app/src/main/java/com/gyf/csams/uikit/theme/Shape.kt
index 79508e4..e2f4bfb 100644
--- a/app/src/main/java/com/gyf/csams/ui/theme/Shape.kt
+++ b/app/src/main/java/com/gyf/csams/uikit/theme/Shape.kt
@@ -1,4 +1,4 @@
-package com.gyf.csams.ui.theme
+package com.gyf.csams.uikit.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes
diff --git a/app/src/main/java/com/gyf/csams/ui/theme/Theme.kt b/app/src/main/java/com/gyf/csams/uikit/theme/Theme.kt
similarity index 96%
rename from app/src/main/java/com/gyf/csams/ui/theme/Theme.kt
rename to app/src/main/java/com/gyf/csams/uikit/theme/Theme.kt
index 1ea718e..042773b 100644
--- a/app/src/main/java/com/gyf/csams/ui/theme/Theme.kt
+++ b/app/src/main/java/com/gyf/csams/uikit/theme/Theme.kt
@@ -1,4 +1,4 @@
-package com.gyf.csams.ui.theme
+package com.gyf.csams.uikit.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
diff --git a/app/src/main/java/com/gyf/csams/ui/theme/Type.kt b/app/src/main/java/com/gyf/csams/uikit/theme/Type.kt
similarity index 95%
rename from app/src/main/java/com/gyf/csams/ui/theme/Type.kt
rename to app/src/main/java/com/gyf/csams/uikit/theme/Type.kt
index f01e380..5dcc466 100644
--- a/app/src/main/java/com/gyf/csams/ui/theme/Type.kt
+++ b/app/src/main/java/com/gyf/csams/uikit/theme/Type.kt
@@ -1,4 +1,4 @@
-package com.gyf.csams.ui.theme
+package com.gyf.csams.uikit.theme
import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle
diff --git a/app/src/main/res/drawable/ic_exchange_rate.xml b/app/src/main/res/drawable/ic_exchange_rate.xml
new file mode 100644
index 0000000..eee489a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_exchange_rate.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/test/java/com/gyf/csams/ExampleUnitTest.kt b/app/src/test/java/com/gyf/csams/ExampleUnitTest.kt
index 479a3ca..070132b 100644
--- a/app/src/test/java/com/gyf/csams/ExampleUnitTest.kt
+++ b/app/src/test/java/com/gyf/csams/ExampleUnitTest.kt
@@ -39,12 +39,5 @@ class ExampleUnitTest {
println( LocalDateTime.now().get(ChronoField.YEAR))
}
- @Test
- fun testChunked(){
- IntRange(0,13).chunked(3){
- println(it)
- }
- }
-
}