How to Construct a RecyclerView with Children of Variable Height? - android

The app I am working on contains lots of listviews. In one case, I have a recyclerView that leverages the GridLayoutManager to create a two column view. I haven't worked with recyclerViews yet as far as adapters go but here is the problem I am having. Each item in the view is being sized to the same height despite having the appropriate wrap_content attributes. I guess my question would be, is there a trick to pull this off where each child element has a different height? Is something going on behind the scenes with a recyclerView that causes all children to have a fixed height of the tallest child element? Does this sound like a recycler problem with the view holders?
My adapter logic is as follows
public ClubViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.onboard_club, parent, false);
return new ClubViewHolder(itemView, mRecyclerClickListener);
}
public void onBindViewHolder(final ClubViewHolder holder, int position) {
Item club = clubList.get(position);
if (null != club) {
holder.clubTitleText.setText(club.getName());
holder.subtitleText.setText(context.getResources().getString(R.string.club_members,club.getNumberMembers()+""));
// Do some imageView logic here
}
}
Do I need to set height in here programmatically?
EDIT: To clarify further, the layout I inflate is the layout in question here. It is a Vertical LinearLayout containing an imageView, and two text views. I need the LinearLayout to wrap the children XML attributes but it currently isn't doing that despite the LL having a height of wrap_content and all children height set to wrap_content as well
SOLUTION: Solution was posted by a user below. I will leave the update here for anyone struggling in the future.
Layout I need: Two columns whose children vary in height
Tie it in with an adapter as you normally would.
Set a StaggeredGridLayoutManager on the recyclerView widget
In the manager constructor pass in a spanCount of 2 and an Orientation of Vertical
Boom, close the sprint ticket and forget about it
Thanks again Stack Community, cheers!

The best way to control placement and size of item for RecyclerView is through its LayoutManager.
If you're looking for GridView with elements that resize themselves according to the content you can use StaggeredGridLayoutManager
Nice example of Staggered Grid Layout can be found here.
On the other hand if you need just few "groups" of different Items you can inflate different ViewHolders depending on some criteria. Here is the sample code for two types of views.
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == FILE_FLAG) {
ViewGroup view = (ViewGroup) LayoutInflater.from(parent.getContext())
.inflate(R.layout.file_item, parent, false);
return new FileViewH(view);
} else if (viewType == DIR_FLAG) {
ViewGroup view = (ViewGroup) LayoutInflater.from(parent.getContext())
.inflate(R.layout.dir_item, parent, false);
return new DirViewH(view);
}
}

Related

What is the prefered way to reuse layouts in an android chat app?

I want to make an android chat app. I made a simple layout for the chat part. I made two layouts, my message and their message. Each of them only have one text view. Imagine that now I want to add a layout for a picture with a caption. In this case, I have to build two layout. One for my message and second for incoming message or their message. But if I do this for every possible message, the number of layout increases and also there are a lot of repetitive things in my layouts.
Like in this layout, you can see that in my and their message there is an image and a caption and these two layouts have similarities. I want to make a layout with image and caption and then use it in my or their message. In this case, I can easily update my layouts and save myself creating lots of layouts for each possible message. What is the best way to do this, or it's impossible or not good?
I know that I can reuse layout in two different ways. first by using include keyword in layout and the second one is to inflate layout. For example like this:
ConstraintLayout item = findViewById(R.id.main);
View child = getLayoutInflater().inflate(R.layout.item, null);
item.addView(child);
What is the best method to reuse layouts and prevent creating layout which only are different slightly and how existing chat applications do the same? I'm using recycler view for chat layout.
Use recycle view instead and use different views for the different views. Check the message type (myMessage,their message,any other) and change your views using an adpater.
In your adapter,
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == 0) {
View v = layoutInflater.inflate(R.layout.chat_row_sender, parent, false);
return new ChatViewHolder2(v);
} else if(viewType ==1){
View v = layoutInflater.inflate(R.layout.chat_row_receiver, parent, false);
return new ChatViewHolder1(v);
}else {
View v = layoutInflater.inflate(R.layout.row_chat_header, parent, false);
return new ChatHeaderViewHolder(v);
}
}
and use 3 viewHolders in your adapter,
public class ChatViewHolder1 extends RecyclerView.ViewHolder {
public ChatViewHolder1(View itemView) {
super(itemView);
...
}
}
public class ChatHeaderViewHolder extends RecyclerView.ViewHolder {
public ChatHeaderViewHolder(View itemView) {
super(itemView);
......
}
}
public class ChatViewHolder2 extends RecyclerView.ViewHolder {
public ChatViewHolder2(View itemView) {
super(itemView);
...
}
}
Use attribute in the xml to reuse layout.
like <Include>

