项目档案

Android赢博体育的更简洁的架构

在这些课堂笔记中,我将介绍Directory赢博体育程序的更新版本。在这个版本中,我将介绍一种基于使用视图模型类的新体系结构方法,该方法将清理赢博体育程序的体系结构。

视图模型是一个用来保存赢博体育程序数据的类。视图模型类存储赢博体育程序的数据,并将其提供给任何需要显示或修改这些数据的组件。视图模型类通常还提供方便的方法,我们可以调用这些方法来更新数据。一旦我们建立了视图模型,我们所要做的就是安排将该视图模型传递给需要使用它的任何组件。

为我们的赢博体育设置视图模型

下面是我们将在目录赢博体育程序的新版本中使用的视图模型类的代码。

导入android.content.Context。import com.google.gson.Gson .reflect. typetoken import java.io.IOException类DirectoryModel(application: application): AndroidViewModel(application) {lateinit var people: List<Person> private val context = application init {try {val json = context. openfileinput ("people.json"). bufferedreader()。使用{it.readText()} val sType = object: TypeToken<List<Person>>(){}。type people = Gson(). fromjson <List<Person b> >(json, sType)} catch (e: IOException) {people = listOf(Person(name =" Acacia Ackles",office ="Steitz 131"), Person(name =" Joe Gregg",office ="Briggs 413"), Person(name =" Kurt Krebsbach",office ="Briggs 411"))}} private fun saveChanges() {val Gson = Gson() val json: String = Gson . tojson (people) try {text. openfileoutput ("people。json, MODE_PRIVATE)。使用{fos -> fos.write(json.toByteArray())}} catch(e: IOException) {e. printstacktrace ()}} fun createPerson(name: String,office: String) {val newPerson = Person(name = name,office = office) people = people + newPerson saveChanges()} fun removePerson(Person: Person) {people = people。{it != person} saveChanges()}

视图模型类是一个扩展ViewModel类的类,或者在本例中扩展AndroidViewModel类。在这个例子中,我必须建立一个扩展AndroidViewModel的类,因为我的代码读取和保存人员列表需要访问赢博体育程序对象,以便我们的赢博体育程序能够调用openFileInput()和openFileOutput()等方法。不需要处理文件的视图模型类通常会继承ViewModel而不是AndroidViewModel。

视图模型类充当赢博体育程序数据的容器。在本例中,这意味着在类中存储Person对象列表。除了这些数据之外,我们还提供了一组方便的方法,用于从数据列表中添加和删除Person对象。

这里需要注意的一个重要细节是,我已经切换到使用不可变列表来存储人员列表。由于链表是不可变的,因此每次要添加或删除项时,都必须重新构建链表。看看createPerson()和removePerson()方法中的代码,看看我是如何做到这一点的。

使用视图模型

我们需要做的下一件事是重写我们的活动以使用视图模型类。

这样做的第一步是向活动添加代码以创建视图模型,然后安排将视图模型传递给可能需要它的任何组件。

这个过程从activity类开始:

class MainActivity: ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState) enableEdgeToEdge() val vm = ViewModelProvider(this, ViewModelProvider. androidviewmodelfactory . getinstance (application)).get(DirectoryModel::class.java) setContent {DirectoryVMTheme {DirectoryApp(vm,modifier = modifier . fillmaxsize ())}}}}

视图模型对象存储在vm变量中,我们将其传递给DirectoryApp组件,该组件是赢博体育程序的根组件。

使用视图模型的第一个好处是它极大地简化了第一个组件的结构:

@Composable fun DirectoryApp(vm: DirectoryModel,modifier: modifier) {val navController = memorbernavcontroller () NavHost(navController,startDestination=“list”){composable(route=“list”){ListScreen(vm = vm, toCreate = {navController.navigate(“create”)},modifier = modifier)} composable(route=“create”){CreateScreen(vm = vm,toList = {navController.navigate(“list”)},modifier = modifier)}}

这里我们需要做的就是将视图模型传递给两个赢博体育程序屏幕的组件,以及两个屏幕可以用来切换到新屏幕的导航功能。如果你将这段代码与赢博体育程序原始版本中DirectoryApp的代码进行比较,你会发现这个版本要清晰得多。

下面是ListScreen组件的代码,它实现了赢博体育程序中的第一个屏幕:

@OptIn (ExperimentalMaterial3Api::类)

@Composable

fun ListScreen(vm: DirectoryModel, toCreate: ()->Unit,modifier: modifier) {

val people = remember {mutableStateOf(vm.people)}

doRemove(person: person) {

vm.removePerson(人)

人。Value = vm.people

}

脚手架(topBar = {)

TopAppBar (

标题= {

文本(

“目录”,

)

},

动作= {

IconButton(onClick = {toCreate()}) {

图标(

imageVector = Icons.Default.Add,

contentDescription = “添加人员”

)

}

},

)

},

modifier = modifier . fillmaxsize ())

{innerPadding ->

列(垂直排列)=排列。Top, modifier = modifier . fillmaxsize ().padding(innerPadding)) {

people.value.forEach {person ->

键(person.id) {

PersonItem(person = person, onRemove = {doRemove(it)})

}

}

}

}

这里需要注意的重要一点是people状态变量。这将从视图模型的人员列表中初始化,并在我们需要删除人员时得到更新。我还提供了一个方便的函数来从列表中删除一个人:该函数将被传递给PersonItem组件,这样它就可以正确地实现滑动删除功能。

下面是赢博体育程序第二个屏幕的代码。通过使用视图模型,这也得到了简化。

@可组合的乐趣CreateScreen(vm: DirectoryModel, toList:() ->单位,modifier: modifier) {val focusManager = LocalFocusManager。当前val name = remember {mutableStateOf("")} val office = remember {mutableStateOf("")} fun makeNewPerson() {vm.createPerson(name.value,office.value) toList()} Column(verticalArrangement = Arrangement。居中,horizontalAlignment =对齐。centerhorizontal, modifier = modifier. fillmaxsize ().padding(10.dp)) {Row(horizontalArrangement = Arrangement。开始,verticalalign = align . bottom) {Text("Name:“)空格符(modifier= modifier .width(10.dp)) TextField(value = Name:”)value, onValueChange = {newValue: String ->名称。value = newValue}, keyboardOptions = keyboardOptions (imeAction = imeAction . done), keyboardActions = keyboardActions (onDone = {focusManager.clearFocus()}))} Spacer(modifier= modifier .height(10.dp)) Row(horizontalArrangement = Arrangement。开始,verticalalign = align . bottom) {Text("Office:“)空格符(modifier= modifier .width(10.dp)) TextField(value = Office:”)value, onValueChange = {newValue: String -> office。value = newValue}, keyboardOptions = keyboardOptions (imeAction = imeAction . done), keyboardActions = keyboardActions (onDone = {focusManager.clearFocus()}))} Spacer(modifier= modifier .height(20.dp)) Button(onClick = {makeNewPerson()}) {Text("Save")}}}

要创建一个新的人物,我们所要做的就是调用视图模型的createPerson()方法,然后调用toList()方法导航回第一个屏幕。createPerson()方法将负责更新人员列表,因此当我们返回到第一个屏幕时,它将显示从视图模型获得的更新后的人员列表。