增加社团注册

优化背景加载
通用文本框、底部提示封装
master
pan 4 years ago
parent 3bcab0cb2e
commit 014d4ae16c
  1. 192
      app/src/androidTest/java/com/gyf/csams/TestPreview.kt
  2. 10
      app/src/main/AndroidManifest.xml
  3. 81
      app/src/main/java/com/gyf/csams/APP.kt
  4. 6
      app/src/main/java/com/gyf/csams/InitActivity.kt
  5. 11
      app/src/main/java/com/gyf/csams/account/ui/AccountActivity.kt
  6. 45
      app/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt
  7. 258
      app/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt
  8. 110
      app/src/main/java/com/gyf/csams/main/model/MainViewModel.kt
  9. 188
      app/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
  10. 146
      app/src/main/java/com/gyf/csams/uikit/BaseView.kt
  11. 41
      app/src/main/java/com/gyf/csams/uikit/ViewModel.kt
  12. 2
      app/src/main/java/com/gyf/csams/uikit/theme/Color.kt
  13. 2
      app/src/main/java/com/gyf/csams/uikit/theme/Shape.kt
  14. 2
      app/src/main/java/com/gyf/csams/uikit/theme/Theme.kt
  15. 2
      app/src/main/java/com/gyf/csams/uikit/theme/Type.kt
  16. 9
      app/src/main/res/drawable/ic_exchange_rate.xml
  17. 7
      app/src/test/java/com/gyf/csams/ExampleUnitTest.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 {
}
}
}

@ -3,6 +3,10 @@
package="com.gyf.csams"> package="com.gyf.csams">
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!--已启用分区存储-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<application <application
android:allowBackup="true" android:allowBackup="true"
@ -33,7 +37,11 @@
</activity> </activity>
<activity <activity
android:name=".ui.MainActivity" android:name=".main.ui.MainActivity"
android:exported="true">
</activity>
<activity android:name=".association.ui.RegAssociationActivity"
android:exported="true"> android:exported="true">
</activity> </activity>
</application> </application>

@ -1,19 +1,100 @@
package com.gyf.csams package com.gyf.csams
import android.app.Application 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.AndroidLogAdapter
import com.orhanobut.logger.DiskLogAdapter import com.orhanobut.logger.DiskLogAdapter
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
class APP : Application() { class APP : Application() {
private lateinit var memoryCache: LruCache<BackgroundImage, Bitmap>
// 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() { override fun onCreate() {
super.onCreate() super.onCreate()
memoryCache = object : LruCache<BackgroundImage, Bitmap>(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(AndroidLogAdapter())
Logger.addLogAdapter(DiskLogAdapter()) Logger.addLogAdapter(DiskLogAdapter())
Logger.i("${BuildConfig.APP_NAME}启动") Logger.i("${BuildConfig.APP_NAME}启动")
} }
} }

