user2348956
user2348956

Reputation: 81

ListView shows wrong items on Scroll

I have a listview for displaying names. It shows wrong names if I scroll the screen. I understand that this is a common bug rooted in getView method of the adapter, however havent been able to solve it despite following the method presented in many existing discussions.

I have pasted my code below. Will be great if someone could identify the bug.

Custom Adapter

public class ListArrayAdapter extends ArrayAdapter<client> {

HashMap<String, Integer> hMap = new HashMap<String, Integer>();
private Context cntxt;
private List<client> client_list;
private int textviewres;

public ListArrayAdapter(Context context, int textViewResourceId, List<client> objects) {
    super(context,textViewResourceId,objects);
    this.cntxt = context;
    this.textviewres = textViewResourceId;
    this.client_list = objects;

    for(int j=0; j<client_list.size(); j++){

        Log.d("ListArrayAdapter", "Position No" +j +": " +client_list.get(j).getName());
        Log.d("ListArrayAdapter", "Total elements in the client list is  "+client_list.size());

    }
}


@Override
public long getItemId(int position) {
    client item = getItem(position);
    return item.getID();
}


@Override
public boolean hasStableIds() {
    return true;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    View clist;


    LayoutInflater inflater = (LayoutInflater) cntxt.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    if (convertView == null) {

        clist = inflater.inflate(textviewres, null);
        TextView tv = (TextView) clist.findViewById(R.id.textv);
        tv.setText(client_list.get(position).getName());

    } else {
        clist = convertView;
    }
    return clist;

}

}

Activity that runs the ListView

public class clients_list extends ActionBarActivity {


private database_handler obj;
private Context context;
static ArrayAdapter<String> adapter;
List<client> values;
ListView list;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_clients);

    list = (ListView) findViewById(R.id.clientlist);

    context = getBaseContext();
    obj = new database_handler(context);

    final List<String> listdata = new ArrayList<String>();

    values = obj.getAllClients();
    int listlength = values.size();

    for (int i = 0; i < listlength; ++i) {
        listdata.add(values.get(i).getName());
    }

    ListArrayAdapter adapter = new ListArrayAdapter(this,R.layout.custom_textv, values);

    list.setAdapter(adapter);

    //Define what happens after clicking the Add Client Button
    Button add_client_button = (Button) findViewById(R.id.addbutton);

    add_client_button.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            // Perform action on click
            Intent addclient = new Intent(v.getContext(), add_client.class);
            v.getContext().startActivity(addclient);
        }
    });

    list.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener() {

        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

            //Display Client Details
            Intent displayclient = new Intent(view.getContext(), display_clientdetails.class);

            Long db_id = parent.getAdapter().getItemId(position);

            String TAG = "client_list";
            Log.d(TAG,"Position is" +position +" ID is " +db_id);

            displayclient.putExtra("client_id",db_id);
            view.getContext().startActivity(displayclient);

        }

    });

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_clients, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

}

Layout for the Activity

<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:background="@color/green"
tools:context="com.example.jaya.myapplication.Clients">

<Button
    android:id="@+id/addbutton"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:drawableLeft="@drawable/ic_addnewclient"
    android:text="@string/add_client"
    android:textColor="@color/white"/>

<ListView
    android:id="@+id/clientlist"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@id/addbutton"
    android:dividerHeight="5dp">

</ListView>

Upvotes: 3

Views: 5592

Answers (3)

Xcihnegn
Xcihnegn

Reputation: 11597

Use ViewHolder pattern is encouraged way to make View in getView(...). Otherwise just change your codes simply:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

   View clist;

   LayoutInflater inflater = (LayoutInflater) cntxt.getSystemService (Context.LAYOUT_INFLATER_SERVICE);

    clist = inflater.inflate(textviewres, null);
    TextView tv = (TextView) clist.findViewById(R.id.textv);
    tv.setText(client_list.get(position).getName());

    return clist;
}

Hope this help!

Upvotes: 0

user2348956
user2348956

Reputation: 81

I could fix the problem myself after reading ListView in ArrayAdapter order get's mixed up when scrolling.

The error lies in the getView method. I had placed the code for assigning the names to the TextViews in the ListView, in the If Block for (convertView == null). Since the convertView is not null after the first set of items is displayed, the code was not able to assign new names to Textviews wand was just using the re-usable items placed by Android in the convertView.

The correct getView code is as follows:

    @Override
public View getView(int position, View convertView, ViewGroup parent) {

    View clist = convertView;

    if (convertView == null) {
        LayoutInflater inflater = (LayoutInflater) cntxt.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        clist = inflater.inflate(textviewres, null);
    }
    TextView tv = (TextView) clist.findViewById(R.id.textv);
    tv.setText(client_list.get(position).getName());

    return clist;

}

Upvotes: 1

krishna
krishna

Reputation: 609

I think you have a problem here

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

View clist;


LayoutInflater inflater = (LayoutInflater) cntxt.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {

    clist = inflater.inflate(textviewres, null);
    TextView tv = (TextView) clist.findViewById(R.id.textv);
    tv.setText(client_list.get(position).getName());

} else {
    clist = convertView;
}
return clist;

}

So when your convert view is not null you are not populating your list like this

     TextView tv = (TextView) clist.findViewById(R.id.textv);
    tv.setText(client_list.get(position).getName());

I generally do something like this

     public class MyHolder {
    TextView keyword, tweetCount;
    public MyHolder(View v) {
        keyword = (TextView) v.findViewById(R.id.keyword_text);
        tweetCount = (TextView) v.findViewById(R.id.tweets_count_text);
    }

}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View view = convertView;
    MyHolder holder;
    if (view == null) {
        view = LayoutInflater.from(mContext).inflate(R.layout.keyword_contents, parent, false);
        holder = new MyHolder(view);
        view.setTag(holder);
    } else {
        holder = (MyHolder) view.getTag();
    }
    holder.keyword.setText(list.get(position).getKeyword());
    holder.tweetCount.setText(String.valueOf(list.get(position).getCount()));
    return view;
}

I think what holder will do is instead of finding textview again and again it will only findviewbyid once

Upvotes: 5

Related Questions