Reputation: 809
I try to implement MVVM by Mindorks to show data from here :(https://api.themoviedb.org/3/movie/384018?api_key=67bc513a7a353631119fdffe5f7377a8&language=en-US) in my Activity
. I try using databinding for update UI, but after the data is update, databinding
not update my textview. here my Detail Activity Class :
public class DetailActivity extends BaseActivity<ActivityDetailBinding, DetailViewModel> implements DetailNavigator {
@Inject
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() {
return detailViewModel;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
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();
}
}
}
and my BaseActivity
like this :
public abstract class BaseActivity<T extends ViewDataBinding, V extends BaseViewModel> extends AppCompatActivity
implements BaseFragment.Callback {
// TODO
// this can probably depend on isLoading variable of BaseViewModel,
// since its going to be common for all the activities
private ProgressDialog mProgressDialog;
private T mViewDataBinding;
private V mViewModel;
/**
* Override for set binding variable
*
* @return variable id
*/
public abstract int getBindingVariable();
/**
* @return layout resource id
*/
public abstract
@LayoutRes
int getLayoutId();
/**
* Override for set view model
*
* @return view model instance
*/
public abstract V getViewModel();
@Override
public void onFragmentAttached() {
}
@Override
public void onFragmentDetached(String tag) {
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
performDependencyInjection();
super.onCreate(savedInstanceState);
performDataBinding();
}
public T getViewDataBinding() {
return mViewDataBinding;
}
@TargetApi(Build.VERSION_CODES.M)
public boolean hasPermission(String permission) {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
}
public void hideKeyboard() {
View view = this.getCurrentFocus();
if (view != null) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
}
public void hideLoading() {
if (mProgressDialog != null && mProgressDialog.isShowing()) {
mProgressDialog.cancel();
}
}
public void showLoading() {
hideLoading();
mProgressDialog = CommonUtils.showLoadingDialog(this);
}
public boolean isNetworkConnected() {
return NetworkUtils.isNetworkConnected(getApplicationContext());
}
public void performDependencyInjection() {
AndroidInjection.inject(this);
}
@TargetApi(Build.VERSION_CODES.M)
public void requestPermissionsSafely(String[] permissions, int requestCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(permissions, requestCode);
}
}
// public void showLoading() {
// hideLoading();
// mProgressDialog = CommonUtils.showLoadingDialog(this);
// }
private void performDataBinding() {
mViewDataBinding = DataBindingUtil.setContentView(this, getLayoutId());
this.mViewModel = mViewModel == null ? getViewModel() : mViewModel;
mViewDataBinding.setVariable(getBindingVariable(), mViewModel);
mViewDataBinding.setLifecycleOwner(this);
mViewDataBinding.executePendingBindings();
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
this is my ViewModel
class :
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);
}, 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 {
originalName.set(detailResponse.getOriginal_title());
}
originalName.notifyChange();
if (detailResponse.getFirst_air_date() != null) {
releaseDate.set(detailResponse.getFirst_air_date());
} else {
releaseDate.set(detailResponse.getRelease_date());
}
overview.set(String.valueOf(detailResponse.getOverview()));
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);
}
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;
}
}
and this is my DetailResponse
Class :
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;
}
}
}
and last one, here how I try to get data in my UI using databinding, this is my layout :
<?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>
<variable
name="viewModel"
type="test.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"
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"
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"
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"
tools:text="@string/hint_genres" />
<TextView
android:id="@+id/text_duration"
style="@style/FontText.Normal.White"
android:layout_marginTop="@dimen/margin10dp"
android:layout_marginEnd="@dimen/margin15dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_release"
tools:text="@string/hint_duration" />
<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.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_duration" />
<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_duration"
tools:text="@string/hit_rating" />
<TextView
android:id="@+id/text_default_rating"
style="@style/FontText.Rating"
android:textStyle="normal"
app:layout_constraintRight_toLeftOf="parent"
app:layout_constraintStart_toEndOf="@+id/text_rating"
app:layout_constraintTop_toBottomOf="@+id/text_duration"
tools: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"
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>
I try to show one data first, that is OriginalName
for text_title
, went I debug
my application
public ObservableField<String> getOriginalName() {
return originalName;
}
getOriginalName
in class ViewModel, just called before I retrieve my response, but after OriginalName
is set, the UI not update and getOriginalName
is never called again.I already add originalName.notifyChange();
after the data is set, but it doesn't seem change anything.
I already frustased.. so please... I hope anybody can help me to show me what to do. Thank you very much.
Upvotes: 0
Views: 547
Reputation: 3404
Remove this below line from Detail Activity
setContentView(R.layout.activity_detail);
In your base activity you have performDataBinding method which set the layout id using DataBindingUtil class.
mViewDataBinding = DataBindingUtil.setContentView(this, getLayoutId());
this.mViewModel = mViewModel == null ? getViewModel() : mViewModel;
mViewDataBinding.setVariable(getBindingVariable(), mViewModel);
mViewDataBinding.setLifecycleOwner(this);
mViewDataBinding.executePendingBindings();
Upvotes: 1