Setup span in StaggeredGridLayoutManager - android

I need to create a view like the following (this is an iOS screenshot since I've already created it).
Each blue box is part of a linear layout of a RecyclerView, while each green item is part of a staggered layout.
I'm able to create that structure. The problem is that I cannot make each gray box half of the yellow box. Using the StaggeredGridLayoutManager I'm not able to control the span (size) of the boxes that appear for the inner recycler view. Only a GridLayoutManager has the chance to set a span size lookup.
The desiderata is to calculate the boxes based on the with of the container. For example: if the screen is 99, the yellow box should be 66 while the gray 33.
Do you have any suggestions on how to achieve this?
Here a snippet of the code I'm using:
public class BoxViewAdapter extends RecyclerView.Adapter<BoxViewAdapter.BoxViewHolder> {
private List<Box> mValues;
private Context mContext;
protected BoxViewAdapter.ItemListener mListener;
public BoxViewAdapter(Context context, List<Box> values, BoxViewAdapter.ItemListener itemListener) {
mValues = values;
mContext = context;
mListener = itemListener;
}
#Override
public BoxViewAdapter.BoxViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.box_view_item, parent, false);
return new BoxViewAdapter.BoxViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull BoxViewAdapter.BoxViewHolder holder, int position) {
holder.bind(mValues.get(position));
}
#Override
public int getItemCount() {
return mValues.size();
}
public interface ItemListener {
void onItemClick();
}
public class BoxViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public Box item;
private RecyclerView recyclerView;
public BoxViewHolder(View v) {
super(v);
v.setOnClickListener(this);
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.HORIZONTAL);
recyclerView = v.findViewById(R.id.cardRecyclerView);
recyclerView.setLayoutManager(staggeredGridLayoutManager);
}
public void bind(Box item) {
this.item = item;
recyclerView.setAdapter(new CardViewAdapter(itemView.getContext(), item.cards, null));
}
#Override
public void onClick(View view) {
if (mListener != null) {
mListener.onItemClick();
}
}
}
}

In the meantime, I've resolved this way.
In onBindViewHolder of CardViewAdapter I simply set LayoutParams for itemView. Since the item view is the rendered inside a RecyclerView, StaggeredGridLayoutManager knows how to deal with it.
#Override
public void onBindViewHolder(#NonNull CardViewHolder viewHolder, int position) {
if (mUseLayoutParameters) {
int singleScreenWidth = DisplayUtils.getScreenWidth() / 3;
Card card = mValues.get(position);
if (card.isDoubleSized) {
singleScreenWidth = singleScreenWidth * 2;
}
StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams(singleScreenWidth, ViewGroup.LayoutParams.MATCH_PARENT);
viewHolder.itemView.setLayoutParams(layoutParams);
}
viewHolder.bind(mValues.get(position));
}

Related

Move an item of recyclerview into another recyclerview on button click on custom adapter

