So I am trying to create an android app on alcohol and mixers, but have been stuck on the following problem for a while now...
I want to display every alcohol category (eg: Gin, Vodka, Whiskey, etc..) in a RecyclerView that scrolls horizontally, and every alcohol type (eg: Bourbon and Scotch for the Whiskey Category) in a RecyclerView that scrolls vertically.
I have created one adapter for each RecyclerView (CategoryAdapter for the horizontal RecyclerView called category, and MixerAdapter for the vertical RecyclerView called categoryDetails).
So far I've managed to create and display category as desired, but have some difficulties for categoryDetails.
Basically, I can't figure out how to update the contents of categoryDetails when an item of category is selected:
For example
If the user selects Whiskey in category, I want categoryDetails to display Bourbon and Scotch.
If the user then selects Gin, I want categoryDetails to only display Gin and Flavoured Gin, etc...
I hope I've been clear enough on what it is I want to accomplish!
Any help would be much appreciated, thanks!!
Here is a screenshot of how the screen appears when the activity is loaded.
If a user selects Rum (white on black RecyclerView), I want the RecyclerView currently showing Gin and Flavored Gin (black on white RecyclerView) to show the alcohols associated with the Rum category.
Screenshot
Here is the XML file holding the two recyclerViews category and categoryDetails
<?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=".TipsDrinks">
<android.support.v7.widget.RecyclerView
android:id="#+id/category"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="#android:color/black"
android:orientation="horizontal"
android:scrollbars="horizontal"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/SpinnerPrompt" >
</android.support.v7.widget.RecyclerView>
<android.support.v7.widget.RecyclerView
android:id="#+id/categoryDetails"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:background="#android:color/white"
android:orientation="horizontal"
android:scrollbars="vertical"
android:visibility="visible"
app:layout_constraintBottom_toTopOf="#+id/adView4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/category"
app:layout_constraintVertical_bias="0.0">
</android.support.v7.widget.RecyclerView>
</android.support.constraint.ConstraintLayout>
The Class associated with the previous layout
public class TipsDrinks extends AppCompatActivity {
private CategoryAdapter categoryAdapter; // Adapter used for the category RecyclerView
public MixerAdapter mixerAdapter; //Adapter used for the categoryDetails RecyclerView
private RecyclerView categories; // The RecyclerView holding the name of each alcohol category
public RecyclerView catDetails; // The RecyclerView holding each type of that alcohol category
private DrinkMenu drinkMenu; // The DrinkMenu is another Class holding every Alcohol Category, the type of each alcohol and the mixers good with it
private ArrayList<String> drinkCat = new ArrayList<>(); // A String ArrayList holding the name of each alcohol category (Gin, Vodka, Rum, Whiskey, Other)
private ArrayList<Drink> drinkMixers = new ArrayList<>(); // A Drink(String, ArrayList<String>) ArrayList holding the mixers of every Drink
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tips_drinks);
categories = findViewById(R.id.category);
catDetails = findViewById(R.id.categoryDetails);
drinkMenu = new DrinkMenu();
setCategoryView(); // Creates the category RecyclerView
setDrinkMixers(); // Creates the categoryDetails RecyclerView
}
private void setCategoryView(){
categoryAdapter = new CategoryAdapter(drinkCat);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(TipsDrinks.this, LinearLayoutManager.HORIZONTAL, false);
categories.addItemDecoration(new DividerItemDecoration(TipsDrinks.this, DividerItemDecoration.HORIZONTAL));
categories.setLayoutManager(layoutManager);
categories.setItemAnimator(new DefaultItemAnimator());
categories.setAdapter(categoryAdapter);
prepareCategories();
}
private void prepareCategories(){
drinkCat.clear();
drinkCat.addAll(drinkMenu.getDrinkCategories());
for (String drink : drinkCat) {
System.out.println(drink);
}
categoryAdapter.notifyItemInserted(drinkCat.size() - 1);
}
private void setDrinkMixers() {
mixerAdapter = new MixerAdapter(drinkMixers);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
catDetails.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
catDetails.setLayoutManager(layoutManager);
catDetails.setItemAnimator(new DefaultItemAnimator());
catDetails.setAdapter(mixerAdapter);
prepareMixers();
}
private void prepareMixers() {
drinkMixers.clear();
drinkMixers.addAll(drinkMenu.getDrinkMixers(categoryAdapter.getCurrentCategory()));
mixerAdapter.notifyItemChanged(drinkMixers.size() -1);
}
}
The CategoryAdapter Class for the category RecyclerView
public class CategoryAdapter extends RecyclerView.Adapter<CategoryAdapter.MyViewHolder> {
private List<String> drinkList; //A List of String holding the category for each drink
private String textName = "Gin";
class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private TextView name; // The TextView holding the name of the category
MyViewHolder(View view) {
super(view);
view.setOnClickListener(this);
name = view.findViewById(R.id.catName);
}
#Override
public void onClick(View v) {
//Code to update the contents of the categoryDetails RecyclerView
}
}
CategoryAdapter(ArrayList<String> drinkList) { this.drinkList = drinkList; }
#Override
public CategoryAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.category_text, parent, false);
return new MyViewHolder(itemView);
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(CategoryAdapter.MyViewHolder holder, int position) {
String drink = drinkList.get(position);
holder.name.setText(drink);
textName = holder.name.getText().toString();
}
#Override
public int getItemCount() {
return drinkList.size();
}
public String getCurrentCategory() {
return textName;
}
}
The MixerAdapter Class for the categoryDetails RecyclerView
public class MixerAdapter extends RecyclerView.Adapter<MixerAdapter.MyViewHolder>{
private List<Drink> mixerList;
class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
TextView mixerCat, mixers;
MyViewHolder(View view) {
super(view);
view.setOnClickListener(this);
mixerCat = view.findViewById(R.id.mixerCat2);
mixers = view.findViewById(R.id.mixers2);
}
#Override
public void onClick(View v) {
if (mixers.getVisibility() == mixerCat.getVisibility()) {
mixers.setVisibility(View.GONE);
}
else {
mixers.setVisibility(View.VISIBLE);
}
}
}
MixerAdapter(ArrayList<Drink> mixerList) {
this.mixerList = mixerList;
}
#Override
public MixerAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.drinks_row, parent, false);
return new MixerAdapter.MyViewHolder(itemView);
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(MixerAdapter.MyViewHolder holder, int position) {
Drink drink = mixerList.get(position);
StringBuilder mixerStringList = new StringBuilder("");
holder.mixerCat.setText(drink.getSorM());
for (String mixer: drink.getMixers()) {
mixerStringList.append(mixer).append("\n");
}
holder.mixers.setText(mixerStringList.toString().trim());
}
#Override
public int getItemCount() {
return mixerList.size();
}
}
Create a callback interface in CategoryAdapter. When user clicks on an item in Category RecycleView, it's activity responsibility to populate MixerAdapter with new items as per selection.
CategoryAdapter
public class CategoryAdapter extends RecyclerView.Adapter<CategoryAdapter.MyViewHolder> {
private CategoryInterface callback;
CategoryAdapter(ArrayList<String> drinkList, CategoryInterface listener) {
this.drinkList = drinkList;
callback = listener;
}
#Override
public void onClick(View v) {
//Code to update the contents of the categoryDetails RecyclerView
callback.onItemSelected(getAdapterPosition());
}
public interface CategoryInterface {
void onItemSelected(int position);
}
}
Activity
public class TipsDrinks extends AppCompatActivity implements CategoryInterface {
private void setCategoryView(){
categoryAdapter = new CategoryAdapter(drinkCat, this);
}
#Override
void onItemSelected(int position) {
//Reassign items in MixerAdapter
}
}
that depends on your implementations , one global method which should work for all implementations is using HolderView method and using onTouchListener on the groups's root view
v.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP){
//the child should expand and be updated
}
return false;
}
});
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 am creating an AlertDialog custom class, called ActionDialog, which will contains a RecyclerView containing Buttons. I have a List of Button that I populate in the custom class ActionDialog (for now i just populate with useless Button just to try to use it, except one which I create in another class).
The problem is that when i create the AlertDialog, all buttons are showing empty, they are showed but with no text/no clicklistener (as you can see in the image below).
(I have added a custom ActionListener to a Button in another class and then give it as parameter in ActionDialog class. Will it lose the ActionListener?)
Here is the result.
I will leave here my ActionDialog class code, and the adapter class.
This is ActionDialog class:
public class ActionDialog extends AlertDialog{
private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private Button actionButtons;
private List<Button> buttons;
private Activity context;
public ActionDialog(#NonNull Activity context, Button actionButtons) {
super(context);
this.context = context;
this.actionButtons = actionButtons;
buttons = new ArrayList<>();
initButton();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//requestWindowFeature(Window.FEATURE_NO_TITLE);
}
private void initButton(){
initZoneButton();
//TODO init all buttons
Button b1 = new Button(context);
b1.setText("ExampleButton1");
Button b2 = new Button(context);
b2.setText("ExampleButton2");
b1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String a;
}
});
buttons.add(b1);
buttons.add(b2);
}
private void initZoneButton(){
buttons.add(actionButtons); //this button is created in another class and give as parameter in this class
}
public void createDialog(){
Builder mBuilder = new Builder(context);
View view = context.getLayoutInflater().inflate(R.layout.dialog_actionbuttons_layout, null);
mRecyclerView = view.findViewById(R.id.dialog_actionbuttons_rv);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(context);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new ActionButtonsAdapter(buttons);
mRecyclerView.setAdapter(mAdapter);
mBuilder.setView(view);
mBuilder.create().show();
}
}
Here is the RecyclerView adapter class:
public class ActionButtonsAdapter extends RecyclerView.Adapter<ActionButtonsAdapter.ViewHolder>{
private List<Button> dataButtons;
static class ViewHolder extends RecyclerView.ViewHolder {
Button actionButton;
ViewHolder(View v) {
super(v);
actionButton = v.findViewById(R.id.action_button_rv);
}
}
public ActionButtonsAdapter(List<Button> dataButtons){
this.dataButtons = dataButtons;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.actionButton = dataButtons.get(position);
//i think the problem is here, maybe
}
#Override
public ActionButtonsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_actionbutton_layout, parent, false);
return new ViewHolder(v);
}
#Override
public int getItemCount() {
return dataButtons.size();
}
}
I think in the onBindViewHolder method you should do what ever you want to do with your button.
Also there is no need for the list of buttons here. Make a list the data you need to be held in the Buttons RecyclerView.
I have a RecyclerView that will display Genres for restaurants lets say, So I will create a List of strings to hold these genres names (chickens, meats, etc,..)
Setting its text
holder.actionButton.setText(// Make use of position here);
Or Click Listeners.
Update
You can check google samples for recyclerview here
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
Log.d(TAG, "Element " + position + " set.");
// Get element from your dataset at this position and replace the contents of the view
// with that element
viewHolder.getTextView().setText(mDataSet[position]);
}
wheres mDataset is Array of Strings.
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
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!!!