Custom Adapter getView() method is not called
Asked Answered
O

6

42

Here is the code of the fragment in which I am setting a custom adapter to the list.

There no errors but the ListView is empty. I have implemented getCount() which returns right number of items in my ArrayList. I don't see ("Inside", "GetView") in the logcat

Fragment

public class ServiceCarListFragment extends Fragment {

    private String url;
    private ArrayList<CarDetail> carDetailList = new ArrayList<CarDetail>();
    private CarListAdapter adapter;
    private ListView mList;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        url = getActivity().getIntent().getStringExtra("url");
        new DownloadCarDetail().execute(url);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        View v = inflater.inflate(R.layout.fragment_service_car_list, container, false);
        mList = (ListView) v.findViewById(R.id.list);
        mList.setAdapter(adapter);

        for (CarDetail car : carDetailList) {
            // START LOADING IMAGES FOR EACH STUDENT
            car.loadImage(adapter);
        }
        return v;
    }

    class DownloadCarDetail extends AsyncTask<String, String, ArrayList<CarDetail>> {

        @Override
        protected ArrayList<CarDetail> doInBackground(String... params) {
            // TODO Auto-generated method stub
            ArrayList<CarDetail> carDetailList = JsonParser.parseJson(params[0]);
            return carDetailList;
        }

        @Override
        protected void onPostExecute(ArrayList<CarDetail> carDetailList) {
            // TODO Auto-generated method stub
            ServiceCarListFragment.this.carDetailList = carDetailList;
            Log.d("dccs", String.valueOf(ServiceCarListFragment.this.carDetailList.size()));
            adapter = new CarListAdapter(getActivity(), ServiceCarListFragment.this.carDetailList);
            Log.d("dccs", String.valueOf((adapter.getCount())));
        }

    }
}

CustomAdapter

public class CarListAdapter extends BaseAdapter {

    private ArrayList<CarDetail> items = new ArrayList<CarDetail>();
    private Context context;

    public CarListAdapter(Context context, ArrayList<CarDetail> items) {
        this.context = context;
        this.items = items;
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return items.size();
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return items.get(position);
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        Log.d("Inside", "GetView");
        LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        ViewHolder holder = null;
        CarDetail car = items.get(position);

        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.car_list_row, null);
            holder = new ViewHolder();
            holder.tvCarName = (TextView) convertView.findViewById(R.id.tvCarName);
            holder.tvDailyPriceValue = (TextView) convertView.findViewById(R.id.tvWeeklyPriceValue);
            holder.tvWeeklyPriceValue = (TextView) convertView.findViewById(R.id.tvWeeklyPriceValue);
            holder.imgCar = (ImageView) convertView.findViewById(R.id.imgCar);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.tvCarName.setText(car.getCarName());
        if (car.getImage() != null) {
            holder.imgCar.setImageBitmap(car.getImage());
        } else {
            // MY DEFAULT IMAGE
            holder.imgCar.setImageResource(R.drawable.ic_action_call);
        }

        return convertView;
    }

    static class ViewHolder {
        TextView tvCarName;
        TextView tvDailyPriceValue;
        TextView tvWeeklyPriceValue;
        ImageView imgCar;
    }

}
Outspeak answered 2/5, 2013 at 12:37 Comment(0)
P
169

The only reasons getView is not called are:

  1. getCount returns 0.
  2. you forget to call setAdapter on the ListView.
  3. If the ListView's visibility (or its container's visibility) is GONE. Thanks to @TaynãBonaldo for the valuable input.
  4. ListView is not attached to any viewport layout. That is, mListView = new ListView(...) is used without myLayout.addView(mListView).

In the onPostExcute, after you create a new instance of CarListAdapter I will suggest you to update the new instance to your ListView. Indeed you need to call again

 mList.setAdapter(adapter);

Edit: setAdapter should be always called on the ui thread, to avoid unexpected behaviours

Edit2:

The same applies to RecyclerView. Make sure that

  • getItemCount is returning a value grater than 0 (usually the dataset size)
  • both setLayoutManager and setAdapter have to be called on the UI Thread
  • The visibility of the widget has to be set to VISIBLE
