I have tried to implement a user list with pagination using paging library. But despite being able to fetch all data from back-end, the PagedList is null while observing LiveData.
Here are the codes.
UserModel.java
class User{
#SerializedName("user_id")
public int user_id;
#SerializedName("username")
public String username;
#SerializedName("name")
public String name;
#SerializedName("familyname")
public String familyname;
#SerializedName("pp_dir")
public String pp_url; }
public class UserModel {
#SerializedName("users")
public List<User> list;
#SerializedName("last_page")
public boolean lastPage;
#SerializedName("total_page")
public int totalPage;
#SerializedName("total_user_count")
public int totalUserCount;}
UserSearchDataSource.java
public class UserSearchDataSource extends ItemKeyedDataSource<Integer, User> {
public static final int PAGE_SIZE = 15;
private static final int FIRST_PAGE = 1;
#Override
public void loadInitial(#NonNull LoadInitialParams<Integer> params, #NonNull LoadInitialCallback<User> callback) {
RetrofitClient.getInstance().getApi().getAllUsers(FIRST_PAGE, PAGE_SIZE).enqueue(new Callback<UserModel>() {
#Override
public void onResponse(Call<UserModel> call, Response<UserModel> response) {
if (response.body() != null) {
callback.onResult(response.body().list);
System.out.println(response.body().list);
}
else {
System.out.println(response.code());
System.out.println(response.message());
}
}
#Override
public void onFailure(Call<UserModel> call, Throwable t) {
System.out.println(t.getMessage());
}
});
}
#Override
public void loadAfter(#NonNull LoadParams<Integer> params, #NonNull LoadCallback<User> callback) {
RetrofitClient.getInstance().getApi().getAllUsers(FIRST_PAGE, PAGE_SIZE).enqueue(new Callback<UserModel>() {
#Override
public void onResponse(Call<UserModel> call, Response<UserModel> response) {
if (response.body() != null) {
//Integer key = response.body().lastPage ? null : params.key + 1;
callback.onResult(response.body().list);
}
}
#Override
public void onFailure(Call<UserModel> call, Throwable t) {
}
});
}
#Override
public void loadBefore(#NonNull LoadParams<Integer> params, #NonNull LoadCallback<User> callback) {
RetrofitClient.getInstance().getApi().getAllUsers(FIRST_PAGE, PAGE_SIZE).enqueue(new Callback<UserModel>() {
#Override
public void onResponse(Call<UserModel> call, Response<UserModel> response) {
if (response.body() != null) {
//Integer key = (params.key > 1) ? params.key - 1 : null;
callback.onResult(response.body().list);
}
else {
System.out.println(response.message());
}
}
#Override
public void onFailure(Call<UserModel> call, Throwable t) {
System.out.println(t.getMessage());
}
});
}
#NonNull
#Override
public Integer getKey(#NonNull User item) {
return item.user_id;
} }
UserSearchDataSourceFactory.java
public class UserSearchDataSourceFactory extends DataSource.Factory<Integer, User> {
private MutableLiveData<UserSearchDataSource> userLiveDataSource = new MutableLiveData<>();
public UserSearchDataSourceFactory() {
}
#NonNull
#Override
public DataSource<Integer, User> create() {
UserSearchDataSource dataSource = new UserSearchDataSource();
userLiveDataSource.postValue(dataSource);
return dataSource;
}
public MutableLiveData<UserSearchDataSource> getUserLiveDataSource() {
return userLiveDataSource;
} }
UserSearchAdapter.java
public class UserSearchAdapter extends PagedListAdapter<User, UserSearchAdapter.ViewHolder> {
private Context context;
UserSearchAdapter(Context context) {
super(DIFF_CALLBACK);
this.context = context;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.user_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
User model = getItem(position);
if (model != null) {
Picasso.get().load(model.pp_url).into(holder.userPp);
holder.tvUsername.setText(model.username);
holder.tvFullname.setText(model.name.concat(" ").concat(model.familyname));
}
else {
Toast.makeText(context, "Item is null", Toast.LENGTH_LONG).show();
}
}
private static DiffUtil.ItemCallback<User> DIFF_CALLBACK = new DiffUtil.ItemCallback<User>() {
#Override
public boolean areItemsTheSame(#NonNull User oldItem, #NonNull User newItem) {
return oldItem.user_id == newItem.user_id;
}
#SuppressLint("DiffUtilEquals")
#Override
public boolean areContentsTheSame(#NonNull User oldItem, #NonNull User newItem) {
return oldItem.equals(newItem);
}
};
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView userPp;
TextView tvUsername, tvFullname;
public ViewHolder(#NonNull View itemView) {
super(itemView);
userPp = itemView.findViewById(R.id.image_user);
tvUsername = itemView.findViewById(R.id.tv_username);
tvFullname = itemView.findViewById(R.id.tv_fullname);
}
} }
UserSearchViewModel.java
public class UserSearchViewModel extends AndroidViewModel {
private LiveData<PagedList<User>> userPagedList;
LiveData<UserSearchDataSource> liveDataSource;
public UserSearchViewModel(Application application) {
super(application);
}
public LiveData<PagedList<User>> getUserPagedList() {
UserSearchDataSourceFactory userSearchDataSourceFactory = new UserSearchDataSourceFactory();
liveDataSource = userSearchDataSourceFactory.getUserLiveDataSource();
PagedList.Config config = (new PagedList.Config.Builder())
.setEnablePlaceholders(true)
.setPageSize(UserSearchDataSource.PAGE_SIZE)
.build();
userPagedList = new LivePagedListBuilder<>(userSearchDataSourceFactory, config).build();
return userPagedList;
} }
UserSearchFragment.java
mViewModel.getUserPagedList().observe(this, (PagedList<User> users) -> {
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setHasFixedSize(true);
adapter = new UserSearchAdapter(getContext());
adapter.submitList(users);
recyclerView.setAdapter(adapter);
//System.out.println(users.get(0).name);
});
Related
I'm making a simple app that has to make a call to an API that returns an object with some attributes and is shown in a RecyclerView.
The call is being made to https://jsonplaceholder.typicode.com/photos?_start=0&_limit=5
The app doesn't crash, the recyclerview is being generated but it is empty. I used the debugger and saw that the list in the adapter of the recyclerview is empty (the size is 0).
I believe the issue is with the structure of the java objects I made but I can't confirm it for sure and I can't seem to modify my object structure to match that of the returned object. I'm not seeing an object with other objects inside of like with other apis I've worked on (when I check the above link with a json online reader).
I usually make my object and another object container (which has a list of the first object). My suspicion is that the issue is there, please help me find the problem.
Below the main activity, object, object container, adapter, retrofit object, object dao and object controller.
Activity:
public class PhotoActivity extends AppCompatActivity implements AdapterPhotoRecyclerView.SelectedPhotoListener {
private AdapterPhotoRecyclerView adapterPhotoRecyclerView;
private RecyclerView recyclerView;
private ProgressBar progressBar;
private LinearLayoutManager linearLayoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photo);
linearLayoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
progressBar = findViewById(R.id.photo_activity_progress_bar);
makeCall("photos?_start=0&_limit=5");
adapterPhotoRecyclerView = new AdapterPhotoRecyclerView(this);
recyclerView = findViewById(R.id.photo_activity_recyclerview);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapterPhotoRecyclerView);
}
public void makeCall(String fixedUrl) {
MyPhotoController myPhotoController = new MyPhotoController();
myPhotoController.getPhotos(fixedUrl, new ResultListener<MyPhotoContainer>() {
#Override
public void finish(MyPhotoContainer result) {
progressBar.setVisibility(View.VISIBLE);
adapterPhotoRecyclerView.setMyPhotoList(result.getmPhotoList());
progressBar.setVisibility(View.GONE);
}
});
}
#Override
public void selectePhoto(Integer position, List<MyPhoto> myPhotoList) {
MyPhoto clickedPhoto = myPhotoList.get(position);
Toast.makeText(this, clickedPhoto.getTitle(), Toast.LENGTH_SHORT).show();
}
}
Adapter of the RecyclerView
public class AdapterPhotoRecyclerView extends RecyclerView.Adapter<AdapterPhotoRecyclerView.PhotoViewHolder> {
private List<MyPhoto> myPhotoList;
private SelectedPhotoListener selectedPhotoListener;
public AdapterPhotoRecyclerView(SelectedPhotoListener selectedPhotoListener) {
myPhotoList = new ArrayList<>();
this.selectedPhotoListener = selectedPhotoListener;
}
public void setMyPhotoList(List<MyPhoto> myPhotoList) {
this.myPhotoList = myPhotoList;
notifyDataSetChanged();
}
public List<MyPhoto> getMyPhotoList() {
return myPhotoList;
}
#NonNull
#Override
public PhotoViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_cell_photo, parent, false);
PhotoViewHolder photoViewHolder = new PhotoViewHolder(view);
return photoViewHolder;
}
#Override
public void onBindViewHolder(#NonNull PhotoViewHolder holder, int position) {
MyPhoto myPhoto = myPhotoList.get(position);
holder.bindPhoto(myPhoto);
}
#Override
public int getItemCount() {
if (myPhotoList == null){
return 0;
} else {
return myPhotoList.size();
}
}
public class PhotoViewHolder extends RecyclerView.ViewHolder {
private ImageView thumbnail;
private TextView title;
public PhotoViewHolder(#NonNull View itemView) {
super(itemView);
this.thumbnail = itemView.findViewById(R.id.recyclerview_cell_photo_thumbnail);
this.title = itemView.findViewById(R.id.recyclerview_cell_photo_title);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
selectedPhotoListener.selectePhoto(getAdapterPosition(), myPhotoList);
}
});
}
public void bindPhoto(MyPhoto myPhoto) {
Glide.with(itemView).load(myPhoto.getThumbnailUrl()).placeholder(R.mipmap.ic_launcher).into(thumbnail);
title.setText(myPhoto.getTitle());
}
}
public interface SelectedPhotoListener {
public void selectePhoto(Integer position, List<MyPhoto> myPhotoList);
}
}
Object dao
public class MyPhotoDao extends MyRetrofit {
private JsonPlaceholderService service;
public MyPhotoDao() {
super("https://jsonplaceholder.typicode.com/");
service = retrofit.create(JsonPlaceholderService.class);
}
public void getPhotos(String fixedUrl, final ResultListener<MyPhotoContainer> listenerOfTheController) {
Call<MyPhotoContainer> call = service.jsonPlaceholderPhoto(fixedUrl);
call.enqueue(new Callback<MyPhotoContainer>() {
#Override
public void onResponse(Call<MyPhotoContainer> call, Response<MyPhotoContainer> response) {
MyPhotoContainer myPhotoContainer = response.body();
listenerOfTheController.finish(myPhotoContainer);
}
#Override
public void onFailure(Call<MyPhotoContainer> call, Throwable t) {
}
});
}
public void getAlbum(String fixedUrl, final ResultListener<List<Album>> listenerOfTheController){
Call<List<Album>> call = service.jsonPlaceholderAlbum(fixedUrl);
call.enqueue(new Callback<List<Album>>() {
#Override
public void onResponse(Call<List<Album>> call, Response<List<Album>> response) {
List<Album> albumList = response.body();
listenerOfTheController.finish(albumList);
}
#Override
public void onFailure(Call<List<Album>> call, Throwable t) {
}
});
}
}
Object controller
public class MyPhotoController {
public void getPhotos(String fixedUrl, final ResultListener<MyPhotoContainer> listenerOfTheView) {
MyPhotoDao myPhotoDao = new MyPhotoDao();
myPhotoDao.getPhotos(fixedUrl, new ResultListener<MyPhotoContainer>() {
#Override
public void finish(MyPhotoContainer result) {
listenerOfTheView.finish(result);
}
});
}
public void getAlbums(String fixedUrl, final ResultListener<List<Album>> listenerOfTheView){
MyPhotoDao myPhotoDao = new MyPhotoDao();
myPhotoDao.getAlbum(fixedUrl, new ResultListener<List<Album>>() {
#Override
public void finish(List<Album> result) {
listenerOfTheView.finish(result);
}
});
}
}
Retrofit object
public abstract class MyRetrofit {
protected Retrofit retrofit;
public MyRetrofit(String baseUrl) {
OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient.build())
.addConverterFactory(GsonConverterFactory.create());
retrofit = builder.build();
}
}
Object I'm trying to GET
public class MyPhoto implements Serializable {
#SerializedName("AlbumId")
private Integer albumNumber;
#SerializedName("id")
private Integer photoId;
private String title;
#SerializedName("url")
private String photoUrl;
private String thumbnailUrl;
public Integer getAlbumNumber() {
return albumNumber;
}
public Integer getPhotoId() {
return photoId;
}
public String getTitle() {
return title;
}
public String getPhotoUrl() {
return photoUrl;
}
public String getThumbnailUrl() {
return thumbnailUrl;
}
}
Object container
public class MyPhotoContainer implements Serializable {
#SerializedName("array")
private List<MyPhoto> mPhotoList;
public List<MyPhoto> getmPhotoList() {
return mPhotoList;
}
}
If there is anything missing please let me know.
Any help and comments are apreciated!
JSON payload does not fit to POJO classes. You do not need to use MyPhotoContainer class at all. Response JSON is a JSON Array with directly placed JSON Objects. getPhotos method should look similar to getAlbum method. Try:
public void getPhotos(String fixedUrl, final ResultListener<List<MyPhoto>> listenerOfTheView)
I'm loading json file from online and saving it on Sqlite such that when app is offline...Then still user will be able to see the data.
It works fine in MainActivity.
But when I try to covert it into fragment, I'm getting errors of Fragment cannot be cast in to FlowerAdapter$FlowerClickListener
Here is the error file
My Fragment name is nepali.
Here is the Mainactivity
public class MainActivity extends AppCompatActivity implements FlowerAdapter.FlowerClickListener, FlowerFetchListener {
private static final String TAG = MainActivity.class.getSimpleName();
private RecyclerView mRecyclerView;
private RestManager mManager;
private FlowerAdapter mFlowerAdapter;
private FlowerDatabase mDatabase;
private Button mReload;
private ProgressDialog mDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
configViews();
mManager = new RestManager();
mDatabase = new FlowerDatabase(this);
loadFlowerFeed();
mReload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
loadFlowerFeed();
}
});
}
private void loadFlowerFeed() {
mDialog = new ProgressDialog(MainActivity.this);
mDialog.setMessage("Loading Flower Data...");
mDialog.setCancelable(true);
mDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mDialog.setIndeterminate(true);
mFlowerAdapter.reset();
mDialog.show();
if (getNetworkAvailability()) {
getFeed();
} else {
getFeedFromDatabase();
}
}
private void getFeedFromDatabase() {
mDatabase.fetchFlowers(this);
}
private void configViews() {
mReload = (Button) findViewById(R.id.reload);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool());
mRecyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.VERTICAL, false));
mFlowerAdapter = new FlowerAdapter(this);
mRecyclerView.setAdapter(mFlowerAdapter);
}
#Override
public void onClick(int position) {
}
public void getFeed() {
Call<List<Flower>> listCall = mManager.getFlowerService().getAllFlowers();
listCall.enqueue(new Callback<List<Flower>>() {
#Override
public void onResponse(Call<List<Flower>> call, Response<List<Flower>> response) {
if (response.isSuccessful()) {
List<Flower> flowerList = response.body();
for (int i = 0; i < flowerList.size(); i++) {
Flower flower = flowerList.get(i);
SaveIntoDatabase task = new SaveIntoDatabase();
task.execute(flower);
mFlowerAdapter.addFlower(flower);
}
} else {
int sc = response.code();
switch (sc) {
case 400:
Log.e("Error 400", "Bad Request");
break;
case 404:
Log.e("Error 404", "Not Found");
break;
default:
Log.e("Error", "Generic Error");
}
}
mDialog.dismiss();
}
#Override
public void onFailure(Call<List<Flower>> call, Throwable t) {
mDialog.dismiss();
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
public boolean getNetworkAvailability() {
return Utils.isNetworkAvailable(getApplicationContext());
}
#Override
public void onDeliverAllFlowers(List<Flower> flowers) {
}
#Override
public void onDeliverFlower(Flower flower) {
mFlowerAdapter.addFlower(flower);
}
#Override
public void onHideDialog() {
mDialog.dismiss();
}
public class SaveIntoDatabase extends AsyncTask<Flower, Void, Void> {
private final String TAG = SaveIntoDatabase.class.getSimpleName();
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Flower... params) {
Flower flower = params[0];
try {
} catch (Exception e) {
Log.d(TAG, e.getMessage());
}
return null;
}
}
}
and FlowerDatabase class is
public class FlowerDatabase extends SQLiteOpenHelper {
private static final String TAG = FlowerDatabase.class.getSimpleName();
public FlowerDatabase(Context context) {
super(context, Constants.DATABASE.DB_NAME, null, Constants.DATABASE.DB_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
try {
db.execSQL(Constants.DATABASE.CREATE_TABLE_QUERY);
} catch (SQLException ex) {
Log.d(TAG, ex.getMessage());
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(Constants.DATABASE.DROP_QUERY);
this.onCreate(db);
}
public void addFlower(Flower flower) {
Log.d(TAG, "Values Got " + flower.getName());
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(Constants.DATABASE.PRODUCT_ID, flower.getProductId());
values.put(Constants.DATABASE.CATEGORY, flower.getCategory());
values.put(Constants.DATABASE.PRICE, Double.toString(flower.getPrice()));
values.put(Constants.DATABASE.INSTRUCTIONS, flower.getInstructions());
values.put(Constants.DATABASE.NAME, flower.getName());
try {
db.insert(Constants.DATABASE.TABLE_NAME, null, values);
} catch (Exception e) {
}
db.close();
}
public void fetchFlowers(FlowerFetchListener listener) {
FlowerFetcher fetcher = new FlowerFetcher(listener, this.getWritableDatabase());
fetcher.start();
}
public class FlowerFetcher extends Thread {
private final FlowerFetchListener mListener;
private final SQLiteDatabase mDb;
public FlowerFetcher(FlowerFetchListener listener, SQLiteDatabase db) {
mListener = listener;
mDb = db;
}
#Override
public void run() {
Cursor cursor = mDb.rawQuery(Constants.DATABASE.GET_FLOWERS_QUERY, null);
final List<Flower> flowerList = new ArrayList<>();
if (cursor.getCount() > 0) {
if (cursor.moveToFirst()) {
do {
Flower flower = new Flower();
flower.setFromDatabase(true);
flower.setName(cursor.getString(cursor.getColumnIndex(Constants.DATABASE.NAME)));
flower.setPrice(Double.parseDouble(cursor.getString(cursor.getColumnIndex(Constants.DATABASE.PRICE))));
flower.setInstructions(cursor.getString(cursor.getColumnIndex(Constants.DATABASE.INSTRUCTIONS)));
flower.setCategory(cursor.getString(cursor.getColumnIndex(Constants.DATABASE.CATEGORY)));
flower.setProductId(Integer.parseInt(cursor.getString(cursor.getColumnIndex(Constants.DATABASE.PRODUCT_ID))));
flowerList.add(flower);
publishFlower(flower);
} while (cursor.moveToNext());
}
}
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
mListener.onDeliverAllFlowers(flowerList);
mListener.onHideDialog();
}
});
}
public void publishFlower(final Flower flower) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
mListener.onDeliverFlower(flower);
}
});
}
}
}
and FlowerAdapter Class is
public class FlowerAdapter extends RecyclerView.Adapter<FlowerAdapter.Holder> {
private static final String TAG = FlowerAdapter.class.getSimpleName();
private final FlowerClickListener mListener;
private List<Flower> mFlowers;
public FlowerAdapter(FlowerClickListener listener) {
mFlowers = new ArrayList<>();
mListener = listener;
}
#Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View row = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_item, null, false);
return new Holder(row);
}
#Override
public void onBindViewHolder(Holder holder, int position) {
Flower currFlower = mFlowers.get(position);
holder.mName.setText(currFlower.getName());
holder.minstruction.setText(currFlower.getInstructions());
}
#Override
public int getItemCount() {
return mFlowers.size();
}
public void addFlower(Flower flower) {
mFlowers.add(flower);
notifyDataSetChanged();
}
/**
* #param position
* #return
*/
public Flower getSelectedFlower(int position) {
return mFlowers.get(position);
}
public void reset() {
mFlowers.clear();
notifyDataSetChanged();
}
public class Holder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView mName, minstruction;
public Holder(View itemView) {
super(itemView);
mName = (TextView) itemView.findViewById(R.id.flowerName);
minstruction = (TextView) itemView.findViewById(R.id.flowerPrice);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
mListener.onClick(getLayoutPosition());
}
}
public interface FlowerClickListener {
void onClick(int position);
}
}
My fragment class is that I try to convert above Mainactivity code into Frament class is
public class nepali extends Fragment {
private static final String TAG = nepali.class.getSimpleName();
private RecyclerView mRecyclerView;
private RestManager mManager;
private FlowerAdapter mFlowerAdapter;
private FlowerDatabase mDatabase;
private Button mReload;
private ProgressDialog mDialog;
View view;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.activity_data, container, false);
configViews();
mManager = new RestManager();
mDatabase = new FlowerDatabase(getActivity());
loadFlowerFeed();
mReload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
loadFlowerFeed();
}
});
return view;
}
private void loadFlowerFeed() {
mDialog = new ProgressDialog(getActivity());
mDialog.setMessage("Loading Flower Data...");
mDialog.setCancelable(true);
mDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mDialog.setIndeterminate(true);
mFlowerAdapter.reset();
mDialog.show();
if (getNetworkAvailability()) {
getFeed();
} else {
getFeedFromDatabase();
}
}
private void getFeedFromDatabase() {
mDatabase.fetchFlowers(this);
}
private void configViews() {
mReload = (Button) view.findViewById(R.id.reload);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool());
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
mFlowerAdapter = new FlowerAdapter((FlowerAdapter.FlowerClickListener) this);;
mRecyclerView.setAdapter(mFlowerAdapter);
}
public void getFeed() {
Call<List<Flower>> listCall = mManager.getFlowerService().getAllFlowers();
listCall.enqueue(new Callback<List<Flower>>() {
#Override
public void onResponse(Call<List<Flower>> call, Response<List<Flower>> response) {
if (response.isSuccessful()) {
List<Flower> flowerList = response.body();
for (int i = 0; i < flowerList.size(); i++) {
Flower flower = flowerList.get(i);
SaveIntoDatabase task = new SaveIntoDatabase();
task.execute(flower);
mFlowerAdapter.addFlower(flower);
}
} else {
int sc = response.code();
switch (sc) {
case 400:
Log.e("Error 400", "Bad Request");
break;
case 404:
Log.e("Error 404", "Not Found");
break;
default:
Log.e("Error", "Generic Error");
}
}
mDialog.dismiss();
}
#Override
public void onFailure(Call<List<Flower>> call, Throwable t) {
mDialog.dismiss();
Toast.makeText(getActivity(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
public boolean getNetworkAvailability() {
return Utils.isNetworkAvailable(getActivity());
}
public void onDeliverFlower(Flower flower) {
mFlowerAdapter.addFlower(flower);
}
public void onHideDialog() {
mDialog.dismiss();
}
public class SaveIntoDatabase extends AsyncTask<Flower, Void, Void> {
private final String TAG = SaveIntoDatabase.class.getSimpleName();
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Flower... params) {
Flower flower = params[0];
try {
} catch (Exception e) {
Log.d(TAG, e.getMessage());
}
return null;
}
}
}
Lastly the FlowerService class is
public interface FlowerService {
#GET("/routine/first.json")
Call<List<Flower>> getAllFlowers();
}
and FlowerFetchListener Class is
public interface FlowerFetchListener {
void onDeliverAllFlowers(List<Flower> flowers);
void onDeliverFlower(Flower flower);
void onHideDialog();
}
Please Help....and Thanks in advance.
You should not call db and network from activity or fragment. Try to learn MVVM architecture and use ViewModel to store the data from db or network. You may put a lot of effort making your app work but it will still lead to crashes (especially after you introduce fragment and call db and API from there). You will need to handle your data state during configuration changes. Listen to this talk and start writing clean code https://m.youtube.com/watch?v=5qlIPTDE274
The error you get is because you pass wrong parameter into adapter constructor ‘new FlowerAdapter((FlowerAdapter.FlowerClickListener) this)’. If you want to pass a listener to the adapter you need to pass the class which implements the listener: either activity - then pass getActivity(), or fragment ‘this’ - then make fragment implement implements FlowerAdapter.FlowerClickListener.Be aware that it can be null when fragment is not attached to activity, eg configuration change.
This is the event object that I want to inflate with RecyclerView:
public class Event {
private String mName;
private String mId;
private String mDate;
private String mPlace;
private User mUser;
private Category mCat;
private String mDescription;
public void setmEventCat(Map<String, Category> mEventCat) {
this.mEventCat = mEventCat;
}
public Map<String, Category> getmEventCat() {
return mEventCat;
}
private Map<String,Category> mEventCat;
public String getmDescription() {
return mDescription;
}
public void setDescription(String mDescription) {
this.mDescription = mDescription;
}
public Category getCat() {
return mCat;
}
public void setCat(Category mCat) {
this.mCat = mCat;
}
public String getDate() {
return mDate;
}
public String getPlace() {
return mPlace;
}
private ArrayList<User> mList;
public Event() {
}
public String getName() {
return mName;
}
public void setName(String mName) {
this.mName = mName;
}
public void setDate(String mDate) {
this.mDate = mDate;
}
public void setPlace(String mPlace) {
this.mPlace = mPlace;
}
public String getId() {
return mId;
}
public void setId(String mId) {
this.mId = mId;
}
}
The nested Category object:
public class Category implements Serializable{
private String mCatName;
private String mCatID;
public Category() {
}
public Category(String mCatName) {
this.mCatName = mCatName;
}
public String getCatName() {
return mCatName;
}
public String getCatID() {
return mCatID;
}
public void setCatName(String mCatName) {
this.mCatName = mCatName;
}
public void setCatID(String mCatID) {
this.mCatID = mCatID;
}
}
How I retrieve the data from firebase:
mDataBase = FirebaseDatabase.getInstance().getReference("Event");
mEvents = new ArrayList<Event>();
mEvent=new Event();
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
mDataBase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot != null && dataSnapshot.getValue() != null) {
try {
for (DataSnapshot eventSnapshot : dataSnapshot.getChildren()) {
Event event = eventSnapshot.getValue(Event.class);
String id = eventSnapshot.getKey();
mEvents.add(event);
mRecyclerView.scrollToPosition(mEvents.size() - 1);
mAdapter.notifyItemInserted(mEvents.size() - 1);
}
}
catch (Exception ex) {
Log.e("ERROR", ex.getMessage());
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
mAdapter = new EventAdapter(mContext, mEvents);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
My EventAdapter:
#Override
public void onBindViewHolder(EventAdapter.ViewHolder holder, int position) {
mEvent = mEvents.get(position);
holder.mEventName.setText(mEvent.getName());
//Every time I tried to add this line to set category name
//the NullPointerException error occurs
holder.mEventCategory.setText(mEvent.getCat().getCatName());
holder.mEventDate.setText(mEvent.getDate());
}
The ViewHolder Class:
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView mEventName, mEventDate,mEventCategory;
public ViewHolder(View itemView) {
super(itemView);
mEventName = itemView.findViewById(R.id.eventName_tv);
mEventDate = itemView.findViewById(R.id.date_tv);
mEventCategory = itemView.findViewById(R.id.categoryTv);
}
}
#Override
public EventAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.search_event, parent, false);
return new ViewHolder(view);
}
The problem is the Event is displayed as I expected, but I cannot get the category inside the event and bind it to my widget by simply calling the getCat(). I know this may caused by asynchronous Firebase API. How can I set up the TextView with the nested category object.
This is my FINAL piece of my application, any hints would be a great help.
Thanks in advance!
I figure out the question that may be useful for other people. Before adding the event object to the list, I retrieve the nested object by key and assign it to category object. Finally, linking it with event...
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot != null && dataSnapshot.getValue() != null) {
try {
for (DataSnapshot eventSnapshot : dataSnapshot.getChildren()) {
Event event = eventSnapshot.getValue(Event.class);
String id = eventSnapshot.getKey();
mCategory = eventSnapshot.child(id).getValue(Category.class);
event.setCat(mCategory);
mEvents.add(event);
mRecyclerView.scrollToPosition(mEvents.size() - 1);
mAdapter.notifyItemInserted(mEvents.size() - 1);
}
}
catch (Exception ex) {
Log.e("ERROR", ex.getMessage());
}
}
}
Then binding the desired data with widget by calling the pojo getter method (nothing's changed here).
#Override
public void onBindViewHolder(EventAdapter.ViewHolder holder, int position) {
mEvent = mEvents.get(position);
holder.mEventName.setText(mEvent.getName());
holder.mEventCategory.setText(mEvent.getCat().getCatName());
holder.mEventDate.setText(mEvent.getDate());
}
In my Application in android i want to GET data from my web service to my recycle view, instead i got my data i got an error, here is my code
public class UserMenuActivity extends AppCompatActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_menu);
retrofit = new Retrofit.Builder().baseUrl("http://172.31.32.17:9290/")
.addConverterFactory(GsonConverterFactory.create())
.build();
afs = retrofit.create(AdminFullService.class);
responseLoginHome = (ResponseLoginHome) getIntent().getSerializableExtra("data");
initViews();
getCustomer = new getCustomer();
}
private void initViews() {
recyclerViewCustomer = (RecyclerView) findViewById(R.id.recycler_view_user_menu);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
recyclerViewCustomer.setLayoutManager(layoutManager);
recyclerViewCustomer.setHasFixedSize(true);
adapterCustomer = new DataAdapterCustomer(this, getCustomerList);
loadData();
}
private void loadData() {
Call<ResponseCustomerData> callCustomer = afs.customerDataView("bearer " + responseLoginHome.getData().getAuth_token());
callCustomer.enqueue(new Callback<ResponseCustomerData>() {
#Override
public void onResponse(Call<ResponseCustomerData> call, Response<ResponseCustomerData> response) {
ResponseCustomerData responseCustomerData = response.body();
getCustomerList.clear();
getCustomerList.addAll(responseCustomerData.getData()); //this is where i got error in my logcat
recyclerViewCustomer.setAdapter(adapterCustomer);
adapterCustomer.notifyDataSetChanged();
getCustomer.getTanggalLahir().substring(0,10);
Toast.makeText(UserMenu.this, getCustomer.getTanggalLahir(), Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<ResponseCustomerData> call, Throwable t) {
Log.d("Error", t.getMessage());
}
});
}
}
and i have got this error
java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.List com.example.ipul.aprovementfullservice.Model.ResponseCustomerData.getData()' on a null object reference
at com.example.ipul.aprovementfullservice.UI.UserMenu$1.onResponse(UserMenu.java:77)
when i use my debug mode the responseCustomerData is null, and then i got confused why i got error like this, i create my model response just like my web service ask
sorry for bad grammar, English is not my native language
So i hope you all hand me your help, thank you
EDIT this is my list getCustomer
public class getCustomer{
public String getNoHP() {
return noHP;
}
public void setNoHP(String noHP) {
this.noHP = noHP;
}
public String getRegistrationDate() {
return registrationDate;
}
public void setRegistrationDate(String registrationDate) {
this.registrationDate = registrationDate;
}
public String getNama() {
return nama;
}
public void setNama(String nama) {
this.nama = nama;
}
public String getNoID() {
return noID;
}
public void setNoID(String noID) {
this.noID = noID;
}
public String getJenisID() {
return jenisID;
}
public void setJenisID(String jenisID) {
this.jenisID = jenisID;
}
public String getTanggalLahir() {
return tanggalLahir;
}
public void setTanggalLahir(String tanggalLahir) {
this.tanggalLahir = tanggalLahir;
}
public String getJenisKelamin() {
return jenisKelamin;
}
public void setJenisKelamin(String jenisKelamin) {
this.jenisKelamin = jenisKelamin;
}
public String getNamaIbuKandung() {
return namaIbuKandung;
}
public void setNamaIbuKandung(String namaIbuKandung) {
this.namaIbuKandung = namaIbuKandung;
}
public String getAlamat() {
return alamat;
}
public void setAlamat(String alamat) {
this.alamat = alamat;
}
public String getProvinsi() {
return provinsi;
}
public void setProvinsi(String provinsi) {
this.provinsi = provinsi;
}
public String getKota() {
return kota;
}
public void setKota(String kota) {
this.kota = kota;
}
public String getKecamatan() {
return kecamatan;
}
public void setKecamatan(String kecamatan) {
this.kecamatan = kecamatan;
}
public String getKelurahan() {
return kelurahan;
}
public void setKelurahan(String kelurahan) {
this.kelurahan = kelurahan;
}
public String getKodepos() {
return kodepos;
}
public void setKodepos(String kodepos) {
this.kodepos = kodepos;
}
public String getAlamatDomosili() {
return alamatDomosili;
}
public void setAlamatDomosili(String alamatDomosili) {
this.alamatDomosili = alamatDomosili;
}
public String getProvinsiDomisili() {
return provinsiDomisili;
}
public void setProvinsiDomisili(String provinsiDomisili) {
this.provinsiDomisili = provinsiDomisili;
}
public String getKotaDomisili() {
return kotaDomisili;
}
public void setKotaDomisili(String kotaDomisili) {
this.kotaDomisili = kotaDomisili;
}
public String getKecamatanDomisili() {
return kecamatanDomisili;
}
public void setKecamatanDomisili(String kecamatanDomisili) {
this.kecamatanDomisili = kecamatanDomisili;
}
public String getKelurahanDomisili() {
return kelurahanDomisili;
}
public void setKelurahanDomisili(String kelurahanDomisili) {
this.kelurahanDomisili = kelurahanDomisili;
}
public String getKodeposDomisili() {
return kodeposDomisili;
}
public void setKodeposDomisili(String kodeposDomisili) {
this.kodeposDomisili = kodeposDomisili;
}
public String getFotoID() {
return fotoID;
}
public void setFotoID(String fotoID) {
this.fotoID = fotoID;
}
public String getRegisterStatus() {
return registerStatus;
}
public void setRegisterStatus(String registerStatus) {
this.registerStatus = registerStatus;
}
public String getKeterangan() {
return keterangan;
}
public void setKeterangan(String keterangan) {
this.keterangan = keterangan;
}
public String getFotoTandaTangan() {
return fotoTandaTangan;
}
public void setFotoTandaTangan(String fotoTandaTangan) {
this.fotoTandaTangan = fotoTandaTangan;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
and this my model ResponseCustomerData
public class ResponseCustomerData{
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getSuccess() {
return success;
}
public void setSuccess(String success) {
this.success = success;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public List<getCustomer> getData() {
return data;
}
public void setData(List<getCustomer> data) {
this.data = data;
}
}
Please try this, you have to check if your response is null or not.
Call<ResponseCustomerData> callCustomer = afs.customerDataView("bearer " + responseLoginHome.getData().getAuth_token());
callCustomer.enqueue(new Callback<ResponseCustomerData>() {
#Override
public void onResponse(Call<ResponseCustomerData> call, Response<ResponseCustomerData> response) {
ResponseCustomerData responseCustomerData = response.body();
getCustomerList.clear();
if(responseCustomerData!=null){ // check data retrive or not
if(responseCustomerData.getData()!=null){
getCustomerList.addAll(responseCustomerData.getData());
recyclerViewCustomer.setAdapter(adapterCustomer);
adapterCustomer.notifyDataSetChanged();
getCustomer.getTanggalLahir().substring(0,10);
Toast.makeText(UserMenu.this, getCustomer.getTanggalLahir(), Toast.LENGTH_SHORT).show();
} else {
Log.d("Error", responseCustomerData.getError());
}
}
}
#Override
public void onFailure(Call<ResponseCustomerData> call, Throwable t) {
Log.d("Error", t.getMessage());
}
});
I am trying to write an object in my startup activty to Parcel then send it to my Main activity using an intent. However I get a null pointer exception when retrieving the ParacelableArrayExtra in my Main activity. So i put in a check for null to handle the the null condition. However, the forecast Object does have data before being put into the intent so this condition should not be met.
I seems as though the Array of objects never gets put inside the intent. I this because I have my data being parceled incorrectly?
StartUpActivity.java
public class StartUpActivity extends AppCompatActivity {
public static final String FORECAST_KEY = "FORECAST_KEY";
private Forecast[] mForecasts;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new BackgroundTask(this).execute();
}
#Override
public void startActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
}
private class BackgroundTask extends AsyncTask {
private Intent mIntent;
private Context mContext;
private static final String TAG = "BACKGROUND_TASK";
public BackgroundTask(Context context) {
mContext = context;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mIntent = new Intent(StartUpActivity.this, MainActivity.class);
}
#Override
protected Object doInBackground(Object[] params) {
mForecasts = getForecasts();
return null;
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
// mForecasts is populated and does contain data
mIntent.putExtra(StartUpActivity.FORECAST_KEY, mForecasts);
// mForecasts in not located in mIntent
startActivity(mIntent);
finish();
}
// Other methods omitted
}
}
MainActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
setSupportActionBar(mToolbar);
intent.getParcelableArrayExtra(StartUpActivity.FORECAST_KEY);
Parcelable[] allForecastParcelables = getIntent().getParcelableArrayExtra(StartUpActivity.FORECAST_KEY);
if (allForecastParcelables != null) {
mForecasts = new Forecast[allForecastParcelables.length];
for (int i = 0 ; i < allForecastParcelables.length; i++) {
mForecasts[i] = (Forecast) allForecastParcelables[i];
}
}
else {
mForecasts = null;
}
setupSlidingTabs();
}
Here are my Model objects that implement Parcelable
Forecast.java
public class Forecast implements Parcelable{
Day[] mDays;
public Day[] getDailyForecast() {
return mDays;
}
public void setDailyForecast(Day[] days) {
mDays = days;
}
#Override
public int describeContents() {
return 0;
}
public Forecast() {}
private Forecast(Parcel in) {
in.readTypedArray(mDays, Day.CREATOR);
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedArray(mDays, flags);
}
public static final Creator<Forecast> CREATOR = new Creator<Forecast>() {
#Override
public Forecast createFromParcel(Parcel source) {
return new Forecast(source);
}
#Override
public Forecast[] newArray(int size) {
return new Forecast[size];
}
};
}
Day.java
public class Day implements Parcelable {
private Hour[] mHours;
private Average mAverage;
public Hour[] getHourlyForecast() {
return mHours;
}
public void setHourlyForecast(Hour[] hours) {
mHours = hours;
}
public Average getAverageForecast() {
return mAverage;
}
public void setAverageForecast(Average average) {
mAverage = average;
}
#Override
public int describeContents() {
return 0;
}
public Day() {}
private Day(Parcel in) {
in.readTypedArray(mHours, Hour.CREATOR);
mAverage = in.readParcelable(getClass().getClassLoader());
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedArray(mHours, flags);
dest.writeParcelable(mAverage, flags);
}
public static final Creator<Day> CREATOR = new Creator<Day>() {
#Override
public Day createFromParcel(Parcel source) {
return new Day(source);
}
#Override
public Day[] newArray(int size) {
return new Day[size];
}
};
}
Average.java
public class Average implements Parcelable {
private String mWindCompassDireciton;
public String getWindCompassDireciton() {
return mWindCompassDireciton;
}
public void setWindCompassDireciton(String windCompassDireciton) {
mWindCompassDireciton = windCompassDireciton;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mWindCompassDireciton);
}
public Average() {}
private Average(Parcel in) {
mWindCompassDireciton = in.readString();
}
public static final Creator<Average> CREATOR = new Creator<Average>() {
#Override
public Average createFromParcel(Parcel source) {
return new Average(source);
}
#Override
public Average[] newArray(int size) {
return new Average[size];
}
};
}
And Hour is similar to average.java
Is there anything I am overlooking?
Each ArrayList and object inside the parcelable object should be peaceable too, Check this example. but i recommend you to send GSON and convert it to String and send it between activities as a string and convert it again to your object, Check this example.