How to store and retrive RecyclerView Items for offline use - java

Guys please dont make this question as a duplicate as I have not found any simple or easy implementation of realm in my app. I am in the process of creating a chat app and what I want to do is make it possible for the user to be able to access and read earlier messages that he received even without internet connection.
In short I want my app to be still accessible without an internet connection but am finding it difficult doing that as am new to local data storage.
Below are my codes for your perusal:
Fragment
public class ChatFragment extends Fragment {
public RecyclerView mChatsList;
public View mView;
public List<ChatsModel> mChatsModel;
public ChatsAdapter mChatsAdapter;
private Realm realm;
public ChatFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
mView = inflater.inflate(R.layout.fragment_chat, container, false);
((AppCompatActivity) getActivity()).getSupportActionBar().setShowHideAnimationEnabled(true);
initUI();
realm = Realm.getDefaultInstance();
return mView;
}
//Method and views initializer
public void initUI() {
mChatsList = (RecyclerView) mView.findViewById(R.id.chatsList);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
mChatsModel = new ArrayList<ChatsModel>();
mChatsAdapter = new ChatsAdapter(getActivity(), mChatsModel);
mChatsList.setLayoutManager(layoutManager);
mChatsList.setHasFixedSize(true);
mChatsAdapter.notifyDataSetChanged();
mChatsList.setAdapter(mChatsAdapter);
RecyclerView.ItemAnimator itemAnimator = new DefaultItemAnimator();
itemAnimator.setAddDuration(1000);
itemAnimator.setRemoveDuration(1000);
mChatsList.setItemAnimator(itemAnimator);
prepareItems();
Realm realm = Realm.getInstance(getActivity());
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
ChatsModel chat = realm.createObject(ChatsModel.class);
chat.setUsername("username");
chat.setDate("date");
chat.setPicture("Picture");
}
}, new Realm.Transaction.Callback() {
#Override
public void onSuccess() {
Main.Mess(getString(R.string.real_sucess));
}
#Override
public void onError(Exception e) {
Main.Mess(getString(R.string.real_error));
}
});
}
// This is a simple method to add items to our recyclerview
private void prepareItems() {
Rests mRests = RestService.createService(Rests.class, Session.getToken(getActivity()));
mRests.suggest(new Callback<List<ChatsModel>>() {
#Override
public void success(List<ChatsModel> mChatsModel, Response response) {
RealmList<ChatsModel> mChatsModel2 = new RealmList<ChatsModel>();
mChatsAdapter.setUsers(mChatsModel);
}
#Override
public void failure(RetrofitError error) {
Main.Mess(getString(R.string.server_error));
}
});
}
}
My Adapter
public class ChatsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private RealmList<ChatsModel> mChatsModel;
private Realm realm;
public Activity mActivity;
public ChatsAdapter(#NonNull Activity mActivity) {
super();
this.mChatsModel = new RealmList<>();
this.realm = Realm.getDefaultInstance();
this.mActivity = mActivity;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder (ViewGroup parent, int viewType) {
if(viewType == TYPE_HEADER) {
View v = LayoutInflater.from (parent.getContext ()).inflate (R.layout.ad_view, parent, false);
return new HeaderViewHolder (v);
} else if(viewType == TYPE_ITEM) {
View v = LayoutInflater.from (parent.getContext ()).inflate (R.layout.chats_item, parent, false);
return new ContentViewHolder (v);
}
return null;
}
private ChatsModel getItem (int position) {
return mChatsModel.get (position);
}
#Override
public void onBindViewHolder (RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof HeaderViewHolder) {
HeaderViewHolder headerHolder = (HeaderViewHolder) holder;
if (Constant.SHOW_ADS) {
headerHolder.mAdView.setVisibility(View.VISIBLE);
AdRequest adRequest = new AdRequest.Builder().build();
headerHolder.mAdView.loadAd(adRequest);
} else {
headerHolder.mAdView.setVisibility(View.GONE);
}
}else if (holder instanceof ContentViewHolder) {
ContentViewHolder contentHolder = (ContentViewHolder) holder;
ChatsModel item = getItem (position - 1);
contentHolder.username.setText(item.getUsername());
contentHolder.date.setText(item.getDate());
contentHolder.message.setText(item.getMessage());
Picasso.with(mActivity.getApplicationContext())
.load(Constant.IMAGE_SMALL + item.getPicture())
.error(R.drawable.user)
.into(contentHolder.picture);
}
}
#Override
public int getItemViewType (int position) {
if(isPositionHeader (position)) {
return TYPE_HEADER;
}
return TYPE_ITEM;
}
public void setUsers(RealmList<ChatsModel> friendsItems) {
this.mChatsModel = friendsItems;
notifyDataSetChanged();
}
public List<ChatsModel> getSuggestionsModel() {
return this.mChatsModel;
}
private boolean isPositionHeader (int position) {
return position == 0;
}
#Override
public int getItemCount () {
return mChatsModel.size ();
}
public class HeaderViewHolder extends RecyclerView.ViewHolder {
public AdView mAdView;
public HeaderViewHolder(View itemView) {
super(itemView);
mAdView = (AdView) itemView.findViewById(R.id.ad_view);
}
}
public class ContentViewHolder extends RecyclerView.ViewHolder {
public ImageView picture;
public TextView username, date, message;
public LinearLayout chat;
public ContentViewHolder(View v) {
super(v);
picture = (ImageView) v.findViewById(R.id.picture);
username = (TextView) v.findViewById(R.id.username);
date = (TextView) v.findViewById(R.id.date);
message = (TextView) v.findViewById(R.id.message);
}
}
}
Okay so thats my code.