I have two recycler views and both of them use the same custom adapter.
https://imgur.com/3WMGgiL - this is how the item from the recycler view looks like
What i want to do is, whenever the "Anuleaza rezervarea" button is clicked, i want to remove the item from the current recyclerview and add it to another recycler view
How can i do that? I'll leave my custom adapter code below. I really don't understand what to write in the on click listener.
public class RecyclerViewAdapterRezervare extends RecyclerView.Adapter<RecyclerViewAdapterRezervare.RecycleViewHolder> {
private List<Rezervare> rezervari;
private Context context;
private LayoutInflater inflater;
private String numeUtilizator;
private String status;
private Rezervare rezervare = new Rezervare();
private DatabaseReference reff;
public RecyclerViewAdapterRezervare(List<Rezervare> rezervari, Context context, String numeUtilizator) {
this.rezervari = rezervari;
this.context = context;
this.numeUtilizator = numeUtilizator;
this.inflater = LayoutInflater.from(context);
}
#NonNull
#Override
public RecycleViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.rezervari_layout, parent, false);
return new RecycleViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull RecycleViewHolder holder, int position) {
rezervare = rezervari.get(position);
holder.btn_anuleaza.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new AlertDialog.Builder(view.getContext()).setTitle("Anulare rezervare").setMessage("Sunteti sigur ca doriti anularea rezervarii?")
.setPositiveButton(R.string.da, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
holder.btn_anuleaza.setText(R.string.anulat);
holder.btn_anuleaza.setBackgroundColor(view.getResources().getColor(R.color.red));
}
}).setNegativeButton(R.string.nu, null).show();
}
});
}
#Override
public int getItemCount() {
return rezervari.size();
}
public static class RecycleViewHolder extends RecyclerView.ViewHolder {
public TextView tv_nume_teren;
public TextView tv_adresa_teren;
public TextView tv_ora;
public TextView tv_data;
public Button btn_anuleaza;
public Button btn_navigheaza;
public static boolean isCanceled;
public RecycleViewHolder(#NonNull View itemView) {
super(itemView);
tv_nume_teren = itemView.findViewById(R.id.tv_rezervare_nume_teren);
tv_adresa_teren = itemView.findViewById(R.id.tv_rezervare_adresa_teren);
btn_anuleaza = itemView.findViewById(R.id.btn_anuleaza);
btn_navigheaza = itemView.findViewById(R.id.btn_maps);
tv_ora = itemView.findViewById(R.id.tv_rezervare_ore);
tv_data = itemView.findViewById(R.id.tv_rezervare_data);
isCanceled = false;
if(isCanceled){
btn_anuleaza.setText(R.string.anulat);
btn_anuleaza.setBackgroundColor(itemView.getResources().getColor(R.color.red));
} else {
btn_anuleaza.setText(R.string.anuleaza_rezervare);
btn_anuleaza.setBackgroundColor(itemView.getResources().getColor(R.color.teal_700));
}
}
}
When i click on the "Anuleaza rezervare" button, the two buttons become red and unclickable, and i want to transfer the item to another recycler view. How can i do that? Thank you!
You can achieve this by calling,
int removeIndex = 2;
data.remove(removeIndex);
adapter.notifyItemRemoved(removeIndex);
on the first recyclerView and update the second by calling,
String newItem = "This is a new item.";
int updateIndex = 3; // Your index to insert new the item
data.set(updateIndex, newItem);
adapter.notifyItemChanged(updateIndex);
for the second recyclerView.

How do I make a dynamic list of items the children of a RecyclerView's items

