Different viewtypes in recyclerview with GridLayoutManager - android

I know many have asked this question before, I too have in fact I have worked on a chat app that uses a recyclerview to show chats, I use view types pick a message layout depending on the sender but I feel this question is different.
I have a Recyclerview which is displaying content with a GridLayoutManager
heres an image to show that, in my image I have 3 items; A, B and C, I would like C to fit the width, I am really not sure if this can be achieved with view types, below is my Adapter code
Adapter:
public class StoreDisplayItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private ArrayList<StoreDisplayProduct> products;
private Interfaces.OnItemClickedListener onItemClickedListener;
public StoreDisplayItemAdapter(Context context, ArrayList<StoreDisplayProduct> products, Interfaces.OnItemClickedListener onItemClickedListener) {
this.context = context;
this.products = products;
this.onItemClickedListener = onItemClickedListener;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new ItemViewHolder(inflater.inflate(R.layout.store_display_item, parent, false));
}
public StoreDisplayProduct getProductsItem(int pos) {
return products.get(pos);
}
public void setProducts(ArrayList<StoreDisplayProduct> products) {
this.products = products;
notifyDataSetChanged();
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
StoreDisplayProduct product = getProductsItem(position);
ItemViewHolder viewHolder = (ItemViewHolder) holder;
viewHolder.txtProductType.setText(product.getProductTypeDisplayName());
viewHolder.txtName.setText(product.getName());
viewHolder.img.post(() -> Picasso.with(context)
.load(product.getDisplayImage().getFullSize())
.fit()
.transform(new RoundedCornersTransformation(10,10))
.placeholder(R.color.asbestos)
.centerCrop()
.into(viewHolder.img));
}
#Override
public int getItemCount() {
return this.products.size();
}
public class ItemViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.txtProductType)
TextView txtProductType;
#BindView(R.id.img)
SquareImageView img;
#BindView(R.id.txtName)
TextView txtName;
public ItemViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
itemView.setOnClickListener(v -> {
onItemClickedListener.onItemClicked(v, getAdapterPosition());
});
}
}
}
And how I use the adapter
recyclerStoreDisplayProducts.setAdapter(storeDisplayItemAdapter);
recyclerStoreDisplayProducts.setLayoutManager(new GridLayoutManager(getContext(), 2));
recyclerStoreDisplayProducts.setHasFixedSize(true);
recyclerStoreDisplayProducts.setNestedScrollingEnabled(true);

You can use span count like
GridLayoutManager layoutManager = new GridLayoutManager(this, 4);
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
if (position<3)
return 1;
else
return 2;
}
});

You can use the Staggered Grid Layout Manager as per your question #Aubry,
//you need to add the code for creating a staggered layout manager
recyclerStoreDisplayProducts.setAdapter(storeDisplayItemAdapter);
StaggeredGridLayoutManager staggeredGridLayoutManager = new
StaggeredGridLayoutManager(mNoOfColumns,StaggeredGridLayoutManager.HORIZONTAL);
recyclerStoreDisplayProducts.setHasFixedSize(true);
recyclerStoreDisplayProducts.setNestedScrollingEnabled(true);
recyclerStoreDisplayProducts.setLayoutManager(staggeredGridLayoutManager);

Related

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

LinearLayoutManager - repetition in a circle

Is there any easy way to make LinearLayoutManager to be as a circle (repetition items in a circle)?
I investigated it and found only way to make own custom LayoutManager, so it would be cool to find a way with less code
First thing to do is change your adapter's getItemCount() method to return Integer.MAX_VALUE. This is not actually infinite, but it's over two billion so I doubt anyone's going to scroll that far.
Next, whenever you retrieve an item from your data source, don't just use position. Instead, use position % dataSource.size()... this will repeat your items as the user scrolls.
Here's an example:
public class MainActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recycler = (RecyclerView) findViewById(R.id.recycler);
recycler.setAdapter(new MyAdapter());
}
private static class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
private final List<String> items;
private MyAdapter() {
items = new ArrayList<>();
items.add("A");
items.add("B");
items.add("C");
items.add("D");
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View itemView = inflater.inflate(R.layout.itemview, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
String item = items.get(position % items.size());
((TextView) holder.itemView).setText(item);
}
#Override
public int getItemCount() {
return Integer.MAX_VALUE;
}
}
private static class MyViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(View itemView) {
super(itemView);
}
}
}

