Edit
As the intention was to make a line selection algorithm, the previous solution did not fully meet, making the new solution simpler.
In the new solution, the Adapter
has a SparseArray
(could be a HashMap
, but the SparseArray
is more recommending on the Android platform).
For each position, the SparseArray
or save 1 for the position if it is marked or 0 otherwise.
As a request, the second click unchecks the item, such as a checkbox.
public class TestBaseAdapter extends SimpleAdapter {
// Armazena as linhas que possuem cliques, para marcação
SparseArray<Integer> mCliques = new SparseArray<Integer>();
// Usado para adicionar um valor a uma View, no metodo setTag
int mResId;
public TestBaseAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) {
super(context, data, resource, from, to);
mResId = resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
updateBackground(view, position);
// Guardo na View, sua posicao
// Facilita ao fazer a limpeza manual do background caso seja necessario
view.setTag(mResId, position);
return view;
}
/***
* Atualiza o background da View conforme a regra de cliques
* @param view
* @param position
*/
void updateBackground(View view, int position) {
// Se houve nao tem clique, entao remove o background
if(mCliques.get(position, 0) == 0) {
view.setBackgroundResource(0);
} else { // Adiciona o background em caso positivo
view.setBackgroundResource(R.color.primary);
}
}
/***
* Atualiza a ultima posicao clicada e o numero de cliques.
* E atualiza o Background da view conforme o resultado
* @param adapterView
* @param view
* @param position
*/
public void updateClick(AdapterView adapterView, View view, int position) {
// Se a posicao foi clicada e ja estava marcada, remove a marcação
if(mCliques.get(position, -1) != -1) {
mCliques.remove(position);
} else { // Adiciona a marcação
mCliques.put(position, 1);
}
// Podemos usar o "notifyDataSetChanged", com isso todas as Views
// que estao visiveis serao reconstruidas
// notifyDataSetChanged();
// Ou podemos atualizar manualmente as visiveis, as demais
// serao construídas pelo Adapter
updateVisibleViews(adapterView);
}
void updateVisibleViews(AdapterView adapterView) {
for(int i = 0, childCount = adapterView.getChildCount(); i < childCount; ++i) {
View view = adapterView.getChildAt(i);
int position = (Integer) view.getTag(mResId);
updateBackground(view, position);
}
}
/***
* Retorna a lista dos indices dos itens selecionados
* @return
*/
public ArrayList<Integer> getSelectedItems() {
ArrayList<Integer> selecao = new ArrayList<Integer>(mCliques.size());
for(int i = 0, size = mCliques.size(); i < size; ++i) {
selecao.add(mCliques.keyAt(i));
}
return selecao;
}
}
To accomplish this task, it is necessary to create a subclass of the SimpleAdapter
, because we need to incorporate the "coloring" rule into the Adapter
, making it simpler and giving responsibility to the right element.
I made an example of how it can be done, my solution is just a basis, just adapting to your specific case.
Since I don’t have the same data as you, I had to improvise:
This is the construction of Adapter
and the data of ListView
in my Activity
:
final ArrayList<HashMap<String, String>> songsListData = new ArrayList<HashMap<String, String>>();
// Construindo dados fake, apenas para ter um ListView cheio
for (int i = 0; i < 100; i++) {
// creating new HashMap
HashMap<String, String> song = new HashMap<String, String>();
song.put("NUMERO", "" + i);
songsListData.add(song);
}
ListView lv = (ListView) view.findViewById(R.id.fpa_list);
// Crio o meu Adapter customizado, que herda de SimpleAdapter
mAdapter = new TestBaseAdapter(getActivity(), songsListData, R.layout.playlist_item, new String[]{"NUMERO"}, new int[] {android.R.id.text1});
lv.setAdapter(mAdapter);
// Seto um OnItemClickListener apenas para delegar ao Adapter a atualizacao
// do ultimo item clicado e do numero de cliques
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mAdapter.updateClick(parent, view, position);
}
});
Now comes the SimpleAdapter
I did to apply the click rule:
public class TestBaseAdapter extends SimpleAdapter {
// Armazena a quantidade de clicks em uma determinada linha e sua posicao
int mUltimaPosicaoClicada = -1, mNumeroCliques = 0;
// Usado para adicionar um valor a uma View, no metodo setTag
int mResId;
public TestBaseAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) {
super(context, data, resource, from, to);
mResId = resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
updateBackground(view, position);
// Guardo na View, sua posicao
// Facilita ao fazer a limpeza manual do background caso seja necessario
view.setTag(mResId, position);
return view;
}
/***
* Atualiza o background da View conforme a regra de cliques
* @param view
* @param position
*/
void updateBackground(View view, int position) {
if(position == mUltimaPosicaoClicada) {
switch (mNumeroCliques) {
case 0:
view.setBackgroundResource(0);
case 1:
view.setBackgroundResource(R.color.primary);
break;
case 2:
default:
view.setBackgroundResource(R.color.black_87p);
break;
}
} else {
view.setBackgroundResource(0);
}
}
/***
* Atualiza a ultima posicao clicada e o numero de cliques.
* E atualiza o Background da view conforme o resultado
* @param adapterView
* @param view
* @param position
*/
public void updateClick(AdapterView adapterView, View view, int position) {
// Logica para guardar o numero de cliques e o ultimo item clicado
if(mUltimaPosicaoClicada == position) {
mNumeroCliques++;
} else {
mUltimaPosicaoClicada = position;
mNumeroCliques = 1;
}
// Podemos usar o "notifyDataSetChanged", com isso todas as Views
// que estao visiveis serao reconstruidas
// notifyDataSetChanged();
// Ou podemos atualizar manualmente as visiveis, as demais
// serao construídas pelo Adapter
updateVisibleViews(adapterView);
}
void updateVisibleViews(AdapterView adapterView) {
for(int i = 0, childCount = adapterView.getChildCount(); i < childCount; ++i) {
View view = adapterView.getChildAt(i);
int position = (Integer) view.getTag(mResId);
updateBackground(view, position);
}
}
}
I would like to make a comment:
In the method updateClick
of TestAdapter
, I left the update of the items can be done in two ways:
- Use the
notifyDataSetChanged
to force the update of all View's
visible by the ListView
. This will force the method getView
be called again for all visible items. It may not be a good alternative if the construction of the items is complex, causing an "overhead".
- Manually update the
View's
that are visible. This is much more performative, but it involves adding extra logic. In this case I added the position of the item as a tag in the View.setTag
within the method getView
, to make it easy to update later in the method updateVisibleViews
in the TestAdapter
.
Diogo, I think that kind of logic needs to go through
Adapter
(give toAdapter
the responsibility to "paint" the lines when clicked). I say this because the "recycling algorithm" of `Listview is the cause of this problem, of elements that you didn’t click are color when they shouldn’t. I’ll try to put together a code to help you, but it’s gonna get a little big.– Wakim
blz put Adapter to help you...
– Diogo Odelli