Inside of my RecyclerView I want to have a dynamic number of items that are children of each item in my RecyclerView. I temporarily made extra layouts and set their visibility to gone and then turn them visible when the add button is pressed, but I know that isn't good practice. I don't think a ListView nested inside of a RecyclerView would be the right thing. If it is, then please enlighten me on how I would go about that. note: I can't get the first line of my code to show in the code snippet for some reason
Exercise Adapter
public class ExerciseAdapter extends RecyclerView.Adapter<ExerciseAdapter.ExerciseAdapterViewHolder> {
private List<Exercise> mExerciseList;
private static int counter = 0;
public class ExerciseAdapterViewHolder extends RecyclerView.ViewHolder {
final TextView anotherSetTextview;
final LinearLayout linearLayout4;
final LinearLayout linearLayout5;
ExerciseAdapterViewHolder(View view) {
super(view);
anotherSetTextview = view.findViewById(R.id.another_set);
linearLayout4 = view.findViewById(R.id.layout4);
linearLayout5 = view.findViewById(R.id.layout5);
}
}
#Override
public ExerciseAdapterViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
Context context = viewGroup.getContext();
int layoutIdForListItem = R.layout.list_item_cardview;
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(layoutIdForListItem, viewGroup, false);
return new ExerciseAdapterViewHolder(view);
}
#Override
public void onBindViewHolder(final ExerciseAdapterViewHolder holder, int position) {
Exercise currentExercise = mExerciseList.get(position);
String count = Integer.toString(position + 1) + ".";
holder.anotherSetTextview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
counter++;
if(counter==1)
holder.linearLayout4.setVisibility(View.VISIBLE);
else if(counter==2)
holder.linearLayout5.setVisibility(View.VISIBLE);
}
});
}
#Override
public int getItemCount() {
if (null == mExerciseList) return 0;
return mExerciseList.size();
}
public void setExerciseData(List<Exercise> exerciseData) {
mExerciseList = exerciseData;
notifyDataSetChanged();
}
public void addNewExercise(Exercise e){
mExerciseList.add(e);
notifyDataSetChanged();
}
}
I'm making a workout tracker app for my class and I want the user to add exercises as needed and then they add the sets for each exercise as needed. Basically each Exercise object in the RecyclerView needs to have their own dynamic number of Set objects as the children. This screenshot of what I have so far should help visualize what I'm going for. I'm still new to posting on here, so forgive me for the poor formatting.
you can inflate the view dynamically in recycler view item. just have a Linear layout in R.layout.list_item_cardview layout, create a the data you want as another child layout and inflate the child layouts as you wish.
public class ExerciseAdapter
extendsRecyclerView.Adapter<ExerciseAdapter.ExerciseAdapterViewHolder> {
private List<Exercise> mExerciseList;
public class ExerciseAdapterViewHolder extends RecyclerView.ViewHolder {
final TextView anotherSetTextview;
final LinearLayout layout;
ExerciseAdapterViewHolder(View view) {
super(view);
anotherSetTextview = view.findViewById(R.id.another_set);
layout = view.findViewById(R.id.layout);
}
}
#Override
public ExerciseAdapterViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
Context context = viewGroup.getContext();
int layoutIdForListItem = R.layout.list_item_cardview;
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(layoutIdForListItem, viewGroup, false);
return new ExerciseAdapterViewHolder(view);
}
#Override
public void onBindViewHolder(final ExerciseAdapterViewHolder holder, int position) {
final Exercise currentExercise = mExerciseList.get(position);
for(int i=0;i<currentExercise.count;i++)
{
LayoutInflater inflater = LayoutInflater.from(holder.itemView.getContext());
LinearLayout linearLayout = inflater.inflate(R.layout.child, null);
holder.layout.addView(linearLayout);
}
holder.anotherSetTextview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
currentExercise.count++;
notifyDataSetChanged();
}
});
}
#Override
public int getItemCount() {
if (null == mExerciseList) return 0;
return mExerciseList.size();
}
public void setExerciseData(List<Exercise> exerciseData) {
mExerciseList = exerciseData;
notifyDataSetChanged();
}
public void addNewExercise(Exercise e){
mExerciseList.add(e);
notifyDataSetChanged();
}
}

nested recyclerview inside adapter