@ -10,8 +10,8 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.account.ui.AccountActivity import com.gyf.csams.account.ui.AccountActivity
import com.gyf.csams.ui.AnimationText import com.gyf.csams.main.ui.MainActivity
import com.gyf.csams.ui.MainActivity import com.gyf.csams.uikit.AnimationText
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
class InitActivity : ComponentActivity() { class InitActivity : ComponentActivity() {
@ -50,7 +50,7 @@ private fun Init(initViewModel:InitViewModel= viewModel()){
when (isValid) { when (isValid) {
false -> context.startActivity(Intent(context, AccountActivity::class.java)) false -> context.startActivity(Intent(context, AccountActivity::class.java))
true -> context.startActivity(Intent(context,MainActivity::class.java)) true -> context.startActivity(Intent(context, MainActivity::class.java))
} }
} }

@ -31,8 +31,8 @@ import androidx.navigation.compose.rememberNavController
import com.gyf.csams.BuildConfig import com.gyf.csams.BuildConfig
import com.gyf.csams.account.model.AccountViewModel import com.gyf.csams.account.model.AccountViewModel
import com.gyf.csams.account.model.DialogMessage import com.gyf.csams.account.model.DialogMessage
import com.gyf.csams.ui.AnimationText import com.gyf.csams.uikit.AnimationText
import com.gyf.csams.ui.theme.CSAMSTheme import com.gyf.csams.uikit.theme.CSAMSTheme
enum class AccountRoute{ enum class AccountRoute{
@ -178,7 +178,7 @@ private fun Account(accountViewModel: AccountViewModel = viewModel(),
/** /**
* 学号 * 学号
* *TODO 需要把逻辑封装到[com.gyf.csams.uikit.BaseTextField]
* @param accountViewModel * @param accountViewModel
*/ */
@Composable @Composable
@ -297,9 +297,10 @@ private fun PasswordDialog(accountViewModel: AccountViewModel = viewModel(), mes
} }
/** /**
* 姓名文本框 * 姓名文本框
* * TODO 需要把逻辑封装到[com.gyf.csams.uikit.BaseTextField]
* @param name 姓名 * @param name 姓名
* @param accountViewModel * @param accountViewModel
*/ */
@ -328,7 +329,7 @@ private fun Name(name:String, accountViewModel: AccountViewModel = viewModel()){
/** /**
* 密码框 * 密码框
* *TODO 需要把逻辑封装到[com.gyf.csams.uikit.BaseTextField]
* @param accountViewModel * @param accountViewModel
*/ */
@Composable @Composable

@ -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<Uri>()
val picture:LiveData<Uri> =_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("功能尚未实现,敬请期待")
}
}

@ -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<String>,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)
}

