Olá povo,
Depois de quase um mês sem postar, resolvi aproveitar o feriadão para colocar um post colaborativo e que me tirou um trauma. Senta que lá vem história...
Um dia desses, meu amigo André Melo (a.k.a. Deco Balaca) me perguntou como fazia uma ListView com seleção múltipla, e que os itens selecionados ficassem com o background diferente. Quatro anos mexendo com o danado do Android e me enrolei pra fazer isso.
A classe ListView tem uma propriedade
android:listSelector que nos permite modificar o visual dos itens da lista de acordo com o seu estado (eu falei de selector nesse post
aqui).
O problema é que apesar de haver a propriedade
android:state_checked (usado também em componentes como Checkbox e ToggleButton), devemos usar a propriedade
android:state_activated para indicar que um item da lista está checado (#tenso).
Muito bem! Tudo resolvido? Sim, se você não quiser dar suporte a versões anteriores à 3.0 (Honeycomb), caso contrário você terá que fazer as coisas manualmente.
Meu aluno do
TECDAM, Carlos Eduardo Carneiro, me deu essa dica. Abaixo temos um adapter que verifica se a linha está checada, em caso positivo, ele muda o background da View da linha correspondente.
class MultiSelectAdapter extends ArrayAdapter<String>{
public MultiSelectAdapter(
Context context,
int textViewResourceId,
List<String> objects) {
super(context, textViewResourceId, objects);
}
@Override
public View getView(int position,
View convertView, ViewGroup parent) {
View v = super.getView(
position, convertView, parent);
ListView listView = (ListView) parent;
int color = listView.isItemChecked(position) ?
Color.argb(0xFF, 0x31, 0xB6, 0xE7) :
Color.TRANSPARENT;
v.setBackgroundColor(color);
return v;
}
}
Estou usando um ArrayAdapter simples, mas essa abordagem funcionaria para um adapter customizado também, só que você setaria o background do layout que você carregou...
Resolvido esse problema, resolvi aproveitar esse post para mostrar como utilizar um recurso muito utilizado em aplicações Android: o Action Mode. Essa é uma característica da ActionBar do Android que permite exibir opções de menu de acordo com a necessidade.
Se tornou praticamente padrão, excluir múltiplos itens de uma lista dando um clique longo em um dos itens e depois selecionar outros (podemos ver isso no Gmail, galeria de mídia, etc.). No nosso exemplo, vou mostrar como permitir excluir múltiplos itens de uma ListView. Mas você pode estar se perguntando: ActionBar não é só pro Android 3.0 ou superior? Dê uma olhada nesse
link e conheça o Sherlock, pois vamos usá-lo aqui.
O exemplo consta apenas de uma Activity mostrada (uma parte) abaixo:
public class MainActivity
extends SherlockActivity
implements
OnItemClickListener,
OnItemLongClickListener,
ActionMode.Callback {
private ListView listView;
private List<String> nomes;
private ActionMode actionMode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.Theme_Sherlock_Light_DarkActionBar);
nomes = new ArrayList<String>();
nomes.add("nglauber");
nomes.add("CESAR.edu");
nomes.add("CESAR");
nomes.add("Unibratec");
listView = new ListView(this);
listView.setOnItemClickListener(this);
listView.setOnItemLongClickListener(this);
listView.setAdapter(new MultiSelectAdapter(this,
android.R.layout.simple_list_item_1, nomes));
setContentView(listView);
}
@Override
public void onItemClick(
AdapterView<?> adapterView, View view,
int position, long id) {
if (actionMode == null) {
// Faça algo ao clicar no item normalmente
} else {
int checkedCount =
atualizarItensMarcados(listView, position);
if (checkedCount == 0) {
actionMode.finish();
}
}
}
@Override
public boolean onItemLongClick(
AdapterView<?> adapterView, View view,
int position, long id) {
boolean consumed = (actionMode == null);
if (consumed) {
actionMode = startActionMode(this);
listView.setChoiceMode(
ListView.CHOICE_MODE_MULTIPLE);
listView.setItemChecked(position, true);
atualizarItensMarcados(listView, position);
}
return consumed;
}
A nossa classe implementa três interfaces, onde as duas primeiras são bem conhecidas. A última é que vamos detalhar mais no próximo trecho de código. O atributo actionMode é que vai exibir a opção de excluir e mostrar quantos itens estão selecionados. Esse atributo é inicializado no método
onItemLongClick através da chamada do método
startActionMode. Nesse momento, habilitamos a seleção múltipla na ListView e já checamos o item da posição onde clicamos.
Sendo assim, no método
onItemClick, nós verificamos se actionMode é igual a null, nesse caso, não estamos com o ActionMode ativo na ActionBar e devemos processar o clique normalmente. Caso contrário, atualizamos a lista com novo item que foi clicado. Mas se não houver mais itens selecionados, desativamos o ActionMode chamando o método
finish.
Vamos agora ver o código da interface ActionMode.Callback e o método atualizarItensMarcados.
@Override
public boolean onCreateActionMode(
ActionMode mode, Menu menu) {
getSupportMenuInflater().inflate(
R.menu.menu_delete_list, menu);
return true;
}
@Override
public boolean onPrepareActionMode(
ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(
ActionMode mode, MenuItem item) {
if (item.getItemId() == R.id.action_delete) {
SparseBooleanArray checked =
listView.getCheckedItemPositions();
for (int i = checked.size()-1; i >= 0; i--) {
if (checked.valueAt(i)) {
nomes.remove(checked.keyAt(i));
}
}
actionMode.finish();
return true;
}
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
actionMode = null;
listView.clearChoices();
((BaseAdapter) listView.getAdapter())
.notifyDataSetChanged();
listView.setChoiceMode(ListView.CHOICE_MODE_NONE);
}
private int atualizarItensMarcados(
ListView l, int position) {
SparseBooleanArray checked =
l.getCheckedItemPositions();
l.setItemChecked(position,
l.isItemChecked(position));
int checkedCount = 0;
for (int i = 0; i < checked.size(); i++) {
if (checked.valueAt(i)) {
checkedCount++;
}
}
actionMode.setTitle(
checkedCount + " selecionados");
return checkedCount;
}
}
O método
onCreateActionMode permite carregar um arquivo de menu para actionMode, note que ele retorna true para informar que o actionMode pode ser criado. Já o método
onPrepareActionMode é usado para quando queremos atualizar o actionMode após sua criação, retornamos falso para indicar que ele não foi atualizado.
Quando uma opção de menu do ActionMode é selecionada, o método
onActionItemClicked é chamado. Nesse método, checamos se a opção selecionada foi a de excluir, em caso positivo, obtemos a lista das posições da lista que estão marcadas. Removemos essas posições da lista e depois finalizamos o ActionMode.
Ao chamar o método finish do ActionMode, o método onDestroyActionMode é chamado. Nesse momento, setamos o atributo actionMode para null, desmarcamos os itens (caso haja algum), chamamos o
notifyDatasetChanged para que a ListView seja redesenhada, e por fim, voltamos o tipo de seleção da lista como nenhum.
O método atualizarItensMarcados apenas retorna a quantidade de itens que estão checados e seta o título da actionMode.
O arquivo de menu utilizado no método onCreateActionMode é mostrado abaixo:
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_delete"
android:icon="@android:drawable/ic_menu_delete"
android:orderInCategory="100"
android:showAsAction="ifRoom"/>
</menu>
E o resultado, podemos ver na figura a seguir:
Qualquer dúvida, deixem seus comentários.
4br4ç05,
nglauber