I am trying to get some data from a website and load it into my app.
The app has 1 MainActivity and 3 fragments and uses ViewBinding.
Unfortunately I can not create a instance of viewmodel in the 1st Fragment.
I already searched stackoverflow and the internet (a lot) but I somehow can not get all the solutions given working.
I get the exception on materialViewModel.getMaterials().observe in the HomeFragment.
Any help is highly appreciated.
My HomeFragment:
public class HomeFragment extends Fragment {
private MaterialViewModel materialViewModel;
private FragmentHomeBinding binding;
ArrayList<MaterialModel> posts;
public HomeFragment() {
}
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentHomeBinding.inflate(inflater, container, false);
materialViewModel = new ViewModelProvider(getActivity()).get(MaterialViewModel.class);
initializeRecyclerView();
getPosts();
return binding.getRoot();
}
private void initializeRecyclerView() {
posts = new ArrayList<>();
binding.rvMain.setLayoutManager(new LinearLayoutManager(getActivity()));
}
private void getPosts() {
materialViewModel.getMaterials().observe(getActivity(), new Observer<List<MaterialModel>>() {
#Override
public void onChanged(#Nullable List<MaterialModel> posts) {
binding.rvMain.setAdapter(new ItemAdapter((ArrayList<MaterialModel>) posts));
}
});
getIsLoaded();
}
public void getIsLoaded(){
materialViewModel.getIsLoaded().observe(getActivity(), new Observer<Boolean>() {
#Override
public void onChanged(#Nullable Boolean aBoolean) {
if (aBoolean == true){
//activityMainBinding.progressBarID.setVisibility(View.GONE);
Log.w("PAR-LOADED ", "DATA IS LOADED");
}
}
});
}
#Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
My MaterialViewmodel:
public class MaterialViewModel extends AndroidViewModel {
private MutableLiveData<List<MaterialModel>> matts;
private MaterialRepository materialRepository;
public MaterialViewModel(Application application) {
super(application);
materialRepository = new MaterialRepository(application);
}
public void init() {
if (this.matts == null) {
matts = materialRepository.getAllMaterials();
}
}
public LiveData<List<MaterialModel>> getMaterials() {
return this.matts;
}
public MutableLiveData<Boolean> getIsLoaded() {
return materialRepository.dataIsLoaded;
}
}
My MaterialRepository:
public class MaterialRepository {
API_Service service;
private String posts;
ArrayList<String> arrayLiNames = new ArrayList<>();
ArrayList<String> arrayLiLinks = new ArrayList<>();
public ArrayList<MaterialModel> materialData = new ArrayList<>();
public MutableLiveData<List<MaterialModel>> mattis;
public MutableLiveData<Boolean> dataIsLoaded;
public MaterialRepository(Application application) {
super();
service = RetrofitClass.getClient(application).create(API_Service.class);
}
public MutableLiveData<List<MaterialModel>> getAllMaterials() {
Call<List<MaterialModel>> postResponseCall = service.getMaterials();
postResponseCall.enqueue(new Callback<List<MaterialModel>>() {
#Override
public void onResponse(Call<List<MaterialModel>> call, Response<List<MaterialModel>> response) {
if (response.code() == 200) {
Log.w("PAR-RESPONSE-CODE ", String.valueOf(response.code()));
posts = String.valueOf(response.body());
//new MyTask().execute();
}
}
#Override
public void onFailure(Call<List<MaterialModel>> call, Throwable t) {
}
});
return mattis;
}
private class MyTask extends AsyncTask<Void, Void, List<MaterialModel>> {
...
#Override
protected List<MaterialModel> doInBackground(Void... params) {
#Override
protected void onPostExecute(List<MaterialModel> result) {
mattis.setValue(result);
dataIsLoaded.setValue(true);
}
}
}
Related
I am training to work with api and the problem arose that when scrolling the RecyclerView, I need the data to be added, namely, "page" is added, and thus the content is added.
I have achieved that before reaching the end of the list, the "onReachEndListener" method is triggered, but I cannot understand the implementation of adding to the sheet. PLEASE HELP ME!
public class MainActivity extends AppCompatActivity {
private SwitchMaterial switchMaterial;
private TextView textViewPopularity, textViewTopRated;
private RecyclerView recyclerViewPosters;
private MovieAdapter movieAdapter;
private MainViewModel viewModel;
private ProgressBar progressBar;
private static final int LOADER_ID = 122;
private LoaderManager loaderManager;
private static int page = 1;
private static String methodOfSort;
private static boolean isLoaning = true;
private static String lang;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.itemMain:
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
break;
case R.id.itemFavourite:
Intent i = new Intent(this, FavouriteActivity.class);
startActivity(i);
break;
}
return super.onOptionsItemSelected(item);
}
private int getColumnCount() {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width = (int) (displayMetrics.widthPixels / displayMetrics.density);
return width / 185 > 2 ? width / 2 : 2;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lang = Locale.getDefault().getLanguage();
viewModel = new ViewModelProvider(this).get(MainViewModel.class);
recyclerViewPosters = findViewById(R.id.recyclerViewPosters);
switchMaterial = findViewById(R.id.switchSort);
textViewPopularity = findViewById(R.id.textViewPopularity);
textViewTopRated = findViewById(R.id.textViewTopRated);
progressBar = findViewById(R.id.progressBarLoading);
movieAdapter = new MovieAdapter();
recyclerViewPosters.setLayoutManager(new GridLayoutManager(this, getColumnCount()));
recyclerViewPosters.setAdapter(movieAdapter);
switchMaterial.setChecked(true);
myOnClick();
LiveData<List<Movie>> moviesFromLiveData = viewModel.getMovies();
moviesFromLiveData.observe(this, new Observer<List<Movie>>() {
#Override
public void onChanged(#Nullable List<Movie> movies) {
movieAdapter.setMovies(movies);
}
});
}
private void myOnClick() {
switchMaterial.setOnCheckedChangeListener((compoundButton, b) -> {
page = 1;
setMethodOfSort(b);
});
switchMaterial.setChecked(false);
textViewPopularity.setOnClickListener(view -> {
setMethodOfSort(false);
switchMaterial.setChecked(false);
});
textViewTopRated.setOnClickListener(view -> {
setMethodOfSort(true);
switchMaterial.setChecked(true);
});
movieAdapter.setOnPosterClickListener(position -> {
Movie movie = movieAdapter.getMovies().get(position);
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
intent.putExtra("id", movie.getId());
startActivity(intent);
});
movieAdapter.setOnReachEndListener(() -> {
if (!isLoaning){
viewModel.deleteAllMovies();
progressBar.setVisibility(View.VISIBLE);
viewModel.loadDataImage(lang, methodOfSort, Integer.toString(page));
Log.d("isLoaning", Integer.toString(page));
}
});
recyclerViewPosters.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (!recyclerView.canScrollVertically(1)) {
Log.d("isLoaning", Integer.toString(page));
LiveData<List<Movie>> moviesFromLiveData = viewModel.getMovies();
moviesFromLiveData.observe(MainActivity.this, new Observer<List<Movie>>() {
#Override
public void onChanged(#Nullable List<Movie> movies) {
viewModel.insertMovie(movies);
movieAdapter.addMovies(movies);
page++;
isLoaning = false;
}
});
}
}
});
}
private void setMethodOfSort(boolean b){
if(b) {
methodOfSort = SORT_BY_TOP_RATED;
Log.d("setMethodOfSort", methodOfSort);
textViewPopularity.setTextColor(getResources().getColor(R.color.white));
textViewTopRated.setTextColor(getResources().getColor(R.color.teal_200));
}
else {
methodOfSort = SORT_BY_POPULARITY;
Log.d("setMethodOfSort", methodOfSort);
textViewPopularity.setTextColor(getResources().getColor(R.color.teal_200));
textViewTopRated.setTextColor(getResources().getColor(R.color.white));
}
viewModel.loadData(lang, methodOfSort ,Integer.toString(page));
}
Model:
public class MainViewModel extends AndroidViewModel {
private static MovieDatabase database;
private LiveData<List<Movie>> movies;
private LiveData<List<FavouriteMovie>> favouriteMovies;
private LiveData<String> methodOfSort;
private CompositeDisposable compositeDisposable;
private MutableLiveData<Throwable> errors;
public static final String SMALL_POSTER_SIZE = "w185";
private static final String KEY_POSTER_PATH = "poster_path";
public static final String SORT_BY_POPULARITY = "popularity.desc";
public static final String SORT_BY_TOP_RATED = "vote_average.desc";
public static final int POPULARITY = 0;
public static final int TOP_RATED = 1;
public MainViewModel(#NonNull Application application) {
super(application);
database = MovieDatabase.getInstance(getApplication());
movies = database.movieDao().getAllMovies();
favouriteMovies = database.movieDao().getAllFavouriteMovies();
errors = new MutableLiveData<>();
}
public LiveData<Throwable> getErrors() {
return errors;
}
public void clearErrors() {
errors.setValue(null);
}
public void loadData(String lang, String sort, String page) {
ApiFactory apiFactory = ApiFactory.getInstance();
ApiService apiService = apiFactory.getApiService();
compositeDisposable = new CompositeDisposable();
Disposable disposable = apiService.getMovieResponse("32f91e104228c0c9ff3630899838e82e",
lang,
sort,
"1000",
page)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MovieResponse>() {
#Override
public void accept(MovieResponse movieResponse) throws Exception {
deleteAllMovies();
insertMovie(movieResponse.getMovies());
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
errors.setValue(throwable);
}
});
compositeDisposable.add(disposable);
}
public void loadDataImage(String lang, String sort, String page) {
ApiFactory apiFactory = ApiFactory.getInstance();
ApiService apiService = apiFactory.getApiService();
compositeDisposable = new CompositeDisposable();
Disposable disposable = apiService.getMovieResponse(
"32f91e104228c0c9ff3630899838e82e",
lang,
sort,
"1000",
page)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MovieResponse>() {
#Override
public void accept(MovieResponse movieResponse) throws Exception {
insertMovie(movieResponse.getMovies());
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
errors.setValue(throwable);
}
});
compositeDisposable.add(disposable);
}
public Movie getMovieById(int id) {
try {
return new GetMovieTask().execute(id).get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
public FavouriteMovie getFavouriteMovieById(int id) {
try {
return new GetFavouriteMovieTask().execute(id).get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
public LiveData<List<FavouriteMovie>> getFavouriteMovies() {
return favouriteMovies;
}
public void deleteAllMovies() {
new DeleteMoviesTask().execute();
}
#SuppressWarnings("unchecked")
public void insertMovie(List<Movie> movie) {
new InsertTask().execute(movie);
}
public void deleteMovie(Movie movie) {
new DeleteTask().execute(movie);
}
public LiveData<List<Movie>> getMovies() {
return movies;
}
public void insertFavouriteMovie(FavouriteMovie movie) {
new InsertFavouriteTask().execute(movie);
}
public void deleteFavouriteMovie(FavouriteMovie movie) {
new DeleteFavouriteTask().execute(movie);
}
private static class DeleteFavouriteTask extends AsyncTask<FavouriteMovie, Void, Void> {
#Override
protected Void doInBackground(FavouriteMovie... movies) {
if (movies != null && movies.length > 0) {
database.movieDao().deleteFavouriteMovie(movies[0]);
}
return null;
}
}
private static class InsertFavouriteTask extends AsyncTask<FavouriteMovie, Void, Void> {
#Override
protected Void doInBackground(FavouriteMovie... movies) {
if (movies != null && movies.length > 0) {
database.movieDao().insertFavouriteMovie(movies[0]);
}
return null;
}
}
private static class DeleteTask extends AsyncTask<Movie, Void, Void> {
#Override
protected Void doInBackground(Movie... movies) {
if (movies != null && movies.length > 0) {
database.movieDao().deleteMovie(movies[0]);
}
return null;
}
}
private static class InsertTask extends AsyncTask<List<Movie>, Void, Void> {
#Override
protected Void doInBackground(List<Movie>... movies) {
if (movies != null && movies.length > 0) {
database.movieDao().insertMovie(movies[0]);
}
return null;
}
}
private static class DeleteMoviesTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... integers) {
database.movieDao().deleteAllMovies();
return null;
}
}
private static class GetMovieTask extends AsyncTask<Integer, Void, Movie> {
#Override
protected Movie doInBackground(Integer... integers) {
if (integers != null && integers.length > 0) {
return database.movieDao().getMovieById(integers[0]);
}
return null;
}
}
private static class GetFavouriteMovieTask extends AsyncTask<Integer, Void, FavouriteMovie> {
#Override
protected FavouriteMovie doInBackground(Integer... integers) {
if (integers != null && integers.length > 0) {
return database.movieDao().getFavouriteMovieById(integers[0]);
}
return null;
}
}
#Override
protected void onCleared() {
compositeDisposable.dispose();
super.onCleared();
}
}
Adapter:
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MovieViewHolder> {
private List<Movie> movies;
private OnPosterClickListener onPosterClickListener;
private OnReachEndListener onReachEndListener;
private onStartLoadingListener onStartLoadingListener;
public MovieAdapter(){
movies = new ArrayList<>();
}
public interface OnPosterClickListener{
void OnPosterClick(int position);
}
public interface OnReachEndListener{
void onReachEnd();
}
public interface onStartLoadingListener{
void onStartLoading();
}
public void setOnStartLoadingListener(onStartLoadingListener onStartLoadingListener) {
this.onStartLoadingListener = onStartLoadingListener;
}
public void setOnPosterClickListener(OnPosterClickListener onPosterClickListener) {
this.onPosterClickListener = onPosterClickListener;
}
public void setOnReachEndListener(OnReachEndListener onReachEndListener) {
this.onReachEndListener = onReachEndListener;
}
#NonNull
#Override
public MovieViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.movie_item, parent, false);
return new MovieViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull MovieViewHolder holder, int position) {
if (movies.size() >= 20 && position > movies.size() - 4 && onReachEndListener != null){
onReachEndListener.onReachEnd();
}
Movie movie = movies.get(position);
Picasso.get().load(movie.getPosterPath()).into(holder.imageViewSmallPoster);
}
#Override
public int getItemCount() {
return movies.size();
}
class MovieViewHolder extends RecyclerView.ViewHolder{
private ImageView imageViewSmallPoster;
public MovieViewHolder(#NonNull View itemView) {
super(itemView);
imageViewSmallPoster = itemView.findViewById(R.id.imageViewSmallPoster);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(onPosterClickListener != null){
onPosterClickListener.OnPosterClick(getAdapterPosition());
}
}
});
}
}
public void clear(){
this.movies.clear();
notifyDataSetChanged();
}
public List<Movie> getMovies() {
return movies;
}
public void addMovies(List<Movie> movies){
this.movies.addAll(movies);
notifyDataSetChanged();
}
public void setMovies(List<Movie> movies) {
this.movies = movies;
notifyDataSetChanged();
}
}
I am using retrofit2 for fetching data from the server and after fetching saving data in room database and then showing in recycler view. But it is no displayed (my data what I get using retrofit). I try display it in my fragment. In file ...data/data/databese/source.db these data are saved. I see it. So, that means that my code works. But I can't understand why it is not displayed.
my database class:
#Database(entities = {Source.class}, exportSchema = false, version = 1)
public abstract class SourceDatabase extends RoomDatabase {
private static final String DB_NAME = "source.db";
public abstract SourceDao sourceDao();
private static SourceDatabase instance;
public static SourceDatabase getInstance(Context context) {
if (instance == null) {
instance =buildDatabaseInstance(context);
}
return instance;
}
private static SourceDatabase buildDatabaseInstance(Context context) {
return Room.databaseBuilder(context,
SourceDatabase.class,
DB_NAME).build();
}
}
repository:
public class DataBaseRepository {
private static DataBaseRepository dataBaseRepository;
private SourceDao sourceDao;
private LiveData<List<Source>> allSourcestoDb;
private Context context;
public static DataBaseRepository getInstance(Context context) {
if (dataBaseRepository == null) {
dataBaseRepository = new DataBaseRepository(context);
}
return dataBaseRepository;
}
public DataBaseRepository(Context context) {
this.context = context;
SourceDatabase db = SourceDatabase.getInstance(context);
sourceDao = db.sourceDao();
allSourcestoDb = sourceDao.getSources();
}
public void getSourceListTodb(String key) {//отправка данных в LiveData
RestClient restClient = RestClient.getInstance();
restClient.startRetrofit();
restClient.getServerApi().getNews(key).enqueue(new Callback<News>() {
#Override
public void onResponse(Call<News> call, Response<News> response) {
Completable.fromAction(new Action (){
#Override
public void run() throws Exception {
if (response.body() != null) {
List<Source> list = response.body().getSources();
sourceDao.insert(list);
}
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onComplete() {
}
#Override
public void onError(Throwable e) {
}
});
}
#Override
public void onFailure(Call<News> call, Throwable t) {
Log.d("error", "Can't parse data " + t);
}
});
}
public LiveData<List<Source>> getAllSourcestoDb() {
return allSourcestoDb;
}
}
dao:
#Dao
public interface SourceDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(List<Source> sources);
#Query("SELECT * FROM source")
LiveData<List<Source>> getSources();
}
viewModel:
public class SourceViewModel extends AndroidViewModel {
private DataBaseRepository dataBaseRepository;
private LiveData<List<Source>> allSources; //for db
public SourceViewModel(#NonNull Application application) {
super(application);
dataBaseRepository =DataBaseRepository.getInstance(application); //for db
allSources = dataBaseRepository.getAllSourcestoDb();
}
public LiveData<List<Source>> getAllSources() {
return allSources;
}
}
and fragment:
public class SavedDataFragment extends Fragment {
private SourceViewModel sourceViewModel;
private DataBaseRepository dataBaseRepository;
private RecyclerView recyclerView;
private List<Source> sourceList;
private SavedDataAdapter adapter;
public SavedDataFragment() {
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.saved_data,container,false);
DataSharedPreference sharedPreference = DataSharedPreference.getSPInstance();
String api_key = sharedPreference.loadText(getActivity());
dataBaseRepository = new DataBaseRepository(getActivity());
sourceViewModel = ViewModelProviders.of(this).get(SourceViewModel.class);
recyclerView = view.findViewById(R.id.recyclerViewSavedFragment);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
sourceList = new ArrayList<>();
adapter = new SavedDataAdapter(getActivity(), sourceList);
recyclerView.setAdapter(adapter);
sourceViewModel.getAllSources().observe(this, new Observer<List<Source>>() {
#Override
public void onChanged(List<Source> sources) {
adapter.setSourceList(sourceList);
}
});
dataBaseRepository.getSourceListTodb(api_key);
return view;
}
}
adapter:
public class SavedDataAdapter extends RecyclerView.Adapter<SavedDataAdapter.SourceSavedViewHolder> {
private LayoutInflater inflater;
private List<Source> sources;
public SavedDataAdapter(Context context, List<Source> sources) {
this.sources = sources;
this.inflater = LayoutInflater.from(context);
}
#NonNull
#Override
public SavedDataAdapter.SourceSavedViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.saved_item, parent, false);
return new SourceSavedViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final SavedDataAdapter.SourceSavedViewHolder holder, int position) {
final Source source = sources.get(position);
holder.sourceId.setText(source.getId());
holder.sourceName.setText(source.getName());
holder.sourceDescription.setText(source.getDescription());
holder.sourceURL.setText(source.getUrl());
holder.sourceCategory.setText(source.getCategory());
holder.sourceLanguage.setText(source.getLanguage());
holder.sourceCountry.setText(source.getCountry());
}
#Override
public int getItemCount() {
return sources.size();
}
public void setSourceList(List<Source> sources) {
this.sources = sources;
notifyDataSetChanged();
}
public static class SourceSavedViewHolder extends RecyclerView.ViewHolder {
TextView sourceName, sourceId, sourceDescription, sourceURL, sourceCategory, sourceLanguage, sourceCountry;
public SourceSavedViewHolder(View view) {
super(view);
sourceName = view.findViewById(R.id.sourceName);
sourceId = view.findViewById(R.id.sourceIdItem);
sourceDescription = view.findViewById(R.id.sourceDescription);
sourceURL = view.findViewById(R.id.sourceURL);
sourceCategory = view.findViewById(R.id.sourceCategory);
sourceLanguage = view.findViewById(R.id.sourceLanguage);
sourceCountry = view.findViewById(R.id.sourceCountry);
}
}
}
In your Fragment inside onChanged,
you're setting adapter.setSourceList(sourceList) where sourceList is an empty arrayList.
You should instead setSourceList to sources which is the updated list passed as an argument to onChanged method
That is :-
sourceViewModel.getAllSources().observe(this, new Observer<List<Source>>() {
#Override
public void onChanged(List<Source> sources) {
adapter.setSourceList(sources); // sources and not sourceList
}
});
Also there are few more things that should be taken care of.
For ex- in your observe method, you have passed this as first argument which is wrong when using Fragments as it may causes memory leaks. Instead you should pass viewLifeOwner..
More can found on this link Use viewLifecycleOwner as the LifecycleOwner
Try ti change this:
#Query("SELECT * FROM source")
To:
#Query("SELECT * FROM Source")
I have problem with observing 3 different lists with live data observer for viewing them in one single recycler view.
This is the scenario: In ListsFragment, I have TabLayout with 3 tabs (WANT, PLAYING, PLAYED) and 1 recycler view. On different tab click I load different lists of games from database and I am presenting them with Live Data observer pattern in the recycler view. Until this moment everything works OK.
The problem occurs when I press one game DELETE button, the database is updated but in the recycler view is loading wrong list and not the list of the selected tab (Probably because there are 3 active observers), so the data on the screen is updated with wrong games.
Gif of the behaviour:
https://gfycat.com/showyembarrassedbarasingha
Can anyone please help me, I tried many solutions but nothing helped and I am stuck more than 1 month.
This is the code.
Fragment:
public class ListsFragment extends Fragment implements VisitedGameInterface {
public static final String TAG = ListsFragment.class.getSimpleName();
private static final int WANT = 0;
private static final int PLAYING = 1;
private static final int PLAYED = 2;
private int selectedTab = WANT;
private GameViewModel gameViewModel;
private GameVisitedAdapter gameAdapter;
private TextView noGamesTV;
public ListsFragment() {
// Required empty public constructor
}
public static ListsFragment newInstance(Bundle bundle) {
ListsFragment fragment = new ListsFragment();
fragment.setArguments(bundle);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_lists, container, false);
TabLayout listsTabLayout = view.findViewById(R.id.lists_tab_layout);
RecyclerView listsRecyclerView = view.findViewById(R.id.lists_recycler_view);
noGamesTV = view.findViewById(R.id.no_games_tv);
listsTabLayout.addTab(listsTabLayout.newTab().setText(getString(R.string.want)), true);
listsTabLayout.addTab(listsTabLayout.newTab().setText(getString(R.string.playing)));
listsTabLayout.addTab(listsTabLayout.newTab().setText(getString(R.string.played)));
listsTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
listsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), RecyclerView.VERTICAL, false));
listsRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
gameAdapter = new GameVisitedAdapter(getActivity(), this);
listsRecyclerView.setAdapter(gameAdapter);
gameViewModel = ViewModelProviders.of(this).get(GameViewModel.class);
gameViewModel.getWantedGames().observe(this, new Observer<List<Game>>() {
#Override
public void onChanged(List<Game> wantedGames) {
if (wantedGames.size() != 0) {
noGamesTV.setVisibility(View.GONE);
}else {
noGamesTV.setText(R.string.no_wanted_games);
noGamesTV.setVisibility(View.VISIBLE);
}
gameAdapter.setGames(wantedGames);
gameAdapter.notifyDataSetChanged();
}
});
listsTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
if (getActivity() != null && tab.getText() != null) {
if (tab.getText().equals("WANT")) {
selectedTab = tab.getPosition();
gameViewModel.getWantedGames().observe(getActivity(), new Observer<List<Game>>() {
#Override
public void onChanged(List<Game> wantedGames) {
if (wantedGames.size() != 0) {
noGamesTV.setVisibility(View.GONE);
} else {
noGamesTV.setText(R.string.no_wanted_games);
noGamesTV.setVisibility(View.VISIBLE);
}
gameAdapter.setGames(wantedGames);
gameAdapter.notifyDataSetChanged();
}
});
} else if (tab.getText().equals("PLAYING")) {
selectedTab = tab.getPosition();
gameViewModel.getPlayingGames().observe(getActivity(), new Observer<List<Game>>() {
#Override
public void onChanged(List<Game> playingGames) {
if (playingGames.size() != 0) {
noGamesTV.setVisibility(View.GONE);
} else {
noGamesTV.setText(R.string.no_playing_games);
noGamesTV.setVisibility(View.VISIBLE);
}
gameAdapter.setGames(playingGames);
gameAdapter.notifyDataSetChanged();
}
});
} else if (tab.getText().equals("PLAYED")){
selectedTab = tab.getPosition();
gameViewModel.getPlayedGames().observe(getActivity(), new Observer<List<Game>>() {
#Override
public void onChanged(List<Game> playedGames) {
if (playedGames.size() != 0) {
noGamesTV.setVisibility(View.GONE);
} else {
noGamesTV.setText(R.string.no_played_games);
noGamesTV.setVisibility(View.VISIBLE);
}
gameAdapter.setGames(playedGames);
gameAdapter.notifyDataSetChanged();
}
});
}
}
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
return view;
}
#Override
public void onGameDelete(final Game game) {
if (selectedTab == WANT){
game.setWanted(false);
showSnackBar(game, WANT);
} if (selectedTab == PLAYING){
game.setPlaying(false);
showSnackBar(game, PLAYING);
} if (selectedTab == PLAYED){
game.setPlayed(false);
showSnackBar(game, PLAYED);
}
gameViewModel.update(game);
gameAdapter.notifyDataSetChanged();
}
#Override
public void onVisitedGameClick(Game game) {
Intent intent = new Intent(getActivity(), GameActivity.class);
intent.putExtra("game", game);
startActivity(intent);
}
private void showSnackBar(final Game snackGame, final int selectedTab){
Snackbar snackbar = Snackbar.make(Objects.requireNonNull(getView()), snackGame.getName() + " deleted", Snackbar.LENGTH_LONG)
.setAction(R.string.undo, new View.OnClickListener() {
#Override
public void onClick(View view) {
if (selectedTab == WANT) snackGame.setWanted(true);
if (selectedTab == PLAYING) snackGame.setPlaying(true);
if (selectedTab == PLAYED) snackGame.setPlayed(true);
gameViewModel.insert(snackGame);
}
});
snackbar.show();
}
}
XML of ListsFragment
<RelativeLayout
android:id="#+id/lists_layout" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
tools:context=".fragment.ListsFragment">
<com.google.android.material.tabs.TabLayout
android:id="#+id/lists_tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabSelectedTextColor="#color/button_blue"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/lists_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/lists_tab_layout"/>
<TextView
android:id="#+id/no_games_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
tools:text="No games at this moment"
android:textColor="#color/text_black"
android:visibility="invisible"/>
</RelativeLayout>
The adapter:
public class GameVisitedAdapter extends RecyclerView.Adapter<GameVisitedAdapter.GameVisitedViewHolder> {
private Context context;
private LayoutInflater inflater;
private List<Game> games = new ArrayList<>();
private VisitedGameInterface visitedGameInterface;
public GameVisitedAdapter(Context context, VisitedGameInterface visitedGameInterface){
this.context = context;
inflater = LayoutInflater.from(context);
this.visitedGameInterface = visitedGameInterface;
}
public void setGames(List<Game> games){
this.games = games;
}
#NonNull
#Override
public GameVisitedViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.item_game_visited, parent, false);
return new GameVisitedViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull GameVisitedViewHolder holder, int position) {
Game game = games.get(position);
if (game.getCover() != null) {
Glide
.with(context)
.load(context.getString(R.string.cover_url) + game.getCover().getImage_id() + ".jpg")
.centerCrop()
.placeholder(context.getResources().getDrawable(R.drawable.placeholder))
.into(holder.gameCover);
}
holder.gameTitle.setText(game.getName());
SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy");
String dateString = formatter.format((new Date((long) game.getFirst_release_date() * 1000)));
holder.gameYear.setText(dateString);
}
#Override
public int getItemCount() {
return games.size();
}
class GameVisitedViewHolder extends RecyclerView.ViewHolder {
private ImageView gameCover, gameDeleteButton;
private TextView gameTitle, gameYear;
GameVisitedViewHolder(#NonNull View itemView) {
super(itemView);
gameCover = itemView.findViewById(R.id.game_cover);
gameTitle = itemView.findViewById(R.id.game_name);
gameYear = itemView.findViewById(R.id.game_release_year);
gameDeleteButton = itemView.findViewById(R.id.game_delete_button);
gameDeleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
visitedGameInterface.onGameDelete(games.get(getAdapterPosition()));
}
});
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
visitedGameInterface.onVisitedGameClick(games.get(getAdapterPosition()));
}
});
}
}
}
The ViewModel
public class GameViewModel extends AndroidViewModel {
private GameRepository gameRepository;
public GameViewModel(#NonNull Application application) {
super(application);
gameRepository = new GameRepository(application);
}
public void insert(Game game){
gameRepository.insert(game);
}
public void update(Game game){
gameRepository.update(game);
}
public void deleteGame(Game game){
gameRepository.delete(game);
}
public void deleteAll(){
gameRepository.deleteAll();
}
public void DeleteVisitedGames(){ gameRepository.deleteVisitedGames();}
public LiveData<List<Game>> getWantedGames(){
return gameRepository.getWantedGames();
}
public LiveData<List<Game>> getPlayingGames(){
return gameRepository.getPlayingGames();
}
public LiveData<List<Game>> getPlayedGames(){
return gameRepository.getPlayedGames();
}
public LiveData<List<Game>> getVisitedGames(){
return gameRepository.getVisitedGames();
}
public LiveData<List<Game>> getAllGamesFromDB(){
return gameRepository.getAllGamesFromDB();
}
}
The Repository:
public class GameRepository {
private GameDao gameDao;
private LiveData<List<Game>> wantedGames;
private LiveData<List<Game>> playingGames;
private LiveData<List<Game>> playedGames;
private LiveData<List<Game>> visitedGames;
private LiveData<List<Game>> allGamesFromDB;
public GameRepository(Application application){
GameDatabase gameDatabase = GameDatabase.getInstance(application);
gameDao = gameDatabase.gameDao();
wantedGames = gameDao.getWantedGames();
playingGames = gameDao.getPlayingGames();
playedGames = gameDao.getPlayedGames();
visitedGames = gameDao.getVisitedGames();
allGamesFromDB = gameDao.getAllGamesFromDB();
}
public LiveData<List<Game>> getWantedGames(){
return wantedGames;
}
public LiveData<List<Game>> getPlayingGames(){
return playingGames;
}
public LiveData<List<Game>> getPlayedGames(){
return playedGames;
}
public LiveData<List<Game>> getVisitedGames(){
return visitedGames;
}
public LiveData<List<Game>> getAllGamesFromDB(){
return allGamesFromDB;
}
public void insert(Game game){
new InsertAsyncTask(gameDao).execute(game);
}
public void update(Game game){
new UpdateAsyncTask(gameDao).execute(game);
}
public void delete(Game game){
new DeleteItemAsyncTask(gameDao).execute(game);
}
public void deleteAll(){
new DeleteAllAsyncTask(gameDao).execute();
}
public void deleteVisitedGames() {new DeleteVisitedAsyncTask(gameDao).execute();}
//-------------INSERT GAME ASYNC TASK
private static class InsertAsyncTask extends AsyncTask<Game, Void, Void> {
private GameDao asyncDao;
public InsertAsyncTask(GameDao asyncDao){
this.asyncDao = asyncDao;
}
#Override
protected Void doInBackground(Game... games) {
asyncDao.insert(games[0]);
return null;
}
}
//-------------UPDATE GAME ASYNC TASK
private static class UpdateAsyncTask extends AsyncTask<Game, Void, Void> {
private GameDao asyncDao;
public UpdateAsyncTask(GameDao asyncDao){
this.asyncDao = asyncDao;
}
#Override
protected Void doInBackground(Game... games) {
asyncDao.updateGame(games[0]);
return null;
}
}
//----------DELETE GAME ASYNC TASK
private static class DeleteItemAsyncTask extends AsyncTask <Game, Void, Void> {
private GameDao asyncDao;
public DeleteItemAsyncTask(GameDao asyncDao){
this.asyncDao = asyncDao;
}
#Override
protected Void doInBackground(Game... games) {
asyncDao.deleteGame(games[0]);
return null;
}
}
//-------------DELETE ALL ASYNC TASK
private static class DeleteAllAsyncTask extends AsyncTask <Void, Void, Void>{
private GameDao asyncDao;
public DeleteAllAsyncTask(GameDao asyncDao){
this.asyncDao = asyncDao;
}
#Override
protected Void doInBackground(Void... voids) {
asyncDao.deleteAllGames();
return null;
}
}
//-------------DELETE VISITED GAMES ASYNC TASK
private static class DeleteVisitedAsyncTask extends AsyncTask <Void, Void, Void>{
private GameDao asyncDao;
public DeleteVisitedAsyncTask(GameDao asyncDao){
this.asyncDao = asyncDao;
}
#Override
protected Void doInBackground(Void... voids) {
asyncDao.deleteVisitedGames();
return null;
}
}
}
The DAO:
#Dao
public interface GameDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(Game game);
#Delete
void deleteGame(Game game);
#Update
void updateGame(Game game);
#Query("DELETE FROM game_table")
void deleteAllGames();
#Query("SELECT * FROM game_table WHERE isWanted")
LiveData<List<Game>> getWantedGames();
#Query("SELECT * FROM game_table WHERE isPlaying")
LiveData<List<Game>> getPlayingGames();
#Query("SELECT * FROM game_table WHERE isPlayed")
LiveData<List<Game>> getPlayedGames();
#Query("SELECT * FROM game_table WHERE isVisited")
LiveData<List<Game>> getVisitedGames();
#Query("SELECT * FROM game_table")
LiveData<List<Game>> getAllGamesFromDB();
#Query("DELETE FROM game_table WHERE isVisited")
void deleteVisitedGames();
}
Database:
#Database(entities = Game.class, version = 8, exportSchema = false)
#TypeConverters({Converters.class})
public abstract class GameDatabase extends RoomDatabase {
public abstract GameDao gameDao();
private static GameDatabase INSTANCE;
public static GameDatabase getInstance(Context context){
if (INSTANCE == null){
synchronized (GameDatabase.class){
if (INSTANCE == null){
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
GameDatabase.class, "game_database")
.fallbackToDestructiveMigration()
.addCallback(sRoomDatabaseCallback)
.build();
}
}
}
return INSTANCE;
}
private static RoomDatabase.Callback sRoomDatabaseCallback = new RoomDatabase.Callback(){
#Override
public void onOpen(#NonNull SupportSQLiteDatabase db) {
super.onOpen(db);
}
};
}
My 2cents: even after you change tabs, your observers are still active, as you are using the same table for everything and I believe (just glance at the code) you are updating your game entry (e.g: isPlayed set to false) thus it send updates to all active observers thus sending update to the recylerview.
With that in mind, in case I am right here, you can:
Make 3 distinct tables (can be in the same database)
use viewpager to have 3 fragments with 3 diffent recyclers (actually you can use viewpager2 with a viewholder instead of making fragments)
have 3 recyclers (and you do a visibility.Gone every time user changes the tab)
In case I am not right, option 2 e 3 still solve your problem. I hope it helps!
p.s: you could also remove and insert observers everytime users changes tab, I find it to be less straightforward though.
Good day, so my problem as follows.
When I initialize the retrofit call it has following flow.
1.In the Presenterclass, it goes to getData method.
2.Then it goes to Interactorclass and creates a instance of itself.
3.Then it starts to fetch data from getMovieList method.
4.When it gets to call.enqueue it says OnDataCallback is null.
5.Goes back to Presenter and returns a null List.
6.It resumes call.enqueue does the api call as required.
7.Returns to Presenter and returns the List of objects.
I have tried so many things but non seem to work, maybe I'm understanding something incorrectly ,any help would be appreciated thanks.
Interactor Class.
public class MovieListInteractor implements IMovieContarctor.MovieListInteractor {
private InteractorListener mListener;
private List<MovieListModel> mList;
public MovieListInteractor(InteractorListener mListener) {
this.mListener = mListener;
}
public int getMovieSize() {
return mList.size();
}
#Override
public String getMoviePosterPath(int pos) {
return mList.get(pos).getPosterPath();
}
#Override
public MovieListModel getMovie(int pos) {
return mList.get(pos);
}
#Override
public List<MovieListModel> getMovieList() {
if (mList == null)
mList = new ArrayList<>();
ServerMovieCall mCall = new ServerMovieCall();
mCall.getMovieList(new IOnDataCallback<List<MovieListModel>>() {
#Override
public void onSuccess(List<MovieListModel> data) {
mList.addAll(data);
}
#Override
public void onFailure(String message) {
Log.d(TAG, "onFailure: " + message);
}
});
return mList;
}
}
Presenter class
public class MovieListPresenter implements IMovieContarctor.MovieListPresenter, InteractorListener {
private MovieListFragment mView;
private MovieListInteractor mInteractor;
private List<MovieListModel> mList;
public MovieListPresenter(MovieListFragment mView) {
this.mView = mView;
mInteractor = new MovieListInteractor(this);
getData();
}
//TODO: Add functionality so that the view could call mView.refreshAdapterList
//TODO: And from the view - adapter.notifiyDataSetChange;
#Override
public void onSuccess(List<MovieListModel> data, String msg) {
mView.onDataChange(data, msg);
}
#Override
public void onFailure(String msg) {
mView.onDataFailure(msg);
}
#Override
public void bindAtPosition(int position, MovieListViewHolder holder) {
holder.setValue(mInteractor.getMovie(position));
}
#Override
public void getData() {
mList = mInteractor.getMovieList();
}
#Override
public String getMoviePosterPath(int pos) {
return mInteractor.getMoviePosterPath(pos);
}
#Override
public int getSize() {
return mInteractor.getMovieSize();
}
}
Adapter
public class MovieListAdapter extends RecyclerView.Adapter<MovieListViewHolder> {
private MovieListPresenter mPresenter;
private Context ctx;
public MovieListAdapter(MovieListPresenter mPresenter, Context context) {
this.mPresenter = mPresenter;
this.ctx = context;
}
#Override
public MovieListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MovieListViewHolder(LayoutInflater.from(ctx).inflate(R.layout.main_view_movie_list, parent, false));
}
#Override
public void onBindViewHolder(MovieListViewHolder holder, int position) {
mPresenter.bindAtPosition(position, holder);
String image_url = IMAGE_URL_BASE_PATH + mPresenter.getMoviePosterPath(position);
Picasso.with(ctx).load(image_url).placeholder(android.R.drawable.sym_def_app_icon)
.error(android.R.drawable.sym_def_app_icon).into(holder.mImage);
}
#Override
public int getItemCount() {
return mPresenter.getSize();
}
}
MovieCall class
public class ServerMovieCall {
private Retrofit retrofit;
private static ServerMovieCall mInstance;
private Context ctx;
public static ServerMovieCall getInstance() {
if (mInstance == null) {
mInstance = new ServerMovieCall();
}
return mInstance;
}
public ServerMovieCall() {
configureClient();
}
private void configureClient() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(logging).addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl url = request.url().newBuilder().addQueryParameter("api_key", Constants.API_KEY).build();
request = request.newBuilder().url(url).build();
return chain.proceed(request);
}
}).build();
retrofit = new Retrofit.Builder().baseUrl(Constants.API_BASE_URL).client(client)
.addConverterFactory(GsonConverterFactory.create()).build();
}
public void getMovieList(final IOnDataCallback<List<MovieListModel>> onDataCallback) {
IServerMovieApiHelper iMovieReviewApiService = retrofit.create(IServerMovieApiHelper.class);
Call<MovieListResponse> call = iMovieReviewApiService.getMovieList();
call.enqueue(new Callback<MovieListResponse>() {
#Override
public void onResponse(Call<MovieListResponse> call, Response<MovieListResponse> response) {
if (response.isSuccessful()) {
onDataCallback.onSuccess(response.body().getMovies());
} else {
onDataCallback.onFailure(response.message());
}
}
#Override
public void onFailure(Call<MovieListResponse> call, Throwable t) {
onDataCallback.onFailure(t.getLocalizedMessage());
}
});
}
Contract interfaces
public interface IMovieContarctor {
public interface MovieListInteractor {
List<MovieListModel> getMovieList();
MovieListModel getMovie(int pos);
String getMoviePosterPath(int pos);
}
public interface MovieListPresenter {
void bindAtPosition(int position, MovieListViewHolder holder);
String getMoviePosterPath(int pos);
int getSize();
void getData();
}
public interface MovieListView {
void onDataChange(List<MovieListModel> data, String message);
void onDataFailure(String message);
}
}
DataCallback Listener.
public interface IOnDataCallback<T> {
void onSuccess(T data);
void onFailure(String message);
}
In Interactor class:
#Override
public List<MovieListModel> getMovieList() {
if (mList == null)
mList = new ArrayList<>();
ServerMovieCall mCall = new ServerMovieCall();
mCall.getMovieList(new IOnDataCallback<List<MovieListModel>>() {
#Override
public void onSuccess(List<MovieListModel> data) {
mList.addAll(data);
mListener.onSuccess(mList, "Your message");
}
#Override
public void onFailure(String message) {
Log.d(TAG, "onFailure: " + message);
}
});
return mList;
}
In Presenter class:
#Override
public void onSuccess(List<MovieListModel> data, String msg) {
mList = data;
mView.onDataChange(data, msg);
}
I wonder what is the correct pattern in android to show user data from database and in the meantime download data from server. Because users hate waiting for content in application i have to prioritize Database Fetcher, but i suppose there may be problems with synchronization in data (older data can replacing new).
My current code:
public class MyDataFragment extends Fragment implements LoaderManager.LoaderCallbacks<Object> {
private final static int LOADER_DOWNLOAD = 0x02;
private final static int LOADER_DATABASE = 0x03;
private SampleAdapter adapter;
private MainActivity mActivity;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = (MainActivity) activity;
}
public Context getContext() {
if (mActivity != null) {
return mActivity.getApplicationContext();
}
return null;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
adapter = new SampleAdapter();
getLoaderManager().restartLoader(LOADER_DATABASE, null, this);
getLoaderManager().restartLoader(LOADER_DOWNLOAD, null, this);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.profile_fragment, container, false);
// create listview and connect to adapter
return v;
}
#Override
public Loader<Object> onCreateLoader(int id, Bundle args) {
switch (id) {
case LOADER_DATABASE:
return new DataLoaderFromDatabase(getContext());
case LOADER_DOWNLOAD:
return new DataLoaderFromRequest(getContext());
}
return null;
}
#Override
public void onLoadFinished(Loader<Object> loader, Object data) {
List<Object> myObjectList = (List) data;
switch (loader.getId()) {
case LOADER_DOWNLOAD:
if (myObjectList.isEmpty()) {
showError();
} else {
adapter.setData(myObjectList);
}
break;
case LOADER_DATABASE:
if (!myObjectList.isEmpty()) {
adapter.setData(myObjectList);
}
break;
}
}
private void showError() {
Toast.makeText(getContext(), "Connection problem", Toast.LENGTH_SHORT).show();
}
#Override
public void onLoaderReset(Loader<Object> loader) {
}
public static class DataLoaderFromRequest extends BetterLoader {
public DataLoaderFromRequest(Context ctx) {
super(ctx);
}
#Override
public Object load() {
RestClient restClient = new RestClient();
//Send request to database
Response response = restClient.sendRequestForData();
if (response.isSucceed()) {
return response.getData();
} else {
return Collections.emptyList();
}
}
}
public static class DataLoaderFromDatabase extends BetterLoader {
public DataLoaderFromDatabase(Context ctx) {
super(ctx);
}
#Override
public Object load() {
//get data from Database
return new Select().all().from(MyModelData.class).queryList();
}
}
}