0

I want to update my buttons in a ListView. The code I currently have works fine but has a glitch, when reaching to the last item it suddenly updates the first item too with the wrong data from the last post as it is the last post.I don't know what happens, in my log I get nothing that indicates the first item is being updated. I got this code and idea from this similar question.

Here is the code I use:

/**
     * Updates the like/dislike button states 
     */
    public void updateButtons() {
        ListView lv = ((ListView) findViewById(R.id.post_list));
        Log.d("Child Count: ", Integer.toString(lv.getCount()));
        int firstVisible;
        int child;
        for(int i=0; i < lv.getCount(); i++) {
            firstVisible = lv.getFirstVisiblePosition() - lv.getHeaderViewsCount();
            child = i - firstVisible;
            if(child < 0 || child >= lv.getChildCount()) {
                continue;
            }
            if(updated.contains(i)) continue;
            Log.d("Debug", "Updating child:" + Integer.toString(child));
            LinearLayout row = (LinearLayout) lv.getChildAt(child);
            if(likeable.get(i) == 0) {
                LinearLayout btn = (LinearLayout) row.findViewById(R.id.btn_like);
                btn.setClickable(false);
                ImageView imgView = (ImageView) row.findViewById(R.id.img_like);
                imgView.setImageDrawable(getResources().getDrawable(R.drawable.ic_like_selected));
                TextView txtView = (TextView) row.findViewById(R.id.text_like);
                txtView.setTextColor(Color.parseColor("#60007c"));
            }
            if(dislikeable.get(i) == 0) {
                LinearLayout btn = (LinearLayout) row.findViewById(R.id.btn_dislike);
                btn.setClickable(false);
                ImageView imgView = (ImageView) row.findViewById(R.id.img_dislike);
                imgView.setImageDrawable(getResources().getDrawable(R.drawable.ic_dislike_selected));
                TextView txtView = (TextView) row.findViewById(R.id.text_dislike);
                txtView.setTextColor(Color.parseColor("#60007c"));
            }
            Log.d("Debug", "Adding child" + Integer.toString(i) + "TO the updated list");
            updated.add(i);
        }
    }

I run this code everytime the user scrolls the listview :

ListView lv = ((ListView) findViewById(R.id.post_list));
lv.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // TODO Auto-generated method stub
        updateButtons();
    }
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        // TODO Auto-generated method stub
        updateButtons();
    }
});
Community
  • 1
  • 1
Miro Markaravanes
  • 3,285
  • 25
  • 32

3 Answers3

1

It's not very clear what is the problem. The first item(the one from the adapter or the first visible one) updates again when you get to the last item by scrolling?

My personal opinion is that you've approached this the wrong way. From the question it seems you want to update some buttons in each of the rows of the ListView. You should do this by updating the data in the adapter and calling notifyDataSetChanged() and not by using the OnScrollListener. This would make sense to do on a user action(liked/disliked item), I doubt you created a really awkward system of likes/dislikes on user scroll.

Also, there is no point in using lv.getCount() to iterate over all of the adapter items if you just want to touch the visible rows(with the code you currently do you might as well do the updating in the adapter in one go and call notifyDatasetChanged()). Use the getFirstVisiblePosition() and getLastVisiblePosition(). You could optimize the code by not calling the updateButtons() method unless you have a new row appearing on the screen due to scrolling, which you can observe/calculate from the parameters of the onScroll() method(and I see an updated list that holds already updated items?!).

