androidlistviewandroid-asynctaskandroid-adaptergetview

How to use AsyncTask with ListView Adapter


So, I have a ListView which I'm showing on my TodayFragment. There are TodayTaskAdapter, that customises this list. When the actvity is loading, it takes a lot of time because of the list. And slows down app. The question is:

How should I use AsyncTask right? And what is the right place to use it:

  1. In fragment, when setting adapter;
  2. In getView() method of adapter to do the operation there in backgroud;
  3. Or someplace else?

I'm not quite into it yet. Need help.

TodayFragment

public class TodayFragment extends AbstractTabFragment
{
    public static final String TODAY_PAGE = "TODAY_PAGE";
    private static final int LAYOUT = R.layout.fragment_today;

    private ListView task_list;

    private TodayTaskAdapter today_task_adapter;

    private Event event;
    private Event event1;
    private Event event3;

    private Event event4;
    private Event event5;

    private Event event6;
    private Event event7;

    private Event event8;
    private Event event9;

    public static TodayFragment newInstance(int page, Context context)
    {
        Bundle args = new Bundle();
        args.putInt(TODAY_PAGE, page);

        TodayFragment fragment = new TodayFragment();
        fragment.setArguments(args);
        fragment.setContext(context);
        fragment.setTitle(context.getString(R.string.tab_today_name));

        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        view = inflater.inflate(LAYOUT, container, false);

        task_list = (ListView) view.findViewById(R.id.tasks_list);

        today_task_adapter = new TodayTaskAdapter(getActivity(), R.layout.today_list_item);

        eventCreation();

        today_task_adapter.add(new EventTypeSection("Schedule"));
        today_task_adapter.add(event);
        today_task_adapter.add(event1);
        today_task_adapter.add(event3);

        today_task_adapter.add(new EventTypeSection("ToDo"));
        today_task_adapter.add(event4);
        today_task_adapter.add(event5);

        today_task_adapter.add(new EventTypeSection("Work Task"));
        today_task_adapter.add(event6);
        today_task_adapter.add(event7);

        today_task_adapter.add(new EventTypeSection("Birthday"));
        today_task_adapter.add(event8);
        today_task_adapter.add(event9);

        task_list.setAdapter(today_task_adapter);

        return view;
    }

    private void eventCreation()
    {
        event = new Schedule(1,"Algorythm Theory", ScheduleType.LESSON, "10:20 AM", "11:55 AM", "07.01.2017", EventType.SCHEDULE, "Waiting", 79);
        event1 = new Schedule(2,"Object-oriented programming", ScheduleType.LABORATORY_WORK, "12:00 AM", "01:30 PM", "07.01.2017", EventType.SCHEDULE, "Waiting", 63);
        event3 = new Schedule(2,"Aplied Math", ScheduleType.EXAM, "01:50 PM", "03:35 PM", "07.01.2017", EventType.SCHEDULE, "Waiting", 17);

        event4 = new ToDo(5,"Clean house", "09:30 AM", "03:35 PM", "07.01.2017", EventType.TODO, "Waiting", 91);
        event5 = new ToDo(7,"Buy grocery and other things", "01:30 AM", "02:25 PM", "07.01.2017", EventType.TODO, "Waiting", 23);

        event6 = new WorkTask(5,"Refactoring", "09:30 AM", "01:35 PM", "07.01.2017", EventType.WORKTASK, "Waiting", 81);
        event7 = new WorkTask(7,"Build database", "04:30 PM", "05:25 PM", "07.01.2017", EventType.WORKTASK, "Waiting", 33);

        event8 = new Birthday(8,"Taras's birthday", "09:30 AM", "01:35 PM", "07.01.2017", EventType.BIRTHDAY, "Waiting", 71, new Location(1, 2, "Roksolany", "Lviv", "Ukraine"));
        event9 = new Birthday(9,"John's birthday", "04:30 PM", "05:25 PM", "07.01.2017", EventType.BIRTHDAY, "Waiting", 53, new Location(1, 2, "Roksolany", "Lviv", "Ukraine"));
    }
}

