parent
3bcab0cb2e
commit
014d4ae16c
@ -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 { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -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}启动") |
||||||
|
|
||||||
|
|
||||||
} |
} |
||||||
} |
} |
@ -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) |
||||||
|
|
||||||
|
} |
@ -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
Loading…
Reference in new issue