Phew answered 2/5, 2013 at 12:42 Comment(11)
it worked but can you explain why because in fragment lifecycle onCreate() is called before onCreateView() and i m calling the asyncTask in onCreate() and setting the adapter in onCreateView() which is called after.Outspeak
@Outspeak first of all, nobody can assure yuo that the AsyncTask excution is finished before or after the onCreateView is called. That is what async means. Two, in the onPostExcute you create a new CarListAdapter and your ListView has to be informed that the adapter has changedPhew
i mean by setting the adapter in onPosExecute method is a good way for this type of situation or there is a better architecture to avoid such problemsOutspeak
can you rephrase last comment?Phew
Just complementing your answer .. if the visibility of the root view (ListView itself or the viewGroup that the ListView is a child) as View.GONE the getView() method of the adapter will not be called.Aorta
maybe your listView is emptyEquitation
This is the most methodical way to go around this problem. I checked one by one, and found out that my view was set to gone in onPostExecute of API call.Vories
There is one more: the function you are using is not running on UI threadEphemerid
i am using gridview also i setadapter but still not workingCleaning
Another problem that I faced. If you are using constraint layout, then you have to give constraints to listView or you have to give width and heigth to your listView. Width and height is required but, if you don't give A.Studio only gives warning.Tetrasyllable
My problem is : parent view width is 0. So I can't see my listView.Aboveboard
M
5

you must verify that the list has elements might have an error when adding items to your list . To verify , use the method:

adapter.getCount();
Minnieminnnie answered 3/2, 2015 at 17:7 Comment(0)
U
3

I faced similar problem. Here is a simple work around to solve it:

In your onCreateView, you will have to wait before the view gets created. So change your lines from this:

mList = (ListView)v.findViewById(R.id.list);
mList.setAdapter(adapter);

CHANGE THE ABOVE TWO LINES INTO:

mList = (ListView)v.findViewById(R.id.list);
mList.post(new Runnable() {
    public void run() {
        mList.setAdapter(adapter);
    }
});

Hope this will help others who would run into similar problem

Uraninite answered 30/11, 2013 at 10:18 Comment(1)
I don not see how this is helping. ASAIK, the view is created when the Activity layout is inflated. I don not see any need to "wait" further for its creation. I am facing a similar problem, and this is not a solution.Drawtube
C
0

You are missing the super class in the constructor. See my example below:

public AppDataAdapter(Activity a, int textViewResourceId, ArrayList<AppData> entries) {
    super(a, textViewResourceId, entries);
    this.entries = entries;
    this.activity = a;
}
Carder answered 2/5, 2013 at 12:44 Comment(7)
I would add that: adapter = new CarListAdapter... doesn't actually set the adapter for the view. So he has a race condition between onCreateView and onPostExecuteCommitteeman
You're right, I hadn't spotted that he wasn't binding the new adapter to the listview in post execute. Blackbelt ftwCarder
@Committeeman can you elaborate what should be done to avoid this race condition and explain .. i am new to android development..it would be gr8 if you could explain ..thanksOutspeak
setAdapter binds an adapter to a listview (tells the listview where the data it is showing is coming from). In onCreate() you are starting the aSyncTask to download the data then in onCreateView you set the adapter. However, if it takes longer to create the adapter (in the aSyncTask) than it does to reach the onCreateView() function, the listview will set the adapter before it is populated with the car data i.e it is empty. The easy solution is to only bind the adapter AFTER you KNOW you have created it successfully. i.e as blackbelt said, just bind the adapter in onPostExecute() :)Carder
Another option is creating the empty Adapter in the onCreate and binding it in the onCreateView, in onPostExecute updating existing Adapter with the data received and calling notifyDataSetChanged which will make the adapter refresh the list with the new information. If you implement it this way you can also refresh the data later if you want without creating a new adapter.Committeeman
I opt for Raanan's solution in my code. Simply add/set/remove etc from the underlying ArrayList and call adapter.notifyDataSetChange and it will refresh the listView for youCarder
i was unable to do exactly what Raanan told can you provide some sample code how u did it.Outspeak
S
0

What you have been doing is

In your adapter

public CarListAdapter(Context context , ArrayList<CarDetail> items) {

    this.context = context;
    this.items = items;

}

in your Fragment

adapter = new CarListAdapter(getActivity(),ServiceCarListFragment.this.carDetailList);

I hope you will be using FragmentActivity

You need to call

adapter = new CarListAdapter(YOUR_ACTIVITY_CONTEXT, carDetailList);

where YOUR_ACTIVITY_CONTEXT will be your FragmentActivity

Sims answered 2/5, 2013 at 12:54 Comment(0)
A
0

I had the same problem. And after trying all tips above my getView was still not being called. So I tried to remove the ScrollView that I used outside the ListView. Then the getView worked well. Just for add one more posibility. I Hope help someone.

Amherst answered 28/6, 2018 at 12:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.