@ -1,10 +1,11 @@
package com.gyf.csams.ui.model package com.gyf.csams.main.model
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.uikit.StringForm
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -20,7 +21,7 @@ class MarqueeViewModel:ViewModel() {
private val _marqueeIndex=MutableLiveData(0) private val _marqueeIndex=MutableLiveData(0)
var marqueeIndex:LiveData<Int> = _marqueeIndex var marqueeIndex:LiveData<Int> = _marqueeIndex
var marqueeJob:Job? = null private var marqueeJob:Job? = null
@ -38,6 +39,10 @@ class MarqueeViewModel:ViewModel() {
} }
} }
/**
* 海报轮播
*
*/
class CarouselViewModel: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) 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<Int> = _index val index:LiveData<Int> = _index
var job:Job? = null private var job:Job? = null
init { init {
start() start()
} }
fun start(){ private fun start(){
job = viewModelScope.launch { job = viewModelScope.launch {
do{ do{
_index.postValue(if (_index.value==imageList.size-1) 0 else _index.value?.plus(1)) _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) 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(){ class ListViewModel:ViewModel(){
private val _name=MutableLiveData("") val name = StringForm(formDesc = "社团名称",textLength = 5)
val name:LiveData<String> = _name
val nameDesc="社团名称"
val namePlaceholder="请输入$nameDesc"
private val _desc=MutableLiveData("") val desc = StringForm(formDesc = "社团简介",textLength = 10)
val desc:LiveData<String> = _desc
val descDesc="社团简介"
val descPlaceholder="请输入$descDesc"
//注册请求响应信息 //社团列表加载数量
private val _snackBarMsg=MutableLiveData<String>() val associationListSize=10
val snackBarMsg:LiveData<String> = _snackBarMsg
//社团列表 //社团列表
private val _associationList=MutableLiveData<MutableList<AssociationDto>>(mutableListOf()) private val _associationList=MutableLiveData<MutableList<AssociationDto>>(mutableListOf())
val associationDto:LiveData<MutableList<AssociationDto>> = _associationList val associationDto:LiveData<MutableList<AssociationDto>> = _associationList
val searchDesc="搜索"
init { init {
loadAssociation() loadAssociation()
} }
fun onChangeName(name:String){ /**
_name.value=name * TODO 社团检索
} *
* @param callback
fun onChangeDesc(desc:String){ */
_desc.value=desc fun search(callback: (value: String) -> Unit){
} Logger.i("搜索条件[社团名称:${name.formValue.value},社团简介:${desc.formValue.value}]")
callback("功能尚未实现,敬请期待")
fun search(){
Logger.i("使用社团名称:${_name.value},社团简介:${_desc.value} 搜索社团")
_snackBarMsg.value="搜索失败,请联系管理员"
} }
/** /**
@ -131,7 +154,7 @@ class ListViewModel:ViewModel(){
* 加载更多社团列表 * 加载更多社团列表
* *
*/ */
fun addMore(){ fun addMore(callback:(message:String) -> Unit){
viewModelScope.launch { viewModelScope.launch {
val c = _associationList.value val c = _associationList.value
@ -147,15 +170,26 @@ class ListViewModel:ViewModel(){
Logger.i("t.size=${t.size}") Logger.i("t.size=${t.size}")
_associationList.postValue(t) _associationList.postValue(t)
Logger.i("加载更多社团size=${_associationList.value?.size}") 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("功能尚未实现,敬请期待")
} }
} }

@ -1,28 +1,25 @@
package com.gyf.csams.ui package com.gyf.csams.main.ui
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape 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.material.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
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.res.painterResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -32,13 +29,10 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.ui.model.AssociationDto import com.gyf.csams.association.ui.RegAssociationActivity
import com.gyf.csams.ui.model.CarouselViewModel import com.gyf.csams.main.model.*
import com.gyf.csams.ui.model.ListViewModel import com.gyf.csams.uikit.*
import com.gyf.csams.ui.model.MarqueeViewModel import com.gyf.csams.uikit.theme.CSAMSTheme
import com.gyf.csams.ui.theme.CSAMSTheme
import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch
/** /**
@ -48,11 +42,14 @@ import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
CSAMSTheme { CSAMSTheme {
Body() Body()
} }
} }
} }
} }
@ -67,25 +64,15 @@ fun Body() {
NavHost(navController, startDestination = MainMenu.Main.name) { NavHost(navController, startDestination = MainMenu.Main.name) {
composable(MainMenu.Main.name) { composable(MainMenu.Main.name) {
Main(navController = navController) Main(navController = navController)
ShowSnackbar(scaffoldState = scaffoldState)
} }
composable(MainMenu.List.name) { composable(MainMenu.List.name) {
AssociationList(navController = navController) AssociationList(navController = navController)
ShowSnackbar(scaffoldState = scaffoldState)
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()
}
}
} }
composable(MainMenu.Center.name) { composable(MainMenu.Center.name) {
Center(navController = navController) Center(navController = navController)
ShowSnackbar(scaffoldState = scaffoldState)
} }
} }
} }
@ -98,16 +85,20 @@ fun Body() {
* *
*/ */
@Composable @Composable
fun Center(navController: NavController){ fun Center(model:CenterViewModel= viewModel(), scaffoldModel: ScaffoldModel= viewModel(), navController: NavController){
MainFrame(background = { CenterBackground() }, mainMenu = MainMenu.Center, nav = navController) { MainFrame(background = { Background(image = BackgroundImage.center,alpha = 0.5F) }, mainMenu = MainMenu.Center, nav = navController) {
Column(modifier = Modifier Column(modifier = Modifier
.weight(0.33F) .weight(0.33F)
.fillMaxWidth(),verticalArrangement = Arrangement.Bottom) { .fillMaxWidth(),verticalArrangement = Arrangement.Bottom) {
Card(backgroundColor = Color.White) { Card(backgroundColor = MaterialTheme.colors.background) {
Row(modifier = Modifier.fillMaxWidth(),verticalAlignment = Alignment.CenterVertically) { Row(modifier = Modifier
.fillMaxWidth()
.clickable(onClick = {
model.openMyAssociation { scaffoldModel.update(it) }
}),verticalAlignment = Alignment.CenterVertically) {
Spacer(modifier = Modifier.weight(0.33F)) Spacer(modifier = Modifier.weight(0.33F))
Row(modifier = Modifier.weight(0.33F),horizontalArrangement = Arrangement.Center) { Row(modifier = Modifier.weight(0.33F),horizontalArrangement = Arrangement.Center) {
Text(text = "我的社团") Text(text = model.myAssociationDesc)
} }
Row(modifier = Modifier.weight(0.33F),horizontalArrangement = Arrangement.End) { Row(modifier = Modifier.weight(0.33F),horizontalArrangement = Arrangement.End) {
Icon( 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 @Composable
fun Main(navController: NavController) { 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)) { Column(modifier = Modifier.weight(0.33F)) {
Notification() Notification()
MessageBoard() 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 @Composable
fun AssociationList(navController: NavController) { fun AssociationList(navController: NavController) {
MainFrame( MainFrame(
background = { background = { Background(image = BackgroundImage.list) },
AssociationListBackground()
},
mainMenu = MainMenu.List, mainMenu = MainMenu.List,
nav = navController nav = navController
) { ) {
@ -195,37 +156,28 @@ fun AssociationList(navController: NavController) {
} }
/** /**
* 添加社团按钮 * 注册社团按钮
* *
*/ */
@Composable @Composable
fun RegisterAssociation() { fun RegisterAssociation() {
val context= LocalContext.current
Row( Row(
horizontalArrangement = Arrangement.End, horizontalArrangement = Arrangement.End,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(10.dp) .padding(10.dp)
) { ) {
IconButton(onClick = {
context.startActivity(Intent(context, RegAssociationActivity::class.java))
}) {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_add_fill), painter = painterResource(id = R.drawable.ic_add_fill),
contentDescription = null, contentDescription = null,
modifier = Modifier.size(50.dp) 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()
)
} }
/** /**
@ -233,7 +185,7 @@ fun AssociationListBackground() {
* *
*/ */
@Composable @Composable
fun AssociationListBody(model: ListViewModel = viewModel()) { fun AssociationListBody(model: ListViewModel = viewModel(),scaffoldModel: ScaffoldModel= viewModel()) {
val associationList: MutableList<AssociationDto>? by model.associationDto.observeAsState() val associationList: MutableList<AssociationDto>? by model.associationDto.observeAsState()
val listState = rememberLazyListState() val listState = rememberLazyListState()
@ -253,7 +205,7 @@ fun AssociationListBody(model: ListViewModel = viewModel()) {
}else{ }else{
Box(modifier = Modifier Box(modifier = Modifier
.weight(0.35F) .weight(0.35F)
.border(width = 1.dp, color = Color.Black)) .border(width = 1.dp, color = MaterialTheme.colors.onBackground))
} }
Spacer(modifier = Modifier.weight(0.1F)) 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){ if(listState.layoutInfo.totalItemsCount-listState.firstVisibleItemIndex==model.associationListSize/2-1){
model.addMore() model.addMore { scaffoldModel.update(it) }
} }
} }
@ -296,48 +242,23 @@ fun Association(associationDto: AssociationDto) {
* *
*/ */
@Composable @Composable
fun AssociationSearch(model: ListViewModel = viewModel()) { fun AssociationSearch(model: ListViewModel = viewModel(),scaffoldModel: ScaffoldModel= viewModel()) {
val name: String by model.name.observeAsState("")
Card(modifier = Modifier.padding(horizontal = 50.dp, vertical = 10.dp)) { Card(modifier = Modifier.padding(horizontal = 50.dp, vertical = 10.dp)) {
Column { Column {
Row {
val focusManager = LocalFocusManager.current BaseTextField(form = model.name,singeLine = true,modifier = Modifier.padding(horizontal = 10.dp))
Spacer(modifier = Modifier.weight(0.05F)) BaseTextField(form = model.desc,singeLine = true,modifier = Modifier.padding(horizontal = 10.dp))
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))
}
Spacer( Spacer(
modifier = Modifier modifier = Modifier.height(10.dp)
.fillMaxWidth()
.height(10.dp)
) )
Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth()) { Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth()) {
OutlinedButton(onClick = { model.search() }, modifier = Modifier.width(100.dp)) { OutlinedButton(onClick = { model.search { scaffoldModel.update(it) } }, modifier = Modifier.width(100.dp)) {
Text(text = "搜索") Text(text = model.searchDesc)
} }
} }
Spacer( Spacer(
modifier = Modifier modifier = Modifier.height(10.dp)
.fillMaxWidth()
.height(10.dp)
) )
} }
@ -350,19 +271,23 @@ fun AssociationSearch(model: ListViewModel = viewModel()) {
* *
*/ */
@Composable @Composable
fun Notification() { fun Notification(mainViewModel: MainViewModel= viewModel(),scaffoldModel: ScaffoldModel= viewModel()) {
Row( Row(
horizontalArrangement = Arrangement.End, horizontalArrangement = Arrangement.End,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(10.dp) .padding(10.dp)
) { ) {
IconButton(onClick = {
mainViewModel.openNotification { scaffoldModel.update(it) }
}) {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_notification), painter = painterResource(id = R.drawable.ic_notification),
contentDescription = null, contentDescription = null
modifier = Modifier.size(50.dp)
) )
} }
}
} }
/** /**
@ -376,7 +301,7 @@ fun MyBorder(content: @Composable BoxScope.() -> Unit) {
modifier = Modifier modifier = Modifier
.border( .border(
width = 1.dp, width = 1.dp,
color = Color.Black, color = MaterialTheme.colors.onBackground,
shape = RoundedCornerShape(size = 20.dp) shape = RoundedCornerShape(size = 20.dp)
), ),
) { ) {
@ -390,18 +315,19 @@ fun MyBorder(content: @Composable BoxScope.() -> Unit) {
* *
*/ */
@Composable @Composable
fun MessageBoard(model: MarqueeViewModel = viewModel()) { fun MessageBoard(model: MarqueeViewModel = viewModel(),mainViewModel:MainViewModel= viewModel(),scaffoldModel: ScaffoldModel= viewModel()) {
MyBorder { MyBorder {
Row( Row(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
IconButton(onClick = { mainViewModel.sendMessage { scaffoldModel.update(it) } }) {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_comments), painter = painterResource(id = R.drawable.ic_comments),
contentDescription = null, contentDescription = null,
modifier = Modifier
.size(50.dp)
) )
}
Row( Row(
horizontalArrangement = Arrangement.Center, horizontalArrangement = Arrangement.Center,
modifier = Modifier modifier = Modifier

@ -1,4 +1,4 @@
package com.gyf.csams.ui package com.gyf.csams.uikit
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.animation.Crossfade import androidx.compose.animation.Crossfade
@ -6,14 +6,21 @@ import androidx.compose.animation.animateColor
import androidx.compose.animation.core.* import androidx.compose.animation.core.*
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier 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.res.painterResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -21,10 +28,13 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.compose.navigate import androidx.navigation.compose.navigate
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.gyf.csams.APP
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.ui.model.CarouselViewModel import com.gyf.csams.main.model.CarouselViewModel
import com.gyf.csams.ui.model.MarqueeViewModel import com.gyf.csams.main.model.MarqueeViewModel
import com.gyf.csams.ui.theme.CSAMSTheme 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){ fun AnimationText(text:String){
val infiniteTransition = rememberInfiniteTransition() val infiniteTransition = rememberInfiniteTransition()
val color by infiniteTransition.animateColor( val color by infiniteTransition.animateColor(
initialValue = Color.Red, initialValue = MaterialTheme.colors.primary,
targetValue = Color.Green, targetValue = MaterialTheme.colors.onPrimary,
animationSpec = infiniteRepeatable( animationSpec = infiniteRepeatable(
animation = tween(1000, easing = LinearEasing), animation = tween(1000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse repeatMode = RepeatMode.Reverse
@ -94,7 +104,7 @@ fun MenuIconButton(_menu: MainMenu,menu: MainMenu,modifier: Modifier,onClick: ()
*/ */
@Composable @Composable
fun MainBottomAppBar(menu:MainMenu, nav: NavController, modifier: Modifier=Modifier){ 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) 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 @Composable
fun MarqueeText(model: MarqueeViewModel = viewModel(), offset: State<Float>) { fun MarqueeText(model: MarqueeViewModel = viewModel(), offset: State<Float>) {
val poetryIndex: Int by model.marqueeIndex.observeAsState(0) val poetryIndex: Int by model.marqueeIndex.observeAsState(0)
@ -163,7 +178,7 @@ fun MarqueeText(model: MarqueeViewModel = viewModel(), offset: State<Float>) {
} }
/** /**
* 界面框架 * 导航界面框架
* *
* @param background 背景 * @param background 背景
* @param mainMenu 菜单 * @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 @Composable
fun CarouselPreview() { fun <T:StringForm> BaseTextField(modifier:Modifier=Modifier,form:T, singeLine:Boolean=false){
val model = CarouselViewModel() val name: String by form.formValue.observeAsState("")
// ClubActivitiesImage(model = model) val focusManager = LocalFocusManager.current
OutlinedTextField(
modifier = modifier,
Carousel(model = model) { value = name,
Card( onValueChange = {form.onChange(it)},
modifier = Modifier label={ Text(text = form.formDesc)},
.height(300.dp) placeholder = { Text(text = form.formPlaceholder)},
.fillMaxWidth() singleLine = singeLine,
// .rotate(180F) keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
) { keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done),
Image( trailingIcon={ Text(text = "${name.length}/${form.textLength}") })
painter = painterResource(id = R.drawable.hot_activity_background), }
contentDescription = null
)
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 @Preview
@Composable @Composable
@ -235,7 +319,7 @@ fun AnimationTextPreview(){
AnimationText(text = "6666") AnimationText(text = "6666")
} }
@Preview //@Preview
@Composable @Composable
fun MyBottomAppBarPreview(){ fun MyBottomAppBarPreview(){
val nav= rememberNavController() val nav= rememberNavController()

@ -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<T>(val formDesc:String){
protected val _formValue= MutableLiveData<T>()
val formValue: LiveData<T> = _formValue
val formPlaceholder="请输入$formDesc"
abstract fun onChange(value:T)
}
open class StringForm(formDesc: String, val textLength: Int) : FormName<String>(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<String>()
val message:LiveData<String> = _message
fun update(message:String?=null){
_message.value=message
}
}

@ -1,4 +1,4 @@
package com.gyf.csams.ui.theme package com.gyf.csams.uikit.theme
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color

@ -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.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes import androidx.compose.material.Shapes

@ -1,4 +1,4 @@
package com.gyf.csams.ui.theme package com.gyf.csams.uikit.theme
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme

@ -1,4 +1,4 @@
package com.gyf.csams.ui.theme package com.gyf.csams.uikit.theme
import androidx.compose.material.Typography import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle

File diff suppressed because one or more lines are too long

@ -39,12 +39,5 @@ class ExampleUnitTest {
println( LocalDateTime.now().get(ChronoField.YEAR)) println( LocalDateTime.now().get(ChronoField.YEAR))
} }
@Test
fun testChunked(){
IntRange(0,13).chunked(3){
println(it)
}
}
} }

Loading…
Cancel
Save