One RecyclerAdapter for two activity

I make app with a couple of different categories of pictures. I want to display those pictures with RecyclerView.
I want to display button as a image when I clicked button before.
Can I make it in one adapter? Now I have two adapters, that code is as follow:
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
private int[] images = {
R.drawable.imageA1, .... R.drawable.imageA10
};
private int[] images2 = {
R.drawable.imageB1, .... R.drawable.imageB10
};
class ViewHolder extends RecyclerView.ViewHolder{
public int currentItem;
public ImageView itemImage, itemImage2;
public TextView itemTitle;
public ViewHolder(View itemView) {
super(itemView);
itemImage = (ImageView)itemView.findViewById(R.id.imageView);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.article_layout, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
viewHolder.itemImage.setImageResource(images[i]);
}
#Override
public int getItemCount() {
return images.length;
}
}
You can set method in adapter that you will call when users clicks button:
// for button A
public void setImages() {
this.showImages = true;
}
// for button B
public void setImages2() {
this.showImages = false;
}
Then modify your on bind method to bind appropriate images:
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
if (this.showImages)
viewHolder.itemImage.setImageResource(images[i]);
else
viewHolder.itemImage.setImageResource(images2[i]);
}
You need to pass images array dynamically to your adapter as below
private int[] images = {
R.drawable.imageA1, .... R.drawable.imageA10
};
private int[] images2 = {
R.drawable.imageB1, .... R.drawable.imageB10
};
on click of button A
RecyclerAdapter myAdapter = new RecyclerAdapter(images)
yourRecyclerview.setAdapter(myAdapter )
on click of button B
RecyclerAdapter myAdapter = new RecyclerAdapter(images2)
yourRecyclerview.setAdapter(myAdapter )
change in your adapter as following
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
private int[] images = new int[];
public RecyclerAdapter(int[] imgs){
images = imgs;
}
class ViewHolder extends RecyclerView.ViewHolder{
public int currentItem;
public ImageView itemImage, itemImage2;
public TextView itemTitle;
public ViewHolder(View itemView) {
super(itemView);
itemImage = (ImageView)itemView.findViewById(R.id.imageView);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.article_layout, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
viewHolder.itemImage.setImageResource(images[i]);
}
#Override
public int getItemCount() {
return images.length;
}
}
Yes possible. make constructor in adpater like that:
public RecyclerAdapter(Context context,int[] images)
{
this.diff_images=images;
this.context=context;
}
the diff_images is also int array in adapter class.
and make images array in your activity class. when you press button one then pass image1 array in adpater and if when press button2 then pass image2 array (if your want to show same view for both cases and if you want to show different view for both images)
if your adapter already set like:
recycleViewAdapter = new RecyclerAdapter(this, imageArray);
recyclerView.setAdapter(recycleViewAdapter);
then you can also make one more method in adapter class and only notify adapter for update image
public void setNewImageList(Context context, int[] imageArray) {
this.context = context;
this.diff_images = imageArray;
notifyDataSetChanged();
}
try this.

RecyclerView, slow scrollToPosition, setSelected

Prompt, faced with scrollToPosition speed. I RecyclerView 900 positions and need to quickly establish a position as scrollToPosition very slow, c ListView such problems were not setSelection working out perfectly. It is possible to accelerate the scrollToPosition?
public class EventAdapter extends RecyclerView.Adapter<EventAdapter.Holder> {
final static public String TAG = "EventAdapter";
Context context;
List<Event> items;
public EventAdapter(List<Event> items) {
this.items = items;
}
#Override
public EventAdapter.Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list_event, parent, false);
context = parent.getContext();
return new EventAdapter.Holder(v);
}
#Override
public void onBindViewHolder(EventAdapter.Holder holder, int position) {
holder.setModel(context, items.get(position), position);
}
#Override
public int getItemCount() {
return items.size();
}
public class Holder extends RecyclerView.ViewHolder {
public View block;
Holder(View view) {
super(view);
block = (View) view.findViewById(R.id.cv);
}
public void setModel(Context context, Event model, int position) {
}
}
}
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
rv.setLayoutManager(linearLayoutManager);
rv.setHasFixedSize(true);
linearLayoutManager.scrollToPosition(index);
There are couple of ways to do it:
1) Try smoothScrollToPosition as : [will certainly work]
Toast.makeText(this, "Scroll...", Toast.LENGTH_SHORT).show();
myList.smoothScrollToPosition(0);
2) Also read about scrollToPositionWithOffset as this may help too