I am developing an android app where I have used nested RecyclerView but I am not achieving what I want.I want to achieve on this list it should show Title and below multiple images like json below how can I achieve that I am using two viewholder and it should scrool as well.
json structure
below Adapter class
public class UranAdapter extends RecyclerView.Adapter {
public List<Exhibit> exhibitList;
public Context context;
public UranAdapter(List<Exhibit> uranList, Context context) {
this.exhibitList = uranList;
this.context = context;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView;
switch (viewType) {
case Exhibit.TEXT_TYPE:
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.exhibit_list, parent, false);
return new ViewHolder(itemView);
case Exhibit.IMAGE_TYPE:
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.exhibit_list2, parent, false);
return new ImageViewHolder(itemView);
}
return null;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder viewHolder, int i) {
}
#Override
public int getItemViewType(int position) {
return exhibitList.get(position).type;
}
public int getItemCount() {
return exhibitList.size();
}
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Exhibit exhibit = exhibitList.get(position);
switch (exhibit.type) {
case Exhibit.TEXT_TYPE:
((ViewHolder) holder).exhibition_textView.setText(exhibit.getTitle());
break;
case Exhibit.IMAGE_TYPE:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
((ViewHolder) holder).exhibition_imageView.setImageResource(exhibit.image);
}
break;
}
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView exhibition_imageView;
TextView exhibition_textView;
public ViewHolder(View view) {
super(view);
exhibition_textView = (TextView) view.findViewById(R.id.exhibition_textview);
}
}
public static class ImageViewHolder extends RecyclerView.ViewHolder {
public ImageView exhibition_imageView;
TextView exhibition_textView;
public ImageViewHolder(View view) {
super(view);
exhibition_imageView = (ImageView) view.findViewById(R.id.exhibition_imageview);
}
}
}
below MainActivity
public class MainActivity extends AppCompatActivity {
public List<Exhibit> exhibitList = new ArrayList<>();
Context context;
RecyclerView recyclerView;
public UranAdapter uranAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ApiInterface apiInterface = ApiClient.getApiService();
Call<ExhibitsLoader> call = apiInterface.getExhibitList();
call.enqueue(new Callback<ExhibitsLoader>() {
#Override
public void onResponse(Call<ExhibitsLoader> call, Response<ExhibitsLoader> response) {
exhibitList = response.body().getExhibitList();
exhibitList.add(new Exhibit(Exhibit.IMAGE_TYPE));
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
uranAdapter = new UranAdapter(exhibitList, context); // changes
recyclerView.setAdapter(uranAdapter);
}
#Override
public void onFailure(Call<ExhibitsLoader> call, Throwable t) {
}
});
}
}
below image what I want
I think what you need here is flatMap the embedded image list into one-level list and you're going correctly to have two ViewHolder of different types, one for title, and one for image. Don't understand why your ViewHolders are static.
You forget to call notifyDatasetChanged() after setting the adapter.
uranAdapter = new UranAdapter(exhibitList, context); // changes
recyclerView.setAdapter(uranAdapter);
uranAdapter.notifyDatasetChanged();
Use Expandable CardView for this ..
Like : https://www.youtube.com/watch?v=z9qScBaKfnM&t=727s
Why don't you use two RecyclerViews, one inside the other?
One RecyclerView will have the title and a children RecyclerView. The second RecyclerView(the children RecyclerView) will have just the images.
Check this post: Recyclerview inside Recyclerview , get click position of child row inside parent Adapter

Selected image in recyclerview is get unselected upon scroll

In my recyclerview has one imageview and textview. I'm changing image of ImageView onn onClickListener. Now the problem is if i click on image of position 3 and scroll down... image of position 8 is also changed and again if i scroll up...image of position 2 is changed.
public class PortraitListviewAdapter extends RecyclerView.Adapter<PortraitListviewAdapter.ViewHolder> {
Context context;
static List<PortraitParentListAdapterBean> list;
static List<String> selectedPosition ;
public PortraitListviewAdapter(Context context, List<PortraitParentListAdapterBean> list) {
this.context = context;
this.list = list;
selectedPosition = new ArrayList<>();
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.listview_parent_portrait, parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
PortraitParentListAdapterBean portBean = list.get(position);
Log.i("pos",position+"");
holder.parentHeading.setText(portBean.getHeading());
if (selectedPosition.contains(list.get(position).getHeading())){
holder.parentImage.setImageResource(R.drawable.sad);
}
}
#Override
public int getItemCount() {
return list.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
protected TextView parentHeading;
protected ImageView parentImage;
public ViewHolder(View itemView) {
super(itemView);
parentHeading = (TextView)itemView.findViewById(R.id.parent_heading);
parentImage = (ImageView)itemView.findViewById(R.id.imageView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
selectedPosition.add(list.get(getAdapterPosition()).getHeading());
parentImage.setImageResource(R.drawable.sad);
}
});
}
}
}
the above code is my implementation for recyclerview adapter. please help to understand the concept.
SOLUTION
change
if (selectedPosition.contains(list.get(position).getHeading())){
holder.parentImage.setImageResource(R.drawable.sad);
}
with
if (selectedPosition.contains(list.get(position).getHeading())){
holder.parentImage.setImageResource(R.drawable.sad);
} else {
holder.parentImage.setImageResource(R.drawable.your_default_drawable);
}
EXPLANATION
When you scroll your RecyclerView the system doesn't recreate always your ViewHolder but reuse one you previously scrolled that is no longer visible, so you need to reset your standard values in order to avoid showing wrong values.

Giving more spacing between 3 and 4 row item in Recycler GridLayoutManager

