RecyclerView doesn't expand underneath the row - android

I'm trying to obtain an effect similar to this on my RecyclerView
I've tried to Google I/O approach that is described on similar questions here. My adapter code looks as follows.
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
ViewHolder itemViewHolder = (ViewHolder) holder;
final boolean isExpanded = position == mExpandedPosition;
itemViewHolder.mNotesTV.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(v -> {
mExpandedPosition = isExpanded ? -1 : position;
notifyItemChanged(position);
});
}
private class ViewHolder extends RecyclerView.ViewHolder {
TextView mNotesTV;
ViewHolder(View itemView) {
super(itemView);
mNotesTV = itemView.findViewById(R.id.notesTV);
}
}
The xml file for the TextView I'm trying to expand looks like this
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="#+id/notesTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Details" />
</RelativeLayout>
The xml file with the RecyclerView looks like this
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false">
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:clickable="true"
android:src="#drawable/ic_add_white_24dp" />
<android.support.v7.widget.RecyclerView
android:id="#+id/historyLV"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp" />
</android.support.design.widget.CoordinatorLayout>
And what I get, instead of the effect shown above (RecyclerView item expands and underneath are the desired items), I just get my TextView inside the actual row instead of below.

If you don't already have a layout for your viewholder, make one like the one below. Change the IDs and colors according to your preference:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/alwaysVisible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white"
android:gravity="center"
android:padding="#dimen/activity_horizontal_margin"
android:text="02 Mar, 2018"
android:textColor="#android:color/holo_blue_bright" />
<TextView
android:id="#+id/conditionallyVisible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/holo_blue_dark"
android:gravity="center"
android:padding="#dimen/activity_horizontal_margin"
android:text="Details"
android:textColor="#android:color/white"
android:visibility="gone" />
</LinearLayout>
Next, inflate it in your adapter like this (assuming the above layout is called item_layout:
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(mContext).inflate(R.layout.item_layout, parent, false);
return new ViewHolder(itemView);
}
In your viewholder class, get both the TextViews in the above layout like this:
private class ViewHolder extends RecyclerView.ViewHolder {
TextView mDateTV;
TextView mDetailsTV;
ViewHolder(View itemView) {
super(itemView);
mDateTV = itemView.findViewById(R.id.alwaysVisible);
mDetailsTV = itemView.findViewById(R.id.conditionallyVisible);
}
}
In onBindViewHolder, do the following:
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
ViewHolder itemViewHolder = (ViewHolder) holder;
final boolean isExpanded = position == mExpandedPosition;
itemViewHolder.mDetailsTV.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(v -> {
mExpandedPosition = isExpanded ? -1 : position;
notifyItemChanged(position);
});
}
That's all. I hope you can get this to work.

Maybe your requirement is make expandable recyclerview.
You can check this library https://github.com/thoughtbot/expandable-recycler-view

Related

Android horizontal recyclerview automatically scrolls to the centre

