I have created an adapter for a ListView in android which behaves really strange. The listview is passed a list of objects, which is sorted by some rule (comparing numbers, wow...). Now, when the list is shown in the view duplicate entries are shown, they are not sorted, entries are missing, and when I scroll the list even entries change! What is going on?
Here is the code of the adapter, I can post more code if that is necessary:
public class StatViewAdapter extends BaseAdapter {
Activity activity;
ArrayList<Entry> entries;
TextView txtName;
TextView txtOK;
TextView txtNOK;
TextView txtHist;
TextView txtPrandom;
TextView txtPhist;
TextView txtPtotal;
//public StatViewAdapter(Activity activity, ArrayList<HashMap<String, String>> list){
public StatViewAdapter(Activity activity, ArrayList<Entry> entries){
super();
this.activity=activity;
this.entries = entries;
Collections.sort(this.entries, new Comparator<Entry>() {
@Override
public int compare(Entry o1, Entry o2) {
if (o1.getPriority() > o2.getPriority()) {
return 1;
}
if (o1.getPriority() < o2.getPriority()) {
return -1;
}
return 0;
}
});
for (int i=0;i<this.entries.size();i++) {
String name = this.entries.get(i).name();
int p = this.entries.get(i).getPriority();
System.out.println(String.format("%s: %d", name, p));
}
}
@Override
public int getCount() {
// TODO Auto-generated method stub
//return list.size();
return this.entries.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return this.entries.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
LayoutInflater inflater=this.activity.getLayoutInflater();
if(convertView == null){
convertView=inflater.inflate(R.layout.list_view, null);
txtName=(TextView) convertView.findViewById(R.id.listname);
txtOK=(TextView) convertView.findViewById(R.id.listok);
txtNOK=(TextView) convertView.findViewById(R.id.listnok);
txtHist=(TextView) convertView.findViewById(R.id.listhist);
txtPrandom =(TextView) convertView.findViewById(R.id.listprandom);
txtPhist=(TextView) convertView.findViewById(R.id.listphist);
txtPtotal=(TextView) convertView.findViewById(R.id.listptot);
}
Entry entry = this.entries.get(position);
txtName.setText(entry.name());
txtOK.setText(Integer.toString(entry.number_ok));
txtNOK.setText(Integer.toString(entry.number_nok));
txtHist.setText(entry.history);
txtPrandom.setText(Integer.toString(entry.randomIndex));
txtPhist.setText(Integer.toString(entry.histIndex));
txtPtotal.setText(Integer.toString(entry.getPriority()));
return convertView;
}
}
-
1Obviously, because you are storing references to views of item directly in AdapterSelvin– Selvin2017年01月23日 20:25:47 +00:00Commented Jan 23, 2017 at 20:25
-
what? can you explain? Its not obvious to me...Alex– Alex2017年01月23日 20:26:39 +00:00Commented Jan 23, 2017 at 20:26
-
Analyze what would happen if convertView is not null but is not the same as last view returned from getViewSelvin– Selvin2017年01月23日 20:28:40 +00:00Commented Jan 23, 2017 at 20:28
-
I have not the slightest idea!Alex– Alex2017年01月23日 20:29:38 +00:00Commented Jan 23, 2017 at 20:29
-
1Too hard? All views references would be references to previous view not current... That's why viewholder pattern exits (not only)Selvin– Selvin2017年01月23日 20:30:35 +00:00Commented Jan 23, 2017 at 20:30
1 Answer 1
The purpose of public View getView(int position, View convertView, ViewGroup parent) is to bind the data of the object at the given position to a view. The view might be a recycled one (convertView) or you need to create one in the method. The problem is that you should not keep references to those views (txtName, txtOk) in the Adapter.
public class StatViewAdapter extends BaseAdapter {
Activity activity;
ArrayList<Entry> entries;
//TextView txtName;
//TextView txtOK;
// ...
public StatViewAdapter(Activity activity, ArrayList<Entry> entries){
// ...
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater=this.activity.getLayoutInflater();
if(convertView == null){
convertView=inflater.inflate(R.layout.list_view, null);
}
TextView txtName=(TextView) convertView.findViewById(R.id.listname);
TextView txtOK=(TextView) convertView.findViewById(R.id.listok);
// ...
Entry entry = this.entries.get(position);
txtName.setText(entry.name());
txtOK.setText(Integer.toString(entry.number_ok));
// ....
return convertView;
}
It works but it still isn't a good way to do it, you might want to use a RecyclerView or implement the ViewHolder pattern.
The idea of the ViewHolder pattern is that you don't have to call findViewById every time you bind a view because it's computationally expensive and it could make the scroll laggy.
You might want to use a RecyclerView because it's a newer and more flexible view that does the same job of a ListView and it has the ViewHolder pattern built-in.
4 Comments
RecyclerView? I don't want to recycle a view, just to present the data in the listview....?