很可能是一个新手的问题,因为我对Android开发相当陌生——我在配置更改/导航时在我的@Composable中保存AndroidView的状态时遇到了麻烦,因为factory block被调用(如预期的那样)并且我的图表被重新实例化。
@Composable
fun ChartView(viewModel:ViewModel, modifier:Modifier){
val context = LocalContext.current
val chart = remember { DataChart(context) }
AndroidView(
modifier = modifier,
factory = { context ->
Log.d("DEBUGLOG", "chart init")
chart
},
update = { chart ->
Log.d("DEBUGLOG", "chart update")
})
}
DataChart
是一个具有复杂图表的第三方组件,我想保留缩放/滚动状态。我知道我可以使用ViewModel跨配置更改保存UI状态,但考虑到保存缩放/滚动状态的复杂性,我想问一下是否有其他更简单的方法来实现这一点?
我试图将整个图表实例移动到viewModel,但由于它使用上下文,我收到了有关上下文对象泄漏的警告。
任何帮助都将不胜感激!
我想说的是,将图表实例移动到视图模型中的直觉是正确的,但是,正如您所指出的,当视图以外的对象需要上下文依赖性时,上下文依赖性可能会变得很麻烦。对我来说,这变成了一个依赖注入的问题,其中依赖关系是上下文,或者从更广泛的意义上讲,是整个数据图表。我很想知道你是如何获取视图模型的,但我假设它依赖于Android视图模型提供程序(通过viewModels()
或某种ViewModelProvider.Factory
)。
这个问题的直接解决方案是将视图模型转换为< code>AndroidViewModel的子类,该子类通过视图模型的构造函数提供对应用程序上下文的引用。虽然它仍然是一种反模式,应该谨慎使用,但Android团队已经认识到某些用例是有效的。我个人不使用< code>AndroidViewModel,因为我认为它是一个问题的粗糙解决方案,而这个问题本来可以通过对依赖图的优化来解决。然而,这是官方文件批准的,这只是我个人的看法。根据经验,我必须说它的使用使得测试一个视图模型成为一个事后的噩梦。如果您对依赖注入库感兴趣,我强烈推荐新的< code>Hilt实现,它刚刚在上个月发布了稳定的< code>1.0.0版本。
除此之外,我现在将为您的困境提供两种可能的解决方案:一种是利用AndroidViewModel
,另一种是不利用。如果视图模型已经具有上下文之外的其他依赖项,AndroidViewModel
解决方案不会为您节省太多开销,因为您可能已经实例化了ViewModelProvider。工厂
在某个点。这些解决方案将考虑Android<code>片段活动或<code>DialogFragment</code>中实现,并对生命周期挂钩等进行一些调整。
使用 AndroidViewModel
import android.app.Application
import androidx.lifecycle.AndroidViewModel
class MyViewModel(application: Application) : AndroidViewModel(application) {
val dataChart: DataChart
init {
dataChart = DataChart(application.applicationContext)
}
}
碎片可能在哪里
class MyFragment : Fragment() {
private val viewModel: MyViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View { ... }
}
没有AndroidViewModel
import androidx.lifecycle.ViewModel
class MyViewModel(args: Args) : ViewModel() {
data class Args(
val dataChart: DataChart
)
val dataChart: DataChart = args.dataChart
}
碎片可能在哪里
class MyFragment : Fragment() {
private lateinit var viewModel: MyViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val applicationContext: Context = requireContext().applicationContext
val dataChart = DataChart(applicationContext)
val viewModel: MyViewModel by viewModels {
ArgsViewModelFactory(
args = MyViewModel.Args(
dataChart = dataChart,
),
argsClass = MyViewModel.Args::class.java,
)
}
this.viewModel = viewModel
...
}
}
其中,ArgsViewModelFactory
是我自己创建的,如下所示
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class ArgsViewModelFactory<T>(
private val args: T,
private val argsClass: Class<T>,
) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = modelClass.getConstructor(
argsClass,
).newInstance(
args,
)
}
编辑(通过刀柄模块):
@Module
@InstallIn(...)
object DataChartModule {
@Provides
fun provideDataChart(
@ApplicationContext context: Context,
): DataChart = DataChart(context)
}
这是我知道的最简单的方法。当我旋转手机时,它会保持状态,不会触发WebView上的重新加载。它应该适用于每个视图。
首先,创建一个应用程序类
class MainApplication : Application() {
private var view: WebView? = null
override fun onCreate() {
super.onCreate()
LOG("started application")
}
fun loadView(context: Context): WebView {
if (view == null) {
view = WebView(context)
//init your view here
}
return view!!
}
}
然后将应用程序类添加到清单.xml
<manifest>
...
<application
android:name=".MainApplication"
...
最后,将其添加到composable中
AndroidView(modifier = Modifier.fillMaxSize(), factory = { context ->
val application = context.applicationContext as MainApplication
application.loadView(context = context)
})
就是这样。我不确定这是否会导致内存泄漏,但我还没有遇到问题。
如果要在配置更改时保留AndroidView的状态,请使用< code>rememberSaveable而不是< code > remembers 。
虽然remember有助于您在重新组合时保留状态,但在配置更改时不会保留状态。为此,必须使用rememberSaveable。rememberSaveable自动保存任何可以保存在包中的值。对于其他值,可以传入一个自定义的saver对象。
如果数据图表是一种可打包、可序列化的数据类型或其他可以捆绑存储的数据类型:
val chart = rememberSaveable { DataChart(context) }
如果上述方式不起作用,请创建一个MapSav以保存数据、缩放/滚动状态...我假设DataChart具有zoomIndex、scrollingIndex、值您需要保存的属性:
fun getDataChartSaver(context: Context) = run {
val zoomKey = "ZoomState"
val scrollingKey = "ScrollingState"
val dataKey = "DataState"
mapSaver(
save = { mapOf(zoomKey to it.zoomIndex, scrollingKey to it.scrollingIndex, dataKey to it.values) },
restore = {
DataChart(context).apply{
zoomIndex = it[zoomKey]
scrollingIndex = it[scrollingKey]
values = it[dataKey]
}
}
)
}
用途:
val chart = rememberSaveable(stateSaver = getDataChartSaver(context)){ DataChart(context) }
查看更多存储状态的方法
问题内容: 示例应用程序: http //angular.github.com/angular- phonecat/step-11/app/#/phones 如果您选择最后一个手机“ Motorola charm”,它将为您显示手机的详细信息。当您在浏览器上浏览时,它将重新加载数据,并且滚动条位于顶部。 导航返回时,自动滚动到剩余位置的最佳方法是什么?而且,为什么角度重新加载数据? 我的计算机上有
我是RxJava的新手,并将其与MVP架构一起使用。 我发现了一些使用保留片段在配置更改时保存可观察对象的示例(仍然不确定这是否是最好的方法)。不过,我发现的示例是直接在活动或片段上处理可观察对象,而不是从演示者那里。 因此,我尝试并设置了这个快速示例(仅使用Reactivex的RxJava和RxAndroid库)以进行测试,看起来效果不错。此示例所做的是: 使用无头保留片段启动活动 我想知道我这
慢慢地,jpanel的背景色将变得比以前更不透明。值得注意的是,我正在使用jpanel的挫折方法。以下是一些您可能想要查看的代码链接。 自定义GUI按钮 它所在的Gui--请看第158行。
我已经开发了一个自定义滚动文件附加器,它工作得很好。唯一的问题是,每次我重新启动服务器或更改log4j2时,它都会被重新初始化。xml文件(文件的任何部分)和之前的所有日志会突然被删除。我还没有观察到默认附加器的这种行为,所以我想知道我能做些什么来保留我的配置。 附言:我试图使它成为一个单例,但除此之外,它不起作用,我真的不想让它远离被重新配置,我只想保留我以前生成的日志。 使现代化 显然,每次服
问题内容: 当我从一个视图控制器移动到另一个视图控制器时,第一个控制器上的开关会重置自身,并且不会保持其状态。查看其他控制器后,如何恢复状态?以及在关闭应用程序后如何使其保存状态。我看过各种stackOverflow问题和响应以及Apple文档,但似乎没有任何效果。 这是我的带有开关的View Controller的类。 我是Swift和Xcode的初学者。预先感谢您的时间和帮助:) 问题答案:
我知道React可能会异步和批量地执行状态更新,以进行性能优化。因此,在调用之后,您永远不能相信会更新状态。但是,您是否信任React按照调用时的顺序更新状态 相同的组件? 不同的组件? 任何有文档支持的答案都是非常感谢的。