Java Android:MVVM模式下屏幕旋转时UI和数据不存在
我尝试在我的Java Android:MVVM模式下屏幕旋转时UI和数据不存在,java,android,mvvm,data-binding,android-databinding,Java,Android,Mvvm,Data Binding,Android Databinding,我尝试在我的活动中实现从这里显示数据:()。我尝试使用数据绑定更新UI,一切都很顺利,直到我尝试旋转屏幕,我从logcat中看到数据总是重新加载,我的ImageView总是刷新,这是我的活动: public class DetailActivity extends BaseActivity<ActivityDetailBinding, DetailViewModel> implements DetailNavigator { @Inject ViewMode
活动中实现从这里显示数据:()。我尝试使用数据绑定更新UI,一切都很顺利,直到我尝试旋转屏幕,我从logcat
中看到数据总是重新加载,我的ImageView
总是刷新,这是我的活动:
public class DetailActivity extends BaseActivity<ActivityDetailBinding, DetailViewModel> implements DetailNavigator {
@Inject
ViewModelProviderFactory factory;
private DetailViewModel detailViewModel;
public static final String INTENT_ID = "id_intent";
public static final String INTENT_FLAG = "id_flag";
private ActivityDetailBinding mActivityDetailBinding;
public static Intent newIntent(Context context) {
return new Intent(context, DetailActivity.class);
}
@Override
public int getBindingVariable() {
return BR.viewModel;
}
@Override
public int getLayoutId() {
return R.layout.activity_detail;
}
@Override
public DetailViewModel getViewModel() {
detailViewModel = ViewModelProviders.of(this, factory).get(DetailViewModel.class);
return detailViewModel;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
detailViewModel.setNavigator(this);
mActivityDetailBinding = getViewDataBinding();
initView();
initData(savedInstanceState);
}
private void initData(Bundle savedInstanceState) {
Bundle extras = getIntent().getExtras();
if (extras != null) {
int id = extras.getInt(INTENT_ID, 0);
int flag = extras.getInt(INTENT_FLAG, 0);
detailViewModel.fetchDetail(id, flag);
}
}
private void initView() {
if (getSupportActionBar() != null) {
getSupportActionBar().hide();
}
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
@Override
public void ShowProgressDialog(Boolean loading) {
if (loading) {
showLoading();
} else {
hideLoading();
}
}
}
这是我的ViewModelFactory
类:
@Singleton
public class ViewModelProviderFactory extends ViewModelProvider.NewInstanceFactory {
private final DataManager dataManager;
private final SchedulerProvider schedulerProvider;
@Inject
public ViewModelProviderFactory(DataManager dataManager,
SchedulerProvider schedulerProvider) {
this.dataManager = dataManager;
this.schedulerProvider = schedulerProvider;
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
if (modelClass.isAssignableFrom(DetailViewModel.class)) {
return (T) new DetailViewModel(dataManager,schedulerProvider);
}
throw new IllegalArgumentException("Unknown class name");
}
}
public class DetailViewModel extends BaseViewModel<DetailNavigator> {
private final ObservableField<String> originalName = new ObservableField<>();
private final ObservableField<String> releaseDate = new ObservableField<>();
private final ObservableField<String> overview = new ObservableField<>();
private final ObservableField<String> genreMovie = new ObservableField<>();
private final ObservableField<String> posterPath = new ObservableField<>();
private final ObservableField<String> voteAverage = new ObservableField<>();
public DetailViewModel(DataManager dataManager, SchedulerProvider schedulerProvider) {
super(dataManager, schedulerProvider);
}
public void fetchDetail(int id, int flag) {
if (flag == 1) {
getNavigator().ShowProgressDialog(true);
getCompositeDisposable().add(getDataManager()
.getApiHelper().doDetailMovie(id, URLConfig.API_KEY, getDataManager().getLanguage())
.subscribeOn(getSchedulerProvider().io())
.observeOn(getSchedulerProvider().ui())
.subscribe(detailResponse -> {
setUpData(detailResponse);
getNavigator().ShowProgressDialog(false);
// getNavigator().updateView();
}, throwable -> {
getNavigator().ShowProgressDialog(false);
}));
} else if (flag == 2) {
getNavigator().ShowProgressDialog(true);
getCompositeDisposable().add(getDataManager()
.getApiHelper().doDetailTV(id, URLConfig.API_KEY, getDataManager().getLanguage())
.subscribeOn(getSchedulerProvider().io())
.observeOn(getSchedulerProvider().ui())
.subscribe(detailResponse -> {
setUpData(detailResponse);
getNavigator().ShowProgressDialog(false);
}, throwable -> {
getNavigator().ShowProgressDialog(false);
}));
}
}
private void setUpData(DetailResponse detailResponse) {
if (detailResponse.getOriginal_name() != null) {
originalName.set(detailResponse.getOriginal_name());
} else if (detailResponse.getOriginal_title() != null) {
originalName.set(detailResponse.getOriginal_title());
} else {
}
if (detailResponse.getFirst_air_date() != null) {
releaseDate.set(detailResponse.getFirst_air_date());
} else {
releaseDate.set(detailResponse.getRelease_date());
}
if (!detailResponse.getOverview().equals("")) {
overview.set(detailResponse.getOverview());
} else {
overview.set(getNavigator().noDesc());
}
posterPath.set(String.valueOf(detailResponse.getPoster_path()));
voteAverage.set(String.valueOf(detailResponse.getVote_average()));
String genres = "";
for (int i = 0; i < detailResponse.getGenreList().size(); i++) {
genres = genres + detailResponse.getGenreList().get(i).getName();
if (i != detailResponse.getGenreList().size() - 1) {
genres = genres + ", ";
}
}
genreMovie.set(genres);
}
public ObservableField<String> getOriginalName() {
return originalName;
}
public ObservableField<String> getReleaseDate() {
return releaseDate;
}
public ObservableField<String> getOverview() {
return overview;
}
public ObservableField<String> getGenreMovie() {
return genreMovie;
}
public ObservableField<String> getPosterPath() {
return posterPath;
}
public ObservableField<String> getVoteAverage() {
return voteAverage;
}
}
这是我的DetailResponse
课程:
public class DetailResponse {
@SerializedName("original_name")
private String original_name ;
@SerializedName("original_title")
private String original_title ;
@SerializedName("release_date")
private String release_date ;
@SerializedName("first_air_date")
private String first_air_date ;
@SerializedName("vote_average")
private Double vote_average ;
@SerializedName("overview")
private String overview ;
@SerializedName("poster_path")
private String poster_path;
@SerializedName("genres")
private List<Genre> genreList;
public String getOriginal_name() {
return original_name;
}
public String getOriginal_title() {
return original_title;
}
public String getRelease_date() {
return release_date;
}
public String getFirst_air_date() {
return first_air_date;
}
public Double getVote_average() {
return vote_average;
}
public String getOverview() {
return overview;
}
public String getPoster_path() {
return poster_path;
}
public List<Genre> getGenreList() {
return genreList;
}
public static class Genre{
@SerializedName("name")
private String name ;
public String getName() {
return name;
}
}
}
公共类详细信息响应{
@SerializedName(“原始名称”)
私有字符串原始名称;
@序列化名称(“原始标题”)
私有字符串原始标题;
@序列化名称(“发布日期”)
私有字符串发布日期;
@序列化名称(“首次发布日期”)
私有字符串首个\u air\u日期;
@序列化名称(“投票平均值”)
私人双票平均;
@序列化名称(“概述”)
私有字符串概述;
@序列化名称(“海报路径”)
私家车路线;;
@序列化名称(“流派”)
私有列表genreList;
公共字符串getOriginal_name(){
返回原始名称;
}
公共字符串getOriginal_title(){
返回原始标题;
}
公共字符串getRelease_date(){
返回发布日期;
}
公共字符串getFirst\u air\u date(){
返回第一个空气日期;
}
公众双倍投票(平均值){
返回平均值;
}
公共字符串getOverview(){
退货概述;
}
公共字符串getPoster_path(){
回程;;
}
公共列表getGenreList(){
返回genreList;
}
公共静态类体裁{
@序列化名称(“名称”)
私有字符串名称;
公共字符串getName(){
返回名称;
}
}
}
最后一个,我是如何尝试使用数据绑定在UI中获取数据的,这是我的布局:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".ui.detail.DetailActivity">
<data>
<import type="android.view.View" />
<variable
name="viewModel"
type="id.dicoding.eriza.moviecatalogue.ui.detail.DetailViewModel" />
</data>
<androidx.core.widget.NestedScrollView
android:id="@+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:animateLayoutChanges="true">
<com.github.florent37.shapeofview.shapes.ArcView
android:id="@+id/shape_header"
android:layout_width="match_parent"
android:layout_height="@dimen/size300dp"
android:alpha="0.7"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:shape_arc_cropDirection="outside"
app:shape_arc_height="@dimen/size30dp"
app:shape_arc_position="bottom">
<com.flaviofaria.kenburnsview.KenBurnsView
android:id="@+id/image_header"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/poster_avengerinfinity"
app:imageDetailUrl="@{viewModel.posterPath}"
android:tint="#6F000000" />
</com.github.florent37.shapeofview.shapes.ArcView>
<com.github.florent37.shapeofview.shapes.RoundRectView
android:id="@+id/shape_poster"
android:layout_width="@dimen/size150dp"
android:layout_height="@dimen/size200dp"
android:layout_marginTop="@dimen/margin250dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/shape_header"
app:shape_roundRect_bottomLeftRadius="@dimen/corner10dp"
app:shape_roundRect_bottomRightRadius="@dimen/corner10dp"
app:shape_roundRect_topLeftRadius="@dimen/corner10dp"
app:shape_roundRect_topRightRadius="@dimen/corner10dp">
<ImageView
android:id="@+id/image_poster"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/hint_poster"
android:scaleType="fitXY"
app:imageDetailUrl="@{viewModel.posterPath}"
android:src="@drawable/poster_avengerinfinity" />
</com.github.florent37.shapeofview.shapes.RoundRectView>
<TextView
android:id="@+id/text_title"
style="@style/FontText.Title.Detail"
android:layout_marginTop="@dimen/margin15dp"
android:textSize="@dimen/font_large_size"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/shape_poster"
android:text="@{viewModel.originalName}"
tools:text="@string/hint_title" />
<TextView
android:id="@+id/text_title_release"
style="@style/FontText"
android:layout_marginTop="@dimen/margin10dp"
android:text="@string/text_release"
app:layout_constraintEnd_toStartOf="@+id/guideline"
android:visibility="@{viewModel.isConnected ? View.VISIBLE : View.GONE}"
app:layout_constraintTop_toBottomOf="@+id/text_title" />
<TextView
android:id="@+id/text_release"
style="@style/FontText"
android:layout_marginStart="@dimen/margin2dp"
android:layout_marginTop="@dimen/margin10dp"
android:text="@{viewModel.releaseDate}"
android:visibility="@{viewModel.isConnected ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toEndOf="@+id/text_title_release"
app:layout_constraintTop_toBottomOf="@+id/text_title"
tools:text="@string/hint_release" />
<TextView
android:id="@+id/text_genres"
style="@style/FontText.Normal"
android:layout_marginStart="@dimen/margin15dp"
android:layout_marginTop="@dimen/margin10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_release"
android:text="@{viewModel.genreMovie}"
tools:text="@string/hint_genres" />
<RatingBar
android:id="@+id/rating_bar"
android:layout_width="wrap_content"
android:layout_height="45dp"
android:layout_marginStart="@dimen/margin15dp"
android:layout_marginTop="@dimen/margin5dp"
android:isIndicator="true"
android:numStars="5"
android:rating="3.8"
android:stepSize="0.1"
android:visibility="@{viewModel.isConnected ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toStartOf="parent"
app:ratingBar="@{viewModel.voteAverage}"
app:layout_constraintTop_toBottomOf="@+id/text_genres" />
<TextView
android:id="@+id/text_rating"
style="@style/FontText.Rating.Orange"
android:layout_marginStart="@dimen/margin5dp"
app:layout_constraintRight_toLeftOf="parent"
app:layout_constraintStart_toEndOf="@+id/rating_bar"
app:layout_constraintTop_toBottomOf="@+id/text_genres"
android:text="@{viewModel.voteAverage}"
tools:text="@string/hit_rating" />
<TextView
android:id="@+id/text_default_rating"
style="@style/FontText.Rating"
android:textStyle="normal"
android:visibility="@{viewModel.isConnected ? View.VISIBLE : View.GONE}"
app:layout_constraintRight_toLeftOf="parent"
app:layout_constraintStart_toEndOf="@+id/text_rating"
app:layout_constraintTop_toBottomOf="@+id/text_genres"
android:text="@string/hint_default_rating" />
<TextView
android:id="@+id/text_desc"
style="@style/FontText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin15dp"
android:layout_marginTop="@dimen/margin25dp"
android:layout_marginEnd="@dimen/margin15dp"
android:paddingBottom="@dimen/padding100dp"
app:layout_constraintTop_toBottomOf="@+id/rating_bar"
android:text="@{viewModel.overview}"
tools:text="@string/hint_desc" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</layout>
我认为ViewModel
应该管理活动生命周期中相关数据的UI,tt允许在应用程序中保留配置更改,但在我的情况下,data
是保留的,但是当我检查logcat
时,fetchDetail()DetailViewModel
类中的在屏幕旋转时总是被调用,而我的ImageView
在我旋转屏幕时也会刷新,所以我认为viewModel
在屏幕旋转时不会管理我的UI。我的代码有问题吗
希望任何人都能帮助我,告诉我哪里是我的错,我必须做什么。非常感谢。打开应用程序。之后进入飞行模式。然后旋转屏幕。检查viewmodel是否在配置更改后仍然有效。viewmodel
在飞机模式下的配置更改时仍然有效,并且ImageView
是alos survive,但是我在没有连接时设置的视图
的一些可见性:消失了,并且只有当活动
第一次创建说没有连接
时才会显示一条错误消息。我不明白的是为什么方法FetchDetail()
当我旋转屏幕时,是否总是调用?这使得我的ViewModel
无法保存已经存在的UIcreated@user1506104因此ViewModel
无法避免调用onCreate()
的Activity
?
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".ui.detail.DetailActivity">
<data>
<import type="android.view.View" />
<variable
name="viewModel"
type="id.dicoding.eriza.moviecatalogue.ui.detail.DetailViewModel" />
</data>
<androidx.core.widget.NestedScrollView
android:id="@+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:animateLayoutChanges="true">
<com.github.florent37.shapeofview.shapes.ArcView
android:id="@+id/shape_header"
android:layout_width="match_parent"
android:layout_height="@dimen/size300dp"
android:alpha="0.7"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:shape_arc_cropDirection="outside"
app:shape_arc_height="@dimen/size30dp"
app:shape_arc_position="bottom">
<com.flaviofaria.kenburnsview.KenBurnsView
android:id="@+id/image_header"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/poster_avengerinfinity"
app:imageDetailUrl="@{viewModel.posterPath}"
android:tint="#6F000000" />
</com.github.florent37.shapeofview.shapes.ArcView>
<com.github.florent37.shapeofview.shapes.RoundRectView
android:id="@+id/shape_poster"
android:layout_width="@dimen/size150dp"
android:layout_height="@dimen/size200dp"
android:layout_marginTop="@dimen/margin250dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/shape_header"
app:shape_roundRect_bottomLeftRadius="@dimen/corner10dp"
app:shape_roundRect_bottomRightRadius="@dimen/corner10dp"
app:shape_roundRect_topLeftRadius="@dimen/corner10dp"
app:shape_roundRect_topRightRadius="@dimen/corner10dp">
<ImageView
android:id="@+id/image_poster"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/hint_poster"
android:scaleType="fitXY"
app:imageDetailUrl="@{viewModel.posterPath}"
android:src="@drawable/poster_avengerinfinity" />
</com.github.florent37.shapeofview.shapes.RoundRectView>
<TextView
android:id="@+id/text_title"
style="@style/FontText.Title.Detail"
android:layout_marginTop="@dimen/margin15dp"
android:textSize="@dimen/font_large_size"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/shape_poster"
android:text="@{viewModel.originalName}"
tools:text="@string/hint_title" />
<TextView
android:id="@+id/text_title_release"
style="@style/FontText"
android:layout_marginTop="@dimen/margin10dp"
android:text="@string/text_release"
app:layout_constraintEnd_toStartOf="@+id/guideline"
android:visibility="@{viewModel.isConnected ? View.VISIBLE : View.GONE}"
app:layout_constraintTop_toBottomOf="@+id/text_title" />
<TextView
android:id="@+id/text_release"
style="@style/FontText"
android:layout_marginStart="@dimen/margin2dp"
android:layout_marginTop="@dimen/margin10dp"
android:text="@{viewModel.releaseDate}"
android:visibility="@{viewModel.isConnected ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toEndOf="@+id/text_title_release"
app:layout_constraintTop_toBottomOf="@+id/text_title"
tools:text="@string/hint_release" />
<TextView
android:id="@+id/text_genres"
style="@style/FontText.Normal"
android:layout_marginStart="@dimen/margin15dp"
android:layout_marginTop="@dimen/margin10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_release"
android:text="@{viewModel.genreMovie}"
tools:text="@string/hint_genres" />
<RatingBar
android:id="@+id/rating_bar"
android:layout_width="wrap_content"
android:layout_height="45dp"
android:layout_marginStart="@dimen/margin15dp"
android:layout_marginTop="@dimen/margin5dp"
android:isIndicator="true"
android:numStars="5"
android:rating="3.8"
android:stepSize="0.1"
android:visibility="@{viewModel.isConnected ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toStartOf="parent"
app:ratingBar="@{viewModel.voteAverage}"
app:layout_constraintTop_toBottomOf="@+id/text_genres" />
<TextView
android:id="@+id/text_rating"
style="@style/FontText.Rating.Orange"
android:layout_marginStart="@dimen/margin5dp"
app:layout_constraintRight_toLeftOf="parent"
app:layout_constraintStart_toEndOf="@+id/rating_bar"
app:layout_constraintTop_toBottomOf="@+id/text_genres"
android:text="@{viewModel.voteAverage}"
tools:text="@string/hit_rating" />
<TextView
android:id="@+id/text_default_rating"
style="@style/FontText.Rating"
android:textStyle="normal"
android:visibility="@{viewModel.isConnected ? View.VISIBLE : View.GONE}"
app:layout_constraintRight_toLeftOf="parent"
app:layout_constraintStart_toEndOf="@+id/text_rating"
app:layout_constraintTop_toBottomOf="@+id/text_genres"
android:text="@string/hint_default_rating" />
<TextView
android:id="@+id/text_desc"
style="@style/FontText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin15dp"
android:layout_marginTop="@dimen/margin25dp"
android:layout_marginEnd="@dimen/margin15dp"
android:paddingBottom="@dimen/padding100dp"
app:layout_constraintTop_toBottomOf="@+id/rating_bar"
android:text="@{viewModel.overview}"
tools:text="@string/hint_desc" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</layout>