I want that my horizontal recyclerview always shows the first item after setting the adapter instead of scrolling to the centre item. How can I achieve that?
This is my recommendation.xml file:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/img"
android:layout_marginLeft="10dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/zone"
android:textColor="#color/colorPrimary"
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="#+id/xyz"
app:layout_constraintEnd_toEndOf="#+id/xyz"
android:layout_marginTop="3dp"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/xyz"
android:gravity="center"
android:textSize="16sp"
android:layout_marginLeft="15dp"
app:layout_constraintTop_toBottomOf="#id/zone"
android:layout_marginTop="5dp"
app:layout_constraintStart_toEndOf="#id/img"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/day"
android:gravity="center"
android:textSize="16sp"
app:layout_constraintTop_toBottomOf="#id/xyz"
app:layout_constraintStart_toStartOf="#id/xyz"
app:layout_constraintEnd_toEndOf="#id/xyz"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="6dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
This is my adapter class:
class recom_adapter extends RecyclerView.Adapter<recom_adapter.ViewHolder> {
private List<saved_zone> zoneList;
private Context context;
public static PrefConfig prefConfig;
public recom_adapter(List<saved_zone> zoneList, Context context) {
this.zoneList = zoneList;
this.context = context;
}
#NonNull
#Override
public recom_adapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.recommendation,parent,false);
return new recom_adapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull recom_adapter.ViewHolder holder, int position) {
saved_zone sz = zoneList.get(position);
Glide.with(context).load(sz.getImageUrl).into(holder.img);
holder.xyz.setText(sz.getXYZ());
holder.day.setText(sz.getDate());
}
#Override
public int getItemCount() {
return zoneList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
private ImageView img;
TextView zone, xyz, day;
public ViewHolder(View itemView) {
super(itemView);
img = itemView.findViewById(R.id.img);
zone = itemView.findViewById(R.id.zone);
xyz = itemView.findViewById(R.id.xyz);
day = itemView.findViewById(R.id.day);
}
}
}
This is how I am setting the setting the recyclerView in my adapter in my mainActivity:
recomm_recycler.setLayoutManager(new LinearLayoutManager(getActivity(),LinearLayoutManager.HORIZONTAL, true));
recomData = response.body();
recom_adapter = new recom_adapter(recomData, getActivity());
recomm_recycler.setAdapter(recom_adapter);
recomm_recycler.setLayoutManager(new LinearLayoutManager(getActivity(),LinearLayoutManager.HORIZONTAL, true));
There is problem in your above line.
Change the true to false
New Code
recomm_recycler.setLayoutManager(new LinearLayoutManager(getActivity(),LinearLayoutManager.HORIZONTAL, false));
I had a similar issue with vertical recycler view. In my case adding
android:focusableInTouchMode="true"
to RecyclerView's parent helped me.
Try this in your main XML parent layout
XML:
android:descendantFocusability="blocksDescendants"
Java:
listItem.setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);

Two RecyclerView inside the same Scrollview, one does not show content