TodayTaskAdapter

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.twinkle94.dealwithit.R;
import com.example.twinkle94.dealwithit.events.Event;
import com.example.twinkle94.dealwithit.events.task_types.Schedule;
import com.example.twinkle94.dealwithit.events.type_enums.EventType;

import java.util.ArrayList;
import java.util.List;

public class TodayTaskAdapter extends ArrayAdapter
{
    private final String NAME = TodayTaskAdapter.class.getSimpleName();

    private static final int TYPE_HEADER = 0;
    private static final int TYPE_LIST = 1;

    private List<Item> list_of_tasks = new ArrayList<>();

    public TodayTaskAdapter(Context context, int resource)
    {
        super(context, resource);
    }

    public void add(Item event)
    {
        list_of_tasks.add(event);
    }

    @Override
    public int getCount()
    {
        return list_of_tasks.size();
    }

    @Nullable
    @Override
    public Item getItem(int position)
    {
        return list_of_tasks.get(position);
    }

    @Override
    public int getItemViewType(int position)
    {
        return getItem(position).getType() == EventType.NO_TYPE ?
                RowType.HEADER_ITEM.ordinal()
                :
                RowType.LIST_ITEM.ordinal();
    }

    @Override
    public int getViewTypeCount()
    {
        return RowType.values().length;
    }

    @NonNull
    @Override
    public View getView(int position, View convertView, @NonNull ViewGroup parent)
    {
        EventViewHolder eventViewHolder;

        // Get the data item type for this position
        int type = getItemViewType(position);

        if(convertView == null)
        {
            eventViewHolder = new EventViewHolder();

            // Inflate XML layout based on the type
            convertView = getInflatedLayoutForType(type, parent);

            switch (type)
            {
                case TYPE_HEADER:
                    initHeader(convertView, eventViewHolder);
                    break;

                case TYPE_LIST:
                    initViews(convertView, eventViewHolder);
                    break;
            }

            convertView.setTag(eventViewHolder);
        }

        else
        {
            eventViewHolder = (EventViewHolder) convertView.getTag();
        }

        //set all views with values from list
        setViewValues(position, eventViewHolder);

        return convertView;
    }

