Android 我的改装应用程序在Kotlin的底部导航中使用时检索错误
我有一个经过改造的Android应用程序,它在片段中正常工作,检索我想要的数据列表,但是,一旦我决定在jetpack Compose上使用底部导航,同样的代码会给我一个错误 指定为非空的参数为空:方法kotlin.jvm.internal.Intrinsics.checkNotNullParameter,参数初始值 如果我在ComposeTree中将ScreenController的内容替换为composable(search)的内容,它可以工作,但不是这样 错误将nullParameter指向我的ImdbResponseMapper类Android 我的改装应用程序在Kotlin的底部导航中使用时检索错误,android,kotlin,retrofit,android-jetpack,android-jetpack-compose,Android,Kotlin,Retrofit,Android Jetpack,Android Jetpack Compose,我有一个经过改造的Android应用程序,它在片段中正常工作,检索我想要的数据列表,但是,一旦我决定在jetpack Compose上使用底部导航,同样的代码会给我一个错误 指定为非空的参数为空:方法kotlin.jvm.internal.Intrinsics.checkNotNullParameter,参数初始值 如果我在ComposeTree中将ScreenController的内容替换为composable(search)的内容,它可以工作,但不是这样 错误将nullParameter指向
@AndroidEntryPoint
class MovieFragment : Fragment() {
private val viewModel: MovieViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
val movieList = viewModel.movie
val movieSearch = viewModel.movieSearch.value
val navController = rememberNavController()
val title = remember {mutableStateOf("Search Movie")}
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = title.value)
}
)
},
bottomBar = {
val items = listOf(
Screen.Search,
Screen.ToWatchList,
Screen.Watched
)
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.arguments?.getString(KEY_ROUTE)
items.forEach {
BottomNavigationItem(
icon = {Icon(it.icon, contentDescription = null)},
selected = currentRoute == it.route,
onClick = {
navController.popBackStack(
navController.graph.startDestination, false
)
if(currentRoute != it.route){
navController.navigate(it.route)
}
})
}
}
}
) {
ScreenController(
navHostController = navController,
topBarTitle = title,
navController = findNavController(),
movieList = movieList,
viewModel = viewModel,
movieSearch = movieSearch
)
}
}
}
}
}
类ImdbRepositoryImpl(
私有val imdbService:imdbService,
私有值映射器:ImdbResponseMapper
):ImdbRepository{
覆盖挂起有趣的getMovie(查询:字符串):列表{
返回mapper.toDomainList(imdbService.getMovies(search=query.search)
}
}
@HiltViewModel
类MovieViewModel@injectconstructor(
私有val imdbRepository:imdbRepository
):ViewModel(){
val movie:MutableState=mutableStateOf(listOf())
趣味电影(搜索:字符串){
viewModelScope.launch{
movie.value=imdbRepository.getMovie(搜索)
}
}
val movieDetail:MutableState=mutableStateOf(null)
趣味getMovieDetails(imdbID:String){
viewModelScope.launch{
movieDetail.value=imdbRepository.getMovieDetails(imdbID)
}
}
val movieSearch=mutableStateOf(“”)
有趣的onTextChange(文本:字符串){
movieSearch.value=文本
}
这不是Jetpack的错。请仔细检查Model vs json中的非空字段。
@Composable
fun ScreenController(
navHostController: NavHostController,
topBarTitle: MutableState<String>,
navController: NavController,
movieList: MutableState<List<MovieSearch>>,
viewModel: MovieViewModel,
movieSearch: String
) {
NavHost(
navController = navHostController, startDestination = "search"
) {
composable("search") {
topBarTitle.value = "Search For Movies"
Column(
modifier = Modifier.padding(8.dp)
) {
Row {
TextField(
value = viewModel.movieSearch.value,
onValueChange = { newValue ->
viewModel.onTextChange(newValue)
},
modifier = Modifier.weight(3f),
label = {
Text(text = "Search movies")
}
)
Button(
modifier = Modifier.weight(1f),
onClick = { viewModel.getMovie(movieSearch) }) {
Text(text = "Search")
}
}
if (movieList.value.isEmpty()) {
Column(
modifier = Modifier.fillMaxSize()
) {
Text(
modifier = Modifier.align(Alignment.CenterHorizontally),
text = "Nothing to show"
)
}
} else {
LazyColumn() {
items(items = movieList.value) { movie ->
SearchMovieLazyColumnItem(
movieSearch = movie,
onClick = {
val action =
MovieFragmentDirections.actionMovieFragmentToDetailFragment(
movie.id, movie.title
)
navController.navigate(action)
}
)
}
}
}
}
}
composable("toWatchList"){
topBarTitle.value = "Movies To Watch"
}
composable("watched"){
topBarTitle.value = "Watched Movies"
}
}
}
sealed class Screen(val route: String, val label: String, val icon: ImageVector) {
object Search : Screen("search", "Search", Icons.Default.Search)
object ToWatchList : Screen("toWatchList", "ToWatchList", Icons.Default.WatchLater)
object Watched : Screen("watched", "Watched", Icons.Default.Archive)
}
class ImdbResponseMapper : DomainMapper<Response, MovieSearch> {
override fun mapToDomainModel(model: Response): MovieSearch {
return MovieSearch(
id = model.imdbID,
title = model.title,
type = model.type,
year = model.year,
poster = model.poster
)
}
fun toDomainList(initial: List<Response>): List<MovieSearch>{
return initial.map { mapToDomainModel(it) }
}
}
data class Response(
val imdbID: String,
@SerializedName("Poster")
val poster: String,
@SerializedName("Title")
val title: String,
@SerializedName("Type")
val type: String,
@SerializedName("Year")
val year: String
)
data class MovieSearch (
val id: String,
val poster: String,
val title: String,
val type: String,
val year: String
)
class ImdbRepositoryImpl(
private val imdbService: ImdbService,
private val mapper: ImdbResponseMapper
): ImdbRepository {
override suspend fun getMovie(query: String): List<MovieSearch> {
return mapper.toDomainList(imdbService.getMovies(search = query).search)
}
}
@HiltViewModel
class MovieViewModel @Inject constructor(
private val imdbRepository: ImdbRepository
) : ViewModel() {
val movie: MutableState<List<MovieSearch>> = mutableStateOf(listOf())
fun getMovie(search: String) {
viewModelScope.launch {
movie.value = imdbRepository.getMovie(search)
}
}
val movieDetail: MutableState<MovieDetails?> = mutableStateOf(null)
fun getMovieDetails(imdbID: String) {
viewModelScope.launch {
movieDetail.value = imdbRepository.getMovieDetails(imdbID)
}
}
val movieSearch = mutableStateOf("")
fun onTextChange(text: String) {
movieSearch.value = text
}