I am using RecyclerView with GridLayoutManager as a layout for the bus ticket booking. Everything works fine but except the place where I stuck with the logics that I have to use to leave more spacing between 3 and 4 column item in all the rows. Basically my layout should be like as the screenshot attached below.
My Requirement:
Current Output:
I am also posting my complete RecyclerView GridLayoutManager for your reference as follows
MultiSelectRecyclerViewAdapter.java
public class MultiSelectRecyclerViewAdapter extends SelectableAdapter<MultiSelectRecyclerViewAdapter.ViewHolder> {
private ArrayList<String> mArrayList;
private Context mContext;
private ViewHolder.ClickListener clickListener;
public MultiSelectRecyclerViewAdapter (Context context, ArrayList<String> arrayList,ViewHolder.ClickListener clickListener) {
this.mArrayList = arrayList;
this.mContext = context;
this.clickListener = clickListener;
}
// Create new views
#Override
public MultiSelectRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(
R.layout.row_multiselect, null);
ViewHolder viewHolder = new ViewHolder(itemLayoutView,clickListener);
return viewHolder;
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
viewHolder.tvName.setText(mArrayList.get (position));
viewHolder.selectedOverlay.setVisibility(isSelected(position) ? View.VISIBLE : View.INVISIBLE);
}
#Override
public int getItemCount() {
return mArrayList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,View.OnLongClickListener {
public TextView tvName;
private ClickListener listener;
private final View selectedOverlay;
public ViewHolder(View itemLayoutView,ClickListener listener) {
super(itemLayoutView);
this.listener = listener;
tvName = (TextView) itemLayoutView.findViewById(R.id.tvName);
selectedOverlay = (View) itemView.findViewById(R.id.selected_overlay);
itemLayoutView.setOnClickListener(this);
itemLayoutView.setOnLongClickListener (this);
}
#Override
public void onClick(View v) {
if (listener != null) {
listener.onItemClicked(getAdapterPosition ());
}
}
#Override
public boolean onLongClick (View view) {
if (listener != null) {
return listener.onItemLongClicked(getAdapterPosition ());
}
return false;
}
public interface ClickListener {
public void onItemClicked(int position);
public boolean onItemLongClicked(int position);
}
}
}
MultiSelectRecyclerViewActivity.java
public class MultiSelectRecyclerViewActivity extends AppCompatActivity implements MultiSelectRecyclerViewAdapter.ViewHolder.ClickListener {
private Toolbar toolbar;
private RecyclerView mRecyclerView;
private MultiSelectRecyclerViewAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private ArrayList<String> mArrayList = new ArrayList<String>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView(R.layout.activity_multiselect);
toolbar = (Toolbar) findViewById(R.id.toolbar);
for (int i = 1; i <= 48; i++) {
mArrayList.add ( ""+ i);
}
if (toolbar != null) {
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("MultiSelectRecylcerView");
}
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new GridLayoutManager (this,4));
mAdapter = new MultiSelectRecyclerViewAdapter (MultiSelectRecyclerViewActivity.this,mArrayList,this);
mRecyclerView.setAdapter (mAdapter);
}
#Override
public void onItemClicked (int position) {
toggleSelection(position);
}
#Override
public boolean onItemLongClicked (int position) {
toggleSelection(position);
return true;
}
private void toggleSelection(int position) {
mAdapter.toggleSelection (position);
}
}
What I have tried in coding:
I have followed this gist to assign the spacing for my grid items using ItemDecoration as below. But unfortunately I could able to assign spacing only equally between all the items.
mRecyclerView.setLayoutManager(new GridLayoutManager(context, NUM_COLUMNS);
ItemOffsetDecoration itemDecoration = new ItemOffsetDecoration(context, R.dimen.item_offset);
mRecyclerView.addItemDecoration(itemDecoration);
Kindly please help me with any kind of tips and solutions to solve my case. I am completely stuck up how to proceed with this requirement and logic using RecyclerView which I considered should be the optimized approach. Thanks in advance.
Using item decoration here could create some problems. If you are planning to handle click on the item, clicking on blank space would be interpreted as a click on items located in inner columns.
You can configure GridLayoutManager for handling one more column and handle space as a specific view type. Here I put code implementing this approach.
Hope this could help.

Categories

Resources