39

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;
    }

}
yadav_vi
  • 1,239
  • 3
  • 14
  • 43
Ravi
  • 4,632
  • 8
  • 31
  • 45

6 Answers6

164

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
Mike M.
  • 35,854
  • 7
  • 92
  • 90
Blackbelt
  • 148,780
  • 26
  • 271
  • 285
  • 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. – Ravi May 02 '13 at 12:51
  • 1
    @Ravi 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 changed – Blackbelt May 02 '13 at 12:55
  • 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 problems – Ravi May 02 '13 at 12:58
  • can you rephrase last comment? – Blackbelt May 02 '13 at 12:59
  • 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. – Taynã Bonaldo Nov 12 '14 at 02:37
  • maybe your listView is empty –  Apr 21 '15 at 10:18
  • 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. – Shubham Chaudhary Aug 28 '15 at 12:02
  • There is one more: the function you are using is not running on UI thread – user3239558 Feb 05 '16 at 02:52
  • i am using gridview also i setadapter but still not working – Dhara Patel Feb 08 '17 at 13:06
  • 1
    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. – ahmet Mar 24 '17 at 12:03
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();
Kushal
  • 6,695
  • 6
  • 48
  • 72
Luis Felipe
  • 61
  • 1
  • 2
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

  • 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. – Bamaco Mar 26 '15 at 22:00
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;
}
rcbevans
  • 4,875
  • 3
  • 29
  • 44
  • 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 onPostExecute – Raanan May 02 '13 at 12:50
  • You're right, I hadn't spotted that he wasn't binding the new adapter to the listview in post execute. Blackbelt ftw – rcbevans May 02 '13 at 12:53
  • @Raanan 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 ..thanks – Ravi May 02 '13 at 12:54
  • 1
    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() :) – rcbevans May 02 '13 at 12:56
  • 2
    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. – Raanan May 02 '13 at 13:13
  • 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 you – rcbevans May 02 '13 at 13:23
  • i was unable to do exactly what Raanan told can you provide some sample code how u did it. – Ravi May 03 '13 at 06:08
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

Ameer Moaaviah
  • 1,510
  • 11
  • 26
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.

DaniloLM
  • 49
  • 4