user
  • 86,916
  • 18
  • 197
  • 190
  • Thx for the answer, I mean the first item on the adapter. I don't have my custom adapter I use the `SimpleAdapter`. I just obtain a json string from a server, parse it and determine if someone has liked/disliked specific posts already or not and based on it I display the buttons to the user. this needs to be done only once when first the posts are obtained after that the buttons are not going to be changed. it was working when all the posts were on the screen ( expected to work ) but it went wrong when posts got more. – Miro Markaravanes Jun 01 '13 at 13:49
  • THE first item on the list(adapter) updates when i reach the last item (the first item is not on the screen ever.. I doubt how it's being updated).. the problem is it updates wrong. ( It updates with the info from the last post ) – Miro Markaravanes Jun 01 '13 at 13:51
  • and the updated list holds the rows that are already update and thus we don't need to update them again. (for performance). I made the list to prevent the first item from being updated again but it updates. – Miro Markaravanes Jun 01 '13 at 13:54
  • @MiroMarkarian And wouldn't it be much easier to simply update the list after you get/parse it from the json file especially if it doesn't change? – user Jun 01 '13 at 14:02
  • the thing is I'm not updating the list. I update the buttons with the data from the list. – Miro Markaravanes Jun 01 '13 at 14:56
  • @MiroMarkarian Doesn't matter, you change the appearance of the `ListView` and you should do this through the adapter. – user Jun 01 '13 at 15:22
  • I solved it myself. The problem was an android bug I suppose.. see my answer. Thx – Miro Markaravanes Jun 01 '13 at 15:30
1

Have a try with this code . it worked fine for me.

Your Adapter class will be like below.

package com.rajesh.getselecteditem;
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
import android.widget.TextView;

public class ListAdapter extends BaseAdapter {
    Context ctx;
    LayoutInflater lInflater;
    ArrayList<Position> arraylist;

    ListAdapter(Context context, ArrayList<Position> products) {
        ctx = context;
        arraylist = products;
        lInflater = (LayoutInflater) ctx
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public int getCount() {
        return arraylist.size();
    }

    @Override
    public Object getItem(int position) {
        return arraylist.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = convertView;
        if (view == null) {
            view = lInflater.inflate(R.layout.item, parent, false);
        }

        Position pos = getselectedposition(position);
        ((TextView) view.findViewById(R.id.Textview1)).setText(""+pos.position);

        final ImageView imageView1 = (ImageView) view.findViewById(R.id.imageView1);

        CheckBox chkbox = (CheckBox) view.findViewById(R.id.cbBox);

        chkbox.setTag(position);
        chkbox.setChecked(pos.ischeckedflag);
        imageView1.setTag(position);



        chkbox.setOnCheckedChangeListener( new OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton buttonView,
                    boolean isChecked) {
                getselectedposition((Integer) buttonView.getTag()).ischeckedflag = isChecked;
                if(isChecked)
                {
                    imageView1.setVisibility(View.GONE);
                }
                else if(!isChecked)
                {
                    imageView1.setVisibility(View.VISIBLE);
                }
            }
        });


        return view;
    }

    Position getselectedposition(int position) {
        return ((Position) getItem(position));
    }

    ArrayList<Position> getcheckedposition() {
        ArrayList<Position> checkedposition = new ArrayList<Position>();
        for (Position p : arraylist) {
            if (p.ischeckedflag)
                checkedposition.add(p);
        }
        return checkedposition;
    }


}

Your Activity is like below class

package com.rajesh.getselecteditem;
import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {

    ArrayList<Position> listitems = new ArrayList<Position>();
    ListAdapter listAdapter;
    ListView lvMain ;

      /** Called when the activity is first created. */
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
         lvMain = (ListView) findViewById(R.id.listview);



        arraylistvalueadding();
        listAdapter = new ListAdapter(this, listitems);


        lvMain.setAdapter(listAdapter);
      }

      void arraylistvalueadding() {
        for (int i = 1; i <= 20; i++) {
            listitems.add(new Position(i,false));
        }
      }

}

Your Position Class is below .

public class Position {
      int  position;    
      boolean ischeckedflag;


      Position(int name, boolean flag) {
          position = name;     
          ischeckedflag = flag;
      }
    }

Just design your main.xml class with list view.

your listitem.xml is like below.

<?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:orientation="horizontal" >

    <CheckBox
        android:id="@+id/cbBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical" >
    </CheckBox>

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp"
        android:orientation="vertical"
        android:layout_weight="1" >

        <ImageView
            android:id="@+id/imageView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_launcher" />

        <TextView
            android:id="@+id/Textview1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text=""
            android:textSize="20sp" >
        </TextView>       
    </LinearLayout>

