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)
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
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!
I want to list only the disliked items in my recyclerview. I have a full list of items in rv in MainActivity (did not set visibility here). I can set for each items like or dislike by clicking on imagebutton. The MainActivity shows full list of items (cardviews) that shows imagebutton likes or not. If item is liked, this is stored in firebase db as separate entry under Likes with item key (firebase key .push) and not under Items. (in firebase db I have Users, Items, Likes).
Here is my subactivity code, DislikedItemsActivity, where I want to show only items that are disliked by using setVisibility(View.GONE) for items that are liked. This still holds the space between items for the View.GONE items as well (though these cardviews are empty).
mRecyclerView = (RecyclerView) findViewById(R.id.rvItemList);
mRecyclerView .setHasFixedSize(true);
final LinearLayoutManager linearLayoutManager = new
LinearLayoutManager(this);
linearLayoutManager.setReverseLayout(true);
linearLayoutManager.setStackFromEnd(true);
mRecyclerView.setLayoutManager(linearLayoutManager);
final FirebaseRecyclerAdapter<Item, MainActivity.ItemViewHolder>
firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Item,
MainActivity.ItemViewHolder>(
Item.class,
R.layout.list_item,
MainActivity.ItemViewHolder.class,
mDatabase
) {
#Override
protected void populateViewHolder(final MainActivity.ItemViewHolder viewHolder, final Item model, final int position) {
final String itemKey = getRef(position).getKey();
mDatabaseItemsLiked.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// if item is not liked, thus no user set in db ( I want to see only items that are liked in my recyclerview)
if (!dataSnapshot.child(itemKey).hasChild(mAuth.getCurrentUser().getUid())) {
viewHolder.mView.setVisibility(View.VISIBLE);
viewHolder.itemNameSetup(model.getItemName());
viewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent itemSheetIntent = new Intent(DislikedItemsActivity.this, ItemSheetActivity.class);
adatlapIntent.putExtra("item_key", itemKey);
startActivity(itemSheetIntent);
}
});
} else {
viewHolder.mView.setVisibility(View.GONE);
mRecyclerView.getAdapter().notifyItemRemoved(position); //this puts together the visible items, but when scrolling, it gets messed up
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, databaseError.toString());
}
});
}
#Override
public void onBindViewHolder(MainActivity.TermekViewHolder viewHolder, int position) {
super.onBindViewHolder(viewHolder, position);
}
};
mRecyclerView.setAdapter(firebaseRecyclerAdapter);
}
I looked for many solutions like onBindViewHolder, notifyDataChanged, set margin to 0, set layout size in xml to wrap_content. The best I could get is to have the not-liked items without space with mRecyclerView.getAdapter().notifyItemRemoved(position);, but scrolling the list backwards the whole rv gets messed up (duplicate entry, empty spaces, disordered list).
I don't know how to list only the disliked items from the complete item list from MainActivity rv in a new activity? My code above shows only disliked items, but only until I scroll to end of list, if I scroll backwards the rv gets messed up. I logged the positions of views (18items) in onBindViewHolder and first it counts all items in sequence (17,16,15,14...0), but scrolling from end of list to backwards the position jumps from 0 to 4 like 7times (changes always how many times) then it is the same for item 5,6, until item 17 (all of their positions showed in onBindViewHolder 7 or 8 times during scrolling ie. 5,5,5,5,6,6,6,6) and only for backward scrolling and during backward move rv shows only disliked items or empty views or duplicate entry of disliked item.
my xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="horizontal"
android:background="#drawable/hatter"
tools:context="com.example.user.itemlist.ItemsLikedActivity">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/rvItemList"
>
</android.support.v7.widget.RecyclerView>
(don't know how to add pics) When disliked list appears, it shows first item (cardview takes up the full screen), and when I start to scroll the list (from 1. visible item to next visible item) if there is space(item1 vis and next vis item is 4), rearrange and I can see that next visible item(item4) moves to 1. visible item, then the rest of the list is arranged well, until I start to scroll back, then it rearranges the rv with spaces and with double entry. The list goes back and forth until both end (which is the length of full items list and not just the disliked items), but visible items gets all messed up.
use this code to remove occupied space :
ViewGroup.LayoutParams params = holder.itemView.getLayoutParams();
params.height = 0;
holder.itemView.setLayoutParams(params);
I found solution to filter the complete database. In my question I wanted to get only the liked/disliked items in a separate activity, though my previous code showed filtered items, but with gaps.
In below code, I changed the DatabaseReferences (mDatabase -node with complete item list and mDatabaseItemsLiked -node with item uid and user uid).
This gave only empty cards with only number as the likedItems, but to get name from the mDatabase (complete list), I used dataSnapshot.getValue(Item.class).getItemName().
firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Item,
MainActivity.ItemViewHolder>(
Item.class,
R.layout.list_item,
MainActivity.ItemViewHolder.class,
mDatabaseItemsLiked
) {
#Override
protected void populateViewHolder(final MainActivity.ItemViewHolder
viewHolder, final Item model, final int position) {
final String itemKey = getRef(position).getKey();
mDatabase.child(itemKey).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
viewHolder.itemNameSetup(dataSnapshot.getValue(Item.class).getItemName());
viewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent itemSheetIntent = new Intent(LikedItemsActivity.this, ItemSheetActivity.class);
adatlapIntent.putExtra("item_key", itemKey);
startActivity(itemSheetIntent);
}
});
This works for me without any problem. I hope it is network efficient.
You can try to store the liked ítem in boolean array and later in populateViewHolder check if ítem has like o no and set visibility.
I would do like that:
In your class declare :
private boolean [] itemLiked;
In your constructor :
this.itemLiked = new boolean [arrayOfAllItems.size]
On click event:
itemLiked[position] = true; //Where position is row position
onBindViewholder or in your case populateViewHolder:
if (!itemLiked[position]) {
viewHolder.mView.setVisibility(View.GONE); }
Hope it helps, good luck!
EDITED
I do not understand exactly what you want to do, that's why I leave you the code for two cases.
Case 1. Mark and dis-mark the rows.
Case 2. Save to database or delete.
In continuation the complete code
Activity XML add RecyclerView:
<android.support.v7.widget.RecyclerView
android:id="#+id/my_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Make a custom layout for row:
<TextView
android:id="#+id/question_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
android:text="QUESTION"/>
<ImageButton
android:id="#+id/like"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="#android:drawable/ic_input_add"
android:background="#android:color/transparent"
android:layout_marginRight="4dp"/>
<ImageButton
android:id="#+id/dislike"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="#android:drawable/ic_delete"
android:background="#android:color/transparent"
android:layout_marginRight="4dp"/>
Make a Model class:
public class SomeModel {
private String question;
public SomeModel(String question) {
this.question = question;
}
public String getQuestion() {
return question;
}
}
Make Adapter Class:
public class SomeAdapter extends RecyclerView.Adapter {
private ArrayList<SomeModel> arrayList;
private boolean [] item_has_like, item_hase_vote;
public SomeAdapter(ArrayList<SomeModel> arrayList) {
this.arrayList = arrayList;
this.item_has_like = new boolean[arrayList.size()];
this.item_hase_vote = new boolean[arrayList.size()];
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder myViewHolder = null;
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.draw_row, parent, false);
myViewHolder = new MyViewHolder(view);
return myViewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final MyViewHolder myViewHolder = (SomeAdapter.MyViewHolder)holder;
final SomeModel item = arrayList.get(position);
int backGround;
/**In background you can save whateveryou need, example:
* backGround= R.drawable.some_background;
* backGround= View.GONE;
*.....
**/
if (item_hase_vote[position]){
if (item_has_like[position])
{
backGround= Color.GREEN;//
} else {
backGround= Color.RED;
}
} else {
backGround= Color.TRANSPARENT;
}
myViewHolder.questionTV.setText(item.getQuestion());
myViewHolder.questionTV.setBackgroundColor(backGround);
}
#Override
public int getItemCount() {
return arrayList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private TextView questionTV;
private ImageView like, dislike;
public MyViewHolder(final View itemView) {
super(itemView);
questionTV = (TextView)itemView.findViewById(R.id.question_tv);
like = (ImageView)itemView.findViewById(R.id.like);
dislike = (ImageView)itemView.findViewById(R.id.dislike);
like.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Item been voted
item_hase_vote[getAdapterPosition()] = true;
//Item got Like save in boolean array by row position
item_has_like[getAdapterPosition()] = true;
//notify your adapter
notifyDataSetChanged();
/*OR Here comes the code where You save Item in Your Data Base.*/
}
});
dislike.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Item been voted
item_hase_vote[getAdapterPosition()] = true;
// Item got DisLike save in boolean array by row position
item_has_like[getAdapterPosition()] = false;
//notify your adapter
notifyDataSetChanged();
/*OR Here You Remove item on Dislike
arrayList.remove(getAdapterPosition());
notifyItemRemoved(getAdapterPosition());
notifyItemRangeChanged(getAdapterPosition(),arrayList.size());
*/
}
});
}
}
}
And Your Activity:
public class SomeActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_some);
ArrayList<SomeModel> arrayList = new ArrayList<>();
for (int i = 0; i <77 ; i++) {
arrayList.add(new SomeModel("Question " + i));
}
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.my_rv);
SomeAdapter adapter = new SomeAdapter(arrayList);
recyclerView.setAdapter(adapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(layoutManager);
}
}
Saving the selected item by the adapterPosition in boolean, String, int....[], the Adapter always gona know whats going on with every item and like that your list always gona be arranged.
Good Luck!
I also got the same problem. what i thought was if RelativeLayout load one after one, height=0, the specs will remove.So it works for me.
This is my ViewHolder. I Introduces my reletivelayout here.
public static class BlogViewHolder extends RecyclerView.ViewHolder {
View mView;
TextView txtdate;
RelativeLayout con_rel;
String name_day = "no name";
public BlogViewHolder(View itemView) {
super(itemView);
mView=itemView;
con_rel=(RelativeLayout)itemView.findViewById(R.id.con_rel);
txtdate = (TextView)itemView.findViewById(R.id.day);
}
}
The I set height and width
con_ref=FirebaseDatabase.getInstance().getReference().child("/consultation");
FirebaseRecyclerAdapter<Consultation,SelectConsaltation.BlogViewHolder>recyclerAdapter=new FirebaseRecyclerAdapter< Consultation,SelectConsaltation.BlogViewHolder>(
Consultation.class,
R.layout.consultation_card,
SelectConsaltation.BlogViewHolder.class,
con_ref
) {
#Override
protected void populateViewHolder(final SelectConsaltation.BlogViewHolder viewHolder, final Consultation model, final int Consultation) {
Shedule_ref.child(model.getScheduleID()).child("Day").addValueEventListener(new ValueEventListener() {
ViewGroup.LayoutParams params = viewHolder.con_rel.getLayoutParams();
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
name_day = dataSnapshot.getValue(String.class);
if (doctor_id_from_doctor.equals( model.getDoctorID() )){
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
Date strDate = null;
try {
strDate = sdf.parse(model.getDate());
} catch (ParseException e) {
e.printStackTrace();
}
if(System.currentTimeMillis()<=strDate.getTime() ) {
params.height = 300;
params.width =800;
viewHolder.con_rel.setLayoutParams(params);
viewHolder.setDate(model.getDate(),name_day);
}
else {
**params.height = 0;
params.width = 0;
viewHolder.con_rel.setLayoutParams(params);**
}
}
else {
params.height = 0;
params.width = 0;
viewHolder.con_rel.setLayoutParams(params);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
};
recyclerView.setAdapter(recyclerAdapter);
}
My card view code
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="#+id/con_rel"
android:layout_marginRight="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="3dp"
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="wrap_content" android:background="#a2ffffff">
<LinearLayout
android:layout_width="match_parent"
android:layout_marginTop="3dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/day"
android:layout_marginTop="10dp"
android:textSize="18sp"
android:layout_marginLeft="5dp"
android:textColor="#color/colorBlack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/_07th_of_sunday_january_2018_at_9_00am"/>
<TextView
android:id="#+id/nextnumber"
android:layout_marginLeft="5dp"
android:textSize="18sp"
android:textColor="#color/colornextnumber"
android:textStyle="bold"
android:layout_marginTop="20dp"
android:text="#string/next_avealable_number_is_04"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/booknow"
android:textStyle="bold"
android:textSize="18sp"
android:layout_marginTop="20dp"
android:layout_marginLeft="240dp"
android:layout_marginBottom="10dp"
android:textColor="#color/colorbookNow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/book_now"/>
</LinearLayout>
</RelativeLayout>
Here is my inreface
In my application I receive some data from a web server. This data is sent to a RecyclerView. Firstly, all data is displayed fine. However, when I try to refresh it, the data no-longer shows. The data arrives from the web service (as verified using Logcat).
Setting the data
ArrayList<FoodItem> allFood = response.body();
if(foodAdapter != null) {
foodAdapter.swap(allFood);
} else {
foodAdapter = new FoodAdapter(FoodViewForCustomerActivity.this, allFood);
}
photoCollectionView.setAdapter(foodAdapter);
My RecyclerView Adapter
public class FoodAdapter extends RecyclerView.Adapter<FoodAdapter.ViewHolder> {
Context mContext;
ArrayList<FoodItem> foodItems;
public FoodAdapter(Context context, ArrayList<FoodItem> foodItems) {
this.mContext = context;
this.foodItems = foodItems;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.food_item_row, parent, false);
return new ViewHolder(view);
}
public void swap(ArrayList<FoodItem> newFoods) {
foodItems.clear();
foodItems.addAll(newFoods);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
}, 500);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
FoodItem foodItem = foodItems.get(position);
String stringPicture = foodItem.getPhoto();
byte[] decodedPictureStringArray = Base64.decode(stringPicture, Base64.DEFAULT);
Bitmap bitmapPhoto = BitmapFactory.decodeByteArray(decodedPictureStringArray, 0, decodedPictureStringArray.length);
Uri photoUri = getImageUri(mContext, bitmapPhoto);
Picasso.with(mContext).load(photoUri).resize(300, 300)
.centerCrop().into(holder.foodImage);
holder.foodItemNameText.setText(foodItem.getFoodItemName());
holder.foodPriceText.setText(foodItem.getUnitPrice() + " / " + foodItem.getUnitType());
}
public Uri getImageUri(Context inContext, Bitmap inImage) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
inImage.compress(Bitmap.CompressFormat.JPEG, 10, bytes);
String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
return Uri.parse(path);
}
#Override
public int getItemCount() {
return foodItems.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ImageView foodImage;
public TextView foodItemNameText;
public TextView foodPriceText;
public ViewHolder(View itemView) {
super(itemView);
foodImage = (ImageView) itemView.findViewById(R.id.foodPhoto);
foodItemNameText = (TextView) itemView.findViewById(R.id.foodItemNameText);
foodPriceText = (TextView) itemView.findViewById(R.id.foodPriceText);
}
}
}
The RelativeLayout that contains my Toolbar and RecyclerView
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".FoodViewForCustomerActivity">
<include
android:id="#+id/toolbar"
layout="#layout/toolbar"
/>
<android.support.v7.widget.RecyclerView
android:layout_below="#id/toolbar"
android:id="#+id/photoCollectionView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f8f8f8"
android:divider="#null"
android:listSelector="#android:color/transparent"/>
</RelativeLayout>
Sorry, for answering my own question. I solved my question by following solution provided here.Main cause of my problem was that RecyclerView was not calling onCreateViewHolder or onBindView, when i was trying to refresh it.
foodAdapter=new FoodAdapter(FoodViewForCustomerActivity.this,allFood);
Change above line.
Send foodItems arraylist reference in adapter constructor instead of allFood
Correct line is
foodAdapter=new FoodAdapter(FoodViewForCustomerActivity.this,foodItems);
Diagnosis #1
Judging from the comment discussion. It seems like your RecyclerView is being fed the correct data from your source.
However, when the data count is 1, it is hidden underneath other UI elements.
You need to make sure that your RecyclerView is not obscured by other UI elements, such as the Toolbar.
There are multiple ways you can do this.
You could add a marginTop of ?attr/actionBarSize to your RecyclerView.
You could add a layout rule to ConstraintLayout similar to android:layout_constraintTop_toBottomOf ="#id/toolbar".
You could add a layout rule to RelativeLayout similar to android:layout_below="#id/toolbar".
Diagnosis #2
Your code for refreshing data is a little strange:
ArrayList<FoodItem> allFood = response.body();
if(foodAdapter != null) {
foodAdapter.swap(allFood);
} else {
foodAdapter = new FoodAdapter(FoodViewForCustomerActivity.this, allFood);
}
photoCollectionView.setAdapter(foodAdapter);
Normally, you do not set the Adapter every time your data has changed. You set it once on initialization, and then refresh the data with the notify methods.
Instead of what you have, please separate the structure as follows:
Member variable of your FoodViewForCustomerActivity:
private ArrayList<FoodItem> mAllFood = new ArrayList<>();
In onCreate():
foodAdapter = new FoodAdapter(FoodViewForCustomerActivity.this, mAllFood);
photoCollectionView.setAdapter(foodAdapter);
In your data collection method:
ArrayList<FoodItem> food = response.body();
if(foodAdapter != null) {
Log.e(TAG, "Data retrieved (size="+food.size()+"), sending it to Adapter.");
foodAdapter.swap(food);
} else {
Log.e(TAG, "Data retrieved (size="+food.size()+"), but Adapter is null!");
}
Change code like this
public void swap(ArrayList<FoodItem> newFoods){
foodItems.clear();
foodItems.addAll(newFoods);
foodAdapter.notifyDataSetChanged();
}
Fine, you can change too
allFood.clear();
allFood.addAll(newFoods);
notifyDataSetChanged();
I'm trying to figure out how to achieve the same effect of
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
in a RecyclerView implementation. Please help.
There is no built-in support for a "choice mode" structure with RecyclerView. Your options are to either roll it yourself or use a third-party library that offers it.
The DynamicRecyclerView library offers choice modes, but I have not tried it.
This sample app demonstrates implementing it yourself, in this case using the activated state to indicate which is the current choice. The overall pattern is:
Have your RecyclerView.ViewHolder detect a UI operation that indicates a choice (click on a row? click on a RadioButton in the row? etc.).
Keep track of the selection at the level of your RecyclerView.Adapter. In my case, a ChoiceCapableAdapter handles that, in conjunction with a SingleChoiceMode class that implements a ChoiceMode strategy.
When a choice is made, update the newly-chosen row to reflect the choice and update the previously-chosen row to reflect that it is no longer chosen. findViewHolderForPosition() on RecyclerView can help here -- if you track the position of the last choice, findViewHolderForPosition() can give you the ViewHolder for that choice, so you can "un-choose" it.
Keep track of the choice across configuration changes, by putting it in the saved instance state of the activity or fragment that is managing the RecyclerView.
I've created a library for this kind of choice mode applied to the RecyclerView, maybe it can help:
Description
This library has been created to help the integration of a multi-choice selection to the RecyclerView
Implementation
The integration with Gradle is very easy, you just need the jcenter repository and the library:
repositories {
jcenter()
}
...
dependencies {
compile 'com.davidecirillo.multichoicerecyclerview:multichoicerecyclerview:1.0.1'
}
Main steps for usage
Add the MultiChoiceRecyclerView to your xml file
<com.davidecirillo.multichoicesample.MultiChoiceRecyclerView
android:id="#+id/multiChoiceRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Instanciate you object and connect the view
MultiChoiceRecyclerView mMultiChoiceRecyclerView = (MultiChoiceRecyclerView) findViewById(R.id.multiChoiceRecyclerView);
Extend you adapter to the MultiChoiceAdapter and add it to the RecyclerView as per normal usage
public class MyAdapter extends MultiChoiceAdapter<MyViewHolder> {
public MyAdapter(ArrayList<String> stringList, Context context) {
this.mList = stringList;
this.mContext = context;
}
...
}
MyAdapter myAdapter = new MyAdapter(mList, getApplicationContext());
mMultiChoiceRecyclerView.setAdapter(myAdapter);
For more information and customisations:
https://github.com/dvdciri/MultiChoiceRecyclerView
You can follow this:
– Data (String name, boolean selected)
– Adapter with itemClickListener
– Activity or fragment
– activity_main (recyclerView)
– list_item (TextView, CheckBox)
Data
public class MultipleData {
private String mTitle;
private boolean mBoolean;
public MultipleData(String title, boolean mBoolean) {
this.mTitle = title;
this.mBoolean = mBoolean;
}
public String getTitle() {
return mTitle;
}
public void setTitle(String mTitle) {
this.mTitle = mTitle;
}
public boolean isBoolean() {
return mBoolean;
}
public void setBoolean(boolean mBoolean) {
this.mBoolean = mBoolean;
}
}
Your views activity_main.xml (recyclerView)
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.thedeveloperworldisyours.fullrecycleview.multiple.MultipleFragment">
<android.support.v7.widget.RecyclerView
android:id="#+id/multiple_fragment_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
and list_item.xml (TextView, CheckBox)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/multiple_list_item_text"
android:layout_width="match_parent"
android:layout_height="90dp"
android:text="#string/app_name"
android:typeface="monospace"
android:layout_toLeftOf="#+id/multiple_list_item_check_button"
android:gravity="center"
android:textSize="#dimen/multiple_list_item_size_rock_stars"/>
<RadioButton
android:id="#+id/multiple_list_item_check_button"
android:layout_width="wrap_content"
android:layout_height="90dp"
android:layout_alignParentRight="true"
android:checked="false"
android:clickable="false"
android:focusable="false" />
</RelativeLayout>
Adapter with ClickListener
public class MultipleRecyclerViewAdapter extends RecyclerView
.Adapter<MultipleRecyclerViewAdapter
.DataObjectHolder> {
private List<MultipleData> mList;
private static MultipleClickListener sClickListener;
MultipleRecyclerViewAdapter(List<MultipleData> mList) {
this.mList = mList;
}
static class DataObjectHolder extends RecyclerView.ViewHolder
implements View
.OnClickListener {
TextView mTextView;
RadioButton mRadioButton;
DataObjectHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.multiple_list_item_text);
mRadioButton = (RadioButton) itemView.findViewById(R.id.multiple_list_item_check_button);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
sClickListener.onItemClick(getAdapterPosition(), v);
}
}
void changedData(int position) {
if (mList.get(position).isBoolean()) {
mList.get(position).setBoolean(false);
} else {
mList.get(position).setBoolean(true);
}
notifyDataSetChanged();
}
void setOnItemClickListener(MultipleClickListener myClickListener) {
this.sClickListener = myClickListener;
}
#Override
public DataObjectHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.multiple_list_item, parent, false);
DataObjectHolder dataObjectHolder = new DataObjectHolder(view);
return dataObjectHolder;
}
#Override
public void onBindViewHolder(DataObjectHolder holder, int position) {
holder.mTextView.setText(mList.get(position).getTitle());
holder.mRadioButton.setChecked(mList.get(position).isBoolean());
}
#Override
public int getItemCount() {
return mList.size();
}
interface MultipleClickListener {
void onItemClick(int position, View v);
}
}
Activity or fragment
public class MultipleFragment extends Fragment implements MultipleRecyclerViewAdapter.MultipleClickListener{
MultipleRecyclerViewAdapter mAdapter;
public MultipleFragment() {
// Required empty public constructor
}
public static MultipleFragment newInstance() {
return new MultipleFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.multiple_fragment, container, false);
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.multiple_fragment_recycler_view);
MultipleData hendrix = new MultipleData("Jimi Hendrix", false);
MultipleData bowie = new MultipleData("David Bowie", false);
MultipleData morrison = new MultipleData("Jim Morrison", false);
MultipleData presley = new MultipleData("Elvis Presley", false);
MultipleData jagger = new MultipleData("Mick Jagger", false);
MultipleData cobain = new MultipleData("Kurt Cobain", false);
MultipleData dylan = new MultipleData("Bob Dylan", false);
MultipleData lennon = new MultipleData("John Lennon", false);
MultipleData mercury = new MultipleData("Freddie Mercury", false);
MultipleData elton = new MultipleData("Elton John", false);
MultipleData clapton = new MultipleData("Eric Clapton", false);
List<MultipleData> list = new ArrayList<>();
list.add(0, hendrix);
list.add(1, bowie);
list.add(2, morrison);
list.add(3, presley);
list.add(4, jagger);
list.add(5, cobain);
list.add(6, dylan);
list.add(7, lennon);
list.add(8, mercury);
list.add(9, elton);
list.add(10, clapton);
mAdapter = new MultipleRecyclerViewAdapter(list);
recyclerView.setAdapter(mAdapter);
recyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
mAdapter.setOnItemClickListener(this);
return view;
}
#Override
public void onItemClick(int position, View v) {
mAdapter.changedData(position);
}
}
You can see this example in GitHub and this post for multiple choice, and this post for single choice Happy code!!!