How to use recycler view and relative layout in a screen in android studio

I have a recycler view and a relative layout below recycler view. My relative layout consists of three text views.My recycler view consists of three text views and a button. My problem is recycler view is scrolling separately and textviews in relative layout are fixed. But I want both to be scrolled, which means while scrolling the screen scroll should be done for both recycler view and relative layout but not seperately. While scrolling my relative layout should be attached to the end of recycler view. I have searched a lot for doing that but there is no results for my search. So, ended up here please anybody help me out.
You have 2 options, first one (the better one) is to create a footer ViewHolder and add it to RecyclerView as a last item in adapter.
Or you can simply wrap your views in vertical LinearLayout and then wrap it in NestedScrollView like this:
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop" />
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
In order to get your RelativeLayout to scroll with the content of your RecyclerView, you'd need to add the RelativeLayout with static content to the end of the list your Adapter iterates. You'd then override getItemViewType in the adapter and return one type ID for the data in your RecyclerView and another for the footer RelativeLayout. Then in onCreateViewHolder you'd use the view type to inflate the right kind of view (one that binds your data or another that displays your RelativeLayout).
This process can be pretty labor intensive. You might also consider using a library like Epoxy to help create a footer view in your RecyclerView.
You can add view with text views in different layout and add to your recycler as last element. Then check posotion in getItemViewType and if it last return footer type inside RecyclerAdapter.
private static final int FOOTER = 1;
private static final int CHILD = 2;
// inflates the row layout from xml when needed
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == CHILD){
View view = mInflater.inflate(R.layout.list_item, parent, false);
return new ViewHolder(view);
}
else if(viewType == FOOTER ){
View view = mInflater.inflate(R.layout.relative, parent, false);
return new ViewHolder(view);
}
return null;
}
#Override
public int getItemViewType(int position) {
if (position == getItemCount() - 1) {
return FOOTER;
} else {
return CHILD;
}
}

Add new layout to an existing item in RecyclerView

I would like to add a new container layout to an existing XML item of a RecyclerView
Here is an example of what I would like to do (based on R.layout.item_simple XML)
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new ViewHolder(inflater.inflate(R.layout.item_simple, parent, false));
}
R.layout.item_simple content at RecyclerView.Adapter creation:
______ LINEAR LAYOUT
______ IMAGE VIEW
______ TEXT VIEW
What I want to do by adding a new layout to the user view:
______ RELATIVE LAYOUT
______ LINEAR LAYOUT
______ IMAGE VIEW
______ TEXT VIEW
Is it possible to change this item view, preferably into the RecyclerView.Adapter ?
There are two ways to achieve this. First is to add always the additional layout to your item_simple and hide/show. The second approach is to use getItemViewType to return different layout types. This way onCreateViewHolder gets called once for each type, and you can inflate different layouts
You just simply inflate your layout here like item_simpleand use show/hide view by using condition. like
mainlayout.setVisibility(View.GONE);
and
mainlayout.setVisibility(View.VISIBLE);

Discussion on RecyclerViews ability

