Como posso criar uma lista em que, quando você chega ao final da lista, sou notificado para carregar mais itens?
Como posso criar uma lista em que, quando você chega ao final da lista, sou notificado para carregar mais itens?
Respostas:
Uma solução é implementar OnScrollListener
e fazer alterações (como adicionar itens etc.) ao ListAdapter
estado conveniente em seu onScroll
método.
A seguir, é ListActivity
exibida uma lista de números inteiros, começando com 40, adicionando itens quando o usuário rola para o final da lista.
public class Test extends ListActivity implements OnScrollListener {
Aleph0 adapter = new Aleph0();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(adapter);
getListView().setOnScrollListener(this);
}
public void onScroll(AbsListView view,
int firstVisible, int visibleCount, int totalCount) {
boolean loadMore = /* maybe add a padding */
firstVisible + visibleCount >= totalCount;
if(loadMore) {
adapter.count += visibleCount; // or any other amount
adapter.notifyDataSetChanged();
}
}
public void onScrollStateChanged(AbsListView v, int s) { }
class Aleph0 extends BaseAdapter {
int count = 40; /* starting amount */
public int getCount() { return count; }
public Object getItem(int pos) { return pos; }
public long getItemId(int pos) { return pos; }
public View getView(int pos, View v, ViewGroup p) {
TextView view = new TextView(Test.this);
view.setText("entry " + pos);
return view;
}
}
}
Obviamente, você deve usar threads separados para ações de longa duração (como carregar dados da Web) e pode indicar progresso no último item da lista (como o mercado ou os aplicativos do Gmail).
firstVisible + visibleCount >= totalCount
é atendida várias vezes com várias chamadas para o ouvinte. Se a função carregar mais for uma solicitação da Web (provavelmente será), adicione outra verificação para verificar se uma solicitação está em andamento ou não. Por outro lado, verifique se o totalCount não é igual à contagem total anterior, porque não precisamos de várias solicitações para o mesmo número de itens na lista.
Só queria contribuir com uma solução que eu usei para o meu aplicativo.
Também é baseado na OnScrollListener
interface, mas achei que ele tinha um desempenho de rolagem muito melhor em dispositivos low-end, pois nenhum dos cálculos de contagem total / visível é realizado durante as operações de rolagem.
ListFragment
ou ListActivity
implementeOnScrollListener
Adicione os seguintes métodos a essa classe:
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
//leave this empty
}
@Override
public void onScrollStateChanged(AbsListView listView, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
if (listView.getLastVisiblePosition() >= listView.getCount() - 1 - threshold) {
currentPage++;
//load more list items:
loadElements(currentPage);
}
}
}
onde currentPage
é a página da sua fonte de dados que deve ser adicionada à sua lista e threshold
o número de itens da lista (contados no final) que, se visíveis, acionam o processo de carregamento. Se você definir threshold
a 0
, por exemplo, o usuário tem que rolar até o fim da lista, a fim de carregar mais itens.
(opcional) Como você pode ver, a "verificação de carregar mais" é chamada apenas quando o usuário para de rolar. Para melhorar a usabilidade, você pode aumentar e adicionar um indicador de carregamento ao final da lista via listView.addFooterView(yourFooterView)
. Um exemplo para essa visualização de rodapé:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/footer_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="10dp" >
<ProgressBar
android:id="@+id/progressBar1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/progressBar1"
android:padding="5dp"
android:text="@string/loading_text" />
</RelativeLayout>
(opcional) Por fim, remova esse indicador de carregamento chamando listView.removeFooterView(yourFooterView)
se não houver mais itens ou páginas.
ListFragment
sem problemas - basta seguir cada uma das etapas acima e você ficará bem;) Ou você poderia fornecer alguma mensagem de erro?
getListView().setOnScrollListener(this)
Você pode detectar o final da lista com a ajuda de onScrollListener , o código de trabalho é apresentado abaixo:
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (view.getAdapter() != null && ((firstVisibleItem + visibleItemCount) >= totalItemCount) && totalItemCount != mPrevTotalItemCount) {
Log.v(TAG, "onListEnd, extending list");
mPrevTotalItemCount = totalItemCount;
mAdapter.addMoreData();
}
}
Outra maneira de fazer isso (adaptador interno) é a seguinte:
public View getView(int pos, View v, ViewGroup p) {
if(pos==getCount()-1){
addMoreData(); //should be asynctask or thread
}
return view;
}
Esteja ciente de que esse método será chamado várias vezes; portanto, você precisa adicionar outra condição para bloquear várias chamadas de addMoreData()
.
Ao adicionar todos os elementos à lista, ligue para notifyDataSetChanged () dentro do seu adaptador para atualizar o View (ele deve ser executado no encadeamento da UI - runOnUiThread )
getView
. Por exemplo, você não tem garantia de que pos
é visualizado pelo usuário. Pode ser simplesmente material de medição do ListView.
Na Ognyan Bankov GitHub
eu encontrei uma solução simples e funcional!
Ele faz uso do Volley HTTP library
que torna a rede para aplicativos Android mais fácil e, o mais importante, mais rápida. O Volley está disponível no repositório AOSP aberto.
O código fornecido demonstra:
Para uma consistência futura, peguei o repo do Bankov .
Aqui está uma solução que também facilita a exibição de uma exibição de carregamento no final do ListView durante o carregamento.
Você pode ver as aulas aqui:
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingIndicatorHelper.java - Auxiliar para tornar possível o uso do recursos sem estender-se de SimpleListViewWithLoadingIndicator.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.java - Ouvinte que inicia o carregamento de dados quando o usuário está prestes a chegar ao final do ListView.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.java - The EndlessListView. Você pode usar essa classe diretamente ou estender a partir dela.
Pode ser um pouco tarde, mas a seguinte solução aconteceu muito útil no meu caso. De certa forma, tudo o que você precisa fazer é adicionar ao seu ListView a Footer
e criar para ele addOnLayoutChangeListener
.
http://developer.android.com/reference/android/widget/ListView.html#addFooterView(android.view.View)
Por exemplo:
ListView listView1 = (ListView) v.findViewById(R.id.dialogsList); // Your listView
View loadMoreView = getActivity().getLayoutInflater().inflate(R.layout.list_load_more, null); // Getting your layout of FooterView, which will always be at the bottom of your listview. E.g. you may place on it the ProgressBar or leave it empty-layout.
listView1.addFooterView(loadMoreView); // Adding your View to your listview
...
loadMoreView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
Log.d("Hey!", "Your list has reached bottom");
}
});
Este evento é acionado uma vez quando um rodapé se torna visível e funciona como um encanto.
A chave desse problema é detectar o evento carregar mais, iniciar uma solicitação assíncrona de dados e atualizar a lista. Também é necessário um adaptador com indicador de carga e outros decoradores. De fato, o problema é muito complicado em alguns casos extremos. Apenas uma OnScrollListener
implementação não é suficiente, porque às vezes os itens não preenchem a tela.
Eu escrevi um pacote pessoal que suporta uma lista sem fim RecyclerView
e também fornece uma implementação de carregador assíncrono AutoPagerFragment
que facilita muito a obtenção de dados de uma fonte de várias páginas. Ele pode carregar qualquer página desejada em um RecyclerView
evento personalizado, não apenas na próxima página.
Aqui está o endereço: https://github.com/SphiaTower/AutoPagerRecyclerManager
A melhor solução até agora que eu vi está na biblioteca FastAdapter para visualizações de recicladores. Tem um EndlessRecyclerOnScrollListener
.
Aqui está um exemplo de uso: EndlessScrollListActivity
Depois que o usei para uma lista de rolagem interminável, percebi que a configuração é muito robusta. Eu definitivamente recomendo.
Eu tenho trabalhado em outra solução muito semelhante a isso, mas estou usando a footerView
para dar ao usuário a possibilidade de baixar mais elementos clicando em footerView
, estou usando um "menu" que é mostrado acima ListView
e na parte inferior do Na visualização principal, esse "menu" oculta a parte inferior do menu ListView
. Assim, quando o listView
menu está rolando desaparece e quando o estado da rolagem está ocioso, o menu aparece novamente, mas quando o usuário rola para o final do menu listView
, eu "peço" para saber se o footerView
é mostrado nesse caso, o menu não aparece e o usuário pode ver o footerView
para carregar mais conteúdo. Aqui o código:
Saudações.
listView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
if(scrollState == SCROLL_STATE_IDLE) {
if(footerView.isShown()) {
bottomView.setVisibility(LinearLayout.INVISIBLE);
} else {
bottomView.setVisibility(LinearLayout.VISIBLE);
} else {
bottomView.setVisibility(LinearLayout.INVISIBLE);
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
}
});
Eu sei que é uma pergunta antiga e o mundo do Android mudou para o RecyclerViews, mas para qualquer pessoa interessada, você pode achar esta biblioteca muito interessante.
Ele usa o BaseAdapter usado com o ListView para detectar quando a lista foi rolada para o último item ou quando está sendo rolada para longe do último item.
Ele vem com um exemplo de projeto (apenas 100 linhas de código de atividade) que pode ser usado para entender rapidamente como funciona.
Uso simples:
class Boy{
private String name;
private double height;
private int age;
//Other code
}
Um adaptador para armazenar objetos Boy seria semelhante a:
public class BoysAdapter extends EndlessAdapter<Boy>{
ViewHolder holder = null;
if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(parent
.getContext());
holder = new ViewHolder();
convertView = inflater.inflate(
R.layout.list_cell, parent, false);
holder.nameView = convertView.findViewById(R.id.cell);
// minimize the default image.
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Boy boy = getItem(position);
try {
holder.nameView.setText(boy.getName());
///Other data rendering codes.
} catch (Exception e) {
e.printStackTrace();
}
return super.getView(position,convertView,parent);
}
Observe como o método getView do BoysAdapter retorna uma chamada para o getView
método da superclasse EndlessAdapter . Isso é 100% essencial.
Agora, para criar o adaptador, faça:
adapter = new ModelAdapter() {
@Override
public void onScrollToBottom(int bottomIndex, boolean moreItemsCouldBeAvailable) {
if (moreItemsCouldBeAvailable) {
makeYourServerCallForMoreItems();
} else {
if (loadMore.getVisibility() != View.VISIBLE) {
loadMore.setVisibility(View.VISIBLE);
}
}
}
@Override
public void onScrollAwayFromBottom(int currentIndex) {
loadMore.setVisibility(View.GONE);
}
@Override
public void onFinishedLoading(boolean moreItemsReceived) {
if (!moreItemsReceived) {
loadMore.setVisibility(View.VISIBLE);
}
}
};
O loadMore
item é um botão ou outro elemento da interface do usuário que pode ser clicado para buscar mais dados do URL. Quando colocado conforme descrito no código, o adaptador sabe exatamente quando mostrar esse botão e quando desativá-lo. Basta criar o botão no seu xml e colocá-lo como mostrado no código do adaptador acima.
Aproveitar.