    private void setViewValues(int position, EventViewHolder eventViewHolder)
    {
        Item event = getItem(position);

        int type_color;
        String task_title;
        String from_time;
        String to_time;
        String importance;
        String interest;

        if (event != null)
        {
            switch (event.getType())
            {
                case SCHEDULE:
                    type_color = ContextCompat.getColor(getContext(), R.color.colorTypeSchedule);
                    task_title = ((Event)(event)).getTitle();
                    String schedule_type = ((Schedule)(event)).getScheduleType().toString();
                    int schedule_type_image = getSchedule_type_image(event);
                    from_time = ((Event)(event)).getTime_start();
                    to_time = ((Event)(event)).getTime_end();
                    importance = Integer.toString(((Event)(event)).getImportance()) + "%";
                    interest = Integer.toString(((Event)(event)).getImportance() + 9) + "%";

                    eventViewValues(eventViewHolder, type_color, task_title, schedule_type_image, schedule_type, from_time, to_time, importance, interest, View.VISIBLE, View.VISIBLE);
                    break;

                case TODO:
                    type_color = ContextCompat.getColor(getContext(), R.color.colorTypeToDo);
                    task_title = ((Event)(event)).getTitle();
                    from_time = ((Event)(event)).getTime_start();
                    to_time = ((Event)(event)).getTime_end();
                    importance = Integer.toString(((Event)(event)).getImportance()) + "%";
                    interest = Integer.toString(((Event)(event)).getImportance() + 9) + "%";

                    eventViewValues(eventViewHolder, type_color, task_title, 0, "", from_time, to_time, importance, interest, View.GONE, View.GONE);
                    break;

                case WORKTASK:
                    type_color = ContextCompat.getColor(getContext(), R.color.colorTypeWorkTasks);
                    task_title = ((Event)(event)).getTitle();
                    from_time = ((Event)(event)).getTime_start();
                    to_time = ((Event)(event)).getTime_end();
                    importance = Integer.toString(((Event)(event)).getImportance()) + "%";
                    interest = Integer.toString(((Event)(event)).getImportance() + 9) + "%";

                    eventViewValues(eventViewHolder, type_color, task_title, 0, "", from_time, to_time, importance, interest, View.GONE, View.GONE);
                    break;

                case BIRTHDAY:
                    type_color = ContextCompat.getColor(getContext(), R.color.colorTypeBirthday);
                    task_title = ((Event)(event)).getTitle();
                    from_time = ((Event)(event)).getTime_start();
                    to_time = ((Event)(event)).getTime_end();
                    importance = Integer.toString(((Event)(event)).getImportance()) + "%";
                    interest = Integer.toString(((Event)(event)).getImportance() + 9) + "%";

                    eventViewValues(eventViewHolder, type_color, task_title, 0, "", from_time, to_time, importance, interest, View.GONE, View.GONE);
                    break;

                case NO_TYPE:
                    String type = ((EventTypeSection) getItem(position)).getTitle();

                    eventViewHolder.headerText.setText(type);

                    switch(type)
                    {
                        case "Schedule":
                            eventViewHolder.headerText.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.colorTypeSchedule));
                            break;

                        case "ToDo":
                            eventViewHolder.headerText.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.colorTypeToDo));
                            break;