</LinearLayout>
itsrajesh4uguys
  • 4,610
  • 3
  • 20
  • 31
0

Ok I solved the problem myself. The issue was not in the code nor because of my software. The thing was a possible android bug I spotted. :) I had to explicitly set the data again even if it was meant to be the default one. This code worked:

if(likeable.get(i) == 0) {
    LinearLayout btn = (LinearLayout) row.findViewById(R.id.btn_like);
    btn.setClickable(false);
    ImageView imgView = (ImageView) row.findViewById(R.id.img_like);
    imgView.setImageDrawable(getResources().getDrawable(R.drawable.ic_like_selected));
    TextView txtView = (TextView) row.findViewById(R.id.text_like);
    txtView.setTextColor(Color.parseColor("#60007c"));
} else {
    LinearLayout btn = (LinearLayout) row.findViewById(R.id.btn_like);
    btn.setClickable(true);
    ImageView imgView = (ImageView) row.findViewById(R.id.img_like);
    imgView.setImageDrawable(getResources().getDrawable(R.drawable.ic_like));
    TextView txtView = (TextView) row.findViewById(R.id.text_like);
    txtView.setTextColor(Color.parseColor("#555555"));
}
if(dislikeable.get(i) == 0) {
    LinearLayout btn = (LinearLayout) row.findViewById(R.id.btn_dislike);
    btn.setClickable(false);
    ImageView imgView = (ImageView) row.findViewById(R.id.img_dislike);
    imgView.setImageDrawable(getResources().getDrawable(R.drawable.ic_dislike_selected));
    TextView txtView = (TextView) row.findViewById(R.id.text_dislike);
    txtView.setTextColor(Color.parseColor("#60007c"));
} else {
    LinearLayout btn = (LinearLayout) row.findViewById(R.id.btn_dislike);
    btn.setClickable(true);
    ImageView imgView = (ImageView) row.findViewById(R.id.img_dislike);
    imgView.setImageDrawable(getResources().getDrawable(R.drawable.ic_dislike));
    TextView txtView = (TextView) row.findViewById(R.id.text_dislike);
    txtView.setTextColor(Color.parseColor("#555555"));
}

To exactly know what was happening I describe it as much as I understood. The attributes I'm setting on the else sections of the code above are exactly the ones that were set on the rows by default (using layouts). The thing was when the if was not right ( We didn't need to make any changes ) and we were just skipping them. when scrolling through the list they were getting override by android (here is the bug). So I explicitly restored them to their defaults in the code (else sections) the code worked as supposed.

Thanks all for answers.

Miro Markaravanes
  • 3,285
  • 25
  • 32
  • That is not a bug, the `ListView` will recycle rows as you scroll up and down you should always provide defaults for any changes you do to the rows. That doesn't however explain why it happens only for the first row. – user Jun 01 '13 at 15:43
  • @Luksprog it was happening for the first row because I had only 6 posts. when scrolling to the latest one (because the latest one was too long) it was covering all my screen so the child 0 was becoming post number 6. when scrolling up to the first post it was having the same setting as the 6th post. If the posts were more it would happen to other posts too. – Miro Markaravanes Jun 01 '13 at 15:57
  • However thats too bad I have to keep updating the buttons always. Thats too much of useless proccess. – Miro Markaravanes Jun 01 '13 at 15:58
  • @Luksprog omg this getting really strange. I wrote a new adapter and again the first entry is the same as the last one!!!! even it's text and data.. There must be really something I'm missing in my code... – Miro Markaravanes Jun 01 '13 at 17:17
  • I can't help as I don't see your entire code. But as I already said in my answer I advise you to rethink the way you o things. – user Jun 01 '13 at 20:05
  • @Luksprog thx for your help. I changed the way I was doing things, Created a custom adapter and displayed the already updated buttons on the ListView and so I completely deleted that faulty function.. :) Life is good now.. :-p – Miro Markaravanes Jun 01 '13 at 22:28