Android listview adapter ...no redraw - android

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.

Related

How to update view from an onClickListener inside getView of a CustomAdapter in Android

What do I want to achieve?
In the below SS, when user touches 'vote' button, these vertical progress bars (custom) will be set according to the voting percentages retrieved from server for that particular row.
What is the obstacle?
I have onClickListener inside getView of the CustomAdapter, and when I manipulate the ProgressBar instance (which is in ViewHolder Class), supposingly I want to see the updated ProgressBar on ONLY the one row of the listview that has triggered that action, but, I see every once 3 rows that I scroll down.
Example: I clicked first row, so first row has updated its progress bar, but 4th, 7th, 10th... rows are also updated EVEN IF I don't touch 'vote button'.
My Guessing
I think this problem is related to recycling the view, the weird number is 3 in this case but when I make rows smaller it goes '4', so that is the only clue I have.
SS & Codes
ScreenShot: bit.ly/sofscreenshot
Code:
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = mInflater.inflate(layout, null);
holder = new ViewHolder();
//some more initialization
holder.pb1 = (ProgressBar) convertView.findViewById(R.id.leftProgress);
holder.pb2 = (ProgressBar) convertView.findViewById(R.id.rightProgress);
holder.leftVoteButton = (Button) convertView.findViewById(R.id.leftButton);
holder.rightVoteButton = (Button) convertView.findViewById(R.id.rightButton);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.leftVoteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.pb1.setVisibility(View.VISIBLE);
holder.pb2.setVisibility(View.VISIBLE);
/Some codes...
holder.pb1.setProgress(50);
holder.pb2.setProgress(50);
}
});
}
private class ViewHolder {
//some more objects
ProgressBar pb1;
ProgressBar pb2;
Button leftVoteButton;
Button rightVoteButton;
}
All the answers and comments are appreciated, have a great day and thank you.
You're doing it wrong.
The problem is that you need to have a Model somewhere, and change its status. Then the view is updated regarding the model status.
For example, let's say that this is a "StackOverflow" app, and you have a list of answers. The user upvote the second answer. This means that the second element of the List is upvoted.
Now what?
When the adapter is going through your list of object it will "fire" the getView method for that position. Then you have to update that position according to your model. So, if the position is 1, the adapter is trying to show the second Answer, and you have to set the button to "upvoted". Otherwise you have to set it as "normal".
private List<Answer> answers;
public View getView(int position, View convertView, ViewGroup parent) {
// here get your view (or initialize it)
// get the matching answer
Answer answer = answers.get(position);
if(answer.isUpvoted()) {
holder.pb1.setVisibility(View.VISIBLE);
holder.pb2.setVisibility(View.VISIBLE);
} else {
holder.pb1.setVisibility(View.INVISIBLE);
holder.pb2.setVisibility(View.INVISIBLE);
}
holder.leftVoteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.pb1.setVisibility(View.VISIBLE);
holder.pb2.setVisibility(View.VISIBLE);
// not sure on how to get this answer here
// you probably have to go "upper" and manage the click from the ListView
answer.setUpvoted(true);
}
});
}

Listview with button onclick's position changes while scrolling

I have a listview and it's scrolable. I have few listview items and it's pulled from database. My problem is, each listview items have a button and i've setOnclickListener to the button inside getView. Now let's say there's 5 items and i tap on button for item number 1, the position is 0 and when i scroll to the end of the list, i can see the button for item number 5 is clicked. When i scroll till middle of the list, sometimes the button randomly clicked and i can see from my logcat, the particullar button's position is 0. How come?
Here is my getView' code
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.newsfeed_layout, parent, false);
}
final Button btnLike = (Button) v.findViewById(R.id.buttonLike);
btnLike.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String buttonText = btnLike.getText().toString();
if(buttonText.equals("LIKE")){
Toast.makeText(postedItems.this, "Liked This item" + position, Toast.LENGTH_SHORT).show();
btnLike.setPadding(4,0,12,0);
btnLike.setText("UNLIKE");
btnLike.setTextColor(Color.parseColor("#ffffff"));
btnLike.setBackgroundResource(R.drawable.likedredbutton);
}else if(buttonText.equals("UNLIKE")){
Toast.makeText(postedItems.this, "Unlike This Item" + position, Toast.LENGTH_SHORT).show();
btnLike.setPadding(4,0,20,0);
btnLike.setText("LIKE");
btnLike.setTextColor(Color.parseColor("#737373"));
btnLike.setBackgroundResource(R.drawable.cornerstyledbutton);
}
}
});
return v;
}
First, to understand the problem: this occurs because ListView reuses views as you scroll, to improve performance. That's the explanation for the convertView parameter.
Because of this, you need to make sure that whatever state you want to store for each item is stored in the adapter itself or wherever you store its backing data -- and that when you implement getView(), the UI is fully updated to reflect this data (since it will have whatever properties you set on it the last time it was used).
In this case, you need to store whether each item is "liked" or "unliked". And then, always set the properties of btnLike to reflect this before returning.
As an example, your code would have to be more or less like this:
public View getView(final int position, View convertView, ViewGroup parent)
{
View v = convertView;
if (v == null) {
LayoutInflater vi = ...;
}
final Button btnLike = (Button) v.findViewById(R.id.buttonLike);
if (isLiked(position))
setButtonAsUnlike(btnLike);
else
setButtonAsLike(btnLike);
btnLike.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
if (!isLiked(position))
{
// Not liked before, so like now.
setLiked(position, true); // store this value.
setButtonAsUnlike(btnLike); // the button is now "unlike"
}
else
{
// Liked before, unliked now.
setLiked(position, false); // store this value.
setButtonAsLike(btnLike); // the button is now "like"
}
}
});
return v;
}
where isLiked(position) queries the data, setLiked(position, boolean) updates it, and setButtonAsLike(Button) and setButtonAsUnlike(Button) update the visual properties of the Button as you are doing now.

Add differents items to custom list adapter

I have a List view with a custom adapter (imageviews), in the same activity I have a header and footer, the listView is between those.
What I want is to add a button as the last item of the list view so when you arrive to the last item it appear, i cant add the button outside because it wont scroll
Sorry about my english
Regards
use ListView.addFooterView() to add view as a footer which is visible only in the end of the list:
http://developer.android.com/reference/android/widget/ListView.html#addFooterView(android.view.View)
If you already have a custom adapter class implemented, the solution is rather simple. Based on an xml layout implemented for your list-view rows which contains both a Button and an ImageView, you can hide/display them in the adapter's getView() method based on the index. This is a code sample, which I currently don't have the chance to test and might not be the most efficient solution, but it should give you an idea:
class CustomAdapter extends SimpleAdapter {
[...]
#Override
public int getCount() {
// number of images to be displayed + 1 for the button
return images.length + 1;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View row = inflater.inflate(R.layout.row, parent, false);
final ImageView imageView = (ImageView) row.findViewById(R.id.image);
final Button button = (Button) row.findViewById(R.id.button);
if (position == getCount() - 1) {
// The last element
imageView.setVisibility(View.GONE);
// set an OnClickListener on the button or whatever...
} else {
button.setVisibility(View.GONE);
// do your regular ImageView handling...
}
return row;
}
}

Android: ListView changing wrong row layout visibility

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.

Android ListView: getTag() returns null

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.

Categories

Resources