I have listview with hundred of items. Every item had a couple of LinearLayouts but ONE of them is Visibility.GONE! Every item has textviews and an image. On Image Click i want to set the LinearLayout with visibility.Gone to View.VISIBLE. It works fine until you scroll down the listview, then every 4th item has the same layout set to VISIBLE but i only need the Clicked one! Here is the getView method:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ImageView imgForClick;
View vi = convertView;
if (convertView == null)
vi = inflater.inflate(R.layout.custom_row, null);
final LinearLayout hiddenLayout = (LinearLayout)vi.findViewById(R.id.hiddenLayout);
imgForClick = (ImageView)vi.findViewById(R.id.imageView3);
imgForClick.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
hiddenLayout.setVisibility(View.VISIBLE);
}
});
return vi;
}
That's because you are recycling the views, so the layout gets changed on a click and then that layout is used in your other rows to save memory.
You need to remember the state of each of the rows to know whether or not the layout should be visible or not
Have something like this:
public View getView(final int position, View convertView, ViewGroup parent) {
...
if (shouldBeVisible.get(position)) {
hiddenLayout.setVisibility(View.VISIBLE);
} else {
hiddenLayout.setVisibility(View.GONE);
}
That way the layout will always be set one way or another.
shouldBeVisible is a List of something that lets you know which rows should have that layout visible or not.
EDIT--
An alternative is to remove view recycling, however this will dramatically hurt performance and should NOT be done, but I'm just explaining to list all your options.
You would remove the line
if (convertView == null)
Making Android always inflate a new view, instead of using the recycled one when possible.
Related
What I'm trying to do is to have all the rows in the listview translate to the right in order to expose a checkbox for multi-selection. And actually it works ok beside this:
the animation on the visible rows is ok, but there are like 2-3 rows, created in advance (before starting to recycle them) so the scroll is smooth, in which the translation starts when those become visible (onscreen).
I know this is normal behaviour but could there be any way to have those rows translated before they become onscreen? I was thinking of something like setting the translation time to those to 1 but can't know where the returned view will be used by the list.
public View getView(int position, View convertView, ViewGroup viewGroup) {
View view = convertView;
if (view == null) {
view = LayoutInflater.from(context).inflate(R.layout.item_conversation, null);
}
final AQuery aq = new AQuery(view);
// multiSelect
if(multiSelect) {
aq.id(R.id.row);
if((int) aq.getView().getX() == 0) {
animate(aq.getView())
.translationX(marginLeftWhenMultiSelect)
.setDuration(300);
}
} else {...
I have the following code inside my adapter:
public View getView(int position, View convertView, ViewGroup parent)
{
if (convertView==null)
{
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.box_afisare, null);
}
final TextView titlu = (TextView) convertView.findViewById(R.id.titlu);
titlu.setText(Html.fromHtml(textt.get(position)));
titlu.setTextSize(TypedValue.COMPLEX_UNIT_SP,font);
final Integer pos = position;
titlu.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
if (main_contextt.selectie.contains(id_post.get(pos)))
{
Toast.makeText(mContext," REMOVE ",Toast.LENGTH_LONG).show();
titlu.setBackgroundColor(Color.parseColor("#0077CC"));
}
else
{
main_contextt.selectie.add(id_post.get(pos));
titlu.setBackgroundColor(Color.parseColor("#404040"));
}
}
});
return convertView;
}
I manage to colorate the selected line or lines. But when i scroll the listview and those selected lines are no longer in view range of the phone....the background color disapear.
It disapear only if that line/lines is out of view. I think the adapter is redrawing?
What to do to remain the color set on the line/lines even after i scroll the listview?
thanks
ListViews recycle their child views. So if you have 20 items in your ListView but only enough room on the screen to show 4 at a time, the ListView will only have 4 children which it will recycle to show all 20 items. When one view scrolls out of view, it is recycled for the next view coming into view. See this answer for a great explanation.
What you need to do is tie the color to some underlying data. Then you would use code like.
titlu.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
// set data.get(pos).color
}
});
titlu.titlu.setBackgroundColor(data.get(pos).color);
...something like that.
I create ListView and generate its content with BaseAdapter. The ListView contains Button and hidden ImageView for each row. I put OnClickListener on Button to show ImageView.
Scenario:
ListView has 9 rows, and 3 rows visible on screen.
And then I click Button on first row.
Problem:
ImageView on first row shown successfully, but also on fourth and sixth row.
It happens also when I click Button on second row. ImageView on fifth and seventh row is shown.
Question:
Why is that happens? and how to solve it?
This is the code:
UI Thread
.....
adapter = new ContentAdapter(context, content);
listView.setListAdapter(adapter);
.....
ContentAdapter
.....
public View getView(int position, View convertView, ViewGroup parent) {
.....
final ImageView image = (ImageView)convertView.findViewById(R.id.image);
((Button)convertView.findViewById(R.id.button)).setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
image.setVisibility(View.VISIBLE);
}
});
.....
}
.....
do you check for the view if null or something
if(view == null)
view = ((Activity)YourActivity.get()).getLayoutInflater().inflate(R.layout.list_item, null);
The ListView handles memory, i.e. it recycles views, it does not create a new view (vonverView in you case) each time, but instead it uses a previously generated view (which is convertView).
So when you scroll you are seeing (for example) row1's view on your newly created row. this is why you see that the image is already shown.
To avoid that you must set your image's visibility to View.INVISIBLE.
public View getView(int position, View convertView, ViewGroup parent) {
.....
final ImageView image = (ImageView)convertView.findViewById(R.id.image);
image.setVisibility(View.INVISIBLE);
((Button)convertView.findViewById(R.id.button)).setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
image.setVisibility(View.VISIBLE);
}
});
.....
}
With that, you will not see a visible image when you scroll, ALTHOUGH, if you make row1's image visible, and you scroll down, when you scroll up again you will find row1's image invisible too, in this case you have to save the state of images somewhere (ArrayList for example, or a Map<Integer position, Boolean isvisible>).
With that being said, you also have to check ViewHolder Pattern for adapters, for better memory consumption.
Hope this helps
I have a row in a listview that I need to change its layout dynamically. In my array adapter I have the following (simplified) code
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = inflater.inflate(R.layout.default);
}
...
if(condition) {
View view = inflater.inflate(R.layout.new);
...
return view; // doesn't work
}
return convertView;
}
My actual listview is actually pretty complex and I am also using the getViewType for the two different layouts. But the above code should be enough to make my point that the new layout won't display correctly when I want to switch layout dynamically.
My question is, how can I refresh the convertView, or trigger a reload of the adapter/listview to the point that the convertViews will be cleared? I've tried calling notifyDataSetChanged, or calling the listview invalidateViews() but they all didn't worked. My last resort seems to restart the whole activity but I don't think this is an elegant solution.
Hallo all,
I have a ListView which contains a Button in each line. The following code is part of the getView() Method
public View getView(final int position, View convertView, ViewGroup parent) {
View row = convertView;
TextView tv;
Button saveA_button;
EditText edittext;
FITB_ViewWrapper wrapper;
if (row == null) {
LayoutInflater li = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (ChooseMode_Act.modeInfo.equalsIgnoreCase("Training")) {
row = li.inflate(R.layout.exercise_for_training_fitb,parent, false);
}else {
row = li.inflate(R.layout.exercise_for_exam_fitb,parent, false);
}
wrapper=new FITB_ViewWrapper(row);
row.setTag(wrapper);
if (ChooseMode_Act.modeInfo.equalsIgnoreCase("Exam")) {
saveA_button=wrapper.getSaveAnswer_Button();
OnClickListener l=new OnClickListener() {
#Override
public void onClick(View v) {
Integer mp=(Integer)v.getTag();
Log.i("mp","my Position is: "+mp);
}
};
saveA_button.setOnClickListener(l);
}
}else {
wrapper=(FITB_ViewWrapper) row.getTag();
}
For my App i need to known to which item the Button belongs to, so i try to detect it. The code
Log.i("mp","my Position is: "+mp);
puts out a message: mp myPosition is: null
I can't understand, why do i get a "null" but not an Integer? How can i find out the Position of an Item in a ListView?
Thanks a lot.
Log.i("mp","my Position is: "+position);
you have the position already !
public View getView(final int position, View convertView, ViewGroup parent) {
The Views in a ListView are reused as you scroll up and down. Thus, setting values in getView often has unintented consequences, like the image that you meant to set for item number 5 appearing in item number 10, 15 and 20 also.
To avoid this, if you want to set properties in getView you need to make sure you set or unset them for each view.
I'm not sure what exactly you are trying to accomplish with your code, but it might help to move the setTag outside of the if statement, to make sure you are setting it each time that a view is used.