问题
I'm having a problem when my ListView has more itens that can appear on the screen; in other words, when it gets scroll. The problem is, when I click in one of the toggle buttons, it's change the visibility of the ImageView. However, when I click on it, it is changing the visibility of more than the respective clicked.
I'm using an adapter to display the list itens.
I added the code below:
public class CriteriosAdapter extends ArrayAdapter<Criterio> {
private Context context;
public CriteriosAdapter(Context context, List<Criterio> objects) {
super(context, 0, objects);
this.context = context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final Criterio criterio = getItem(position);
final CriterioViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.fragment_row, parent, false);
viewHolder = new CriterioViewHolder();
viewHolder.txtCriterio = (TextView)convertView.findViewById(R.id.txtCriterio);
viewHolder.tgIrregular = (ToggleButton)convertView.findViewById(R.id.tgIrregular);
viewHolder.btnCam = (ImageView) convertView.findViewById(R.id.btnCam);
convertView.setTag(viewHolder);
}
else {
viewHolder = (CriterioViewHolder)convertView.getTag();
}
viewHolder.txtCriterio.setText(criterio.nome);
viewHolder.txtCriterio.setTextColor(context.getColor(R.color.white));
viewHolder.tgIrregular.setChecked(false);
viewHolder.tgIrregular.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (viewHolder.tgIrregular.isChecked()) {
viewHolder.btnCam.setVisibility(View.VISIBLE);
} else {
viewHolder.btnCam.setVisibility(View.INVISIBLE);
}
}
});
viewHolder.btnCam.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
FragmentActivity activity = (FragmentActivity)(context);
android.support.v4.app.FragmentManager fm = activity.getSupportFragmentManager();
FotosFragment alertDialog = new FotosFragment();
alertDialog.show(fm, "fragment_alert");
}
});
return convertView;
}
class CriterioViewHolder {
TextView txtCriterio;
ToggleButton tgIrregular;
ImageView btnCam;
}
}
Could someone help me with it?
Thank you.
EDIT
I added everything as you describe. However, I use that adapter in 5 fragments, if I go to another, and come back or scroll the list, it still lost the value.
I used criterio.getHash, because it is unique for all. And position, can repeat in other fragment, and make it weird.
public class CriteriosAdapter extends ArrayAdapter<Criterio> {
private Context context;
public static Map<String, Criterio> irregularidades = new HashMap<String, Criterio>();
HashMap<String, Boolean> toggleButtonStateTracker = new HashMap<>();
public CriteriosAdapter(Context context, List<Criterio> objects) {
super(context, 0, objects);
this.context = context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final Criterio criterio = getItem(position);
final CriterioViewHolder viewHolder;
if (!toggleButtonStateTracker.containsKey(criterio.getHash())){
toggleButtonStateTracker.put(criterio.getHash(),false);
}
Log.e("Toggle Track:", toggleButtonStateTracker.toString());
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.fragment_row, parent, false);
viewHolder = new CriterioViewHolder();
viewHolder.txtCriterio = (TextView)convertView.findViewById(R.id.txtCriterio);
viewHolder.tgIrregular = (ToggleButton)convertView.findViewById(R.id.tgIrregular);
viewHolder.btnCam = (ImageView) convertView.findViewById(R.id.btnCam);
convertView.setTag(viewHolder);
}
else {
viewHolder = (CriterioViewHolder)convertView.getTag();
}
viewHolder.txtCriterio.setText(criterio.nome);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
viewHolder.txtCriterio.setTextColor(context.getColor(R.color.white));
} else {
viewHolder.txtCriterio.setTextColor(context.getResources().getColor(R.color.white));
}
viewHolder.tgIrregular.setId(criterio.id);
final boolean isChecked = toggleButtonStateTracker.get(criterio.getHash());
viewHolder.tgIrregular.setChecked(isChecked);
viewHolder.btnCam.setTag(criterio.hash);
if (isChecked) {
viewHolder.btnCam.setVisibility(View.VISIBLE);
} else {
viewHolder.btnCam.setVisibility(View.INVISIBLE);
}
viewHolder.tgIrregular.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
toggleButtonStateTracker.put(criterio.getHash(), isChecked);
if (isChecked) {
viewHolder.btnCam.setVisibility(View.VISIBLE);
irregularidades.put(criterio.getHash(), criterio);
} else {
viewHolder.btnCam.setVisibility(View.INVISIBLE);
irregularidades.remove(criterio.getHash());
}
}
});
viewHolder.btnCam.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
FragmentActivity activity = (FragmentActivity) (context);
android.support.v4.app.FragmentManager fm = activity.getSupportFragmentManager();
FotosFragment alertDialog = new FotosFragment();
Bundle args = new Bundle();
args.putString("criterioTag", criterio.getHash());
alertDialog.setArguments(args);
alertDialog.setCancelable(false);
alertDialog.show(fm, "fragment_alert");
}
});
return convertView;
}
class CriterioViewHolder {
TextView txtCriterio;
ToggleButton tgIrregular;
ImageView btnCam;
}
}
回答1:
Your issue is due to the recycling of Views in ListView
(and RecyclerView
) The following happens to you:
- You set visibility of an
ImageView
in one row to invisible with yourToggleButton
- Your row gets scrolled out of sight and therefore gets recycled.
- The recycled view still has an
ImageView
with visilibility set toView.INVISIBLE
- You assign your new values to the row but don't change the visibility
To solve this you should have a List
, HashMap
or anything similar and track which ToggleButton
is checked and which ImageView
is visible. (You can do that with the position)
Then in your public View getView(int position, View convertView, ViewGroup parent)
method you check if this posision's (rows) image should be visible or not and set it accordingly with viewHolder.btnCam.setVisibility()
and the ToggleButton
as well.
EDIT
Add this member variable to your adapterHashMap<Integer,Boolean> toggleButtonStateTracker = new HashMap<>;
In your getView add this
if (! toggleButtonStateTracker.containsKey(position)){
// Now the HashMap definitely contains the key
toggleButtonStateTracker.put(position,false);
}
boolean isChecked = toggleButtonStateTracker.get(position);
viewHolder.tgIrregular.setChecked(isChecked);
if (isChecked){
// if your toggle Button is checked, the btnCam should be invisible
viewHolder.btnCam.setVisibility(View.INVISIBLE);
} else {
viewHolder.btnCam.setVisibility(View.VISIBLE);
}
And finally add this tgIrregular
viewHolder.tgIrregular.setOnCheckedChangedListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
toggleButtonStateTracker.put(getAdapterPosition, isChecked);
}
});
来源:https://stackoverflow.com/questions/34597991/android-listview-with-toggle-button