I'm facing a problem with two RecyclerView inside the same ScrollView (I also tried the same with a NestestScrollView). Inside the ScrollView, I have also some other View objects that form a kind of "header section" of the fragment. Then, I would like to show a horizontal list of RecyclerView, and finally, above the horizontal list, a vertical list of other RecyclerView. However, only the horizontal one is correctly visualized. Even though the Adapter of the vertical one is correctly initialized with some objects, when I run the application, the vertical list is empty. I think it is a problem related to my layout.
This is my .xml file:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
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"
android:id="#+id/profile_coordinator"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".HomeFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp">
<!-- Here I have some other views (ImageView, TextView, etc.) -->
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/active_promos"
android:textStyle="bold"
android:textColor="#color/colorPrimary"
android:textSize="18sp"
android:layout_marginBottom="10sp"/>
<!-- Horizontal List of RecyclerView -->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/promo_recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_marginBottom="20sp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/news"
android:textStyle="bold"
android:textColor="#color/colorPrimary"
android:textSize="18sp"
android:layout_marginBottom="10sp"/>
<!-- Vertical List of RecyclerView -->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/news_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</LinearLayout>
</ScrollView>
Since the objects to show are the same (but the semantics is different), I'm using the same Adapter class that loads two different xml layout according to the type of object to show.
This is the Adapter.java:
public class NewsAdapter extends RecyclerView.Adapter {
private List<NewsPromotion> newsPromotions;
private boolean promos;
public NewsAdapter(List<NewsPromotion> newsPromotions, boolean promos) {
this.newsPromotions = newsPromotions;
this.promos = promos;
}
public void setData(List<NewsPromotion> newsPromotions){
this.newsPromotions = newsPromotions;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder;
if(promos) {
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.promo_recyclerview_item,
parent, false);
viewHolder = new PromoViewHolder(mView);
}else {
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_row,
parent, false);
viewHolder = new NewsViewHolder(mView);
}
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Log.d("Adapter", newsPromotions.get(position).title);
if(promos){
PromoViewHolder viewHolder = (PromoViewHolder) holder;
Picasso.get().load(RestClient.BASE_IMAGE_URL + newsPromotions.get(position).image).into(viewHolder.mImage);
}else {
NewsViewHolder viewHolder = (NewsViewHolder) holder;
Picasso.get().load(RestClient.BASE_IMAGE_URL + newsPromotions.get(position).image).into(viewHolder.mImage);
viewHolder.mTitle.setText(newsPromotions.get(position).title);
viewHolder.mDescription.setText(newsPromotions.get(position).content);
}
}
#Override
public int getItemCount() {
return newsPromotions.size();
}
public class NewsViewHolder extends RecyclerView.ViewHolder {
ImageView mImage;
TextView mTitle;
TextView mDescription;
private NewsViewHolder(View itemView) {
super(itemView);
mImage = itemView.findViewById(R.id.ivImage);
mTitle = itemView.findViewById(R.id.tvTitle);
mDescription = itemView.findViewById(R.id.tvDescription);
}
}
public class PromoViewHolder extends RecyclerView.ViewHolder {
ImageView mImage;
private PromoViewHolder(View itemView) {
super(itemView);
mImage = itemView.findViewById(R.id.ivImage);
}
}
}
And the following is how I initialize the two adapters in the Fragment:
RecyclerView promoRecycleView = activity.findViewById(R.id.promo_recyclerview);
promoAdapter = new NewsAdapter(new ArrayList<NewsPromotion>(), true);
promoRecycleView.setAdapter(promoAdapter);
RecyclerView newsRecycleView = activity.findViewById(R.id.news_recyclerview);
newsAdapter = new NewsAdapter(new ArrayList<NewsPromotion>(), false);
newsRecycleView.setAdapter(newsAdapter);
Finally, this is how I send the object to the adapters:
promoAdapter.setData(promos);
promoAdapter.notifyDataSetChanged();
newsAdapter.setData(news);
newsAdapter.notifyDataSetChanged();
Please post your adapter and item_layout. Because your given layout is working perfectly for me.
No issue with your layout i have run in myAppliaction, some issue in Adapter, please post/paste your activity and adapter code.
If you would check documentation on the RecyclerView, you will see that you need to specify size of the list. There is no wrap content for RecyclerView, because it's dynamic widget. So you would need to align your implementation for something like next.
<?xml version="1.0" encoding="utf-8"?>
<NestedScrollView
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"
android:id="#+id/profile_coordinator"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".HomeFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp">
<!-- Here I have some other views (ImageView, TextView, etc.) -->
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/active_promos"
android:textStyle="bold"
android:textColor="#color/colorPrimary"
android:textSize="18sp"
android:layout_marginBottom="10sp"/>
<!-- Horizontal List of RecyclerView -->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/promo_recyclerview"
android:layout_width="match_parent"
android:layout_height="200dp"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_marginBottom="20sp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/news"
android:textStyle="bold"
android:textColor="#color/colorPrimary"
android:textSize="18sp"
android:layout_marginBottom="10sp"/>
<!-- Vertical List of RecyclerView -->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/news_recyclerview"
android:layout_width="match_parent"
android:layout_height="500dp" />
</LinearLayout>
</LinearLayout>
</ScrollView>

Horizontal and Vertical RecyclerView under the same scroll