Toast message onClick NOT SHOWING (RecycleView)

Im trying show a toast msg when clicked on a item from my RecycleView, Ive tried many examples,
but its not giving me anything. Can somebody give me a different example that i can follow, at the end i wanna set the onClick to show a new fragment. If I can get an example on that, it will be great.
Im using this code:
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.ViewHolder> {
private List<Movie> movies;
private int card_layout;
private Context mContext;
public MovieAdapter(List<Movie> movies, int card_layout, Context context) {
this.movies = movies;
this.card_layout = card_layout;
this.mContext = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
final View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(card_layout, viewGroup, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
final Movie movie = movies.get(i);
viewHolder.movieImage.setImageDrawable(mContext.getDrawable(movie.getImageResourceId(mContext)));
viewHolder.movieName.setText(movie.mName);
viewHolder.currentMovie = movie;
}
#Override
public int getItemCount(){
return movies.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView movieName;
public ImageView movieImage;
public Movie currentMovie;
public ViewHolder( View itemView) {
super(itemView);
movieName = (TextView) itemView.findViewById(R.id.movieName);
movieImage = (ImageView)itemView.findViewById(R.id.movieImage);
itemView.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View itemView){
Toast.makeText(itemView.getContext(),currentMovie.mName,Toast.LENGTH_SHORT ).show();
}
});
}
}
}
Do I have to implement something in my MainActivity as well?
and please dont get mad with me, Im just a starting with all this. All your help will be appriciated. thanks
Layout is not clickable by default. to make clickable add setClickable to true :
View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(
card_layout, viewGroup, false);
itemView.setClickable(true);
itemView.setFocusableInTouchMode(true);
After wasting an hour of time, I found the most appropriate and simplest solution to this problem:
Try this, it will definitely work.No matters whether cards are used in grids or not.
RecyclerView Adapter:
ProductCardRecyclerViewAdapter.java
public class ProductCardRecyclerViewAdapter extends RecyclerView.Adapter<ProductCardViewHolder> {
public final String TAG=getClass().getSimpleName();
Context context;
private List<ProductEntry> productList;
private Integer[] cardImages;
String[] cardTitle;
String[] cardSubtitle;
public ProductCardRecyclerViewAdapter(Context context, Integer[] imageList, String[] cardTitle, String[] cardSubtitle) {
this.cardImages = imageList;
this.cardTitle = cardTitle;
this.cardSubtitle = cardSubtitle;
this.context = context;
}
#NonNull
#Override
public ProductCardViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.product_card, parent, false);
return new ProductCardViewHolder(layoutView);
}
#Override
public void onBindViewHolder(#NonNull ProductCardViewHolder holder, int position) {
// TODO: Put Recycler ViewHolder Cards binding code here in MDC-102
holder.imgCard.setImageResource(cardImages[position]);
holder.productTitle.setText(cardTitle[position]);
holder.productPrice.setText(cardSubtitle[position]);
holder.productCard.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "onClick: Material Card clicked "+cardTitle[position]+" : "+context.getClass());
//TODO: Perform card clicked working
Context c = v.getContext();
Toast.makeText(c, cardTitle[position], Toast.LENGTH_SHORT).show();
}
});
}
#Override
public int getItemCount() {
return cardImages.length;
}
}
RecyclerViewHolder
ProductCardViewHolder.java
public class ProductCardViewHolder extends RecyclerView.ViewHolder {
CardView productCard;
ImageView imgCard;
public TextView productTitle;
public TextView productPrice;
public ProductCardViewHolder(#NonNull View itemView) {
super(itemView);
imgCard = itemView.findViewById(R.id.product_image);
productTitle = itemView.findViewById(R.id.product_title);
productPrice = itemView.findViewById(R.id.product_price);
productCard=itemView.findViewById(R.id.cardofproducts);
// TODO: Find and store views from itemView
}
}
Hope, it will help a lot.

Categories

Resources