I have a GridView in which I want to always show 7 icons, and sometimes an additional icon depending on a request. In the beginning the additional icon is never shown. This is the structure:
0 1 2
3 4 5
6 [7]
All the icons fit into the screen so I don't need/have scroll. Each icon is composed by an image and a text.
For this, I have a CustomAdapter which extends BaseAdapter. I have overriden the getView method in which I set the text and the image for each icon.
public View getView(int position, View convertView, ViewGroup parent) {
View v = null;
if (convertView == null) {
LayoutInflater li = ((Activity) context).getLayoutInflater();
v = li.inflate(R.layout.icon, null);
} else {
v = convertView;
}
TextView tv = (TextView) v.findViewById(R.id.icon_textView);
tv.setText(position);
ImageView iv = (ImageView) v.findViewById(R.id.icon_ImageView);
iv.setImageResource(imageResourcesArray[position]);
if ((position == ADDITIONAL_ICON)) && !showAdditionalIcon) {
v.setVisibility(View.INVISIBLE);
}
return v;
}
The imageResourcesArray[] is an array of integers with the image resources.
The other functions and variables in the CustomAdapter are:
public static final int ADDITIONAL_ICON = 7;
private boolean showAdditionalIcon = false;
public showAdditionalIcon(){
this.showAdditionalIcon = true;
notifyDataSetChanged();
// notifyDataSetInvalidated();
}
public hideAdditionalIcon(){
this.showAdditionalIcon = false;
notifyDataSetChanged();
// notifyDataSetInvalidated();
}
Later on, I create and set the CustomAdapter to the GridView from a class which extends Activity (say ClassA):
GridView grid = (GridView) findViewById(R.id.main_gridView);
customAdapter = new CustomAdapter(this);
grid.setAdapter(customAdapter);
My problem appears when after some calculations and requests to a server, I have to show the additional icon (number 7). So I call (from ClassA):
customAdapter.showAdditionalIcon();
Now, the additional icon appears, but the first icon disappears... I have tried to use notifyDataSetInvalidated() and notifyDataSetChanged() but both had the same result.
Of course, I could generate a new CustomAdapter with the additional icon allowed, but I would preffer not to do it...
Thanks in advance.
I'm not sure if this counts as an answer for you. Root of the problem seems to be the convertView we are using. I did not dig so deep into Android source, but I think there is no guarantee on how views are reused even it is obvious that all views are already visible and there should be no reuse behind the scenes.
What this means is that the view we linked to position 7 as we visualize this whole scenario is actually reused later at position 0. Since your code does not explicitly reset a view to be visible, the view will be reused with visibility set to INVISIBLE, thus the mystery of the disappearing first item.
Simplest solution should be as #Vinay suggest above, by explicitly setting to View.VISIBLE.
if ((position == ADDITIONAL_ICON))) {
if (!showAdditionalIcon)
v.setVisibility(View.INVISIBLE);
else
v.setVisibility(View.VISIBLE);
}
Hope this helps, but I'm really hoping some Android expert pops by to tell us more about how this whole thing of reusing old views actually works.
Related
Could someone please help before this drives me completely insane!
Imagine you have a list view. It has in it 9 items, but there is only space to display 6 without scrolling. If an item is selected the background colour will change to indicate this.
If you select any item from 2 to 8 inclusive all is well in the world.
If you select item 1 it also selects item 9 and vica versa. Also with this selection if you scroll up and down a random number of times, the selection will change. If you continue to scroll up and down, the selection changes back to 1 and 9. The value of the selected item is always the actual item you selected.
This is my code from my adapter :
public class AvailableJobAdapter extends ArrayAdapter<JobDto> {
private Context context;
private ArrayList<JobDto> items;
private LayoutInflater vi;
public AvailableJobAdapter(Context context, ArrayList<JobDto> items) {
super(context, 0, items);
this.context = context;
this.items = items;
vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
JobDto jh = getItem(position);
if (convertView == null) {
convertView = vi.inflate(R.layout.inflator_job_list, null);
holder = new ViewHolder();
holder.numberText = (TextView) convertView.findViewById(R.id.txtNumber);
holder.descriptionText = (TextView) convertView.findViewById(R.id.txtDescription);
holder.statusText = (TextView) convertView.findViewById(R.id.txtStatus);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.numberText.setText(jh.getJobNumber());
holder.descriptionText.setText(jh.getDescription());
holder.statusText.setText(jh.getStatus());
return convertView;
}
public static class ViewHolder {
public TextView numberText;
public TextView descriptionText;
public TextView statusText;
}
}
and this is the code from my click listener :
jobs.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Button btnOk = (Button) findViewById(R.id.btnOk);
view.setSelected(true);
int selected = position;
int pos = val.get(selected);
int firstItem = jobs.getFirstVisiblePosition();
int viewIndex = selected - firstItem;
if (pos == 0) {
jobs.getChildAt(viewIndex).setBackgroundColor(getResources().getColor(R.color.selected));
val.set(selected, 1);
selectedCount ++;
} else {
jobs.getChildAt(viewIndex).setBackgroundColor(getResources().getColor(R.color.unselected));
val.set(selected, 0);
selectedCount --;
}
if (selectedCount > 0 ){
btnOk.setEnabled(true);
} else {
btnOk.setEnabled(false);
}
}
});
I have spent hours researching this and trying various suggestions.
Any help will be greatly appreciated.
****EDIT****
After playing with some suggestion I tested it with a HUGE list. This is exactly what the behaviour is :-
If your screen has space for 10 items, if you select item 1 it also highlights 11, 21, 31, 41 etc.
Anything in between these values behaves correctly.
From this guide:
When your ListView is connected to an adapter, the adapter will instantiate rows until the ListView has been fully populated with enough items to fill the full height of the list. At that point, no additional row items are created in memory.
Instead, as the user scroll through the list, items that leave the screen are kept in memory for later use and then every new row that enters the screen reuses an older row kept around in memory. In this way, even for a list of 1000 items, only ~7 item view rows are ever instantiated or held in memory.
That's the root of the problem you are facing. You are changing the background color of the views in your click listener. But once a selected item is scrolled out of the screen, its view will be reused for the new item that is swiping in. As the reused view had its background color changed, the new item will consequently have that same color.
You need to take in account that views are recycled, so they might be "dirty" when you get them in getView(). I recommend you to take a look at the guide from where I got the quotes above, it's a nice and important read.
One possible way to fix that is to add a boolean field to your JobDto class and use it to track if an item is selected or not. Then in getView(), you could update the background color accordingly. You'll also probably need to add the item root view(convertView) to your ViewHolder in order to change its background color.
In setOnItemClickListener() just update setSelected() true of false for clicked position and notifydataset. In getView put a condition
if(jh.isSelelcted())
{
// background is selected color
}else{
// background is non selected color
}
note : handle else condition.
I have a ListView that is getting populated by my custom BaseAdapter with views that contain a couple of TextViews and ImageButtons. If the user selects an item from a PopupMenu on one of the list items, it changes the text of an associated TextView on that list item. This is working great for all of the items in my ListView except the first one. The first item never updates the TextView for some reason. I suspect it has something to do with my convertView for that item not getting reset, but I'm not sure how to go about fixing it.
Here is my setup. I have a ListView that is getting populated by a custom BaseAdapter. In my BaseAdapter, I have this for my getView():
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
IMOModGroup modGroup = modGroups.get(position);
convertView = modGroup.getConvertView(parent);
}
return convertView;
}
IMOModGroup is a special data structure that holds all the states and data for each list item. So when the convertView comes in null, the BaseAdapter requests a new convertView from the IMOModGroup for that position. Here's the IMOModGroup method that creates the new convertView:
public View getConvertView(ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.modifier_list_item, parent, false);
//...
TextView selectedModText = (TextView) convertView.findViewById(R.id.modName);
selectedModText.setText(R.string.no_modifier_selected);
selectedModText.setTextColor(context.getResources().getColor(R.color.grayed_out_text_color));
//...
return convertView;
}
The user can open a PopupMenu on each list item and make a selection. When they do this, the TextView in the above code is changed to reflect the selection with new text. That code looks like this:
public boolean onMenuItemClick(MenuItem item) {
String selectedMod = item.getTitle().toString();
TextView selectedModText = (TextView) convertView.findViewById(R.id.modName);
selectedModText.setText(selectedMod);
selectedModText.setTextColor(context.getResources().getColor(R.color.imo_blue_light));
//...
return false;
}
All of this is working great on every item in the ListView except for the very first one, which never updates to reflect the TextView change. I've tested to make sure the TextView text is actually getting changed and can be called and viewed at a later time, and it's being set correctly. So it seems like the data is correct, but the view is not getting updated.
I'm pretty sure the problem is that my convertView is not getting reset, so the ListView is just continuing to use the old view with the old text. I tried calling notifyDataSetChanged() on the BaseAdapter after I set the text, and that didn't work.
This question looks promising: First item in list view is not displaying correctly
But I'm not sure if that's my problem or not, and I'm not sure how I would apply that to my scenario.
Any ideas? I've read through a few similar questions on SO and Google, but none of them really seemed to be relevant to my specific scenario. I've been using ListViews and Adapters for quite a while now and this is the first time I've seen something like this. Thanks for your help.
EDITS IN RESPONSE TO COMMENTS
Here is the complete BaseAdapter code...
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.ArrayList;
public class IMOModListAdapter extends BaseAdapter {
private Context context;
private ArrayList<IMOModGroup> modGroups;
public IMOModListAdapter(Context context, ArrayList<IMOModGroup> modGroups) {
this.context = context;
this.modGroups = modGroups;
}
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
IMOModGroup modGroup = modGroups.get(position);
convertView = modGroup.getConvertView(parent);
}
return convertView;
}
public Object getItem(int position) {
return 0;
}
public long getItemId(int position) {
return 0;
}
public int getCount() {
return modGroups.size();
}
}
FURTHER TESTING
As I digged deeper into my debugging, I realized that when the TextView for element 1 in the list is changed, getView() is not even called at all. But when any of the other elements are changed, getView() is called as normal.
As a sanity check, I tried changing the text of the first list item from the outside, from the parent Activity itself. If I try to change the first item, it does nothing, but if I try to change any of the other list items, it works fine! This is so bizarre! This is what I tried:
//Set the text of the first item. This does NOTHING and the TextView
//in the item remains unchanged
IMOModGroup modGroup = modGroups.get(0);
modGroup.setTestText("This is some test text");
modList.notifyDataSetChanged();
//Set the text of the second item. This works completely fine!!!!!
IMOModGroup modGroup = modGroups.get(1);
modGroup.setTestText("This is some test text");
modList.notifyDataSetChanged();
Well I found a solution to this issue. Just researching random SO questions about BaseAdapters and getView(), I finally came across this post by Romain Guy in which he states that you should never use wrap_content for the layout_height of a ListView, since it causes the ListView to call the adapter's getView() tons of times just to create dummy views which it uses to measure the height of the content. It turns out this was causing my issue, so changing my layout_height to match_parent did the trick.
Unfortunately, now this causes a new problem since I can't use match_parent since it will hide other views in my parent layout. But at least the first list item is now updating correctly.
UPDATE:
I learned how to manually and dynamically set the height of my ListView after its adapter is populated. I used the method below, and hopefully this will help someone out running into similar issues. This made it so I didn't have to use wrap_content but I could still set the ListView height to only be as big as its content.
private void setModListHeight() {
int numberOfItems = modGroups.size(); //the ArrayList providing data for my ListView
int totalItemsHeight = 0;
for(int position = 0; position < numberOfItems; position++) {
View item = modListAdapter.getView(position, null, modList);
item.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
totalItemsHeight += item.getMeasuredHeight();
}
int totalDividersHeight = modList.getDividerHeight() * (numberOfItems - 1);
ViewGroup.LayoutParams params = modList.getLayoutParams();
params.height = totalItemsHeight + totalDividersHeight;
modList.setLayoutParams(params);
modList.requestLayout();
}
I have the following situation.
I have a ListView, each item of the ListView is comprised of different widgets (TextViews, ImageViews, etc...) inflated form a Layout in the getView() method of the custom adapter.
Now, I would like to achieve the following:
when a certain event is triggered I want to change the background of a View which is inside the item.
Please how do I do it?
This is the the Item Layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/cardlayout"
android:layout_width="320dp"
android:layout_height="130dp"
android:background="#android:color/transparent"
android:orientation="vertical"
android:paddingBottom="5dp"
android:paddingRight="5dp"
android:paddingTop="5dp" >
<FrameLayout
android:layout_width="320dp"
android:layout_height="117dp" >
<View
android:id="#+id/card"
android:layout_width="320dp"
android:layout_height="117dp"
android:background="#drawable/card_selector" />
</FrameLayout>
</LinearLayout>
I need to change the background of card
I have tried doing this:
View v=lv.getAdapter().getView(index, null, lv);
View card =(View)v.findViewById(R.id.card);
card.setBackgroundResource(R.drawable.pressed_background_card);
But no success :-((
When your event is triggered you should just call a notifyDataSetChanged on your adapter so that it will call again getView for all your visible elements.
Your getView method should take into account that some elements may have different background colors (and not forget to set it to normal color if the element doesn't need the changed background, else with recycling you would have many elements with changed background when you scroll)
edit :
I would try something like this :
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null)
{
convertView = LayoutInflater.from(getContext()).inflate(R.layout.card, parent, false);
}
//This part should also be optimised with a ViewHolder
//because findViewById is a costly operation, but that's not the point of this example
CardView cardView =(CardView)convertView .findViewById(R.id.card);
//I suppose your card should be determined by your adapter, not a new one each time
Card card = getItem(position);
//here you should check sthg like the position presence in a map or a special state of your card object
if(mapCardWithSpecialBackground.contains(position))
{
card.setBackgroundResource(specialBackground);
}
else
{
card.setBackgroundResource(normalBackground);
}
cardView.setCard(card);
return convertView;
}
And on the special event i would add the position of the item into the map and call notifyDataSetChanged.
Use the onitemclicklistener which has method onclicksomething..that takes four or five parameters. (View parent, View view, int position, int id). Use the view parameter to customize your background.
Update
Here's some of my code, If you don't understand I recommend to read about recycling and ViewHolder pattern.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
{
ViewHolder viewHolder;
// If convertView isn't a recycled view, create a new.
if(convertView == null){
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.row_gallery_frame, parent, false);
viewHolder = new ViewHolder();
// Here you must be able to find your Widget inside convertView and set a listener to it I guess?
viewHolder.nameHolder = (TextView) convertView.findViewById(R.id.nameTv);
// Set a reference to newly inflated view
convertView.setTag(viewHolder);
}
// If it is, then get the ViewHolder by tag
else{
viewHolder = (ViewHolder)convertView.getTag();
}
// Set the data
GalleryFrame galleryFrame = galleryFrameArrayList.get(position);
viewHolder.nameHolder.setText(galleryFrame.getName());
return convertView;
}
}
// Viewholder pattern which holds all widgets used
public static class ViewHolder{
public TextView nameHolder;
}
I assume you have a model object that you use to "draw" the list item , and for example the background color is determined based on a boolean or something.
All you need to do, is change the value on which you base your decision which background color should that TextView have.
Your getView() method should have code like that
if (myModelObj.isBrown()) {
myTextView.setBackgroundResource(R.drawable.brown_bg);
else
myTextView.setBackgroundResource(R.drawable.not_brown_bg);
All you should do when ur event is triggered, is set the value of the brown boolean in your model
and call notifyDataSetChanged() on your adapter
EDIT
If for some reason you don't wanna call nofitfyDataSetChanged(), althought it won't move the scroll position of your list and with the right recyclying it won't cause bad performance
You can find the View object that represent the list item you want to edit-if it's visisble-, and simply change the background in it, without refreshing the list at all.
int wantedPosition = 10; // Whatever position you're looking for
int firstPosition = listView.getFirstVisiblePosition() - listView.getHeaderViewsCount();
int wantedChild = wantedPosition - firstPosition
if (wantedChild < 0 || wantedChild >= listView.getChildCount()) {
// Wanted item isn't displayed
return;
}
View wantedView = listView.getChildAt(wantedChild);
then use wantedView to edit your background
This answer can be found here
try this one:
View v=lv.getAdapter().getView(index, null, lv);
View card =(View)v.findViewById(R.id.card);
card.setBackgroundResource(R.drawable.pressed_background_card);
card.invalidate();
v.invalidate();
those function force your views to redraw itself and they will render again.
look at invalidate()
What I normally do is this:
public static class EventDetailsRenderer {
private TextView title;
private TextView description;
private Event item;
public EventDetailsRenderer(View view) {
extractFromView(view);
}
private final void extractFromView(View view) {
title = (TextView) view.findViewById(R.id.EventTitle);
description = (TextView) view.findViewById(R.id.Description);
}
public final void render() {
render(item);
}
public final void render(Event item) {
this.item= item;
title.setText(item.getTitle());
description.setText(item.getDescription());
}
}
private class EventsAdapter
extends ArrayAdapter<Event> {
public EventsAdapter(Context context) {
super(context, R.layout.list_node__event_details, 0);
}
public void addAllItems(Event... services) {
for (int i = 0; i < services.length; i++) {
add(services[i]);
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Event event = getItem(position);
EventDetailsRenderer eventRenderer;
if (convertView != null && convertView.getTag() != null) {
eventRenderer = (EventDetailsRenderer) convertView.getTag();
} else {
convertView = getActivity().getLayoutInflater().inflate(R.layout.list_node__event_details, null);
eventRenderer = new EventDetailsRenderer(convertView);
convertView.setTag(eventRenderer);
}
eventRenderer.render(event);
return convertView;
}
}
NOTE: that this example might not compile I pasted it from some code I have and deleted some lines to show an example but the logic it the same.
And then when you want to render it, just get the children from the list, iterate over them, check if the renderer contains the card you want to flip and call its render method... then you render a specific item in the list without effecting the rest of the items.
Let me know if this works...
Adam.
User EasyListViewAdapters library https://github.com/birajpatel/EasyListViewAdapters
Features
Easier than implementing your own Adapter (ie handling
BaseAdaper#getView).Very Easier to provide multi-row support.
Library takes care of recycling all views, that ensures performance
& helps your list view scroll smoothly.
Cleaner code. By keeping different RowViewSetter classes for
different row-types makes your code easy to manage & easy to reuse.
No data browsing, Library takes care of browsing data through
data-structure when View is being drawn or event occurs so that
Users does not have to look for their data to take actions.
Just by passing correct row-types library will Auto-map your
data-types to row-types to render views. Row views can be created by
using XML or Java (doesn't restrict to XML-Only Approach).
Load More callbacks can be registered to implement paginatation
support to your list.
Handling children viewclicks, you can also register for
Children(present inside your rows) view click events.
All these Views are registered with single OnClickListner so that
this mechanism is very memory efficient when click event occurs
users you gets clickedChildView, rowData,int eventId as callback
params.
I have an application in which I'd like one row at a time to have a certain color. This seems to work about 95% of the time, but sometimes instead of having just one row with this color, it will allow multiple rows to have the color. Specifically, a row is set to have the "special" color when it is tapped. In rare instances, the last row tapped will retain the color despite a call to setBackgroundColor attempting to make it otherwise.
private OnItemClickListener mDirectoryListener = new OnItemClickListener(){
public void onItemClick(AdapterView parent, View view, int pos, long id){
if (stdir.getStationCount() == pos) {
stdir.moreStations();
return;
}
if (playingView != null)
playingView.setBackgroundColor(Color.DKGRAY);
view.setBackgroundColor(Color.MAGENTA);
playingView = view;
playStation(pos);
}
};
I have confirmed with print statements that the code setting the row to gray is always called.
Can anyone imagine a reason why this code might intermittently fail? If there is a pattern or condition that causes it, I can't tell.
I thought it might have something to do with the activity lifecycle setting the "playingView" variable back to null, but I can't reliably reproduce the problem by switching activities or locking the phone.
private class DirectoryAdapter extends ArrayAdapter {
private ArrayList<Station> items;
public DirectoryAdapter(Context c, int resLayoutId, ArrayList<Station> stations){
super(c, resLayoutId, stations);
this.items = stations;
}
public int getCount(){
return items.size() + 1;
}
public View getView(int position, View convertView, ViewGroup parent){
View v = convertView;
LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (position == this.items.size()) {
v = vi.inflate(R.layout.morerow, null);
return v;
}
Station station = this.items.get(position);
v = vi.inflate(R.layout.songrow, null);
if (station.playing)
v.setBackgroundColor(Color.MAGENTA);
else if (station.visited)
v.setBackgroundColor(Color.DKGRAY);
else
v.setBackgroundColor(Color.BLACK);
TextView title = (TextView)v.findViewById(R.id.title);
title.setText(station.name);
return v;
}
};
ListViews don't create instances of contained views for every item in the list, but only for ones that are actually visible on the screen. For performance reasons, they try and maintain as few views as possible, and recycle them. That's what the convertView parameter is.
When a view scrolls off the screen, it may be recycled or destroyed. You can't hold a reference to an old view and assume that it will refer to the item you expect it to in the future. You should save the ID of the list item you need and look that up instead.
Moreover, there are a couple of other issues with your implementation (from a best practices standpoint). You seem to be ignoring the convertView parameter and creating a new view from scratch each time. That can cause your application to bog down a bit while scrolling if you have a long list. Secondly, instead of adding the "more" element the way you do, you're better of setting it with setFooterView().
There's an excellent talk on the ListView from Google I/O 2010 that covers these and other issues. It's an hour long, but definitely worth watching in its entirety.
I have a ListView with a custom list adapter. In the getView() method, am using the ViewHolder 'pattern' as shown in the API Demos for ListView14.java. When i first render the list it seems to load correctly. However, the issue i'm running into is that when i scroll the list, i'm seeing the data for the list show up in the wrong rows (i.e. a TextView that should be in row 10 is showing up in row 2 for example). However, when I do not use the viewholder, and instead call findViewById() every time, then the list view renders correctly.
However, the issue i'm running into is
that when i scroll the list, i'm
seeing the data for the list show up
in the wrong rows (i.e. a TextView
that should be in row 10 is showing up
in row 2 for example).
Most likely, you are improperly recycling your rows, such that the ViewHolders you are manipulating are not the right ones for the row you are returning.
Here is a free excerpt from one of my books that goes into more about row recycling -- perhaps it will help you identify where things are going wrong.
I faced same problem
Solved using below techninc
Reason : Adapter not Loaded Frequentlyy.
in Your Custom Adapter class add ViewHolder using Access Specifiers
private static class ViewHolder {
protected TextView itemName;
}
In Get View method
#Override
public View getView(int position, View view, ViewGroup viewGroup) {
// create a ViewHolder reference
ViewHolder holder;
//check to see if the reused view is null or not, if is not null then reuse it
if (view == null) {
holder = new ViewHolder();
view = mLayoutInflater.inflate(R.layout.list_item, null);
holder.itemName = (TextView) view.findViewById(R.id.list_item_text_view);
// the setTag is used to store the data within this view
view.setTag(holder);
} else {
// the getTag returns the viewHolder object set as a tag to the view
holder = (ViewHolder)view.getTag();
}
// now Use Holder object toget Idss
holder.itemName.setText(" sample text based on position ");
}
Important : And We should not set Any Tag for view object except Viewholder Object
so i think i discovered the real issue here. when you set layout parameters on the fly for each row, you need to make sure you do it for all conditions. my problem was that if it was the first row, i set a layout param (like padding or margins etc), but then if it was a middle row, i didn't explicitly set those params thinking that it would just use what was inflated by the view inflater. This explains why it worked when i inflated the view each time. Here is a before & after:
BEFORE:
if (position == 0) {
layoutParams.topMargin = uiHelper.getDip(15.0f);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP,
RelativeLayout.TRUE);
holder.actionMenu.setLayoutParams(layoutParams);
holder.contentLayout.setBackgroundResource(R.drawable.top_row);
} else if (position == posts.size() - 1) {
holder.contentLayout
.setBackgroundResource(R.drawable.bottom_row);
holder.contentLayout.setPadding(holder.contentLayout
.getPaddingLeft(),
holder.contentLayout.getPaddingTop(),
holder.contentLayout.getPaddingRight(),
holder.contentLayout.getPaddingBottom() + uiHelper.getDip(10.0f));
} else {
holder.contentLayout
.setBackgroundResource(R.drawable.inner_row);
}
AFTER:`
layoutParams.topMargin = uiHelper.getDip(10.0f);
holder.contentLayout.setPadding(holder.contentLayout
.getPaddingLeft(),
holder.contentLayout.getPaddingTop(),
holder.contentLayout.getPaddingRight(),
uiHelper.getDip(10.0f));
if (position == 0) {
layoutParams.topMargin = uiHelper.getDip(15.0f);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP,
RelativeLayout.TRUE);
holder.contentLayout.setBackgroundResource(R.drawable.top_row);
} else if (position == posts.size() - 1) {
holder.contentLayout
.setBackgroundResource(R.drawable.bottom_row);
holder.contentLayout.setPadding(holder.contentLayout
.getPaddingLeft(),
holder.contentLayout.getPaddingTop(),
holder.contentLayout.getPaddingRight(),
uiHelper.getDip(20.0f));
} else {
holder.contentLayout
.setBackgroundResource(R.drawable.inner_row);
}
holder.actionMenu.setLayoutParams(layoutParams);