You can use a SQLite database for storing the chats offline, watch this YouTube tutorial, it is extensive but you can skip through some parts.
When downloading/sending chat messages, mirror them into your database and use the database and ONLY THE DATABASE as the data source for your RecyclerView (never let any online data directly go into the list, always store it in the database first and read it from the database when putting it into your layout).
To improve performance, you can store relevant chat messages in memory (in a separate ArrayList for example) instead of always reading data from the DB that you just wrote into it.

As #geisshirt says in comment, use RealmRecyclerViewAdapter instead of RecyclerView.Adapter. Couple words about android realm adapters you can found in official doc.
Also, you can look at RealmRecyclerViewAdapter example

Related

Show data in Nested Recyclerview in android

As I fetched and show the dates (see image) as the title of the main recyclerview. I want to show the available slots data instead of the 0 1 2 etc elements. The code is attached below
Url for json data
https://run.mocky.io/v3/c9bd7858-0e41-422f-b1d2-cd490c08583b
AppointmentTimeActivity.java
public class AppointmentTimeActivity extends AppCompatActivity {
SharedPrefManager sharedPrefManager;
Button appointmentTimeButton;
private TextView doctorFullName;
ConstraintLayout constraintLayout;
public static List<List<String>> availableSlots;
RecyclerView rvGroup;
public static ArrayList<String> arrayListGroup;
LinearLayoutManager layoutManagerGroup;
GroupAdapter groupAdapter;
private DoctorScheduleResponse timings;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.select_appointment_time);
getSupportActionBar().setTitle("Appointment time");
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
sharedPrefManager = new SharedPrefManager(this);
constraintLayout = findViewById(R.id.constraintLayout);
appointmentTimeButton = findViewById(R.id.book_video_call_appointment_btn);
doctorFullName = findViewById(R.id.doctor_full_name);
rvGroup = findViewById(R.id.rv_group);
String docName = getIntent().getStringExtra("doctorFullName");
doctorFullName.setText(docName);
arrayListGroup = new ArrayList<>();
fetchAndShowAppointmentsTime();
}
private void fetchAndShowAppointmentsTime() {
String id = String.valueOf(SpecialityActivity.doctorID);
Call<DoctorScheduleResponse> call = RetrofitClient.getInstance().getMyInterface().getAppointmentTime("Bearer " + sharedPrefManager.getAccessToken(), id);
call.enqueue(new Callback<DoctorScheduleResponse>() {
#Override
public void onResponse(#NotNull Call<DoctorScheduleResponse> call, #NotNull Response<DoctorScheduleResponse> response) {
arrayListGroup = new ArrayList<>();
if (response.isSuccessful()) {
assert response.body() != null;
for (List<Slot> slots : response.body().getSlot()) {
for (Slot slot : slots) {
arrayListGroup.add(slot.getScheduleDate());
}
}
groupAdapter = new GroupAdapter(AppointmentTimeActivity.this, response.body().getSlot());
layoutManagerGroup = new LinearLayoutManager(getApplicationContext());
rvGroup.setLayoutManager(layoutManagerGroup);
rvGroup.setAdapter(groupAdapter);
}
}
#Override
public void onFailure(#NotNull Call<DoctorScheduleResponse> call, #NotNull Throwable t) {
Toast.makeText(getBaseContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
DoctorScheduleResponse.java
public class DoctorScheduleResponse {
#SerializedName("slot")
#Expose
private List<List<Slot>> slot = null;
public List<List<Slot>> getSlot() {
return slot;
}
public void setSlot(List<List<Slot>> slot) {
this.slot = slot;
}
}
GroupAdater.java
public class GroupAdapter extends RecyclerView.Adapter<GroupAdapter.ViewHolder> {
private Activity activity;
ArrayList<String> arrayListGroup, arrayListMember;
LinearLayoutManager linearLayoutManager;
SharedPrefManager sharedPrefManager;
List<List<Slot>> slotsList;
public GroupAdapter(Activity activity, ArrayList<String> arrayListGroup) {
this.activity = activity;
this.arrayListGroup = arrayListGroup;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.custom_slot_layout, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.dummyTV.setText(arrayListGroup.get(position));
arrayListMember = new ArrayList<>();
sharedPrefManager = new SharedPrefManager(activity);
for (int i = 0; i < 5; i++) {
arrayListMember.add(String.valueOf(i));
}
CustomAdapter customAdapter = new CustomAdapter(arrayListMember);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(activity);
holder.rvMember.setLayoutManager(linearLayoutManager);
holder.rvMember.setAdapter(customAdapter);
}
#Override
public int getItemCount() {
return arrayListGroup.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView dummyTV;
RecyclerView rvMember;
public ViewHolder(#NonNull View itemView) {
super(itemView);
dummyTV = itemView.findViewById(R.id.dummyTextView);
rvMember = itemView.findViewById(R.id.rv_member);
}
}
}
CustomAdapter.java
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.SlotsViewHolder> {
ArrayList<String> slots;
public CustomAdapter(ArrayList<String> slots) {
this.slots = slots;
}
#NonNull
#Override
public SlotsViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.available_slots_list, parent, false);
return new CustomAdapter.SlotsViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull SlotsViewHolder holder, int position) {
holder.tvSlots.setText(slots.get(position));
}
#Override
public int getItemCount() {
return slots.size();
}
public class SlotsViewHolder extends RecyclerView.ViewHolder {
TextView tvSlots;
public SlotsViewHolder(#NonNull View itemView) {
super(itemView);
tvSlots = itemView.findViewById(R.id.tv_slots);
}
}
}
Nested recycler view will be more complex in terms of memory. So you can achieve same UI with different approach.
You should try with single recycler view with dynamic layout binding.
Example: For single item of recycler view, there will be a header(for date) and a viewgroup(may be linear layout) then bind any no. of child views inside that linear layout.
The problem is in GroupAdater.java. Inside onBindViewHolder method you are adding the value of loop variable to your list that you pass to your nested recycler view adaptor.
for (int i = 0; i < 5; i++) {
arrayListMember.add(String.valueOf(i));
}
You have to add the correct date from arrayListGroup object.

Change recyclerview adapter items from own activity after clicking on button inside adapter

I built a simple quiz app with sqlite database
There is some quiz headers and into this quiz headers we have some questions that show by recycler view. All questions have one question title and 4 answers and one correct answer. user chooses the radio button answers and after that click on confirm button that is located as item at the bottom of recycler view.
I can catch the correct answer with a simple way and send it to the activity with an interface. But i want to show the correct and wrong answers with changing the radio buttons color but I can't create another method and change the view holder items because I can't access to the view holders outside of 'onBindViewHolder' method . I can handle this with another adapter . I mean I can create a fake adapter that just show answers . Is it a right way ?
This is my code. It's a little messy. Sorry about that
public class QuestionRecyclerAdapter extends RecyclerView.Adapter<QuestionRecyclerAdapter.ViewHolder> {
private Context context;
private List<QuestionHolder> questionHolders;
private OnQuestionAnswerSelect onQuestionAnswerSelect;
private OnConfirmButtonClicked onConfirmButtonClicked;
public QuestionRecyclerAdapter(Context context, List<QuestionHolder> questionHolders) {
this.context = context;
this.questionHolders = questionHolders;
}
public void setOnQuestionAnswerSelect(OnQuestionAnswerSelect onQuestionAnswerSelect) {
this.onQuestionAnswerSelect = onQuestionAnswerSelect;
}
public void setOnConfirmButtonClicked(OnConfirmButtonClicked onConfirmButtonClicked){
this.onConfirmButtonClicked = onConfirmButtonClicked;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView;
if (viewType == R.layout.question_item)
itemView = LayoutInflater.from(context).inflate(R.layout.question_item, parent, false);
else
itemView = LayoutInflater.from(context).inflate(R.layout.question_recycler_confirm_button, parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
if (position != questionHolders.size()) {
QuestionModel currentModel = questionHolders.get(position).getQuestionModel();
holder.txtQuestion.setText(currentModel.getTitle());
holder.rBtnAnswer1.setText(currentModel.getOption1());
holder.rBtnAnswer2.setText(currentModel.getOption2());
holder.rBtnAnswer3.setText(currentModel.getOption3());
holder.rBtnAnswer4.setText(currentModel.getOption4());
if (onQuestionAnswerSelect != null) {
holder.questionRadioGroup.setOnCheckedChangeListener((v, i) -> {
RadioButton rBtnSelected = holder.questionRadioGroup.findViewById(holder.questionRadioGroup.getCheckedRadioButtonId());
int selectedRadioIndex = holder.questionRadioGroup.indexOfChild(rBtnSelected) + 1;
if (selectedRadioIndex == questionHolders.get(position).getQuestionModel().getCorrectNumber()) {
onQuestionAnswerSelect.onAnswerSelected(questionHolders.get(position).get_id(), true);
} else {
onQuestionAnswerSelect.onAnswerSelected(questionHolders.get(position).get_id(), false);
}
});
}
}else {
holder.btnConfirm.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (onConfirmButtonClicked != null)
onConfirmButtonClicked.onConfirmClicked();
}
});
}
}
#Override
public int getItemCount() {
return questionHolders.size() + 1;
}
#Override
public int getItemViewType(int position) {
return (position == questionHolders.size()) ? R.layout.question_recycler_confirm_button : R.layout.question_item;
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView txtQuestion;
public RadioGroup questionRadioGroup;
public RadioButton rBtnAnswer1;
public RadioButton rBtnAnswer2;
public RadioButton rBtnAnswer3;
public RadioButton rBtnAnswer4;
public Button btnConfirm;
public ViewHolder(#NonNull View itemView) {
super(itemView);
txtQuestion = itemView.findViewById(R.id.txtQuestion);
questionRadioGroup = itemView.findViewById(R.id.questionRadioGroup);
rBtnAnswer1 = itemView.findViewById(R.id.rBtnAnswer1);
rBtnAnswer2 = itemView.findViewById(R.id.rBtnAnswer2);
rBtnAnswer3 = itemView.findViewById(R.id.rBtnAnswer3);
rBtnAnswer4 = itemView.findViewById(R.id.rBtnAnswer4);
btnConfirm = itemView.findViewById(R.id.btnConfirm);
}
}
}
public class QuizActivity extends AppCompatActivity {
RecyclerView questionRecyclerView ;
QuestionRecyclerAdapter adapter ;
QuestionDatabaseHelper questionDatabaseHelper ;
Map<Integer,Boolean> answeredRecords = new HashMap<>();
int score ;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
questionRecyclerView = findViewById(R.id.questionRecyclerView);
questionDatabaseHelper = new QuestionDatabaseHelper(this);
int selectedId = getIntent().getIntExtra(Constants.SELECTED_ID,0);
score = 0 ;
List<QuestionHolder> questionHolders = questionDatabaseHelper.getAllQuestionHoldersById(selectedId);
adapter = new QuestionRecyclerAdapter(this,questionHolders);
questionRecyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
questionRecyclerView.setAdapter(adapter);
adapter.setOnQuestionAnswerSelect(new OnQuestionAnswerSelect() {
#Override
public void onAnswerSelected(int questionNumber, boolean isCorrect) {
answeredRecords.put(questionNumber,isCorrect);
}
});
adapter.setOnConfirmButtonClicked(new OnConfirmButtonClicked() {
#Override
public void onConfirmClicked() {
score = 0 ;
for(Map.Entry<Integer,Boolean> item : answeredRecords.entrySet()){
if (item.getValue())
score++;
}
Log.e("THE SCORE IS ", String.valueOf(score));
}
});
}
private void displayRecords(){
for(Map.Entry<Integer,Boolean> item : answeredRecords.entrySet()){
Log.e("AAA",item.getKey() + " : " + item.getValue());
}
}
}
Make a function in adapter and send holder to activity by calling that function.
first make viewholder named holder1.
ViewHolder holder1;
then at the onBindViewHolder method add this:
holder1 = holder;
public ViewHolder getHolder(){
return holder1;
}
now you can use it in your Activity like this:
adapter.getHolder.rBtnAnswer1.setBackgroundColor(Color.parseColor("#FFFFFF")); //this is a example.

