I am following the clean architecture with mvvm. When I injcet viewmodel factory in fragment and try to execute use case from fragment viewmodel it is giving NPE on use case. I have injected the use case as well.
Please look into the code and point out the mistake
MainModule.java
#Module
public class MainModule {
#Provides
LoginViewModel provideLoginViewModel() {
return new LoginViewModel();
}
#Provides
#Named("LoginActivity")
ViewModelProvider.Factory provideActivityViewModelFactory(LoginViewModel loginViewModel) {
return new ViewModelProviderFactory<>(loginViewModel);
}
#Provides
PasscodeViewModel providePasscodeViewModel(SetPasscode setPasscode) {
return new PasscodeViewModel(setPasscode);
}
#Provides
#Named("PasscodeFragment")
ViewModelProvider.Factory provideFragmentViewModelFactory(PasscodeViewModel passcodeViewModel) {
return new ViewModelProviderFactory<>(passcodeViewModel);
}
}
PasscodeFragment.java
public class PasscodeFragment extends BaseFragment<PasscodeViewModel> implements PasscodeNavigator, View.OnClickListener {
#Inject
#Named("PasscodeFragment")
ViewModelProvider.Factory viewModelFactory;
PasscodeViewModel passcodeViewModel;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
passcodeViewModel.setNavigator(this);
}
public PasscodeFragment() {
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
((MainApplication) getActivity().getApplicationContext()).getComponent().inject(this);
View view = inflater.inflate(R.layout.fragment_passcode, container, false);
initViews(view);
return view;
}
#Override
public PasscodeViewModel getViewModel() {
passcodeViewModel = ViewModelProviders.of(this, viewModelFactory).get(PasscodeViewModel.class);
return passcodeViewModel;
}
PasscodeViewModel.java (HERE THE SETPASSCODE IS COMING NULL)
public class PasscodeViewModel extends BaseViewModel<PasscodeNavigator> {
//use case
private SetPasscode setPasscode;
//data
private String tempPin;
private boolean isConfirming = false;
public PasscodeViewModel() {
}
public PasscodeViewModel(SetPasscode setPasscode) {
this.setPasscode = setPasscode;
}
public void checkForConfirmation(String passcode) {
try {
if (passcode.length() == 4) {
if (isConfirming) {
//second attempt
if (tempPin.equals(passcode)) {
//done
Log.e("ganesh", passcode);
setPasscode(passcode);
} else {
//reset
isConfirming = false;
tempPin = "";
getNavigator().onPinNotMatched();
getNavigator().clearViewsForFirstAttempt();
}
} else {
//first attempt
tempPin = passcode;
isConfirming = true;
getNavigator().clearViewsForSecondAttempt();
}
}
} catch (NumberFormatException e) {
getNavigator().wrongPinFormat();
getNavigator().clearViewsForFirstAttempt();
}
}
private void setPasscode(String passcode) {
//setPasscode IS NULL HERE
setPasscode.execute(new DisposableSingleObserver<Boolean>() {
#Override
public void onSuccess(Boolean aBoolean) {
if (aBoolean)
getNavigator().onPinSetSuccess();
else
getNavigator().onPinSetFailed();
}
#Override
public void onError(Throwable e) {
getNavigator().onPinSetFailed();
}
}, SetPasscode.Params.setPasscode(passcode));
}
#Override
protected void onCleared() {
if (setPasscode != null)
setPasscode.dispose();
}
}
SetPasscode.java
public class SetPasscode extends SingleUseCase<Boolean, SetPasscode.Params> {
private UserRepository repository;
public SetPasscode(PostExecutionThread postExecutionThread, UserRepository repository) {
super(postExecutionThread);
this.repository = repository;
}
#Override
public Single<Boolean> buildUseCaseObservable(SetPasscode.Params params) {
return repository.setPasscode(params.passcode);
}
public static final class Params {
private String passcode;
private Params(String passcode) {
this.passcode = passcode;
}
public static SetPasscode.Params setPasscode(String passcode) {
return new SetPasscode.Params(passcode);
}
}
}
LoginActivity.java
public class LoginActivity extends BaseActivity<LoginViewModel> implements LoginNavigator {
#Inject
#Named("LoginActivity")
ViewModelProvider.Factory viewModelFactory;
LoginViewModel loginViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
getSupportActionBar().hide();
((MainApplication) getApplicationContext()).getComponent().inject(this);
loginViewModel = ViewModelProviders.of(this, viewModelFactory).get(LoginViewModel.class);
loginViewModel.setNavigator(this);
initViews();
}
#Override
public LoginViewModel getViewModel() {
return loginViewModel;
}
Related
Following android's architecture guide, I am experimenting with SavedStateHandle in my view model. So when I post any changes from a thread to the view model, it doesn't reflect on UI. However, without SavedStateHandle the UI changes reflect just fine.
public class UserViewModel extends ViewModel {
UserRepository userRepository;
private static final String USER_KEY = "user_key";
private SavedStateHandle mState;
public UserViewModel(SavedStateHandle savedStateHandle) {
mState = savedStateHandle;
this.userRepository = new UserRepository();
}
public LiveData<String> getUserId() {
return mState.getLiveData(USER_KEY, userRepository.getUserId());
}
public void setUserId(String value) {
mState.set(USER_KEY, value);
}
}
public class UserFragment extends Fragment {
private UserViewModel mViewModel;
public static UserFragment newInstance() {
return new UserFragment();
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.user_fragment, container, false);
}
#Override
public void onViewCreated(#NonNull #NotNull View view, #Nullable #org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewModel = new ViewModelProvider(requireActivity()).get(UserViewModel.class);
mViewModel.getUserId().observe(getViewLifecycleOwner(), s -> {
((TextView) view.findViewById(R.id.text_view_1)).setText(s);
});
Executors.newCachedThreadPool().submit(() -> {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
mViewModel.setUserId("111111");
});
}
public UserViewModel getmViewModel() {
return mViewModel;
}
}
Main Activity
my view only has one framelayout with id sample_fragment
public class MainActivity extends AppCompatActivity {
private final ExecutorService executorService = Executors.newCachedThreadPool();
private UserViewModel userViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
if (savedInstanceState == null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
UserFragment userFragment = new UserFragment();
transaction.replace(R.id.sample_fragment, userFragment);
transaction.commit();
executorService.submit(() -> {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
userViewModel.setUserId("1");
});
}
}
}
with this code, the changes don't reflect on UI. however when I replace the SaveStateHandle with MutableLiveData then a simple postValue changes the screen just fine. From what I have read, it seems the live data is changed only when the lifecycle is in some specific states (like CREATED).
You set a default value in your getUserId, but I don't see where you use postValue on the data in SavedStateHandle.
I would add a mUserId member and move the code in getUserId to the ViewModel constructor to make sure it's initialized before any write to it. Then make set and post methods:
LiveData<String> mUserId;
public UserViewModel(SavedStateHandle savedStateHandle) {
mState = savedStateHandle;
this.userRepository = new UserRepository();
mUserId = mState.getLiveData(USER_KEY, userRepository.getUserId());
}
public LiveData<String> getUserId() {
return mUserId;
}
public void setUserId(String value) {
mState.getLiveData(USER_KEY).setValue(value);
}
public void postUserId(String value) {
mState.getLiveData(USER_KEY).postValue(value);
}
I am learning Room DataBase!!
I Know How to Insert and Retrieve Data from Room DataBase to Recycler View!! But In Delete Operation I am getting error of "No Adapter attached Skipped Layout!"
What I want when anyone click on delete button on recycler view . The Task should be deleted
That's why I also Used delete method in Interface and add Interface in Recycler View Adapter which give call back to MainActivity so that we delete and update the recycler view
All of my codes are given below
Here is my Entity Class named as Task
#Entity
public class Task implements Serializable {
#PrimaryKey(autoGenerate = true)
private int id;
#ColumnInfo(name = "task_name")
private String task_name;
#ColumnInfo
private String task_desc;
#ColumnInfo
private String comment;
#ColumnInfo
private String task_comp_date;
#ColumnInfo
private String activate;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTask_name() {
return task_name;
}
public void setTask_name(String task_name) {
this.task_name = task_name;
}
public String getTask_desc() {
return task_desc;
}
public void setTask_desc(String task_desc) {
this.task_desc = task_desc;
}
public String getTask_comp_date() {
return task_comp_date;
}
public void setTask_comp_date(String task_comp_date) {
this.task_comp_date = task_comp_date;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getActivate() {
return activate;
}
public void setActivate(String activate) {
this.activate = activate;
}
}
My Data Accession Object named as TaskDao
#Dao
public interface TaskDao {
#Query("SELECT * FROM task")
List<Task> getAll();
#Insert
void insert(Task task);
#Delete
void delete(Task task);
#Update
void update(Task task);
}
My DataBase
#Database(entities = {Task.class},version = 1)
public abstract class AppDataBase extends RoomDatabase {
public abstract TaskDao taskDao();
}
My DataBaseClient named as DatabaseClient
public class DatabaseClient {
private Context context;
private static DatabaseClient mInstace;
private AppDataBase appDataBase;
public DatabaseClient(Context context) {
this.context = context;
appDataBase = Room.databaseBuilder(context,AppDataBase.class,"MyDailyTask").build();
}
public static synchronized DatabaseClient getInstance(Context context)
{
if(mInstace == null)
{
mInstace = new DatabaseClient(context);
}
return mInstace;
}
public AppDataBase getAppDataBase()
{
return appDataBase;
}
}
My RecyclerView Adapter
public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.MyViewHolder> {
private Context context;
private List<Task> taskList;
public interface OnDeleteClickListener
{
void OnDeleteClickListener(Task task);
}
private OnDeleteClickListener onDeleteClickListener;
public TaskAdapter(Context context, List<Task> taskList) {
this.context = context;
this.taskList = taskList;
}
public void setOnDeleteClickListener(OnDeleteClickListener onDeleteClickListener) {
this.onDeleteClickListener = onDeleteClickListener;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view;
view = LayoutInflater.from(context).inflate(R.layout.view_task_list,parent,false);
MyViewHolder myViewHolder = new MyViewHolder(view);
return myViewHolder;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
holder.setData(taskList.get(position).getTask_name(),taskList.get(position).getTask_desc(),taskList.get(position).getComment(),taskList.get(position).getTask_comp_date(),position);
}
#Override
public int getItemCount() {
return taskList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
private TextView t1,t2,t3,t4,t5;
private ImageView view;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
t1= itemView.findViewById(R.id.tvCommnt);
t2=itemView.findViewById(R.id.tvDesc);
t3=itemView.findViewById(R.id.tvName);
t4= itemView.findViewById(R.id.tvStart);
t5 = itemView.findViewById(R.id.tvEnd);
view =itemView.findViewById(R.id.tvdele);
}
public void setData(String t01, String t02, String t03, String t05, final int position)
{
t1.setText("Task Comment "+t03);
t2.setText("Task Description "+t02);
t3.setText("Task Name "+ t01);
t4.setText("Start ");
t5.setText("Ënd "+t05);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onDeleteClickListener!=null)
{
onDeleteClickListener.OnDeleteClickListener(taskList.get(position));
taskList.remove(position);
notifyDataSetChanged();
}
}
});
}
}
}
MainActivity.java :
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
ImageView imageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.ivAdd);
recyclerView = findViewById(R.id.rvTask);
imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getApplicationContext(), AddTaskActivity.class));
}
});
new newTask().execute();
}
class newTask extends AsyncTask<Void,Void, List<Task>> implements TaskAdapter.OnDeleteClickListener {
List<Task> tasks;
#Override
protected List<Task> doInBackground(Void... voids) {
tasks = DatabaseClient.getInstance(getApplicationContext()).getAppDataBase().taskDao().getAll();
return tasks;
}
#Override
protected void onPostExecute(List<Task> tasks) {
super.onPostExecute(tasks);
TaskAdapter taskAdapter = new TaskAdapter(MainActivity.this,tasks);
LinearLayoutManager layoutManager = new LinearLayoutManager(MainActivity.this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new DividerItemDecoration(getApplicationContext(),DividerItemDecoration.VERTICAL));
recyclerView.setAdapter(taskAdapter);
taskAdapter.setOnDeleteClickListener(this);
}
#Override
public void OnDeleteClickListener(final Task task) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
DatabaseClient.getInstance(getApplicationContext()).getAppDataBase().taskDao().delete(task);
}
},1000);
}
}
welcome in stack
you interface is declared but not assign so your will throw null pointer when click on item inside your list, but good work your check null before fire method interface
first add setter for OnDeleteClickListener inside your adapter
public void setOnDeleteClickListener(OnDeleteClickListener listener){
this.onDeleteClickListener=listener;
}
and also add code that remove item from you list when user click item
so inside setData method update this code
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (onDeleteClickListener != null) {
onDeleteClickListener.OnDeleteClickListener(taskList.get(position));
//remove item from list and then notify adapter data is changed
taskList.remove(position);
notifyDataSetChanged();
}
}
});
finally to Triggers onDeleteClickListener inside your AsyncTask
update your code here
#Override
protected void onPostExecute(List < Task > tasks) {
super.onPostExecute(tasks);
TaskAdapter taskAdapter = new TaskAdapter(MainActivity.this, tasks, this);
LinearLayoutManager layoutManager = new LinearLayoutManager(MainActivity.this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new DividerItemDecoration(getApplicationContext(), DividerItemDecoration.VERTICAL));
recyclerView.setAdapter(taskAdapter);
//pass this that refer to my interface
taskAdapter.setOnDeleteClickListener(this);
}
inside your OnDelete just use this code to run in another thread
AsyncTask.execute(new Runnable() {
#Override
public void run() {
DatabaseClient.getInstance(getApplicationContext()).getAppDataBase().taskDao().delete(task);
}
});
Advice : Don't name your variable or arguments like String
t01,String t02..etc ,choice name for like what this variable jop
like String taskComment,String taskName ..etc
i hope this help you
I am sending Request on fragment to receive Store Item data from Server which contains urls of images like below
public class StoreFragment extends BaseFragment<FragmentStoreBinding, StoreViewModel> implements StoreNavigator {
#Inject
StoreViewModel viewModel;
#Inject
StoreAdapter storeAdapter;
#Inject
StoreVO storeVO;
public static final String TAG = StoreFragment.class.getSimpleName();
private FragmentStoreBinding binding;
public static StoreFragment newInstance() {
Bundle args = new Bundle();
StoreFragment fragment = new StoreFragment();
fragment.setArguments(args);
return fragment;
}
public static StoreFragment newInstance(StoreVO storeVO) {
Bundle args = new Bundle();
StoreFragment fragment = new StoreFragment();
args.putString(INTENT_EXTRA_STOREVO, new Gson().toJson(storeVO)); //put string, int, etc in bundle with a key value
fragment.setArguments(args);
return fragment;
}
#Override
public int getBindingVariable() {
return BR.viewModel;
}
#Override
public int getLayoutId() {
return R.layout.fragment_store;
}
#Override
public StoreViewModel getViewModel() {
return viewModel;
}
#Override
public void goBack() {
getBaseActivity().onFragmentDetached(TAG);
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel.setNavigator(this);
storeAdapter.setStoreFragment(this);
}
#Override
public void onStart() {
super.onStart();
}
#Override
public void onResume(){
super.onResume();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding = getViewDataBinding();
if (getArguments().containsKey(INTENT_EXTRA_STOREVO)) {
storeVO = new Gson().fromJson(getArguments().getString(INTENT_EXTRA_STOREVO), StoreVO.class);
}
initView();
}
#Override
public void onPrefClicked(String prefSn, boolean sendRequest) {
storeVO.setPrefSn(prefSn);
storeVO.setSearchKeyCode1(prefSn);
switch (prefSn) {
case EAT_PREF_SN:
binding.eatCb.setChecked(true);
break;
case DRINK_PREF_SN:
binding.drinkCb.setChecked(true);
break;
case PLAY_PREF_SN:
binding.playCb.setChecked(true);
break;
case WATCH_PREF_SN:
binding.watchCb.setChecked(true);
break;
case WALK_PREF_SN:
binding.walkCb.setChecked(true);
break;
default:
binding.allCb.setChecked(true);
}
if(sendRequest){
//initStoreView();
viewModel.requestStoreList(storeVO);
}
}
private void initView() {
binding.storeRv.setLayoutManager(new GridLayoutManager(getActivity().getApplicationContext(), 2));
binding.storeRv.setItemAnimator(new DefaultItemAnimator());
binding.storeRv.setAdapter(storeAdapter);
initStoreView();
onPrefClicked(storeVO.getPrefSn(), false);
}
private void initStoreView(){
viewModel.requestStoreList(storeVO);
subscribeStoreListLiveData();
}
private void subscribeStoreListLiveData() {
viewModel.getStoreListLiveData().observe(this, storeList -> {
viewModel.addStoreList(storeList);
if (storeList != null) {
binding.totalCount.setText(String.valueOf(storeList.size()));
}
});
}
}
view model for this fragment is like below
public class StoreViewModel extends BaseViewModel<StoreNavigator> {
public final ObservableList<StoreVO> storeObservableList = new ObservableArrayList<>();
// MutableLiveData는 LifeCycle에 의존하여 메모리관리 등 여러 장점을 지님
private final MutableLiveData<List<StoreVO>> storeListLiveData;
public StoreViewModel(BaseApplication baseApplication) {
super(baseApplication);
storeListLiveData = new MutableLiveData<>();
}
public void requestStoreList(StoreVO storeVO) {
getCompositeDisposable().add(
getRetrofitService().selectStoreInfoList(storeVO)
.subscribeOn(getNetworkHelper().getSchedulerIo())
.observeOn(getNetworkHelper().getSchedulerUi())
.subscribe(storeListLiveData::setValue, throwable -> Dlog.e(throwable.getMessage())));
}
public MutableLiveData<List<StoreVO>> getStoreListLiveData() {
return storeListLiveData;
}
public void addStoreList(List<StoreVO> storeList) {
storeObservableList.clear();
storeObservableList.addAll(storeList);
}
public ObservableList<StoreVO> getStoreObservableList() {
return storeObservableList;
}
public void onFilterClick() {
getNavigator().goToSearchFilterActivity();
}
public void onPrefTypeClicked(String prefSn) {
getNavigator().onPrefClicked(prefSn, true);
}
}
and storeList was attached by adapter like below
public class StoreAdapter extends RecyclerView.Adapter<StoreAdapter.StoreViewHolder> {
private List<StoreVO> storeList;
private StoreFragment storeFragment;
public StoreAdapter() {
this.storeList = new ArrayList<>();
}
#NonNull
#Override
public StoreViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
StoreItemListBinding binding = StoreItemListBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new StoreViewHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull StoreViewHolder holder, int index) {
final int position = index;
holder.bind(storeList.get(index));
holder.itemView.setOnClickListener(v ->
storeFragment.goToPlaceDetailActivity(storeList.get(position)));
}
#Override
public void onViewDetachedFromWindow(#NonNull StoreViewHolder holder) {
holder.viewDetached();
super.onViewDetachedFromWindow(holder);
}
#Override
public int getItemCount() {
return storeList.size();
}
// Override for duplicated bugs
#Override
public long getItemId(int position) {
return super.getItemId(position);
}
// Override for duplicated bugs
#Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
public List<StoreVO> getStoreList() {
return storeList;
}
public void setStoreList(List<StoreVO> storeList) {
this.storeList = storeList;
}
public void clearItems() {
storeList.clear();
}
public void addItems(List<StoreVO> storeList) {
this.storeList.addAll(storeList);
notifyDataSetChanged();
}
public StoreFragment getStoreFragment() {
return storeFragment;
}
public void setStoreFragment(StoreFragment storeFragment) {
this.storeFragment = storeFragment;
}
class StoreViewHolder extends RecyclerView.ViewHolder {
private StoreItemListBinding binding;
private ArrayList<String> urlList;
StoreViewHolder(StoreItemListBinding binding) {
super(binding.getRoot());
this.urlList = new ArrayList<>();
this.binding = binding;
initImageSlider();
}
public void bind(StoreVO storeVO) {
binding.priceTv.setText(String.valueOf(storeVO.getReprMenuPrice()));
binding.addrTv.setText(storeVO.getAreaDetailNm());
binding.nameTv.setSelected(true);
binding.nameTv.setText(storeVO.getStoreNm());
binding.scoreTv.setText(String.format(Locale.KOREA, "%.1f", storeVO.getStoreScore()));
addImageSlider(storeVO);
}
private void initImageSlider() {
binding.storeSlider.setIndicatorAnimation(IndicatorAnimations.FILL); //set indicator animation by using SliderLayout.Animations. :WORM or THIN_WORM or COLOR or DROP or FILL or NONE or SCALE or SCALE_DOWN or SLIDE and SWAP!!
binding.storeSlider.setScrollTimeInSec(3); //set scroll delay in seconds :
}
private void addImageSlider(StoreVO storeVO) {
generateUrlList(storeVO);
setSliderViews();
}
private void generateUrlList(StoreVO storeVO) {
urlList.clear();
if (storeVO.getStoreImageUrlList().size() == 0) {
String url = CommonUtils.createDefaultImageUrl();
urlList.add(url);
} else {
for (String imageUrl : storeVO.getStoreImageUrlList()) {
String url = CommonUtils.createStoreImageUrl(storeVO.getStoreSn(), imageUrl);
urlList.add(url);
}
}
}
private void setSliderViews() {
binding.storeSlider.clearSliderViews();
for (String url : urlList) {
DefaultSliderView sliderView = new DefaultSliderView(binding.getRoot().getContext().getApplicationContext());
sliderView.setImageUrl(url);
sliderView.setImageScaleType(ImageView.ScaleType.CENTER_CROP);
//sliderView.setOnSliderClickListener(view -> Dlog.e(url));
//at last add this view in your layout :
binding.storeSlider.addSliderView(sliderView);
}
}
private void viewDetached() {
urlList.clear();
binding.storeSlider.clearSliderViews();
}
}
}
After storeList updated, Text on items of Recylerview was changed but ImageSlider on item was not changed.
I checked that viewDetached() on StoreViewHolder was called but images remaind on previous store item
i was using image sliders in github: https://github.com/smarteist/android-image-slider
what should i do to updated images for imagesliders on recycler-view items???
To simplify my question,
i was using RecyclerView to show items which contains image slider. That image slider load images through URL. After receive new itemList(storeList) from server, itemList(storeList) for RecylerView and URLs in Item(Store) for ImageSlider was changed but not updated on ImageSlider.
Through debugging, i checked that new UrlList was generated and setSliderViews was called but remains previous image
How do i fix my code to update Images on RecylerView's items?
I just learned dagger 2, then i want implement with android jetpack. but i have problem cannot inject variabel in ViewModel.
This my structure
dagger
-- AppComponent.java
-- AppInjector.java
-- AppModule.java
-- Injectable.java
factory
-- MainViewModelFactory.java
module
-- FragmentBuildersModule.java
-- MainActivityModule.java
-- ViewModelKey.java
-- ViewModelModule.java
-- ViewModelSubcomponent.java
ui
-- MainFragment.java
-- MainViewModel.java
App.java
MainActivity.java
My Code MainViewModelFactory.java
#Singleton
public class MainViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
#Inject
public MainViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
#SuppressWarnings("unchecked")
#Override
public <T extends ViewModel> T create(Class<T> modelClass) {
Provider<? extends ViewModel> creator = creators.get(modelClass);
if (creator == null) {
for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
if (modelClass.isAssignableFrom(entry.getKey())) {
creator = entry.getValue();
break;
}
}
}
if (creator == null) {
throw new IllegalArgumentException("unknown viewmodel class " + modelClass);
}
try {
return (T) creator.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
FragmentBuildersModule.java
#Module
public abstract class FragmentBuildersModule {
#ContributesAndroidInjector
abstract MainFragment contributeMainFragment();
}
MainActivityModule.java
#Module
public abstract class MainActivityModule {
#ContributesAndroidInjector(modules = FragmentBuildersModule.class)
abstract MainActivity cotributeMainActivity();
}
ViewModelKey.java
#Documented
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#MapKey
#interface ViewModelKey {
Class<? extends ViewModel> value();
}
ViewModelModule.java
#Module
public abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(MainViewModel.class)
abstract MainViewModel bindMainViewModel(MainViewModel mainViewModel);
#Binds
abstract ViewModelProvider.Factory bindViewModelFactory(MainViewModelFactory factory);
}
ViewModelSubComponent.java
#Subcomponent
public interface ViewModelSubcomponent {
#Subcomponent.Builder
interface Builder {
ViewModelSubcomponent build();
}
MainViewModel mainViewModel();
}
ui.MainFragment.java
public class MainFragment extends Fragment {
private static final String TAG = "MainFragment";
#Inject
ViewModelProvider.Factory viewModelFactory;
private MainViewModel mViewModel;
public static MainFragment newInstance() {
return new MainFragment();
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.main_fragment, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
mViewModel = ViewModelProviders.of(this, viewModelFactory).get(MainViewModel.class);
// TODO: Use the ViewModel
TextView message = (TextView) getView().findViewById(R.id.message);
message.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// String test = mViewModel.getGlobalCommon().getNama();
GlobalCommon globalCommon = mViewModel.getGlobalCommon();
Toast.makeText(getContext(), "tst", Toast.LENGTH_LONG).show();
}
});
}
}
di.AppComponent.java
#Singleton
#Component(modules = { AndroidInjectionModule.class, AppModule.class, MainActivityModule.class})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(App app);
}
di.AppInjector.java
public class AppInjector {
private AppInjector() {}
public static void init(App app) {
DaggerAppComponent.builder().application(app).build().inject(app);
app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity, Bundle bundle) {
handleActivity(activity);
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
});
}
private static void handleActivity(Activity activity) {
if(activity instanceof HasSupportFragmentInjector){
AndroidInjection.inject(activity);
}
if(activity instanceof FragmentActivity){
FragmentManager.FragmentLifecycleCallbacks fragmentCallback = new FragmentManager.FragmentLifecycleCallbacks() {
#Override
public void onFragmentCreated(FragmentManager fm, Fragment f,
Bundle savedInstanceState) {
if (f instanceof Injectable) {
AndroidSupportInjection.inject(f);
}
}
};
((FragmentActivity) activity).getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(fragmentCallback, true);
}
}
}
di.AppModule.java
#Module(includes = ViewModelModule.class )
public class AppModule {
#Provides
#Singleton
Context provideApplication(Context application) {
return application.getApplicationContext();
}
#Singleton
#Provides
GlobalCommon provideGlobalCommon(){
return new GlobalCommon();
}
}
App.java
public class App extends Application implements HasActivityInjector {
#Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
AppInjector.init(this);
}
#Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector {
#Inject
DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, MainFragment.newInstance())
.commitNow();
}
}
#Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return dispatchingAndroidInjector;
}
}
myerror:
error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> cannot be provided without an #Provides-annotated method.
java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
main.main.xxx.main.main.testdagger2.factory.MainViewModelFactory.<init>(creators)
main.main.xxx.main.main.testdagger2.factory.MainViewModelFactory is injected at
main.main.xxx.main.main.testdagger2.module.ViewModelModule.bindViewModelFactory(factory)
android.arch.lifecycle.ViewModelProvider.Factory is injected at
main.main.xxx.main.main.testdagger2.ui.main.MainFragment.viewModelFactory
main.main.xxx.main.main.testdagger2.ui.main.MainFragment is injected at
dagger.android.AndroidInjector.inject(T)
component path: main.main.xxx.main.main.testdagger2.di.AppComponent → main.main.xxx.main.main.testdagger2.module.MainActivityModule_CotributeMainActivity.MainActivitySubcomponent → main.main.xxx.main.main.testdagger2.module.FragmentBuildersModule_ContributeMainFragment.MainFragmentSubcomponent
please helpme.
i have created a Demo application to Demonstrate the Use of LiveData, RxJava2, Dagger2, Paging, and much more. Since it is too much of the code to post here, I will post the link here. If you think you can make it easier or better open an issue.
https://github.com/mtangoo/Kasuku-Muvi
I wrote a library that should make this more straightforward: https://github.com/radutopor/ViewModelFactory
#ViewModelFactory
class SampleViewModel(#Provided private val repository: Repository, private val userId: Int) : ViewModel() {
private val greeting = MutableLiveData<String>()
init {
val user = repository.getUser(userId)
greeting.value = "Hello, $user.name"
}
fun getGreeting() = greeting as LiveData<String>
}
In the view:
class SampleActivity : AppCompatActivity() {
#Inject
lateinit var sampleViewModelFactory2: SampleViewModelFactory2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sample)
appComponent.inject(this)
val userId = intent.getIntExtra("USER_ID", -1)
val viewModel = ViewModelProviders.of(this, sampleViewModelFactory2.create(userId))
.get(SampleViewModel::class.java)
viewModel.getGreeting().observe(this, Observer { greeting ->
greetingTextView.text = greeting
})
}
}
In a project that is an Activity Model, Presenter and Model, the Activity Theme and Presenter and Presenter Model. When I do #Inject in Presenter to instantiate the Model it is never instantiated.
Do you need a dependency "cascade"?
FATAL EXCEPTION: main Process: fipedaggerrxjava, PID: 22258
java.lang.NullPointerException: Attempt to invoke interface method
'void
fipedaggerrxjava.mvp.SelectMarcaContractMVP$Model.getMarcas(java.lang.String)'
on a null object reference at
fipedaggerrxjava.module.marca.MarcaPresenter.initData(MarcaPresenter.java:35)
at
fipedaggerrxjava.module.marca.MarcaActivity$1.onCheckedChanged(MarcaActivity.java:63)
I already checked in Debug and really the Model that is not being instantiated by Dagger but I can not understand why.
App
public class App extends Application implements HasActivityInjector{
#Inject
public DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder().build().inject(App.this);
}
#Override
public AndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
}
ActivityBuilder
#Module
public abstract class ActivityBuilder {
#Binds
#IntoMap
#ActivityKey(MarcaActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindMarcaActivity (MarcaComponent.Builder builder);
}
AppComponent
#Component(modules = {ActivityBuilder.class, AndroidInjectionModule.class, AppModule.class})
#Singleton
public interface AppComponent {
void inject(App app);
}
AppModule
#Module(subcomponents = MarcaComponent.class)
public class AppModule {
#Provides
#Singleton
#Named("URL_MARCA")
String provideStringURLBase(){
return "https://fipe.parallelum.com.br/api/v1/";
}
#Provides
#Singleton
Context provideContext(App app){
return app;
}
#Provides
#Singleton
Gson provideGsonRepositorie(){
return new GsonBuilder()
.create();
}
#Singleton
#Provides
OkHttpClient provideOkHttpCliente1(){
return new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.build();
}
#Singleton
#Provides
RxJavaCallAdapterFactory provideRxJavaCallAdapterFactory(){
return RxJavaCallAdapterFactory.create();
}
#Provides
#Singleton
Retrofit provideRetrofit(OkHttpClient okHttpClient, Gson gson, RxJavaCallAdapterFactory rxAdapter, #Named("URL_MARCA") String stringBaseURL){
return new Retrofit.Builder()
.baseUrl(stringBaseURL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(rxAdapter)
.client(okHttpClient)
.build();
}
}
MarcaComponent
#Subcomponent(modules = MarcaModule.class)
#PerMarca
public interface MarcaComponent extends AndroidInjector<MarcaActivity>{
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MarcaActivity> {}
}
MarcaModule
#Module
public class MarcaModule{
#Provides
#PerMarca
APIFIPE provideAPIFIPE(Retrofit retrofit){
return retrofit.create(APIFIPE.class);
}
#Provides
#PerMarca
View provideViewMarca(MarcaActivity activity){
return activity;
}
#Provides
#PerMarca
Presenter providePresenterMarca(){
return new MarcaPresenter();
}
#Provides
#PerMarca
Model provideModelMarca(){
return new MarcaModel();
}
}
AdapterMarca
public class AdapterMarca extends BaseAdapter {
private List<Marca> mListMarca;
#Inject
public Context mContext;
public AdapterMarca(List<Marca> listMarca){
this.mListMarca = listMarca;
}
#Override
public int getCount() {
return mListMarca.size();
}
#Override
public Object getItem(int position) {
return mListMarca.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = LayoutInflater.from(mContext).inflate(R.layout.layout_list_item, parent, false);
TextView tvNome = view.findViewById(R.id.tv_marca);
tvNome.setText(mListMarca.get(position).getName().toString());
return view;
}
public void addListMarca(List<Marca> marcaList){
mListMarca.clear();
mListMarca.addAll(marcaList);
notifyDataSetChanged();
}
}
MarcaActivity
public class MarcaActivity extends BaseActivity implements HasActivityInjector, View {
private RadioGroup radioGroupMarca;
private String tipoSelect = "";
private List<Marca> mListMarca;
private AdapterMarca mAdapterMarca;
private ListView listViewMarca;
#Inject
public Presenter mMarcaPresenter;
#Inject
protected DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
#Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(MarcaActivity.this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listViewMarca = findViewById(R.id.lv_marca);
radioGroupMarca = findViewById(R.id.rg_tipo);
radioGroupMarca.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
int id = group.getCheckedRadioButtonId();
switch (id){
case R.id.rb_carros : tipoSelect = "carros";
mMarcaPresenter.initData(tipoSelect);
break;
case R.id.rb_motos : tipoSelect = "motos";
mMarcaPresenter.initData(tipoSelect);
break;
case R.id.rb_caminhoes : tipoSelect = "caminhoes";
mMarcaPresenter.initData(tipoSelect);
break;
}
}
});
}
#Override
public AndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
#Override
public void onMarcaLoader(List<Marca> listMarcas) {
if(mListMarca==null && listMarcas!=null){
initListView();
}
if(mAdapterMarca!=null){
mListMarca.clear();
mListMarca = listMarcas;
mAdapterMarca.addListMarca(mListMarca);
}
}
private void initListView(){
mAdapterMarca = new AdapterMarca(mListMarca);
listViewMarca.setAdapter(mAdapterMarca);
}
}
MarcaPresenter
#PerMarca
public class MarcaPresenter implements Presenter {
#Inject
View mMarcaView;
#Inject
Model mMarcaModel;
#Inject
public MarcaPresenter(){
}
#Override
public void initData(String tipoMarca) {
mMarcaModel.getMarcas(tipoMarca);
}
#Override
public void getMarcas(List<Marca> listMarcas) {
mMarcaView.onMarcaLoader(listMarcas);
}
#Override
public void onShowDialog(String title, String msg) {
mMarcaView.onShowDialog(title, msg);
}
#Override
public void onHideShowDialog() {
mMarcaView.onHideShowDialog();
}
#Override
public void onShowToast(String s) {
mMarcaView.onShowToast(s);
}
}
MarcaModel
#PerMarca
public class MarcaModel implements Model {
#Inject
APIFIPE mApiFIPE;
#Inject
Presenter mMarcaPresenter;
#Inject
public MarcaModel(){
}
#Override
public void getMarcas(String tipoVeiculo) {
final List<Marca> marcaList = new ArrayList<>();
Observable<List<Marca>> observable = mApiFIPE.getRepositories(tipoVeiculo);
observable.subscribe(new Observer<List<Marca>>() {
#Override
public void onCompleted() {
mMarcaPresenter.getMarcas(marcaList);
}
#Override
public void onError(Throwable e) {
mMarcaPresenter.onShowDialog("Erro", "Falha ao carregar lista de marcas");
}
#Override
public void onNext(List<Marca> marcas) {
marcaList.addAll(marcas);
}
});
}
}
In below lines:
public void onCompleted() {
mMarcaPresenter.getMarcas(marcaList);
}
-You trying to use mMarcePResenter and you inject it but you still need to use the App's component get assigned to mMarcaPresenter.
Initialize the injected value in below constructor:
#Inject
public MarcaModel(){
mMarcaPresenter =
}
You have declared provider methods(in MarcaModule) for your model and presenter classes. Dagger uses those provider methods, ending up not supplying the field injected values.
Therefore, you need to supply your dependencies in your module(s), i.e. via Model and Presenter constructors.
MarcaModel
...
Presenter mMarcaPresenter;
#Inject
public MarcaModel(Presenter presenter){
mMarcaPresenter = presenter
}
...
MarcaModule
#Module
public class MarcaModule{
...
#Provides
#PerMarca
Model provideModelMarca(Presenter presenter){
return new MarcaModel(presenter);
}
}
Reading Dagger user's guide carefully might help you even more;
https://google.github.io/dagger/users-guide