I have implemented a RecyclerView and customer Adapter many times, but for some reason I cannot get this one to display any data. I am feeding in data from JSON using retrofit and calling notifyDataSetChanged() once this has been loaded, yet it still remains blank. I have stripped this back to just one text view to try and simplify but still not getting anything. Can anyone see where I am going wrong here?
When I debug, I am getting the List to contain data so I am definitely parsing the data correctly, I just cant get it display in the recycler view. I have even checked the list.size() in the loadTrailerList method and it has data.
My Activity onCreate method:
trailerAdapter = new TrailerAdapter(this);
trailerRecyclerView = findViewById(R.id.trailer_recycler_view);
trailerRecyclerView.setLayoutManager(new LinearLayoutManager(this));
trailerRecyclerView.setAdapter(trailerAdapter);
Retrofit onResponse method:
if (response.body() != null) {
trailers = response.body().getTrailers();
}
trailerAdapter.loadTrailerList(response.body().getTrailers());
My custom adapter:
public class TrailerAdapter extends RecyclerView.Adapter<TrailerAdapter.TrailerViewHolder> {
private final List<Trailer> trailerList = new ArrayList<>();
private final TrailerClickListener listener;
public TrailerAdapter(TrailerClickListener listener) {
this.listener = listener;
}
#NonNull
#Override
public TrailerViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.trailer_list_item, viewGroup, false);
return new TrailerViewHolder(itemView, this);
}
#Override
public void onBindViewHolder(#NonNull TrailerViewHolder trailerViewHolder, int i) {
trailerViewHolder.trailerTitle.setText(trailerList.get(i).getName());
}
#Override
public int getItemCount() {
return trailerList.size();
}
public void loadTrailerList(List<Trailer> trailers) {
this.trailerList.clear();
if (trailers != null) {
trailers.addAll(trailers);
}
notifyDataSetChanged();
}
class TrailerViewHolder extends RecyclerView.ViewHolder {
final TrailerAdapter trailerAdapter;
private final TextView trailerTitle;
private TrailerViewHolder(#NonNull View itemView, TrailerAdapter trailerAdapter) {
super(itemView);
this.trailerAdapter = trailerAdapter;
trailerTitle = itemView.findViewById(R.id.text_view_trailer_title);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.onTrailerClicked(trailerList.get(getAdapterPosition()));
}
});
}
}
}
My List Item XML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/text_view_trailer_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Play Trailer" />
</LinearLayout>
the recycler view in my activity XML:
<android.support.v7.widget.RecyclerView
android:id="#+id/trailer_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimary"
app:layout_constraintTop_toBottomOf="#+id/trailer_divider">
</android.support.v7.widget.RecyclerView>
I am grateful for anyone that can point me in the right direction
It is because you are sending a list to adapter but, you are not initializing your list which is used in the adapter.
try this.
public void loadTrailerList(List<Trailer> trailers) {
this.trailerList.clear();
if (trailers != null) {
trailerList = trailers;
}
notifyDataSetChanged();
}
Doh! I just realised what I was doing wrong:
In my loadTrailerList() method in my adapter, I was calling:
trailers.addAll(trailers);
instead of:
trailerList.addAll(trailers);
to load the list of items into the actual ArrayList! whoops!
Related
I am trying to populate a recyclerview inside the Fragment under a TabLayout. Everything seems to be fine, but the data is not actually populated in the recyclerview, while all the content Logs[Log.e();] is working fine. Can anyone relate the issue ?
My Fragment Is Like :
public class SugarLevelReport extends Fragment
{
private ConstraintLayout view_constraintLayout;
private RecyclerView blood_sugar_recyclerView;
private ProgressBar progressBar;
private ArrayList<PatientRecordModel> patientRecordModelArrayList = new ArrayList<PatientRecordModel>();
private RecyclerView.Adapter bloodSugaradapter;
// for intent data
public String patient_id_from_intent, patient_name_from_intent, patient_dob_from_intent, patient_mobile_from_intent, patient_email_from_intent;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_sugar_level_report, container, false);
findViewById(view);
return view;
}
public void findViewById(View view)
{
view_constraintLayout = (ConstraintLayout)view.findViewById(R.id.view_constraintLayout);
/*progressBar = (ProgressBar)view.findViewById(R.id.progressBar);*/
blood_sugar_recyclerView = (RecyclerView)view.findViewById(R.id.blood_sugar_recyclerView);
blood_sugar_recyclerView.setHasFixedSize(true);
blood_sugar_recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
bloodSugaradapter = new SugarLevelReportAdapter(getActivity(), patientRecordModelArrayList);
blood_sugar_recyclerView.setAdapter(bloodSugaradapter);
loadData();
}
public void loadData()
{
if(GlobalMethods.isNetworkConnected(getActivity()))
{
String[] names = getResources().getStringArray(R.array.names);
for (int i = 0 ; i < names.length ; i++)
{
PatientRecordModel patientRecordModel = new PatientRecordModel();
patientRecordModel.setPatient_name(names[i]);
// log here is working fine , and its giving correct data
Log.e("patientsName", patientRecordModel.getPatient_name());
patientRecordModelArrayList.add(patientRecordModel);
bloodSugaradapter.notifyDataSetChanged();
}
}
else
{
}
}
Adapter Class
public class SugarLevelReportAdapter extends RecyclerView.Adapter<SugarLevelReportAdapter.SugarLevelReportHolder>{
public Context context;
public ArrayList<PatientRecordModel> patientRecordModelArrayList = new ArrayList<PatientRecordModel>();
public SugarLevelReportAdapter(Context context, ArrayList<PatientRecordModel> patientRecordModelArrayList)
{
this.context = context;
this.patientRecordModelArrayList = patientRecordModelArrayList;
}
#Override
public SugarLevelReportHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.sugar_level_report_cardview, parent, false);
SugarLevelReportHolder sugarLevelReportHolder = new SugarLevelReportHolder(view);
return sugarLevelReportHolder;
}
#Override
public void onBindViewHolder(SugarLevelReportHolder holder, int position)
{
PatientRecordModel patientRecordModel = patientRecordModelArrayList.get(position);
Log.e("patient_name",patientRecordModel.getPatient_name());
holder.patient_name_textView.setText(patientRecordModel.getPatient_name());
}
#Override
public int getItemCount()
{
return patientRecordModelArrayList.size();
}
public static class SugarLevelReportHolder extends RecyclerView.ViewHolder
{
public TextView month_textView, date_textView, year_textView;
public TextView patient_name_textView, patient_count_textView, meal_type_textView;
public ImageView overflow_menu_imageView;
public SugarLevelReportHolder(View itemView)
{
super(itemView);
month_textView = (TextView)itemView.findViewById(R.id.month_textView);
date_textView = (TextView)itemView.findViewById(R.id.date_textView);
year_textView = (TextView)itemView.findViewById(R.id.year_textView);
patient_name_textView = (TextView)itemView.findViewById(R.id.patient_name_textView);
patient_count_textView = (TextView)itemView.findViewById(R.id.patient_count_textView);
meal_type_textView = (TextView)itemView.findViewById(R.id.meal_type_textView);
overflow_menu_imageView = (ImageView)itemView.findViewById(R.id.overflow_menu_imageView);
}
}
}
xml code [fragment_sugar_level_report.xml]
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/blood_sugar_recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:scrollbars="vertical"
android:scrollbarSize="1dp"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="8dp">
</android.support.v7.widget.RecyclerView>
So I had a similar problem on my current project. I cannot tell you why it happens but to solve it you need to move the part for updating data to the adapter.
on your adapter create the following method:
public void updateData(List<PatientRecordModel> list){
patientRecordModelArrayList = list;
notifyDatasetChanged();
}
On you Fragment class when you want to update the data just call
bloodSugaradapter.updateData(patientRecordModel);
On the same it is advisable to load your views onViewCreated instead of onCreateView. Also use a data mapping library such as GSON or or jackson to improve speed and reliability in data mapping.See a comparison here Hope this helps
Recently I started coding my really first android project by using Android Studio 3.1.2.
Inside on one of my fragments, I have a recyclerview, in which I want to show data from a JSON API. For the items I created a custom layout which is intended to be used as a CardView.
I proceeded that far, that I receive my data, but my recyclerview remains empty. Also, if the json object is empty, or the API deosn't respond, the idea was to let the recyclerview automatically add an item, that tells the user that there's no data or the API was not available (would be cool, if I could use the same layout here, I created). This is how my code looks so far:
The raw structure of report_compact_card.xml (embedded in android.support.v7.widget.CardView):
<?xml version="1.0" encoding="utf-8"?><android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
app:cardCornerRadius="2dp">
<android.support.constraint.ConstraintLayout
android:id="#+id/linearLayout"
...>
<TextView
android:id="#+id/report_header_textview"
... />
<TextView
android:id="#+id/report_body_textview"
... />
<ImageView
android:id="#+id/report_icon_imageview"
... />
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>
My ReportCompactAdapter:
public class ReportCompactAdapter extends RecyclerView.Adapter<ReportCompactAdapter.ReportCompactViewHolder> {
private Context context;
private ArrayList<Report> reports;
public ReportCompactAdapter(Context context, ArrayList<Report> reports) {
this.context = context;
this.reports = reports;
}
#Override
public ReportCompactViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.report_compact_card, parent, false);
return new ReportCompactViewHolder(view);
}
#Override
public void onBindViewHolder(ReportCompactViewHolder holder, int position) {
//this is where I want to set a "no data" card
if (reports.isEmpty()) {
holder.reportBodyTextView.setText("Keine Meldungen");
holder.reportBodyTextView.setText(":)");
holder.reportIconImageView.setImageResource(R.drawable.ic_report_ok_24dp);
} else {
//here I want to fill my cards with my json data
Report currentReport = reports.get(position);
String currentId = currentReport.getId();
String currentTest = currentReport.getTest();
String currentTOpen = currentReport.getTOpen();
Employee currentEmployee = currentReport.getEmployee();
holder.reportHeaderTextView.setText(currentTest);
holder.reportBodyTextView.setText(currentId + " " + currentTOpen + " " + currentEmployee.getName());
holder.reportIconImageView.setImageResource(R.drawable.ic_report_err_24dp);
}
}
#Override
public int getItemCount() {
return reports.size();
}
public class ReportCompactViewHolder extends RecyclerView.ViewHolder {
public TextView reportHeaderTextView;
public TextView reportBodyTextView;
public ImageView reportIconImageView;
//this is where I try to access my layout
public ReportCompactViewHolder(View itemView) {
super(itemView);
reportHeaderTextView = itemView.findViewById(R.id.report_header_textview);
reportBodyTextView = itemView.findViewById(R.id.report_body_textview);
reportIconImageView = itemView.findViewById(R.id.report_icon_imageview);
}
}
}
Additionally in may OverviewFragment, where I use my recyclerview i'm doing like so:
public class OverviewFragment extends Fragment {
private ArrayList<Report> reports;
private RecyclerView reportRecyclerView;
private ReportCompactAdapter reportCompactAdapter;
private RequestQueue requestQueue;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View fragmentView = inflater.inflate(R.layout.fragment_overview, container, false);
reports = new ArrayList<Report>();
//here I want to set up my recyclerview
reportRecyclerView = fragmentView.findViewById(R.id.report_recyclerview);
reportRecyclerView.setHasFixedSize(true);
reportRecyclerView.setLayoutManager(new LinearLayoutManager(this.getContext()));
//I already set the adapter here to avoid the warning that no adapter is attached
reportRecyclerView.setAdapter(new ReportCompactAdapter(this.getContext(), reports));
//I use volley for Request stuff
requestQueue = Volley.newRequestQueue(this.getContext());
//this guy is intended to fetch my json data
parseJSON();
return fragmentView;
}
private void parseJSON() {
JSONObjectRequest request = new JSONObjectRequest(Request.Method.GET, "myurl.com", null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONArray jsonArray = response.getJSONArray("reports");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject report = jsonArray.getJSONObject(i);
reports.add(new Report(json));
}
//here I set my adapter after parsing my data
reportCompactAdapter = new ReportCompactAdapter(OverviewFragment.this.getContext(), reports);
reportRecyclerView.setAdapter(reportCompactAdapter);
} catch(JSONException e) {
e.printStackTrace();
}
}
}, new Response.OnErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
});
requestQueue.add(request);
}
}
Because of some reason I didn't even get my "no data" card into my recyclerview, neither my filled cards, although "myurl.com" is valid and doesn't throw any error. So my question is, where did I mis a step to successfully squeeze my cards into my recyclerview? Thanks in forward!
You need return atleast 1 item in getItemCount() like below
#Override
public int getItemCount() {
return reports.size()==0?1:report.size();
}
I'm trying to get JSON data to display in a RecyclerView list, but whenever I try to make the call it seems that the RecyclerView comes up empty. I have it set up so that I get the API service/manager in onCreate prior to the configViews() method which I wrote. Am I making the call too early? I thought the problem would be to create the views/adapter prior to the call, but it doesn't seem to be making a difference.
This is the code for the Retrofit call:
listCall.enqueue(new Callback<List<Character>>() {
#Override
public void onResponse(Call<List<Character>> call, Response<List<Character>> response) {
if(response.isSuccessful()){
List<Character> characterList = response.body();
Log.v(TAG, response.toString());
for (int i = 0; i < characterList.size(); i++){
Character character = characterList.get(i);
character.setName(characterList.get(i).getName());
character.setDescription(characterList.get(i).getDescription());
characterAdapter.addCharacter(character);
}
configViews();
characterAdapter.notifyDataSetChanged();
}
else {
int sc = response.code();
}
}
#Override
public void onFailure(Call<List<Character>> call, Throwable t) {
}
});
And that configViews() method is here, albeit simpler than when I first started (I have been moving bits around to test whether they will affect inflation of the RecyclerView):
private void configViews() {
characterAdapter = new CharacterAdapter(this);
recyclerView.setAdapter(characterAdapter);
}
EDIT: Thank you all for your replies! As requested here is the Adapter class:
public class CharacterAdapter extends
RecyclerView.Adapter<CharacterAdapter.Holder> {
private static final String TAG = CharacterAdapter.class.getSimpleName();
private final CharacterClickListener clickListener;
private List<Character> characters;
//Constructor for CharacterAdapter.
public CharacterAdapter(CharacterClickListener listener){
characters = new ArrayList<>();
clickListener = listener;
}
//Inflates CardView layout
#Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View row =
LayoutInflater.from(parent.getContext()).inflate(R.layout.row_item, parent,
false);
return new Holder(row);
}
#Override
public void onBindViewHolder(Holder holder, int position) {
Character currentCharacter = characters.get(position);
holder.name.setText(currentCharacter.getName());
holder.description.setText(currentCharacter.getDescription());
//Picasso loads image from URL
Picasso.with(holder.itemView.getContext())
.load("http://gateway.marvel.com/"+
currentCharacter.getThumbnail()).into(holder.thumbnail);
}
#Override
public int getItemCount() {
return characters.size();
}
public void addCharacter(Character character) {
//Log.d(TAG, character.getThumbnail());
characters.add(character);
notifyDataSetChanged();
}
public Character getSelectedCharacter(int position) {
return characters.get(position);
}
public class Holder extends RecyclerView.ViewHolder implements
View.OnClickListener{
//Holder class created to be implemented by adapter.
private ImageView thumbnail;
private TextView name, description;
public Holder(View itemView) {
super(itemView);
thumbnail = (ImageView)
itemView.findViewById(R.id.character_thumbnail);
name = (TextView) itemView.findViewById(R.id.character_name);
description = (TextView)
itemView.findViewById(R.id.character_description);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
clickListener.onClick(getLayoutPosition());
}
}
public interface CharacterClickListener {
void onClick(int position);
}
}
In principle you have done the right thing. You can instantiate the views before and fire a fetch request. Once you receive the response, you can notify the adapter that the datatset has changed and the adapter will refresh your views.
The only correction here is, you need to call your configViews(); before the for loop. So your code should be like -
configViews();
for (int i = 0; i < characterList.size(); i++){
Character character = characterList.get(i);
character.setName(characterList.get(i).getName());
character.setDescription(characterList.get(i).getDescription());
characterAdapter.addCharacter(character);
}
characterAdapter.notifyDataSetChanged();
Because right now what is happening is you add all your data using add character and then after that you again call config views which reinitialises your adapter at
characterAdapter = new CharacterAdapter(this);
Hence the empty views.
PS: I don't know your adapter's addCharacter method but i am hoping it is doing the right job. If it still doesn't work let me know and then add your addCharacter code as well.
Based on code the snippets, in configViews() method you are creating new instance of CharacterAdapter. The following code snippets will work.
private void configViews() {
//characterAdapter = new CharacterAdapter(this);
recyclerView.setAdapter(characterAdapter);
}
You should leave the call configViews() call in onCreate but move the rest of it into onResume. It'll achieve following:
Ensure views are ready to display data
Refresh data if your app was brought to background and re-opened
This question has been asked a few times but those answers doesn't apply to me. I would like a more general answer about what causes this issue generally.
I have a recyclerview in my activity layout. Rows of the recyclerview is a constraint layout with one imageview and textview:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="50dp"
android:clickable="true">
<ImageView
android:id="#+id/file_icon"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:text="File"
android:id="#+id/file_name"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toRightOf="#+id/file_icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>
I have several items to show in this row structure. I set up the adapter properly. However, when I run my application, textviews that are on the screen doesn't get shown, until I scroll-down and scroll-up again. Basically, in order for those textviews to be shown, they need to be discarded from display and enter again. Here is my adapter code:
public class FileViewAdapter extends RecyclerView.Adapter<FileViewAdapter.Viewholder> {
private Context context;
private List<File> files;
public FileViewAdapter(Context context, List<File> files) {
this.context = context;
this.files = files;
}
public FileViewAdapter(Context context){
this.context = context;
}
#Override
public Viewholder onCreateViewHolder(ViewGroup viewGroup, int i) {
LayoutInflater inflater = LayoutInflater.from(context);
View layout = inflater.inflate(R.layout.file_list_item, viewGroup, false);
return new Viewholder(layout);
}
#Override
public void onBindViewHolder(Viewholder viewholder, int i) {
File file = files.get(i);
if (file.isDirectory()) {
viewholder.fileIcon.setImageDrawable(
context.getResources().getDrawable(R.drawable.ic_folder_black_24dp));
viewholder.wholeThing.setOnClickListener(null);
} else {
viewholder.fileIcon.setImageDrawable(
context.getResources().getDrawable(R.drawable.ic_insert_drive_file_black_24dp));
viewholder.wholeThing.setOnClickListener(null);
}
viewholder.fileName.setText(file.getName());
}
#Override
public int getItemCount() {
return files.size();
}
public void clear() {
files.clear();
notifyDataSetChanged();
}
public void addAll(List<File> newFiles) {
files.addAll(newFiles);
notifyDataSetChanged();
}
class Viewholder extends RecyclerView.ViewHolder {
View wholeThing;
ImageView fileIcon;
TextView fileName;
public Viewholder(View itemView) {
super(itemView);
wholeThing = itemView;
fileIcon = (ImageView) itemView.findViewById(R.id.file_icon);
fileName = (TextView) itemView.findViewById(R.id.file_name);
}
}
}
EDIT: How I am calling the constructor of adapter on activity.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file_viewer);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
}
#Override
public void onResume() {
adapter = new Adapter(this, /*A list of File items*/);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
The problem from what i see is that the notifyDataSetChanged doesnt ocurr in the UiThread so if run ur setData method like this it will work.
this.runOnUiThread(new Runnable() {
public void run() {
mAdapter.setNewData(newDataListForAdapter);
mAdapter.notifyDataSetChanged();
}
});
As stupid as it might sound, calling this line of code after setting data for recyclerView, helped me for this issue:
recyclerView.smoothScrollToPosition(0)
PS: technologies that I was using that may have something to do with this were: RJava, Retrofit2, NavigationUI, Fragments, LiveData, and Databinding.
Please try this. I hope it will be helpful.
RecyclerView recyclerView;
FileViewAdapter adapter;
List<File> files = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file_viewer);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
adapter = new FileViewAdapter(this, files);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
#Override
public void onResume() {
super.onResume();
files = getFiles();//fetch files
adapter.notifyDataSetChanged();//update UI
}
public List<File> getFiles() {
//fetch files
//...
return files;
}
By the way, I did not find the situation happened to you. There may be some other errors in you code you did not show to us.
if you're setting data on the constructor, it might be that the adapter never gets notified about data inserted. Try to add a notifyDataSetChanged () either at the bottom of the constructor or externally (or call your setData method)
I'm working on a simple restaurant POS system, and I had a problem suddenly come up. This problem has only occurred when I run it on a Genymotion tablet device. I do not have this problem on smaller genymotion devices or the 2 physical devices I've tried.
One of my activities uses 5 RecyclerViews to display the menu and sales summary. Every few item clicks, the RecyclerViews decide to just not update and do not properly display my items unless I scroll a bit on ANY of the RecyclerViews or click something. Even though they are not displayed properly, all item clicks register what should be shown (not what is actually shown).
For example, I click beef -> seafood -> chicken. The RecyclerView responsible for showing the menu items still shows seafood. If I click on anything, the correct chicken item gets selected and everything refreshes to what should show up. If I scroll on any RecyclerView (even an unrelated one), everything refreshes properly.
Activity onCreate():
...
cAdapter = new CategoryAdapter();
cAdapter.setOnItemClickListener(new OnItemClickAdapter<Category>() {
#Override
public void onItemClick(Category item, int position) {
miAdapter.setItems(item.getMenuItems());
va1.setDisplayedChild(0);
}
});
miAdapter = new MenuItemAdapter();
miAdapter.setOnItemClickListener(new OnItemClickAdapter<MenuItem>() {
#Override
public void onItemClick(MenuItem item, int position) {
salesOrder.addMenuItem(item);
oeAdapter.notifyDataSetChanged();
updateTotals();
}
});
...
Adapters:
public abstract class ItemRecyclerViewAdapter<T, TVH extends ItemViewHolder<T>> extends RecyclerView.Adapter<TVH> {
private ItemViewHolder.OnItemClickListener<T> listener;
protected List<T> items;
public ItemRecyclerViewAdapter() {
this(new ArrayList<T>());
}
public ItemRecyclerViewAdapter(List<T> items) {
this.items = items;
}
public final void setOnItemClickListener(ItemViewHolder.OnItemClickListener<T> listener) {
this.listener = listener;
}
public void setItems(List<T> items) {
this.items = items;
this.notifyDataSetChanged();
}
public void clear() {
this.items.clear();
this.notifyDataSetChanged();
}
protected abstract TVH createItemViewHolder(ViewGroup parent, int viewType);
#Override
public TVH onCreateViewHolder(ViewGroup parent, int viewType) {
TVH holder = createItemViewHolder(parent, viewType);
holder.setOnItemClickListener(listener);
return holder;
}
#Override
public void onBindViewHolder(TVH holder, int position) {
holder.setItem(items.get(position));
}
#Override
public int getItemCount() {
return items.size();
}
}
ViewHolders:
public abstract class ItemViewHolder<T> extends RecyclerView.ViewHolder {
public ItemViewHolder(View itemView) {
super(itemView);
}
public interface OnItemClickListener<T> {
void onItemClick(T item, int position);
boolean onItemLongClick(T item, int position);
}
public final void setOnItemClickListener(final OnItemClickListener<T> listener) {
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null)
listener.onItemClick(getItem(), getAdapterPosition());
}
});
itemView.setOnLongClickListener(new View.OnLongClickListener(){
#Override
public boolean onLongClick(View v) {
return listener != null && listener.onItemLongClick(getItem(), getAdapterPosition());
}
});
}
public abstract T getItem();
public abstract void setItem(T item);
}
It SEEMS like it's a swapbuffer issue or some race condition I'm not seeing, but I thought I'd see if maybe I'm misunderstanding something first. Any ideas would be much appreciated!
I have that bug too.
After editing my activity_main.xml, I could fix it.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ulimbridge.recylerview.MainActivity">
<android.support.v7.widget.RecyclerView
android:layout_width="0dp"
android:layout_height="0dp"
android:id="#+id/recycler"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</android.support.constraint.ConstraintLayout>
Making RecyclerViews' height and width '0dp' was a solution.