attempting to use incompatible return type

Can someone help me with this error that I keep getting? The program that I'm trying to implement admob banner ad between items in recyclerview. every thing is ok but still this one error that blocked me from go on.
public class RecipeAdapter extends RecyclerView.Adapter<RecipeAdapter.ViewHolder> {
public static final String TAG = RecipeAdapter.class.getSimpleName();
public static final HashMap<String, Integer> LABEL_COLORS = new HashMap<String, Integer>() {{
put("Low-Carb", R.color.colorLowCarb);
put("Low-Fat", R.color.colorLowFat);
put("Low-Sodium", R.color.colorLowSodium);
put("Medium-Carb", R.color.colorMediumCarb);
put("Vegetarian", R.color.colorVegetarian);
put("Balanced", R.color.colorBalanced);
}};
private Context mContext;
private LayoutInflater mInflater;
private ArrayList<Recipe> mDataSource;
private static final int DEFAULT_VIEW_TYPE = 1;
private static final int NATIVE_AD_VIEW_TYPE = 2;
public RecipeAdapter(Context context, ArrayList<Recipe> items) {
mContext = context;
mDataSource = items;
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); }
#Override public int getItemViewType(int position) {
// Change the position of the ad displayed here. Current is after 5
if ((position + 1) % 6 == 0) {
return NATIVE_AD_VIEW_TYPE;
}
return DEFAULT_VIEW_TYPE; }
#NonNull
#Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
switch (viewType) {
default:
view = layoutInflater
.inflate(R.layout.list_item_native_ad, parent, false);
return new ViewHolder(view);
case NATIVE_AD_VIEW_TYPE:
view = layoutInflater.inflate(R.layout.list_item_native_ad, parent, false);
return new ViewHolderAdMob(view);
}
}
#Override public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
// Get relevant subviews of row view
TextView titleTextView = holder.titleTextView;
TextView subtitleTextView = holder.subtitleTextView;
TextView detailTextView = holder.detailTextView;
ImageView thumbnailImageView = holder.thumbnailImageView;
//Get corresponding recipe for row final Recipe recipe = (Recipe) getItem(position);
// Update row view's textviews to display recipe information
titleTextView.setText(recipe.title);
subtitleTextView.setText(recipe.description);
detailTextView.setText(recipe.label);
// Use Picasso to load the image. Temporarily have a placeholder in case it's slow to load
Picasso.with(mContext).load(recipe.imageUrl).placeholder(R.mipmap
.ic_launcher).into(thumbnailImageView);
holder.parentView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent detailIntent = new Intent(mContext, RecipeDetailActivity.class);
detailIntent.putExtra("title", recipe.title);
detailIntent.putExtra("url", recipe.instructionUrl);
mContext.startActivity(detailIntent);
}
});
// Style text views
Typeface titleTypeFace = Typeface.createFromAsset(mContext.getAssets(),
"fonts/JosefinSans-Bold.ttf");
titleTextView.setTypeface(titleTypeFace);
Typeface subtitleTypeFace = Typeface.createFromAsset(mContext.getAssets(),
"fonts/JosefinSans-SemiBoldItalic.ttf");
subtitleTextView.setTypeface(subtitleTypeFace);
Typeface detailTypeFace = Typeface.createFromAsset(mContext.getAssets(),
"fonts/Quicksand-Bold.otf");
detailTextView.setTypeface(detailTypeFace);
detailTextView.setTextColor(android.support.v4.content.ContextCompat.getColor(mContext, LABEL_COLORS
.get(recipe.label)));
}
#Override public int getItemCount() {
return mDataSource.size(); }
#Override public long getItemId(int position) {
return position; }
public Object getItem(int position) {
return mDataSource.get(position); }
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView titleTextView;
private TextView subtitleTextView;
private TextView detailTextView;
private ImageView thumbnailImageView; private View parentView;
public ViewHolder(#NonNull View view){
super(view);
// create a new "Holder" with subviews
this.parentView = view;
this.thumbnailImageView = (ImageView) view.findViewById(R.id.recipe_list_thumbnail);
this.titleTextView = (TextView) view.findViewById(R.id.recipe_list_title);
this.subtitleTextView = (TextView) view.findViewById(R.id.recipe_list_subtitle);
this.detailTextView = (TextView) view.findViewById(R.id.recipe_list_detail);
// hang onto this holder for future recyclage
view.setTag(this);
}
}
public class ViewHolderAdMob extends RecyclerView.ViewHolder {
private final AdView mNativeAd;
public ViewHolderAdMob(View itemView) {
super(itemView);
mNativeAd = itemView.findViewById(R.id.nativeAd);
mNativeAd.setAdListener(new AdListener() {
#Override
public void onAdLoaded() {
super.onAdLoaded();
// if (mItemClickListener != null) {
Log.i("AndroidBash", "onAdLoaded");
// }
}
#Override
public void onAdClosed() {
super.onAdClosed();
// if (mItemClickListener != null) {
Log.i("AndroidBash", "onAdClosed");
// }
}
#Override
public void onAdFailedToLoad(int errorCode) {
super.onAdFailedToLoad(errorCode);
// if (mItemClickListener != null) {
Log.i("AndroidBash", "onAdFailedToLoad");
// }
}
#Override
public void onAdLeftApplication() {
super.onAdLeftApplication();
// if (mItemClickListener != null) {
Log.i("AndroidBash", "onAdLeftApplication");
// }
}
#Override
public void onAdOpened() {
super.onAdOpened();
// if (mItemClickListener != null) {
Log.i("AndroidBash", "onAdOpened");
// }
}
});
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice("") // Remove this before publishing app
.build();
mNativeAd.loadAd(adRequest);
}
}
}
The return type needs to be whatever ViewHolder type you declared for your adapter class.
For example, from the Android RecyclerView example page:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
// ^^^^^^^^^^^^^^^^^^^^^^
// It should return this type ^
public static class MyViewHolder extends RecyclerView.ViewHolder {
// your adapter
}
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {
// Note: returns MyAdapter.MyViewHolder, not RecyclerView.ViewHolder
}
}
In your case, you have
public class RecipeAdapter extends RecyclerView.Adapter<RecipeAdapter.ViewHolder>
which means your onCreateViewHolder has to return RecipeAdapter.ViewHolder not RecyclerView.ViewHolder.
There is a separate issue too, which is that you have two ViewHolder types in the same adapter. To do this, you would need to change the ViewHolder type that your RecyclerView is based on to the generic type (RecyclerView.ViewHolder).
Please review this question, it has good answers for how to do this.