                        case "Work Task":
                            eventViewHolder.headerText.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.colorTypeWorkTasks));
                            break;

                        case "Birthday":
                            eventViewHolder.headerText.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.colorTypeBirthday));
                            break;

                    }

                    break;

                //TODO: add default, when task have no type.
            }
        }
    }

    private int getSchedule_type_image(Item event)
    {
        int schedule_type_image = R.drawable.ic_lesson_type;

        switch(((Schedule)(event)).getScheduleType())
        {
            case LESSON:
                schedule_type_image = R.drawable.ic_lesson_type;
                break;

            case EXAM:
                schedule_type_image = R.drawable.ic_exam_schedule_type;
                break;

            case LABORATORY_WORK:
                schedule_type_image = R.drawable.ic_lab_work_schedule_type;
                break;
        }
        return schedule_type_image;
    }

    //TODO: overload method with less parameters for different types of tasks.
    private void eventViewValues(EventViewHolder eventViewHolder, int type_color, String task_title, int scheduleTypeImage, String schedule_type,
                                 String from_time, String to_time, String importance, String interest,
                                 int task_type_visibility, int task_type_image_visibility)
    {
        eventViewHolder.type_color.setBackgroundColor(type_color);

        eventViewHolder.task_title.setText(task_title);
        eventViewHolder.task_type.setText(schedule_type);
        eventViewHolder.task_type_image.setImageResource(scheduleTypeImage);

        eventViewHolder.task_type.setVisibility(task_type_visibility);
        eventViewHolder.task_type_image.setVisibility(task_type_image_visibility);

        eventViewHolder.from_title.setText("From");
        eventViewHolder.from_time.setText(from_time);
        eventViewHolder.from_image.setImageResource(R.drawable.ic_from_time);

        eventViewHolder.to_title.setText("To");
        eventViewHolder.to_time.setText(to_time);
        eventViewHolder.to_image.setImageResource(R.drawable.ic_to_time);

        eventViewHolder.importance.setText(importance);
        eventViewHolder.importance_image.setImageResource(R.drawable.ic_importance_icon);

        eventViewHolder.interest.setText(interest);
        eventViewHolder.interest_image.setImageResource(R.drawable.ic_interest_today_page);
    }

    private View getInflatedLayoutForType(int type, ViewGroup parent)
    {
        LayoutInflater layoutInflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View inflated_view = null;

        switch(type)
        {
            case TYPE_HEADER:
                inflated_view = layoutInflater.inflate(R.layout.today_list_header, parent, false);
                break;

            case TYPE_LIST:
                inflated_view = layoutInflater.inflate(R.layout.today_list_item, parent, false);
                break;
        }

        return inflated_view;
    }

    private void initHeader(View row, EventViewHolder eventViewHolder)
    {
        eventViewHolder.headerText = (TextView) row.findViewById(R.id.tasks_title);
    }

    private void initViews(View row, EventViewHolder eventViewHolder)
    {
        eventViewHolder.type_color = row.findViewById(R.id.type_color);

        eventViewHolder.task_title = (TextView) row.findViewById(R.id.task_title);
        eventViewHolder.task_type = (TextView) row.findViewById(R.id.task_type);
        eventViewHolder.task_type_image = (ImageView) row.findViewById(R.id.lesson_type);

        eventViewHolder.from_title = (TextView) row.findViewById(R.id.from_title);
        eventViewHolder.from_time = (TextView) row.findViewById(R.id.from_time);
        eventViewHolder.from_image = (ImageView) row.findViewById(R.id.from_time_icon);

        eventViewHolder.to_title = (TextView) row.findViewById(R.id.to_title);
        eventViewHolder.to_time = (TextView) row.findViewById(R.id.to_time);
        eventViewHolder.to_image = (ImageView) row.findViewById(R.id.to_time_icon);

        eventViewHolder.importance = (TextView) row.findViewById(R.id.importance_percent);
        eventViewHolder.importance_image = (ImageView) row.findViewById(R.id.importance_icon);

        eventViewHolder.interest = (TextView) row.findViewById(R.id.interest_percent);
        eventViewHolder.interest_image = (ImageView) row.findViewById(R.id.interest_icon);
    }

    private static class EventViewHolder
    {
        View type_color;

        TextView task_title;
        TextView task_type;
        ImageView task_type_image;

        TextView from_title;
        TextView from_time;
        ImageView from_image;

        TextView to_title;
        TextView to_time;
        ImageView to_image;

        TextView importance;
        ImageView importance_image;

        TextView interest;
        ImageView interest_image;

        //Header text
        TextView headerText;
    }

    private enum RowType
    {
        HEADER_ITEM, LIST_ITEM
    }
}

Solution

  • A list may take a long time to load for various reasons: size of the data, slow connection to backend server, high computational demand, etc. Often these time-consuming tasks are just going to take some time and there is nothing we can do about it except to try to minimize the impact on the user.

    The goal of doing background processing with AsyncTask, Loader, etc. is to take a time-consuming task and move it off of the thread that runs the user interface (the UI thread) and overlap processing as much as possible so the app does not appear to "hang." So, start the AsyncTask as soon as you can. Depending upon your app, the processing may be completed by the time the user needs to see the resulting data which would be great. If not, then you may have to throw up a ProgressBar to let them know that they will need to wait a little. The onPreExecute(), onProgressUpdate() and onPostExecute() methods of AsyncTask will help you communicate with the user (on the UI thread) about the progress your app is making in getting data. See the documentation.

    Also take a look at this example. The AsyncTask is invoked right after the ListView adapter is set. This is a common place to do this. That would be in your fragment. (getView() is not the right place.)

    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    adpt = new SimpleAdapter(new ArrayList(), this);
    ListView lView = (ListView) findViewById(R.id.listview);
    
    lView.setAdapter(adpt);
    
    // Exec async load task
    (new AsyncListViewLoader()).execute("http://10.0.2.2:8080/JSONServer/rest/ContactWS/get");
    }
    

    If you have a lot of data to show, then you may want to implement some logic to do endless scrolling where you show a page or two of data before fetching more. Just search for "listView endless scrolling" for additional information on this.

    I hope that this helps.