To show a list of items I usually use a RecyclerView but I have a requirement that makes it easier for me to use a vertical LinearLayout. I don't want to loose the good resource management of the RecyclerView so I'm looking for a different solution and hope someone can help me.
I think of a layout with some views on top, then a list of items followed by some more views on the bottom. Or a layout that contains more than one RecyclerView. So you have some Views on the top, a list of items followed by some more views and another list of items etc.
My layout could look like this:
View1
View2
RecyclerView
View3
RecyclerView
View4
Usually a RecyclerView works like a scrollable frame in which you can scroll some content. It means that if you start scrolling, your whole screen gets stuck in a certain position and you are just scrolling in the RecyclerView until its end. Then you can continue scrolling the whole screen.
What I want is a RecyclerView that is fully inflated so you are always scrolling the whole screen instead of just the RecyclerView but not loosing the resource management of the RecyclerView.
Does anybody know of a solution of this?
add below code to your custom adapter of recycler view
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == ITEM_TYPE_NORMAL) {
View normalView = LayoutInflater.from(getContext()).inflate(R.layout.my_normal_row, null);
return new MyNormalViewHolder(normalView); // view holder for normal items
} else if (viewType == ITEM_TYPE_HEADER) {
View headerRow = LayoutInflater.from(getContext()).inflate(R.layout.my_header_row, null);
return new MyHeaderViewHolder(headerRow); // view holder for header items
}
}
What i suggest is to use a single recyclerview with custom adapter and inflate each row of recyclerview with diff layout ie.... based on your condition
In this situation i use only one RecycleView implementing my own getItemViewType
The best answer will depend on what behaviour you want for the screen as a whole.
"What I want is a RecyclerView that is fully inflated so you are always scrolling the whole screen instead of just the RecyclerView but not loosing the resource management of the RecyclerView."
This requirement makes me think that the best option indeed is a single RecyclerView using different ViewHolder types like #Rissmon Suresh and #betorcs mentioned.
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
class HeaderViewHolder extends RecyclerView.ViewHolder {
...
}
class NormalViewHolder1 extends RecyclerView.ViewHolder {
...
}
#Override
public int getItemViewType(int position) {
// use position to decide what kind of View you want
// can be fixed or you can access the data to check dinamically
if(position == 0)
return 0;
else
return 1;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case 0: return new HeaderViewHolder(...);
case 1: return new NormalViewHolder1(...);
//...
}
}
}
Hope this helps =)

How to design Gridview like guardian app