I have to do something like Instagram does. I have a horizontal RecyclerView for stories, and, below, a vertical RecyclerView for feed. I want to accomplish the same scroll behavior(the stories should go with feed at scroll, not stay on top as fixed). The only solution I found is NestedScrollView but it makes extremely bad performance for RecyclerViews, my screen freezes almost forever. I tried a lot of tricks found here like nestedScrollEnabled, autoLayoutMeasure etc. but nothing worked. Thanks.
Sorry if this explanation is too abstract. Let me know if you need me to be more explicit.
public class VerticalAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private static final int TYPE_HEADER = 0;
private static final int TYPE_POST = 1;
List<Post> posts;
List<FeedItems> feedItems; //this array is going to populate the horizontal recycler view. Notice that is passed it on the adapter constructor
public VerticalAdapter(List<Post> posts,List<FeedItems> feedItems) {
this.posts = posts;
this.feedItems = feedItems;
}
public void notifyFeedChanged(List<FeedItems> newFeedItems){
this.feedItems.clear();
this.feedItems = newFeedItems; //set the new feed items in the array
notifyItemChanged(0); //tell the main recycler view "Hey, update your first position". This will cause the onBindViewHolder to be called again an thus, the new items will be set into the horizontal recycler view
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER)
return new HeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.your_header_layout, false));
else if (viewType == TYPE_POST)
return new PostViewHolder (LayoutInflater.from(parent.getContext()).inflate(R.layout.your_post_layout, false));
throw new RuntimeException("Don't know this type");
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof HeaderViewHolder){
//set adapter for the horizontal recycler view
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(((HeaderViewHolder) holder).recyclerView.getContext(),LinearLayoutManager.HORIZONTAL, false)
((HeaderViewHolder) holder).recyclerView.setLayoutManager(linearLayoutManager);
if (((HeaderViewHolder) holder).recyclerView.getAdapter() == null){ //only create the adapter the first time. the following times update the values
AnotherAdaterYouNeedToCreateForTheHorizontalRecyclerView adapter = new AnotherAdaterYouNeedToCreateForTheHorizontalRecyclerView(feedItems);
((HeaderViewHolder) holder).recyclerView.setAdapter(adapter);
}else {
((HeaderViewHolder) holder).recyclerView.getAdapter().notifyDataSetChanged();
}
}else if (holder instanceof PostViewHolder){
//just do the normal post binding
}
}
#Override
public int getItemCount() {
return posts.size() + 1; // +1 because of the header
}
#Override
public int getItemViewType(int position) {
return position == 0 ? TYPE_HEADER : TYPE_POST;
}
private class HeaderViewHolder extends RecyclerView.ViewHolder{
RecyclerView recyclerView;
public HeaderViewHolder(View itemView) {
super(itemView);
recyclerView = itemView.findViewById(R.id.the_recycler_view_id_on_the_heaedr_layout_file);
}
}
private class PostViewHolder extends RecyclerView.ViewHolder{
ImageView imageView;
public PostViewHolder(View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.post_image_view_or_whatever);
}
}}
So, your vertical recyclerview has the Post items (or whatever your post class is) drawn vertically, that's the easy thing to achieve. Now, for the horizontal view, you should implement a recyclerview header (check my Adapter example). The header layout will have another horizontal recyclerview.
In your XML Layout try keeping both the "header" recycler view and "Post" recycler view in a nested scroll view. It works for me.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:background="#FFFFFF">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:text="Welcome"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:textColor="#000000"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:id="#+id/date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:textAppearance="#style/TextAppearance.AppCompat.Body1"
android:textColor="#color/black_80" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

Recycler view changing layout for all list items not for single list item

I am implementing chat application in android using xmpp.My Chat is working fine but when i am setting layout for sender and receiver in chat then the inflater is changing layout for all existing list item.
Below is the screenshot when user sends "Hi"
But when receiver sends "hello" then this chat message is comming to left position but it is also taking other messages to left postion and same when sending messages..
Below is the screenshot when user receives "Hello"
And followed by this when sending "How are you"
I dont where i am wrong
My recycler view sender layout
chat_layout_self.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_gravity="right|bottom"
android:gravity="right|bottom"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/chatsymbolself">
<TextView
android:id="#+id/cltv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="hi"
android:textSize="20sp"
android:textColor="#fff"
android:padding="10dp"
android:layout_gravity="left|bottom"/>
</LinearLayout>
</LinearLayout>
chat_layout_other.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_gravity="left|bottom"
android:gravity="left|bottom"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/chatsymbolother">
<TextView
android:id="#+id/cltv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="hi"
android:textSize="20sp"
android:textColor="#fff"
android:padding="10dp"
android:layout_gravity="left"/>
</LinearLayout>
</LinearLayout>
MessageAdapter.java
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MyViewHolder> {
private String user="+918439198269#desktop-5ehan36/Android";
private LayoutInflater inflater;
List<Message> data= Collections.emptyList();
public MessageAdapter(Context context,List<Message> data){
inflater=LayoutInflater.from(context);
this.data=data;
}
#Override
public int getItemViewType(int position) {
Message message = data.get(position);
Log.e("MAdapter", "getItemViewType: from "+message.from);
if (message.from.equals(user)){
return 0;
}
else {
return 1;
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
MyViewHolder holder;
if(viewType==0){
Log.e("MAdapter", "onCreateViewHolder: SEFL");
view=LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_layout_self,parent,false);
}
else{
Log.e("MAdapter", "onCreateViewHolder: OTHER");
view=LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_layout_other,parent,false);
}
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final int itemType = getItemViewType(position);
Message current=data.get(position);
holder.chat.setText(current.msg);
}
#Override
public int getItemCount() {
return data.size();
}
class MyViewHolder extends RecyclerView.ViewHolder{
TextView chat;
public MyViewHolder(View itemView) {
super(itemView);
chat=(TextView)itemView.findViewById(R.id.cltv);
}
}
}
I think, it because of your layout which have circular layout size. you should change the TextView width to wrap_content rather than match_parent.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/cltv"
android:background="#drawable/chatsymbolother"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hi"
android:textSize="20sp"
android:textColor="#fff"
android:padding="10dp"
android:layout_gravity="left"/>
</LinearLayout>
make your viewholder static
You don't need to create deep xml layout, if simple textview is enough.

