I have recycler-view with items in it and can be scrolled vertically. Currently what i achieved is items are added one after another like a list. By i need to place them side by side.
Like the image below
And my output is
My recycler-view setup code:
topicAdapter = new TopicAdapter(topicList, getActivity());
topicListView.setLayoutManager(new LinearLayoutManager(getActivity()));
topicListView.setAdapter(topicAdapter);
and adapter code is:
public class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.CategoryViewHolder> {
private List<Topic> topicList;
Context context;
public TopicAdapter(List<Topic> topicList, Context context) {
this.topicList = topicList;
this.context = context;
}
#Override
public CategoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//inflate the layout file
View groceryProductView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_suggested_topics, parent, false);
CategoryViewHolder holder = new CategoryViewHolder(groceryProductView);
return holder;
}
#Override
public void onBindViewHolder(CategoryViewHolder holder, final int position) {
holder.txtview.setText(topicList.get(position).getName());
}
#Override
public int getItemCount() {
return topicList.size();
}
public class CategoryViewHolder extends RecyclerView.ViewHolder {
TextView txtview;
public CategoryViewHolder(View view) {
super(view);
txtview = view.findViewById(R.id.titleView);
}
}
}
I can suggest you with a simple solution but, you cant achieve complete requirement with this code. You'll get side by side.
Replace
topicListView.setLayoutManager(new LinearLayoutManager(getActivity()));
with
topicListView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
// 3 denotes the number of rows per column
You can do this using Google's latest design component ChipGroup
Else you can use Flexbox-Layout by showing your tags in Grid Layout.
If you wish to go for Flexbox-Layout, check answer of avik
Add This
topicListView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL,false));
Use StaggeredGridLayoutManager for recyclerview
I think a good way to do this is by using Material Choice Chips, you can learn how to use them here. You can then use a ChipGroup to group them and allow them to flow across multiple lines.
However, to solve your question at hand, you can use a GridLayoutManager and then supply a SpanSizeLookup.
Related
I have fragment "Incoming" on one slide of a viewpager. This fragment contains a RecyclerView populated with custom-relative-layouts. The LinearLayoutManager orientation is Vertical.
I have a second fragment "Find" on the next slide of the said viewpager. "Find" will consist of two recyclerviews. It will have a Horizontal recyclerview filled with cardviews (fast loading of profile pictures). Underneath that, I am loading more slowly another recyclerview with a custom-relative-layout, the same as in the "incoming" fragment.
Does that make sense? I'll elaborate some more:
The question is for these three recyclerviews, should I declare a new RecyclerAdapter for each one? The reason I ask is that they'll all have unknown variable item_counts.
Here is the RecyclerAdapter I have for "Incoming":
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder>{
private Context mContext;
public RecyclerAdapter(Context context, List<Incoming> items) {
mContext = context;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View v) {
super(v);
// Define all of the components in the view
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater mInf = LayoutInflater.from(mContext);
View customView = mInf.inflate(R.layout.item_layout_incoming, parent, false);
final ViewHolder vh = new ViewHolder(customView);
return vh;
}
#Override
public int getItemCount(){ return 6; } // THIS IS TEMPORARY; WILL BE VARIABLE
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
// Replace contents
}
For my criteria, should I create another Adapter for my horizontal-cardview-recyclerview? It seems repetitive, but otherwise, how would I handle either inflating cardview or item_layout_incoming?
Seems like there should be a DRY way to do this, without hits to performance. Thanks
You are using fragments so you will create 2 objects of that class. So it's the same thing. you just reduce compiler load by reducing the task of loading the new class into memory and then create its object.
It's better to use two different Adapter because of 2 reasons.
Your code will become ugly I mean so much congested and so many if
else condition.
In future, if you need to change something in layouts then again it
will affect all objects if same adapter class.
So my advice do developer friendly code and create two classes.
I'm trying to implement the below image. My first thought was to have everything above the grid layout be the first row of the grid and use SpanSizeLookup to set the span size to the number of columns in the RecyclerView, but this feels like something that will give me a lot of problems.
I've been reading about putting a RecyclerView inside a NestedScrollView, people say it works, but I can't seem to get it to work properly. The scrolling doesn't seem to work right, I can't get the grid to even show up without setting a minHeight, but then it just looks bad.
Is there another option I'm not considering or is one of these the direction I should be going?
What kind of problems are you anticipating from SpanSizeLookup? You can implement it with a few lines as follows (I'd recommend using values from integers.xml for flexibility).
GridLayoutManager glm = new GridLayoutManager(getContext(), 3);
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override public int getSpanSize(int position) {
return (position == 0) ? 3 : 1;
}
});
If your header layout needs views and fields that your regular layout doesn't have, you'll want to create separate views and tell your adapter about them. Something like this.
#Override
public int getItemViewType(int position) {
if (position == 0)
return TYPE_HEADER;
else
return TYPE_REGULAR;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER) {
MyHeaderView view = (MyHeaderView) LayoutInflater
.from(parent.getContext())
.inflate(R.layout.my_header_view, parent, false);
return new MyHeaderViewHolder(view);
} else {
MyRegularView view = (MyRegularView) LayoutInflater
.from(parent.getContext())
.inflate(R.layout.my_regular_view, parent, false);
return new MyRegularViewHolder(view);
}
}
An example header view could be like this (you'd call bindTo() from MyHeaderViewHolder).
public final class MyHeaderView extends LinearLayout {
#Bind(R.id.image) ImageView imageView;
#Bind(R.id.title) TextView titleView;
public MyHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
ButterKnife.bind(this);
}
public void bindTo(String imageUrl, String title) {
Glide.with(getContext())
.load(imageUrl).into(imageView);
titleView.setText(title);
}
}
use StaggeredGridLayoutManager
use a different layout for your first item (the whole complex view)
get its layout params and setFullSpan
This makes the item as wide as the RecyclerView itself (similar to match_parent).
Set various click listeners in the specific ViewHolder that's responsible for this item. Using this approach would set the whole complex view to behave (scroll) as part of the RecyclerView while still making it available for (input) events.
You could have a look at Bookends
Or another(the way i usually do it) way would be to use a GridLayouManager with a SpanSizeLookUp. And use multiple ViewTypes i.e. one for Header,Footer and Items.
Go for 1 if you have only a single header and what a ListView-ish interface in your code.
Go for 2 if you are not sure about how many Custom ViewTypes you would be adding.It assures you have maximum scalability in the future.
If you are considering massive scalability,I suggest you read this article by Hannes Dorfman .
i'm new to android,
I've been working on a project, and in my news feeds page, I'm trying to include a modular feed RecyclerView, which shows a question with different answer forms, varrying according to the Question type. The way I was doing it so far was by using the include and turning the forms visible when needed. recently since i added more modules, the app started to slowdown segnificantly, so i'm trying to implement ViewStubs.
This is my RecyclerView adapter:
public class ReQuestionAdapter extends RecyclerView.Adapter<FeedItem> {
private ArrayList<Question> myQuestions;
public ReQuestionAdapter(Context context, ArrayList<Question> questions) {
myQuestions = questions ;
}
#Override
public FeedItem onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_re_question, parent, false);
return new FeedItem(view);
}
#Override
public void onBindViewHolder(FeedItem holder, int position) {
Question q = myQuestions.get(position);
holder.bindQuestion(q);
}
#Override
public int getItemViewType(int position) {
return 0;
}
#Override
public int getItemCount() {
return myQuestions.size();
}
}
And this is the ViewHolder class for the adapter:
public class FeedItem extends RecyclerView.ViewHolder{
private Question mQuestion;
public TextView tvName;
public TextView tvTime;
public TextView tvContent;
public ProfilePictureView profilePictureView;
public ViewStub moduleView;
private int moduleType;
public FeedItem(View itemView) {
super(itemView);
}
public void bindQuestion(Question question) {
mQuestion = question;
tvTime = (TextView) itemView.findViewById(R.id.li_q_date);
tvContent = (TextView) itemView.findViewById(R.id.li_q_content);
moduleView = (ViewStub) itemView.findViewById(R.id.module_viewstub);
tvTime.setText(TimeHandler.When(mQuestion.publish_time));
tvContent.setText(mQuestion.content);
moduleType = question.type;
switch (moduleType) {
case Question.TYPE_YN:
moduleView.setLayoutResource(R.layout.module_yes_no);
moduleView.inflate();
break;
case Question.TYPE_CUSTOM:
moduleView.setLayoutResource(R.layout.module_custom);
moduleView.inflate();
break;
default:
break;
}
}
}
Now, the problem is that the ViewStub which contains a certain layout, cannot be reinflated with a new one, the reason for that is that it gets removed from the view hirarchy as soon as it leaves the screen, the symptoms:
When scrolling down the RecyclerView, the first list items that fill the screen are working perfect, but others to load when the previous leave the screen cause the FeedItem binding to bring a NullPointerException. (It canno't find it in the list item layout).
I'm looking for a solution as efficiant as ViewStubs, or a way to make them work properly, since I got many modules and inflating them all in each item as invisible would make my app slow.
In your bindQuestion() method you are referencing two different layouts to inflate, so in essence you have two different view types.
Adapter views have an efficient way way to handle this built right in.
Start by overriding getItemViewType(). When the item at position gets the module_yes_no layout, return 0. When it gets the module_custom layout, return 1.
Then in onCreateViewHolder(), when the viewType parameter is 0, inflate a list_item_re_question view complete with the module_yes_no layout. When viewType == 1, inflate the module_custom version of the view.
Now when you get a view in onBindViewHolder(), it will already have the correct subview, so you proceed to fill out that view as needed. By using getItemViewType(), the RecyclerView is working with you to recycle the exact view you need.
You can even have two FeedItem subclasses, one for module_yes_no and one for module_custom, so in onBindViewHolder(), you just check the class of the ViewHolder and branch accordingly.
That should help improve the performance of your app.
I am having a gridView and the data in the gridView is coming from server.
Now I am having some views in it that will show for some rows and will not show for some rows depends on the sever conditions.
Ex : I am having a LinearLayout which is having an imageView and 2 TextViews, this layout will be visible only for some rows based on server data.
First time it is coming fine but as I scroll down/up, the view of the rows get change.
For Ex: Like in the first row If I am not having this LinearLayout and in 2nd or 3rd row this layout is visible, the when I scroll down and then again scroll up, the first row also get that Layout exact same as the last scrolled position.
I am using Holder pattern, can you please help me here, I am stuck here.
Thank you so much in advanced.
The views are stateless so if you show the linearlayout on someviews you need to remember to hide it for the others.
onBindViewHolder will not give you a fresh view from xml but the view you mutated. Basically just remember to set the LinearLayout back to gone.
A better way would be to use multiple xml files and implement getItemViewType showing and hiding views can cause the scroll to gitter, although if heights remain the same you might get away with it.
public class ExampleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<ContactsContract.Data> data;
private static final int TYPE_A = 0;
private static final int TYPE_B = 1;
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder;
if(viewType == TYPE_A) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.xml_a, parent, false);
viewHolder = new RecyclerView.ViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.xml_b, parent, false);
viewHolder = new RecyclerView.ViewHolder(view);
}
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
holder.setData(data.get(position);
}
#Override
public int getItemViewType(int position) {
if(data.get(position).youCondition()) {
return TYPE_A;
} else {
return TYPE_B;
}
}
#Override
public int getItemCount() {
return data.size();
}
}
This is a basic example of how it could be done. Will need to implement your own ViewHolders i'd suggest making a different one for each view type from a base class that has the set data method.
I am making an app with 100 list items and was wondering if I could get away with not implementing the RecyclerView as I find it hard to implement it.
Quite frankly it depends up to you, Listview makes it easy for you by taking a lot of responsibility which makes it slow at time when you have to show a lot of data, on other hand RecyclerView does what it is best at make's things fast by taking care or bare minimum structure.
RecyclerView is quite easy to implement and you will get chance to learn some of the touch framework of Android because of it.
And performing Animation on RecyclerView is quite easy as well and way better than Listview
Making a custom listview is piece of cake with RecyclerView
here's an example for RecyclerView
private RecyclerView recyclerView;
recyclerView = (RecyclerView)findViewById(R.id.recycler);
MyViewComplainAdapter adapter = new MyViewComplainAdapter(getApplicationContext(), createComplainList());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
in XML
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="vertical" />
your Adapter (Whatever you want to call this thing... lol )
private class MyViewComplainAdapter extends RecyclerView.Adapter<MyViewComplainAdapter.MyViewComplainViewHolder>{
private Context _Context;
private ArrayList<ViewMyComplainData> _List;
private LayoutInflater _Inflater;
public MyViewComplainAdapter(Context context, ArrayList<ViewMyComplainData> list){
_Context = context;
_List = list;
_Inflater = LayoutInflater.from(_Context);
}
#Override
public MyViewComplainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View layout = _Inflater.inflate(R.layout.single_item_view_my_complain,parent,false);
MyViewComplainViewHolder holder = new MyViewComplainViewHolder(layout);
return holder;
}
#Override
public void onBindViewHolder(MyViewComplainViewHolder holder, int position) {
ViewMyComplainData data = _List.get(position);
holder.complaint_number.setText(data.getComplaint_Number()+"");
holder.complaint_type.setText(data.getComplaint_Type()+"");
holder.status.setText(data.getStatus()+"");
}
#Override
public int getItemCount() {
return _List.size();
}
public class MyViewComplainViewHolder extends RecyclerView.ViewHolder{
TextView complaint_number;
TextView complaint_type;
TextView status;
public MyViewComplainViewHolder(View itemView) {
super(itemView);
complaint_number = (TextView)itemView.findViewById(R.id.textView_complaint_number_single_item_view_my_complain);
complaint_type = (TextView)itemView.findViewById(R.id.textView_complaint_type_single_item_view_my_complain);
status= (TextView)itemView.findViewById(R.id.textView_status_single_item_view_my_complain);
}
}
}
yes you will have to make a ArrayList<ViewMyComplainData> using this method createComplainList(), you should figure this out
Technically speaking, RecyclerView doesn't need anything like "notifyDataSetChanged()" when an item is added or deleted from your List, which is a huge improvement performance-wise.