I am trying to make layout like guardian app. I know what is gridview and how to design it and inflate it with data etc.
what i want to design?
This layout have items with images and not with and there is also lazy loading going on in it.
What are the problem i am facing?
1-Confused which viewi should i go with. GridView,ListView or
RecyclerView.
2-if i go with GridView then how to have different item layouts for
some items.
What i have tried?
I have tried using linear layout as seperate xml and then i add that xml to root layout on run time. it works somewhat but problem rise when i need to add clicklistener to show relevent post since there would be more than 100+ post data.
It would be a lot of help if somebody guide me in right direction. Thanks!
EDIT. After going through the answer here. I used this approach. I used to xml. Then i change the layout with getViewType in adapter but that doesn't give such results. I am still looking for more convincing solution.
Here is the code that i have tired.
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder>{
MainDTO mainDTO;
public RecyclerAdapter(MainDTO mainDTO){
this.mainDTO=mainDTO;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
ViewHolder viewHolder;
switch (viewType){
case 0:
view= LayoutInflater.from(parent.getContext()).inflate(R.layout.header,parent,false);
viewHolder=new ViewHolder(view,viewType);
return viewHolder;
default:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.box,parent,false);
viewHolder=new ViewHolder(view,viewType);
return viewHolder;
}
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
ImageLoader imageLoader = ImageLoader.getInstance();
if(position == 0){
imageLoader.displayImage(mainDTO.getPosts().get(position).getThumbnail_images().getFull().getUrl(),holder.thumbnail);
holder.title.setText(mainDTO.getPosts().get(position).getTitle());
}
else if (position > 0 ){
if(mainDTO.getPosts().get(position).getThumbnail_images()!=null)
imageLoader.displayImage(mainDTO.getPosts().get(position).getThumbnail_images().getFull().getUrl(),holder.thumbnail);
holder.title.setText(mainDTO.getPosts().get(position).getTitle());
}
}
#Override
public int getItemCount() {
return mainDTO.getPosts().size();
}
#Override
public int getItemViewType(int position) {
int viewType = 1; //Default is 1
if (position == 0) viewType = 0; //if zero, it will be a header view
return viewType;
}
public static class ViewHolder extends RecyclerView.ViewHolder{
public TextView title;
public ImageView thumbnail;
public ViewHolder(View itemView,int viewType) {
super(itemView);
if(viewType == 0){
title = (TextView)itemView.findViewById(R.id.tv_title);
thumbnail = (ImageView)itemView.findViewById(R.id.iv_thumbnail);
}else if(viewType == 1){
title = (TextView)itemView.findViewById(R.id.tv_title_2);
thumbnail = (ImageView)itemView.findViewById(R.id.iv_thumbnail_2);
}
}
}
}
You will need to use StaggeredGridLayoutManager with RecyclerView to achieve what is being done in the guardian app. See this link StaggeredGridLayoutManager Tutorial
Edit 1
I have written a small sample application which can demonstrate what guardian application is achieving. Here is the Github link. I will explain it along the way with each step:
I used a StaggerdGridLayoutManager since in guardian app you are referring to have occupied different cell heights. This layout enables us to have items with different height.
For every different view type we have to create different view holders. For instance I have created 3 different view holders for every different item type in the sample application.
Override getItemViewType to let the recyclerview adapter know which view to inflate.
For instance of sample, I stored my data objects in an List of type Object to store heterogeneous objects and checked every item if its an instance of a particular class. I created 3 different types:
#Override
public int getItemViewType(int position) {
// we check here which item type to return based on object type
if (items.get(position) instanceof ImageModel)
return ITEM_TYPE_IMAGE;
if (items.get(position) instanceof TextViewModel)
return ITEM_TYPE_TEXT;
if (items.get(position) instanceof ButtonModel)
return ITEM_TYPE_BUTTON;
return -1;
}
Get itemViewType for the current view holder in OnCreateViewHolder in order to determine which layout to inflate.
For the instance of sample:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType){
case ITEM_TYPE_IMAGE:
View image = ((LayoutInflater)BaseApplication.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.item_image, parent, false);
return new ImageViewHolder(image);
case ITEM_TYPE_BUTTON:
View button = ((LayoutInflater)BaseApplication.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.item_button, parent, false);
return new ButtonViewHolder(button);
case ITEM_TYPE_TEXT:
View text = ((LayoutInflater)BaseApplication.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.item_text, parent, false);
return new TextViewHolder(text);
}
return null;
}
Make specific type of items cover full row span.
Since some posts types are occupying full span in guardian application, we can use below code in OnBindViewHolder method to make any item expand to full span of layout.
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder.getItemViewType() == ITEM_TYPE_IMAGE){
StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
layoutParams.setFullSpan(true);
}
}
This makes the item cover all the span of layout like the biggest post in guardian application.
By following above steps, you can create a similar layout like this (image from sample github application):
Here there are 3 different item types: above two items are Buttons, middle one is ImageView and bottom are TextView.
You can use recycler view with GridLayoutManager. And In your adapter make different layout type as per your requirements.
GridLayoutManager manager = new GridLayoutManager(getActivity(), 6);
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
// return your span size as per your layout type.
return 6;
}
}
});
Go to this for more info.
Edit:
Follow my github demo
There is three different kinds of views, one large grid, two small grids and 3-4 list view items. It is hard to use only one kind of view to complete such a task.
I suggest you to create some custom views to handle the grids (large and small), and a list view to handle the list item. After that, you can reuse the custom views for the grids and the list view's custom adapter in other sessions.
If you really want to use one Grid view to handle different views, then create a generic view that has all the functions and disable/enable the functions when you needed. However, this is much more complicated.
You can use RecyclerView because it gives you method to define different item types. but still to create such view you have to do so much code on the basis of its layout.
You have to override getItemViewType method and try to find which view type will be next to display. example code
#Override
public int getItemViewType(int position) {
if (isPositionHeader(position))
return TYPE_HEADER;
return TYPE_ITEM;
}
Hope this will help
You require: Asymmetric Gridview
https://github.com/felipecsl/AsymmetricGridView
Above link will help.
You can define a common onClickListener
I will explain You How to do instead of writing the whole code we will use Recylerview for it
Recylervew which have four Items (Item1,Item2,Item3,Item4)
Item1 : it will contains the View1
Item2 : It will contains the VIew2
Item3 : It will contains the VIew3
Item4 : It will contains the VIew4
View1 : It will contains one Layout for text and other layout for comment and day section
View2 : It will contain the Image View
View3 : It will contains the tablelayout with one row and two columns
View4 : It will contains the linearlayout
TO achieve above layout design, you need to use recyclerView with StaggaredGridLayoutManager.
You have to use RecyclerView with StaggeredGridLayoutManager.

Categories

Resources