Problems calling notifyDataSetChange in RecyclerViewHolder

I am having problems updating my RecyclerView with new data. If I press a confirmation button on a CardView in the first tab, the card should get added to the second tab but it won't update it there until I rotate the screen. I get the data for the card from reading a text file. Please advise me how to call the notifyDataSetChange method after I have added the new data to my text file. I have tried everything and all I get is NullPointerExceptions. The RecyclerViews are in fragments and I use FragementStatePagerAdapter.
I'll put some of my classes here. Ask if you need more information.
RecyclerViewAdapter.java
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolder> {
private List<String> mListTitle;
private List<String> mListDesc;
private List<String> mListPoints;
private List<String> mListDates;
private String fragment_tag;
public RecyclerViewAdapter() {
}
public RecyclerViewAdapter(List<List<String>> super_list, String tag) {
this.mListTitle = super_list.get(0);
this.mListDesc = super_list.get(1);
this.mListPoints = super_list.get(2);
this.mListDates = super_list.get(3);
fragment_tag = tag;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new RecyclerViewHolder(inflater, parent, fragment_tag);
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
holder.mTitleText.setText(mListTitle.get(position));
holder.mDescText.setText(mListDesc.get(position));
holder.mPointsText.setText(mListPoints.get(position));
if (fragment_tag.equals("completed")) {
holder.mDateText.setText(mListDates.get(position));
}
}
#Override
public int getItemCount() {
return mListTitle.size();
}
}
class RecyclerViewHolder extends RecyclerView.ViewHolder {
RecyclerView recyclerView;
RecyclerViewAdapter mAdapter;
public TextView mTitleText, mDescText, mDateText, mPointsText, popupTitle;
public Button mConfButton, popCancelBtn, popAcceptBtn;
public RecyclerViewHolder(View itemView) {
super(itemView);
}
public RecyclerViewHolder(final LayoutInflater inflater, final ViewGroup container, String tag) {
// Inflating the card layout depending on the tag parameter.
super(inflater.inflate
((tag.equals("challenges")) ? R.layout.card_view_chall : R.layout.card_view_comp, container,
false));
mTitleText = itemView.findViewById(R.id.title_holder);
mDescText = itemView.findViewById(R.id.desc_holder);
mPointsText = itemView.findViewById(R.id.points_holder);
mDateText = itemView.findViewById(R.id.date_holder);
if (tag.equals("challenges")) {
mConfButton = itemView.findViewById(R.id.card_conf_button);
mConfButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Setting the layout inflater for popup window.
LayoutInflater pInflater = (LayoutInflater) itemView.getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
ViewGroup container1 = (ViewGroup) pInflater.inflate(R.layout.confirmation_popup, null);
final PopupWindow popupWindow = new PopupWindow(container1, 700, 600, true);
popupTitle = container1.findViewById(R.id.popuptext);
popAcceptBtn = container1.findViewById(R.id.accept_button);
popCancelBtn = container1.findViewById(R.id.cancel_button);
popupTitle.setText(mTitleText.getText().toString());
// Dismisses the popup window
popCancelBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
popupWindow.dismiss();
}
});
// Click listener for dialog accept button.
popAcceptBtn.setOnClickListener(new View.OnClickListener() {
String date;
#Override
public void onClick(View view) {
List<String> list = new ArrayList<>();
list.add(mTitleText.getText().toString());
list.add(mDescText.getText().toString());
list.add(mPointsText.getText().toString());
list.add(date = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date()));
// Saving data from current card into the completed challenges list.
TempDataReader reader = new TempDataReader(itemView.getContext());
new TempDataReader(itemView.getContext()).saveFile(list);
// I want to notify the dataset change here if possible!
popupWindow.dismiss();
}
});
popupWindow.showAtLocation(itemView, Gravity.CENTER, 25, 100);
}
});
}
}
}
SectionsPagerAdapter.java
public class SectionsPagerAdapter extends FragmentStatePagerAdapter{
private ViewPager viewPager;
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public void addFragment(Fragment fragment, String title){
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
}
}
CompletedFragment.java
public class CompletedFragment extends Fragment {
RecyclerView recyclerView;
RecyclerViewAdapter adapter;
public Fragment newInstance() {
return new CompletedFragment();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.completed_fragment, container, false);
recyclerView = view.findViewById(R.id.completed_frag);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
TempDataReader reader = new TempDataReader(getActivity());
List<List<String>> super_list = reader.readCompFile();
if(super_list == null || super_list.size() < 1){
return null;
} else{
adapter = new RecyclerViewAdapter(super_list,"completed");
recyclerView.setAdapter(adapter);
return view;
}
}
}
EDIT:
Added the code for the second fragment, which is the one that should be updated after the onClick at RecyclerViewHolder-class.
You have to add a function in your adapter for adding data:
public void addData(String title, String desc, String point, String date) {
this.mListTitle.add(title);
this.mListDesc.add(desc);
this.mListPoints.add(point);
this.mListDates.add(date);
notifyDataSetChanged();
}
If you want to enable animations call notifyItemInserted() instead of notifyDataSetChanged();
It is important that you add a String to every list because in your onBindViewHolder() you get the item to display from every list with list.get(position). Otherwise you'll get a IndexOutOfBoundsException.
You can create an interface and use as a callback. Send it as a parameter of the RecyclerViewAdapter and then to your RecyclerViewHolder. When the item should be added you call the callback that will get you back to your fragment. There you can read the file again and call notifyDataSetChanged.
I know i explain pretty bad so i will try to change your code so that it does what i said:
this will be your RecyclerViewAdapter:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolder> {
private List<String> mListTitle;
private List<String> mListDesc;
private List<String> mListPoints;
private List<String> mListDates;
private String fragment_tag;
private Runnable callback;
public RecyclerViewAdapter() {
}
public RecyclerViewAdapter(List<List<String>> super_list, String tag, Runnable callBack) {
//add the callback here(Runnable) and save it into a local variable
this.callback=callback;
this.mListTitle = super_list.get(0);
this.mListDesc = super_list.get(1);
this.mListPoints = super_list.get(2);
this.mListDates = super_list.get(3);
fragment_tag = tag;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new RecyclerViewHolder(inflater, parent, fragment_tag, callback);
//send the callback to your viewHolder
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
holder.mTitleText.setText(mListTitle.get(position));
holder.mDescText.setText(mListDesc.get(position));
holder.mPointsText.setText(mListPoints.get(position));
if (fragment_tag.equals("completed")) {
holder.mDateText.setText(mListDates.get(position));
}
}
#Override
public int getItemCount() {
return mListTitle.size();
}
}
class RecyclerViewHolder extends RecyclerView.ViewHolder {
RecyclerView recyclerView;
RecyclerViewAdapter mAdapter;
public TextView mTitleText, mDescText, mDateText, mPointsText, popupTitle;
public Button mConfButton, popCancelBtn, popAcceptBtn;
public RecyclerViewHolder(View itemView) {
super(itemView);
}
public RecyclerViewHolder(final LayoutInflater inflater, final ViewGroup container, String tag, Runnable callback) {
//ADD the callback to the parameters list here
// Inflating the card layout depending on the tag parameter.
super(inflater.inflate
((tag.equals("challenges")) ? R.layout.card_view_chall : R.layout.card_view_comp, container,
false));
mTitleText = itemView.findViewById(R.id.title_holder);
mDescText = itemView.findViewById(R.id.desc_holder);
mPointsText = itemView.findViewById(R.id.points_holder);
mDateText = itemView.findViewById(R.id.date_holder);
if (tag.equals("challenges")) {
mConfButton = itemView.findViewById(R.id.card_conf_button);
mConfButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Setting the layout inflater for popup window.
LayoutInflater pInflater = (LayoutInflater) itemView.getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
ViewGroup container1 = (ViewGroup) pInflater.inflate(R.layout.confirmation_popup, null);
final PopupWindow popupWindow = new PopupWindow(container1, 700, 600, true);
popupTitle = container1.findViewById(R.id.popuptext);
popAcceptBtn = container1.findViewById(R.id.accept_button);
popCancelBtn = container1.findViewById(R.id.cancel_button);
popupTitle.setText(mTitleText.getText().toString());
// Dismisses the popup window
popCancelBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
popupWindow.dismiss();
}
});
// Click listener for dialog accept button.
popAcceptBtn.setOnClickListener(new View.OnClickListener() {
String date;
#Override
public void onClick(View view) {
List<String> list = new ArrayList<>();
list.add(mTitleText.getText().toString());
list.add(mDescText.getText().toString());
list.add(mPointsText.getText().toString());
list.add(date = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date()));
// Saving data from current card into the completed challenges list.
TempDataReader reader = new TempDataReader(itemView.getContext());
new TempDataReader(itemView.getContext()).saveFile(list);
// I want to notify the dataset change here if possible!
//call the callback
callback.run();
popupWindow.dismiss();
}
});
popupWindow.showAtLocation(itemView, Gravity.CENTER, 25, 100);
}
});
}
}
}
And this will be your fragment:
public class CompletedFragment extends Fragment {
RecyclerView recyclerView;
RecyclerViewAdapter adapter;
public Fragment newInstance() {
return new CompletedFragment();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.completed_fragment, container, false);
recyclerView = view.findViewById(R.id.completed_frag);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
TempDataReader reader = new TempDataReader(getActivity());
List<List<String>> super_list = reader.readCompFile();
if(super_list == null || super_list.size() < 1){
return null;
} else{
adapter = new RecyclerViewAdapter(super_list,"completed", new Runnable() {
#Override
public void run() {
//here read the list again and call notifyDataSetChanged on your recycler
}
});
);
recyclerView.setAdapter(adapter);
return view;
}
}
}
Hope it helps and it works for you. If i did anything wrong, let me know, i can't run the code right now so...
edited, i forgot to add code in the callback