Adding components dynamically to RecyclerView

I'm displaying daily events. The number of events/day is variable. Each item in the RecView is a Day which should contain as many views as the number of events.
Here is one item's layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/llDay">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tvDay"
android:text="TODAY"
android:layout_gravity="center_horizontal"
android:layout_marginTop="15dp"
android:textSize="25sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tvNothing"
android:text="No events"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:textSize="18sp"
android:textStyle="italic"
android:visibility="gone"/>
</LinearLayout>
I'd like to add views to the llDay dynamically.
Here is my Adapter:
public class DiaryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
ArrayList<ArrayList<Displayable>> diaryEvents = new ArrayList<>();
Context context;
public DiaryAdapter(ArrayList<ArrayList<Displayable>> diaryEvents, Context context) {
this.diaryEvents = diaryEvents;
this.context = context;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewGroup viewGroup = (ViewGroup) LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_day_diary, parent, false);
DiaryDayViewHolder viewHolder = new DiaryDayViewHolder(viewGroup);
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
DiaryDayViewHolder viewHolder = (DiaryDayViewHolder) holder;
ArrayList<Displayable> events = diaryEvents.get(position);
if (events.size() > 0){
addLayouts(events, viewHolder);
}
else{
viewHolder.tvNothing.setVisibility(View.VISIBLE);
}
}
#Override
public int getItemCount() {
return diaryEvents.size();
}
private void addLayouts(ArrayList<Displayable> events, DiaryDayViewHolder viewHolder) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
for (Displayable event : events) {
switch (event.getEventType()){
case Types.TYPE_TEACHING:
TeachingDiaryLayout teachingDiaryLayout = new TeachingDiaryLayout(context);
teachingDiaryLayout.setLayoutParams(params);
teachingDiaryLayout.setViews((Teaching) event);
viewHolder.llDay.addView(teachingDiaryLayout);
viewHolder.tvDay.setText("DAY"); // TODO: day + date
break;
case Types.TYPE_TASK: // TODO
break;
case Types.TYPE_EXAM: // TODO
break;
}
}
}
}
When the RecyclerView is displayed first, the events are displayed correctly, but after scrolling some events are displayed multiple times. I know that the problem is caussed by calling the addLayouts(...) in the onBindViewHolder(...) but I don't know how to create the correct amount of views for each day.
UPDATE: ViewHolder added:
public static class DiaryDayViewHolder extends RecyclerView.ViewHolder {
LinearLayout llDay;
TextView tvDay;
TextView tvNothing;
public DiaryDayViewHolder(View itemView) {
super(itemView);
llDay = (LinearLayout) itemView.findViewById(R.id.llDay);
tvDay = (TextView) itemView.findViewById(R.id.tvDay);
tvNothing = (TextView) itemView.findViewById(R.id.tvNothing);
}
}
Ok finally figured it out.
Firstly change your layout to:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/llDay">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tvDay"
android:text="TODAY"
android:layout_gravity="center_horizontal"
android:layout_marginTop="15dp"
android:textSize="25sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tvNothing"
android:text="No events"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:textSize="18sp"
android:textStyle="italic"
android:visibility="gone"/>
<LinearLayout
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/llDay1"/>
</LinearLayout>
now in your view holder add another LinearLayout parameter:
LinearLayout llDay,llday1;
and inside the addLayouts method change:
viewHolder.llDay.addView(teachingDiaryLayout);
to
viewHolder.llDay1.addView(teachingDiaryLayout);
Also before the for loop add
viewHolder.llDay1.removeAllViews();
for (Displayable event : events)

Categories

Resources