I'm having an issue getting my list populated correctly by my custom ArrayAdapter (code below). As I understand it, my adapter is only populating the textviewResourceId when it is instanciated since I'm using constructor Adapter(context, rowLayout, textViewResourceId, ArrayList<Items>), but the getView method is only called when rows that were not visible become visible.
This is causing an issue as, when my list is first showing, only the title of my article is showing, and I have to scroll all the way down the list and up for all the views in each row to be populated correctly (since that task is done in getView).
Can anyone point me in the right direction? How could I refactor this so all views in each visible row gets populated right away?
Code to my custom adapter:
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class ArticleArrayAdapter extends ArrayAdapter<Article> {
private final Context context;
private final ArrayList<Article> articles;
@SuppressWarnings("unused")
private final int rowLayout;
public ArticleArrayAdapter(Context context, int rowLayout, int textViewResourceId, ArrayList<Article> articles) {
super(context, rowLayout, textViewResourceId, articles);
this.rowLayout=rowLayout;
this.context = context;
this.articles = articles;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(R.layout.affichageitem, null);
}
else {
TextView viewTitre = (TextView)row.findViewById(R.id.titre);
TextView viewAuteur = (TextView)row.findViewById(R.id.auteur);
TextView viewDate = (TextView)row.findViewById(R.id.date);
ImageView viewLogo = (ImageView)row.findViewById(R.id.category_logo);
viewTitre.setText(articles.get(position).getTitle());
viewAuteur.setText(articles.get(position).getCreator());
viewDate.setText(articles.get(position).getDate());
Drawable drawLogo = context.getResources().getDrawable(R.drawable.logocat);
viewLogo.setImageDrawable(drawLogo);
}
return super.getView(position, convertView, parent);
}
}
Edited version:
public class ArticleArrayAdapter extends ArrayAdapter<Article> {
private final Context context;
@SuppressWarnings("unused")
private final int rowLayout;
public ArticleArrayAdapter(Context context, int rowLayout,int textViewResourceId) {
super(context, rowLayout, textViewResourceId);
this.rowLayout=rowLayout;
this.context = context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(R.layout.affichageitem, null);
}
else {
TextView viewTitre = (TextView)row.findViewById(R.id.titre);
TextView viewAuteur = (TextView)row.findViewById(R.id.auteur);
TextView viewDate = (TextView)row.findViewById(R.id.date);
ImageView viewLogo = (ImageView)row.findViewById(R.id.category_logo);
viewTitre.setText(getItem(position).getTitle());
viewAuteur.setText(getItem(position).getCreator());
viewDate.setText(getItem(position).getDate());
Drawable drawLogo = context.getResources().getDrawable(R.drawable.logocat);
viewLogo.setImageDrawable(drawLogo);
}
return super.getView(position, convertView, parent); // <<- ONLY TITLES
//return row; <<- EMPTY
}
}
rowLayout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageView
android:id="@+id/category_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/logo_desc"
android:padding="20dp" />
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingLeft="5dp" >
<TextView
android:id="@+id/titre"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="18dp"
android:textStyle="bold" />
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity=""
android:orientation="horizontal" >
<TextView
android:id="@+id/auteur"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:textSize="12dp" />
<TextView
android:id="@+id/espace"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/espace"
android:adjustViewBounds="true"
android:textSize="12dp" />
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:textSize="12dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
The problem is that you are not populating new views. What happens is that Android may keep a fixed number of views which will be used for your list view. The views are recycled which is why it's impossible to "populate" all your views before they become visible. This line
if (view == null) {
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(R.layout.affichageitem, null);
}
checks whether a view is being recycled or not. null means it's not, so if you get null you need to inflate a new view. Upto there your code's fine. However, you need to populate the view whether it's a newly inflated view or not. So you shouldn't have the else statement, just have
View row = convertView;
if (row == null) {
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(R.layout.affichageitem, null);
}
TextView viewTitre = (TextView)row.findViewById(R.id.titre);
TextView viewAuteur = (TextView)row.findViewById(R.id.auteur);
TextView viewDate = (TextView)row.findViewById(R.id.date);
ImageView viewLogo = (ImageView)row.findViewById(R.id.category_logo);
viewTitre.setText(getItem(position).getTitle());
viewAuteur.setText(getItem(position).getCreator());
viewDate.setText(getItem(position).getDate());
Drawable drawLogo = context.getResources().getDrawable(R.drawable.logocat);
viewLogo.setImageDrawable(drawLogo);
return row;
The reason why it worked when you scrolled all the way down is that on your way back up getView was receiving recycled views and it jumped right into the else clause you had.
You are using your own collection ArrayList<Article>.
Note that every ArrayAdaper<foo> already has data collection built in, where you can add by add(foo) or addAll(List<foo>) and clear it by clear() method.
Also, ListView can observe this data and refresh when changes happen to this data. Or explicitly when notifyDataSetChanged() is called on adapter.
Problem here is that you are accepting data in constructor, storing in yet another local variable, and notifyDataSetChanged() is not being called. You cannot call it from constructor as Object is still under construction.
So, Don't accept data in constructor. Inside getView() use getItem(position) to get Article item.
Add data externally like:
ArticleArrayAdapter adapter = new ArticleArrayAdapter(context,rowLayout,android.R.layout.simple_list_item_1);
adapter.addAll(articles);
myListView.setAdapter(adapter);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With