Android — асинхронная задача, которая заполняет адаптер для просмотра списка

В основном процесс выглядит следующим образом: когда создается фрагмент (или когда пользователь проводит пальцем, чтобы обновить макет), выполняется AsyncTask. ASyncTask извлекает информацию из URL-адреса и настраивает адаптер, который затем назначается ListView.

Это код Фрагмента:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                     Bundle savedInstanceState) {

    View rootView = inflater.inflate(R.layout.tasks_page, container, false);
    mSwipeRefreshLayout = (SwipeRefreshLayout)
    rootView.findViewById(R.id.list_container);
    mSwipeRefreshLayout.setOnRefreshListener(this);
    mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,    
    android.R.color.holo_green_light, android.R.color.holo_red_light);

    lv = (ListView) rootView.findViewById(android.R.id.list);
    getTasks();

    return rootView;
}

private void getTasks() {
    new TasksRetriever(mSwipeRefreshLayout,tasksAdapter,lv).execute();
}

@Override
public void onRefresh() {
    mSwipeRefreshLayout.setRefreshing(true);
    new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    getTasks();
                }
            }, 1000);
}

Конструктор TaskRetriever:

public TasksRetriever(SwipeRefreshLayout srl, TasksAdapter ta, ListView lv) {
    adpt = ta;
    this.srl = srl;
    this.lv = lv;
}

и postExecute:

@Override
protected void onPostExecute(List<TasksItem> result) {
    super.onPostExecute(result);
    dialog.dismiss();
    if (adpt == null) adpt = new TasksAdapter(result);
    else adpt.setItemList(result);
    lv.setAdapter(adpt);
    adpt.notifyDataSetChanged();
    srl.setRefreshing(false);
}

Пока не уверен, работает ли это, но мне было интересно, на правильном ли я пути, потому что мне это не кажется чистым. С другой стороны, я не могу создать адаптер и назначить его для ListView, пока у меня действительно не будет данных для него... Это нормально или я делаю это неправильно?


comment
вы МОЖЕТЕ создать пустой адаптер и заполнить его позже - в AsyncTask   -  person injecteer    schedule 27.01.2015
comment
Ты на правильном пути. Если вы всегда загружаете новый список (вместо добавления itens) в адаптер, вы можете создать новый адаптер и установить его в виде списка (кстати, вы можете получить адаптер с помощью listView.getAdapter() ). Я также предлагаю вам добавить progressDialog в TasksRetriever, потому что, если пользователь прикасается к устройству или управляет им во время загрузки списка, это может привести к зависанию пользовательского интерфейса или нежелательному поведению.   -  person sagits    schedule 27.01.2015
comment
адаптер открытого класса TasksAdapter расширяет ArrayAdapter‹TasksItem›, поэтому я сделал super(Utils.context,android.R.layout.simple_list_item_1, itemList); itemList содержит список — могу ли я передать null? а может мне и не надо Супер?   -  person Amos    schedule 27.01.2015
comment
Таким образом, передача всех трех объектов (макета, адаптера, списка) в асинхронную задачу является правильным способом, и мне просто нужно реорганизовать код, как это предлагается здесь?   -  person Amos    schedule 27.01.2015


Ответы (2)


Я бы установил адаптер списка в методе onCreate() в вашем представлении списка и сделал его переменной экземпляра (а не локальной), чтобы задача Async могла получить к ней доступ следующим образом, т.е.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                     Bundle savedInstanceState) {

    lv = (ListView) rootView.findViewById(android.R.id.list);
    mAdpt = new TasksAdapter(null);
    lv.setAdapter(mAdpt);

}

А затем в вашем onPostExecute()

@Override
protected void onPostExecute(List<TasksItem> result) {
    dialog.dismiss();  
    mAdpt.clear();
    for( TaskItem ti : result ) 
    {
      mAdpt.add( ti );
    }
    mAdpt.notifyDataSetChanged();
    srl.setRefreshing(false);
}

Также убедитесь, что вы учитываете нулевое значение, передаваемое вашему адаптеру в методе getView().

Надеюсь это поможет.

person Andy B    schedule 27.01.2015
comment
Просто примечание, mAdpt.clear должно быть mAdpt.clear(); - person Marcus; 27.01.2015

вы МОЖЕТЕ создать пустой адаптер и заполнить его позже - в AsyncTask

ваш onPostExecute() должен выглядеть так:

@Override
protected void onPostExecute(List<TasksItem> result) {
    dialog.dismiss();
    if (adpt == null){
      adpt = new TasksAdapter(result);
      lv.setAdapter(adpt);
    }else{
      adpt.clear();
    }
    for( TaskItem ti : result ) adpt.add( ti );
    adpt.notifyDataSetChanged();
    srl.setRefreshing(false);
}
person injecteer    schedule 27.01.2015
comment
в адаптере у меня есть public void setItemList(List‹TasksItem› itemList) { this.itemList = itemList; } - Я думаю, что использую adpt.clear; и adpt.add() лучше кодирует или нет большой разницы? - person Amos; 27.01.2015
comment
@Amos есть явная разница в распределении ресурсов. Вы должны создать adapter только один раз и при необходимости изменить базовый набор данных. Создание нового Adapter — тяжелая работа. Поэтому я проголосовал за ответ Энди Б. - person Marcus; 27.01.2015
comment
вы можете делать что хотите со списком поддержки :) проблема в том, что изменения не отражаются в listview, пока вы не вызовете notifyDataSetChanged(). Единственное место, куда можно позвонить, это onPostExcecute() - person injecteer; 27.01.2015
comment
Спасибо, я хотел знать, что пишу хороший/чистый код, а не просто рабочий (что легко :)) - person Amos; 27.01.2015