Android Studio Java Firebase retrieve key from CardView

So I've been searching the WHOLE internet for a solution but NONE worked for me sadly.
My goal is to retrieve the data from the cardView which is stored on Firebase and use that data when the cardView is clicked to display it on a new Activity.
I've created "Posts" as a collection, so all of the data such as header, date, image etc are stored inside the "Posts" collection.
From my understanding, what I need to do is to retrieve the key from the cardView data that has been clicked and use it to implement the same data on the new activity using the same mutual key.
I'm stuck here for ages, I would LOVE to get some help.. thanks alot!
My code -
RecyclerAdapter -
public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.ViewHolder> {
public List<ListForPost> list_post;
public Context context;
public PostsAdapter(List<ListForPost> list_post) {
this.list_post = list_post;
}
#NonNull
#Override
public PostsAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.posts_intro, parent, false);
context = parent.getContext();
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final PostsAdapter.ViewHolder holder, int position) {
String header_data = list_post.get(position).getHeader();
holder.setHeaderText(header_data);
String date_data = list_post.get(position).getDate1();
holder.setDateText(date_data);
String image_data = list_post.get(position).getImage_url();
holder.setIntroIMG(image_data);
}
#Override
public int getItemCount() {
return list_post.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private View mView;
private ImageView introIMG;
private TextView headerText;
private TextView dateText;
public ViewHolder(View itemView) {
super(itemView);
mView = itemView;
}
public void setHeaderText(String headText) {
headerText = mView.findViewById(R.id.introHeader);
headerText.setText(headText);
}
public void setDateText(String tarihText) {
dateText = mView.findViewById(R.id.introDate);
dateText.setText(tarihText);
}
public void setIntroIMG (String downloadUri) {
introIMG = (ImageView) mView.findViewById(R.id.introImage);
Glide.with(context).load(downloadUri).into(introIMG);
}
}
}
MainActivity -
firebaseFirestore = FirebaseFirestore.getInstance();
firebaseFirestore.collection("Posts").limit(10).addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(QuerySnapshot queryDocumentSnapshots, FirebaseFirestoreException e) {
for (DocumentChange doc: queryDocumentSnapshots.getDocumentChanges()) {
if (doc.getType() == DocumentChange.Type.ADDED) {
ListForPost listForPost = doc.getDocument().toObject(ListForPost.class);
list_post.add(listForPost);
postsAdapter.notifyDataSetChanged();
}
}
}
});
One solution would be set an OnClickListener for mView inside of your ViewHolder. So for your code that would be something like this:
public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.ViewHolder> {
//...
public class ViewHolder extends RecyclerView.ViewHolder {
private View mView;
private ListForPost mListForPost;
public ViewHolder(View itemView) {
super(itemView);
mView = itemView;
mView.setOnClickListener(new OnClickListener() {
#Override
public void onClick() {
//TODO: launch my activity here with mListForPost
}
});
}
private void setListForPost(ListForPost listForPost) {
mListForPost = listForPost;
}
//...
}
}
If you want to make this even nicer (so you don't have to deal with how to call startActivity() or pass in Context as a parameter to your ViewHolder) you can abstract out the details with another listener, maybe a "ListForPostClickedListener":
public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.ViewHolder> {
//...
private ListForPostClickedListener mListForPostClickedListener;
public PostsAdapter(ListForPostClickedListener listForPostClickedListener) {
mListForPostClickedListener = listForPostClickedListener;
}
public class ViewHolder extends RecyclerView.ViewHolder {
//...
public ViewHolder(View itemView) {
//...
mView.setOnClickListener(new OnClickListener() {
#Override
public void onClick() {
mListForPostClickedListener.onClick(mListForPost);
}
});
}
//...
}
public interface ListForPostClickedListener {
void onClick(ListForPost listForPost);
}
}
And in the Activity, Fragment or whatever other context-aware object you use your PostsAdapter from, you implement the interface and launch the necessary Activity:
public class MyActivityWhichManagesTheAdapter extends Activity implements PostsAdapter.ListForPostClickedListener {
//...
public onClick(ListForPost listForPost) {
//TODO: launch my activity here with mListForPost
}
}

Categories

Resources