I have a survey with 2 types of answers:
1)Open (checkbox or radio button) + (user may input text)
2)Ordinary (checkbox or radio button)
When I have a large list like 20+ elements ListView shows wrong type of answer.
I guess that the problem is with getItem(), because the type of the answer depends on it's data.
I have tried many ways to resolve the problem, could not even mention all stackoverflow links.
Maybe, somebody may help me.
public class AnswerAdapter extends ArrayAdapter<Answer> {
public interface ActionsListener {
void onCheckClicked(View v, Answer answer, int position);
void onRadioClicked(View v, Answer answer, int position);
void onOpenClicked(View v, Answer answer, int position);
}
private Display mDisplay;
private LayoutInflater mInflater;
private Context mContext;
private ActionsListener mActionsListener;
private String language;
private ArrayList<Answer> mList;
public AnswerAdapter(Context context, ArrayList<Answer> list, String language, ActionsListener actionsListener) {
super(context, R.layout.item_answer, list);
mList = list;
mInflater = LayoutInflater.from(context);
mContext = context;
mActionsListener = actionsListener;
this.language = language;
mDisplay = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
}
#Nullable
#Override
public Answer getItem(int position) {
return super.getItem(this.getPosition(mList.get(position)));
}
#Override
public View getView(final int position, final View convertView,
ViewGroup parent) {
ViewHolder holder;
View row = convertView;
if (convertView == null) {
row = mInflater.inflate(R.layout.item_answer, parent, false);
holder = new ViewHolder();
holder.answerTitle = (TextView) row.findViewById(R.id.answer_title);
holder.answerRadio = (AppCompatRadioButton) row.findViewById(R.id.answer_radio);
holder.answerCheck = (AppCompatCheckBox) row.findViewById(R.id.answer_check);
holder.answerPicture = (ImageView) row.findViewById(R.id.answer_picture);
holder.answerOpen = (EditText) row.findViewById(R.id.answer_open);
holder.answerRank = (Button) row.findViewById(R.id.answer_rank);
row.setTag(holder);
} else
holder = (ViewHolder) row.getTag();
final Answer answer = getItem(position);
final ViewHolder viewHolderFinal = holder;
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.answer_check:
mActionsListener.onCheckClicked(v, answer, position);
notifyDataSetChanged();
break;
case R.id.answer_radio:
mActionsListener.onRadioClicked(v, answer, position);
notifyDataSetChanged();
break;
case R.id.answer_title:
case R.id.answer_picture:
if (viewHolderFinal.answerCheck.isEnabled() && viewHolderFinal.answerRadio.isEnabled())
if (answer.getPolyAnswer()) {
viewHolderFinal.answerCheck.setChecked(!viewHolderFinal.answerCheck.isChecked());
onClick(viewHolderFinal.answerCheck);
} else {
viewHolderFinal.answerRadio.setChecked(true);
onClick(viewHolderFinal.answerRadio);
}
break;
case R.id.answer_open:
mActionsListener.onOpenClicked(v, answer, position);
if (viewHolderFinal.answerCheck.isEnabled() && viewHolderFinal.answerRadio.isEnabled()) {
if (answer.getPolyAnswer()) {
onClick(viewHolderFinal.answerCheck);
} else {
onClick(viewHolderFinal.answerRadio);
}
}
break;
}
}
};
holder.answerTitle.setOnClickListener(onClickListener);
holder.answerPicture.setOnClickListener(onClickListener);
holder.answerOpen.setOnClickListener(onClickListener);
holder.answerOpen.setHint(language); //language[6]
holder.answerTitle.setText(answer.getTitle());
holder.answerTitle.setTextColor(ContextCompat.getColor(mContext, R.color.colorUIWhite));
if (answer.getPolyAnswer()) {
holder.answerCheck.setVisibility(View.VISIBLE);
holder.answerCheck.setChecked(answer.getCheck());
holder.answerCheck.setEnabled(answer.getEnabled());
holder.answerCheck.setOnClickListener(onClickListener);
} else {
holder.answerRadio.setVisibility(View.VISIBLE);
holder.answerRadio.setChecked(answer.getCheck());
holder.answerRadio.setOnClickListener(onClickListener);
}
if (answer.getIsOpenAnswer()) {
holder.answerOpen.setVisibility(View.VISIBLE);
holder.answerOpen.setText(answer.getOpenAnswer());
holder.answerOpen.setTextColor(ContextCompat.getColor(mContext, R.color.colorUIWhite));
holder.answerOpen.setHintTextColor(ContextCompat.getColor(mContext, R.color.colorUIWhite));
holder.answerOpen.setError(answer.getOpenAnswerError());
}
return row;
}
private static class ViewHolder {
TextView answerTitle;
AppCompatRadioButton answerRadio;
AppCompatCheckBox answerCheck;
ImageView answerPicture;
EditText answerOpen;
Button answerRank;
}
}
Looking at your getView() method, I'd assume you also end up with situations where both your radio and check show as visible at the same time as well.
Because getView() can recycle the view (when convertView is not null), any showing elements retain their visibility.
So you should add an else to your answer.getIsOpenAnswer() where when it is not you set answerOpen's visibility to INVISIBLE or GONE (depending on your design).
In your if statement for answer.getPolyAnswer() you need to probably set answerRadio to INVISIBLE or GONE and answerCheck to INVISIBLE or GONE in your else as well.
Related
I have lisview it contain radioButton and 6 textView. I want to allow check only one item radio button at a time. Default, first item radio button checked when page open. If i choose any item automatically uncheck previous checked item..
Please anyone help me!
Code below :
AdapterClass:
public class AddressCustomAdapter extends BaseAdapter implements ListAdapter {
private final Context context;
private final ArrayList<AddressResult> itemsArrayList;
public AddressCustomAdapter(Context context, ArrayList<AddressResult> itemsArrayList) {
this.context = context;
this.itemsArrayList = itemsArrayList;
}
#Override
public int getCount() {
return itemsArrayList.size();
}
#Override
public Object getItem(int position) {
return itemsArrayList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView;
final ViewHolder holder;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) ((Activity)context).getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.address_list,null);
holder = new ViewHolder();
holder.addrTypeTxt = (TextView)view.findViewById(R.id.addr);
holder.line1Txt = (TextView)view.findViewById(R.id.addrLine1Txt);
holder.line2Txt = (TextView)view.findViewById(R.id.addrLine2Txt);
holder.areaTxt = (TextView)view.findViewById(R.id.addrAreaTxt);
holder.cityTxt = (TextView)view.findViewById(R.id.addrCityTxt);
holder.pincodeTxt = (TextView)view.findViewById(R.id.addrPincodeTxt);
holder.radioButton = (RadioButton) view.findViewById(R.id.radioBtn);
view.setTag(holder);
}else{
holder=(ViewHolder)view.getTag();
}
if(Utility.isNotNull(itemsArrayList.get(position).type)){
holder.addrTypeTxt.setText(itemsArrayList.get(position).type);
}else{
Utility.textViewDisable(holder.addrTypeTxt);
}
if(Utility.isNotNull(itemsArrayList.get(position).line1)){
holder.line1Txt.setText(itemsArrayList.get(position).line1);
}else{
Utility.textViewDisable(holder.line1Txt);
}
if(Utility.isNotNull(itemsArrayList.get(position).line2)){
holder.line2Txt.setText(itemsArrayList.get(position).line2);
}else{
Utility.textViewDisable(holder.line2Txt);
}
if(Utility.isNotNull(itemsArrayList.get(position).area)){
holder.areaTxt.setText(itemsArrayList.get(position).area);
}else{
Utility.textViewDisable(holder.areaTxt);
}
if(Utility.isNotNull(itemsArrayList.get(position).city)){
holder.cityTxt.setText(itemsArrayList.get(position).city+"-");
}else{
Utility.textViewDisable(holder.cityTxt);
}
if(Utility.isNotNull(itemsArrayList.get(position).pincode)){
holder.pincodeTxt.setText(itemsArrayList.get(position).pincode);
}else{
Utility.textViewDisable(holder.pincodeTxt);
}
return view;
}
public static class ViewHolder{
public TextView addrTypeTxt;
public TextView line1Txt;
public TextView line2Txt;
public TextView areaTxt;
public TextView cityTxt;
public TextView pincodeTxt;
public RadioButton radioButton;
}
}
Thanks in advance!!!
Since your radiobuttons do not belong to one radiogroup, they are not working as radiobutton but just as a selector. It defeats the purpose of radiobutto, so I would suggest you to relook the design once. However, as a workaround to achieve what you want, you would want some way to keep track of currently selected radiobutton and once any other is selected toggle the currently selected button
So define a variable in your activity or fragment called currentradioselected
private RadioButton currentRadioButtonSelected
In your radiobuttonclick listener
public void onRadioButtonClicked(View view) {
RadioButton current = (RadioButton) view;
if (current.isChecked() && currentRadioSelected != null ) {
currentRadioButtonSelected.toggle();
currentRadioButtonSelected = current;
} else if (current.isChecked()) {
currentRadioButtonSelected = current;
} else {
//already selected radiobutton unchecked
currentRadioButtonSelected = null;
}
}
Hope this helps
Could anybody explain me, how to realize?
I have an activity with listview and footer with some elements(textview).
Listview built with custom adapter. Each listview item has few elements. And my question: how can i change textview in footer, from custom adapter, when i clicking on some listview's element?
Thx a lot!
/**** My adapter ****/
public class MyListAdapter extends ArrayAdapter<Product> implements UndoAdapter {
private final Context mContext;
private HashMap<Product, Integer> mIdMap = new HashMap<Product, Integer>();
ArrayList<Product> products = new ArrayList<Product>();
final int INVALID_ID = -1;
LayoutInflater lInflater;
String imagePath;
public MyListAdapter(Context context, int textViewResourceId, List<Product> prod) {
//super(context, textViewResourceId, prod);
super(prod);
lInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mContext = context;
for (int i = 0; i < prod.size(); i++) {
//add(prod.get(i));
mIdMap.put(prod.get(i),i);
}
}
#Override
public long getItemId(final int position) {
//return getItem(position).hashCode();
Product item = (Product) getItem(position);
return mIdMap.get(item);
}
#Override
public boolean hasStableIds() {
return true;
}
#Override
public View getView(final int position, View convertView, final ViewGroup parent) {
ViewHolder holder = null;;
Product p = getItem(position);
if (convertView == null) {
convertView = lInflater.inflate(R.layout.item, null);
//convertView.setBackgroundResource(R.drawable.rounded_corners);
int currentTheme = Utils.getCurrentTheme(convertView.getContext());
switch (currentTheme) {
case 0:
convertView.setBackgroundResource(R.drawable.rounded_corners);
break;
case 1:
convertView.setBackgroundResource(R.drawable.border);
break;
default:
convertView.setBackgroundResource(R.drawable.rounded_corners);
break;
}
holder = new ViewHolder();
holder.tvDescr = (TextView) convertView.findViewById(R.id.tvDescr);
holder.list_image = (ImageView) convertView.findViewById(R.id.list_image);
holder.products_amount = (TextView) convertView.findViewById(R.id.amountDigits);
holder.products_price = (TextView) convertView.findViewById(R.id.priceDigits);
holder.ivImage = (ImageView) convertView.findViewById(R.id.ivImage);
holder.unit = (TextView) convertView.findViewById(R.id.unit);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if(p.getProductImageBitmap() != null && p.getProductImageBitmap() != "") {
Log.d("PATH -- ", p.getProductImageBitmap());
ImageLoader imageLoader = ImageLoader.getInstance();
DisplayImageOptions options = new DisplayImageOptions.Builder().cacheInMemory(true)
.resetViewBeforeLoading(true)
.showImageForEmptyUri(R.drawable.ic_launcher)
.showImageOnFail(R.drawable.ic_launcher)
/*.showImageOnLoading(R.id.progress_circular)*/
.build();
imageLoader.displayImage(p.getProductImageBitmap(), holder.list_image, options);
} else {
holder.list_image.setImageResource(R.drawable.ic_launcher);
}
holder.tvDescr.setText(p.getProductName());
holder.ivImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String deletedItem = getItem(position).getProductName();
MyListAdapter.this.remove(getItem(position));
if (MyListAdapter.this.getCount() > 0) {
Toast.makeText(mContext, deletedItem + " " + mContext.getString(R.string.deleted_item), Toast.LENGTH_SHORT).show();
MyListAdapter.this.notifyDataSetChanged();
} else {
Toast.makeText(mContext,mContext.getString(R.string.sklerolist_empty), Toast.LENGTH_SHORT).show();
}
}
});
//Функционал для большой картинки продукта
//открывается новое активити с большой картинкой
holder.list_image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
imagePath = getItem(position).getProductImageBitmap();
if(imagePath != null && imagePath != "") {
Pattern normalPrice = Pattern.compile("^file");
Matcher m2 = normalPrice.matcher(imagePath);
if (m2.find()) {
Intent myIntent = new Intent(view.getContext(), ViewImage.class).putExtra("imagePath", imagePath);
view.getContext().startActivity(myIntent);
}
}
}
});
holder.products_price.setText(fmt(p.getProductPrice()));
holder.products_amount.setText(fmt(p.getProductAmount()));
holder.unit.setText(p.getProductUnit());
return convertView;
}
public static String fmt(double d){
if(d == (long) d)
return String.format("%d",(long)d);
else
return String.format("%s",d);
}
static class ViewHolder {
ImageView list_image;
TextView tvDescr;
TextView products_amount;
TextView products_price;
TextView unit;
ImageView ivImage;
ProgressBar circleProgress;
}
#NonNull
#Override
public View getUndoView(final int position, final View convertView, #NonNull final ViewGroup parent) {
View view = convertView;
if (view == null) {
//view = LayoutInflater.from(mContext).inflate(R.layout.undo_row, parent, false);
view = lInflater.inflate(R.layout.undo_row, parent, false);
}
return view;
}
#NonNull
#Override
public View getUndoClickView(#NonNull final View view) {
return view.findViewById(R.id.undo_row_undobutton);
}
public View getHeaderView(final int position, final View convertView, final ViewGroup parent) {
TextView view = (TextView) convertView;
//View view = convertView;
if (view == null) {
//view = (TextView) LayoutInflater.from(mContext).inflate(R.layout.list_header, parent, false);
//view = lInflater.inflate(R.layout.list_header, parent, false);
}
//view.setText(mContext.getString(R.string.header, getHeaderId(position)));
return view;
}
public long getHeaderId(final int position) {
return position / 10;
}
}
Your ListView has a listener for the click events on list elements.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Do something when a list item is clicked
}
But if you want to pas something else back from the adapter to the Activity or the Fragment that contains that ListView and Adapter, you should create a simple interface and set it as listener to your adapter. After that, set click events on your rows from within the adapter, and notify the Activity or Fragment using your own interface.
For example you have the interface defined like this
public interface OnItemClickedCustomAdapter {
public void onClick(ItemPosition position);
}
and in your Adapter class you will have a private member
private OnItemClickedCustomAdapter mListener;
and a method used to set the listener
public void setOnItemClickedCustomAdapter(OnItemClickedCustomAdapter listener){
this.mListener = listener;
}
From your Activity or Fragment where your ListView is defined, and your adapter is set, you will be able to call setOnItemClickedCustomAdapter with this as parameter, and there you go. Your activity will now listen for your events. To trigger an event, just call mListener.onClick() from your custom adapter. You can pass back data you need back to the Activity or Fragment, and from there you have access to your Header or Footer directly, and you can change the text on them.
The current project I have includes a ListView with a Custom Adapter. However, I am now interested in adding multiple types of views to my ListView but after several attempts I have been unable to add the two sources of code together to successfully integrate them.
Article on ListView with multiple views: ListView Article for multiple views
The custom adapter in my current code retrieves the data from another class called getData which is referenced by "data".
Code from article (ListView with multiple views):
public class MultipleItemsList extends ListActivity {
private MyCustomAdapter mAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAdapter = new MyCustomAdapter();
for (int i = 1; i < 50; i++) {
mAdapter.addItem("item " + i);
if (i % 4 == 0) {
mAdapter.addSeparatorItem("separator " + i);
}
}
setListAdapter(mAdapter);
}
private class MyCustomAdapter extends BaseAdapter {
private static final int TYPE_ITEM = 0;
private static final int TYPE_SEPARATOR = 1;
private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1;
private ArrayList mData = new ArrayList();
private LayoutInflater mInflater;
private TreeSet mSeparatorsSet = new TreeSet();
public MyCustomAdapter() {
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void addItem(final String item) {
mData.add(item);
notifyDataSetChanged();
}
public void addSeparatorItem(final String item) {
mData.add(item);
// save separator position
mSeparatorsSet.add(mData.size() - 1);
notifyDataSetChanged();
}
#Override
public int getItemViewType(int position) {
return mSeparatorsSet.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
}
#Override
public int getViewTypeCount() {
return TYPE_MAX_COUNT;
}
#Override
public int getCount() {
return mData.size();
}
#Override
public String getItem(int position) {
return mData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
int type = getItemViewType(position);
System.out.println("getView " + position + " " + convertView + " type = " + type);
if (convertView == null) {
holder = new ViewHolder();
switch (type) {
case TYPE_ITEM:
convertView = mInflater.inflate(R.layout.item1, null);
holder.textView = (TextView)convertView.findViewById(R.id.text);
break;
case TYPE_SEPARATOR:
convertView = mInflater.inflate(R.layout.item2, null);
holder.textView = (TextView)convertView.findViewById(R.id.textSeparator);
break;
}
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
holder.textView.setText(mData.get(position));
return convertView;
}
}
public static class ViewHolder {
public TextView textView;
}
}
Current code (ListView with custom adapter):
FragmentA.java
package com.example.newsapp;
public class FragmentA extends Fragment{
getData data = getData.getMyData();
public Integer ArticleID;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View V = inflater.inflate(R.layout.fragment_a, container, false);
ListView listView = (ListView)V.findViewById(R.id.list)
CustomList adapter = new
CustomList(getActivity(), data.Headline.toArray(new String[data.Headline.size()]), data.Description.toArray(new String[data.Description.size()]), data.BitmapList.toArray(new Bitmap[data.BitmapList.size()]), data.ArticleID.toArray(new Integer[data.ArticleID.size()]));
listView.setAdapter(adapter);
listView.setOnItemClickListener(this); //Removed on click item event code.
return V;
}
CustomList.java
package com.example.newsapp;
public class CustomList extends ArrayAdapter<String>{
private final Activity context;
private final String[] titleId;
private final String[] descriptionId;
private final Bitmap[] pictureid;
public CustomList(Activity context,
String[] Headline, String[] Description, Bitmap[] BitmapList, Integer[] ArticleID) {
super(context, R.layout.single_row, Headline);
this.context = context;
this.titleId = Headline;
this.descriptionId = Description;
this.pictureid = BitmapList;
}
#Override
public View getView(int position, View view, ViewGroup parent) {
LayoutInflater inflater = context.getLayoutInflater();
View rowView= inflater.inflate(R.layout.single_row, null, true);
TextView txtTitle = (TextView) rowView.findViewById(R.id.tvTitle);
TextView txtDescription = (TextView) rowView.findViewById(R.id.tvDescription);
ImageView imageView = (ImageView) rowView.findViewById(R.id.ivIcon);
txtTitle.setText(titleId[position]);
txtDescription.setText(descriptionId[position]);
imageView.setImageBitmap(pictureid[position]);
return rowView;
}
}
Edit:
public class CustomList extends ArrayAdapter<String>{
private final Activity context;
private final String[] titleId;
private final String[] descriptionId;
private final Bitmap[] pictureid;
public CustomList(Activity context,
String[] Headline, String[] Description, Bitmap[] BitmapList, Integer[] ArticleID) {
super(context, R.layout.single_row, Headline);
this.context = context;
this.titleId = Headline;
this.descriptionId = Description;
this.pictureid = BitmapList;
}
#Override
public View getView(int position, View view, ViewGroup parent) {
int viewType = getItemViewType(position);
View rowView = null;
switch(viewType) {
case 0:
LayoutInflater inflater = context.getLayoutInflater();
rowView= inflater.inflate(R.layout.single_row, null, true);
TextView txtTitle = (TextView) rowView.findViewById(R.id.tvTitle);
TextView txtDescription = (TextView) rowView.findViewById(R.id.tvDescription);
ImageView imageView = (ImageView) rowView.findViewById(R.id.ivIcon);
txtTitle.setText(titleId[position]);
txtDescription.setText(descriptionId[position]);
imageView.setImageBitmap(pictureid[position]);
case 1:
LayoutInflater inflater2 = context.getLayoutInflater();
rowView= inflater2.inflate(R.layout.single_row_loadmore, null, true);
}
return rowView;
}
#Override
public int getViewTypeCount() {
return 2; // TODO make this a static final
}
#Override
public int getItemViewType(int position) {
return position % 2; // 0 or 1
}
}
First, a bit of an aside: you should create a class that encapsulates a headline, description, etc. and use an array/collection of those objects to back your adapter. It will be far easier than managing many disparate arrays of things, especially if one day you decide you need another attribute of an Article (its category, for example).
class Article {
int id;
String headline;
String description;
Bitmap picture;
}
With regard to your ListView, the magic happens in the methods getItemViewType() and getViewTypeCount(). In getViewTypeCount() you return the maximum number of row types -- the article you posted uses two row types and so returns 2. In getItemViewType() you return a value between zero and (viewTypeCount - 1) -- in the article, his implementation can return 0 or 1 because his viewTypeCount is 2.
How you decide which row type applies to each item is entirely up to you. If, for example, you wanted to simply alternate view types on every row, you can do this:
#Override
public int getViewTypeCount() {
return 2; // TODO make this a static final
}
#Override
public int getItemViewType(int position) {
return position % 2; // 0 or 1
}
In other applications you would probably inspect the item at the given position to help you determine what should be returned in getItemViewtype().
The reason this functionality exists is that getView() provides a parameter (called convertView) that is a row which has been recycled. In order to give you an appropriate convertView, ListView needs to first know what row type it was. When you want to implement getView() for an adapter with multiple row types, it generally looks something like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
int viewType = getItemViewType(position);
switch(viewType) {
case 0:
return setUpOneViewType(position, convertView, parent);
case 1:
return setUpAnotherViewType(position, convertView, parent);
}
}
Note the cases for the switch statement correspond to the possible values that can be returned from getItemViewType(). These could be static final members.
I highly suggest watching The World of ListView. That video covers this topic as well as how to properly use convertView in your adapter implementation.
In my app, I display a check list using a ListView. Every row in my check list is represented by a model, which is an object of the ChecklistenPunkt class. There are three different types of rows:
a headline
an additional text
a question
The third type has two Buttons in it which are linked by two OnClickListeners: The first Button is for a positive answer, the second one for a negative answer. When the view is build, both of them are gray, indicating that this question is not yet answered. If the left Buttonis clicked it turns green and the second one is getting a dark gray background. If the right Button is clicked it turns red and the first one is getting a dark gray background. When a Button is clicked the answering state is saved as an ChecklistenPunktStatus. This is an Enum which has three entries (okay, not okay, not answered).
Here is a little image of the Buttons in three rows, showing the different states:
Here is my adapter code:
public class ChecklisteAdapter extends ArrayAdapter<Object> {
private List<ChecklistenPunkt> list;
private SparseArray<ChecklistenPunktStatus> sparseArray;
private Context context;
public ChecklisteAdapter(Context context, List<ChecklistenPunkt> objects) {
super(context, 0);
list = objects;
sparseArray = new SparseArray<ChecklistenPunktStatus>();
for (int i = 0; i < list.size(); i++) {
sparseArray.put(i, ChecklistenPunktStatus.NICHT_SELEKTIERT);
}
this.context = context;
}
#Override
public int getCount() {
return list.size();
}
#Override
public ChecklistenPunkt getItem(int position) {
return list.get(position);
}
#Override
public int getItemViewType(int position) {
ChecklistenPunkt p = (ChecklistenPunkt) list.get(position);
return p.getTyp();
}
#Override
public int getViewTypeCount() {
return 3;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View v = null;
int type = getItemViewType(position);
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final ViewHolder viewHolder = new ViewHolder();
switch (type) {
case Util.CHECKLISTE_FRAGE:
v = inflater.inflate(R.layout.pruefung_durchfuehren_list_item_frage, null);
viewHolder.nummer = (TextView) v.findViewById(R.id.check_nummer);
viewHolder.komponente = (TextView) v.findViewById(R.id.check_komponente);
viewHolder.funktion = (TextView) v.findViewById(R.id.check_funktion);
viewHolder.kriterium = (TextView) v.findViewById(R.id.check_kriterium);
viewHolder.erlaeuterung = (TextView) v.findViewById(R.id.check_erlaeuterung);
viewHolder.io = (Button) v.findViewById(R.id.button_i_o);
viewHolder.nio = (Button) v.findViewById(R.id.button_n_i_o);
viewHolder.io.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
viewHolder.io.setBackgroundColor(Color.GREEN);
viewHolder.io.setEnabled(false);
viewHolder.nio.setBackgroundColor(Color.GRAY);
viewHolder.nio.setEnabled(true);
sparseArray.put(position, ChecklistenPunktStatus.IN_ORDNUNG);
}
});
viewHolder.nio.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
viewHolder.nio.setBackgroundColor(Color.RED);
viewHolder.nio.setEnabled(false);
viewHolder.io.setBackgroundColor(Color.GRAY);
viewHolder.io.setEnabled(true);
sparseArray.put(position, ChecklistenPunktStatus.NICHT_IN_ORDNUNG);
}
});
ChecklistenPunktStatus status = sparseArray.get(position);
if (status == ChecklistenPunktStatus.IN_ORDNUNG) {
viewHolder.io.setBackgroundColor(Color.GREEN);
viewHolder.io.setEnabled(false);
viewHolder.nio.setBackgroundColor(Color.GRAY);
viewHolder.nio.setEnabled(true);
} else if (status == ChecklistenPunktStatus.NICHT_IN_ORDNUNG) {
viewHolder.nio.setBackgroundColor(Color.RED);
viewHolder.nio.setEnabled(false);
viewHolder.io.setBackgroundColor(Color.GRAY);
viewHolder.io.setEnabled(true);
} else if (status == ChecklistenPunktStatus.NICHT_SELEKTIERT) {
viewHolder.nio.setBackgroundColor(Color.parseColor("#cccccc"));
viewHolder.nio.setEnabled(true);
viewHolder.io.setBackgroundColor(Color.parseColor("#cccccc"));
viewHolder.io.setEnabled(true);
}
v.setTag(viewHolder);
break;
case Util.CHECKLISTE_UEBERSCHRIFT:
v = inflater.inflate(R.layout.pruefung_durchfuehren_list_item_head, null);
viewHolder.headline = (TextView) v.findViewById(R.id.check_headline);
viewHolder.headnummer = (TextView) v.findViewById(R.id.check_headnummer);
v.setTag(viewHolder);
break;
case Util.CHECKLISTE_ZUSATZTEXT:
v = inflater.inflate(R.layout.pruefung_durchfuehren_list_item_text, null);
viewHolder.text = (TextView) v.findViewById(R.id.check_text);
viewHolder.textnummer = (TextView) v.findViewById(R.id.check_textnummer);
v.setTag(viewHolder);
break;
default:
throw new IllegalArgumentException("ViewType " + type + " unbekannt!");
}
} else {
v = convertView;
}
ChecklistenPunkt clp = getItem(position);
ViewHolder viewHolder = (ViewHolder) v.getTag();
if (clp != null) {
switch (type) {
case Util.CHECKLISTE_FRAGE: {
viewHolder.nummer.setText(clp.getNummer());
viewHolder.komponente.setText(clp.getKomponente());
viewHolder.funktion.setText(clp.getFunktion());
viewHolder.kriterium.setText(clp.getKriterium());
viewHolder.erlaeuterung.setText(clp.getErlaeuterung());
ChecklistenPunktStatus status = sparseArray.get(position);
if (status == ChecklistenPunktStatus.IN_ORDNUNG) {
viewHolder.io.setBackgroundColor(Color.GREEN);
viewHolder.io.setEnabled(false);
viewHolder.nio.setBackgroundColor(Color.GRAY);
viewHolder.nio.setEnabled(true);
} else if (status == ChecklistenPunktStatus.NICHT_IN_ORDNUNG) {
viewHolder.nio.setBackgroundColor(Color.RED);
viewHolder.nio.setEnabled(false);
viewHolder.io.setBackgroundColor(Color.GRAY);
viewHolder.io.setEnabled(true);
} else if (status == ChecklistenPunktStatus.NICHT_SELEKTIERT) {
viewHolder.nio.setBackgroundColor(Color.parseColor("#cccccc"));
viewHolder.nio.setEnabled(true);
viewHolder.io.setBackgroundColor(Color.parseColor("#cccccc"));
viewHolder.io.setEnabled(true);
}
break;
}
case Util.CHECKLISTE_UEBERSCHRIFT: {
viewHolder.headline.setText(clp.getHeadline());
viewHolder.headnummer.setText(clp.getNummer());
break;
}
case Util.CHECKLISTE_ZUSATZTEXT: {
viewHolder.text.setText(Html.fromHtml(clp.getZusatz()));
viewHolder.textnummer.setText(clp.getNummer());
break;
}
default:
throw new IllegalArgumentException("ViewType " + type + " unbekannt!");
}
}
return v;
}
public SparseArray<ChecklistenPunktStatus> getSparseArray() {
return sparseArray;
}
static class ViewHolder {
protected TextView nummer;
protected TextView komponente;
protected TextView kriterium;
protected TextView funktion;
protected TextView erlaeuterung;
protected TextView text;
protected TextView textnummer;
protected TextView headline;
protected TextView headnummer;
protected Button io;
protected Button nio;
}
}
EDIT:
When the convertView is null, I create a new View by inflating my layout. Then I create a new ViewHolder, find the different TextViews and Buttons according to the Views viewType and put them into the ViewHolder. After that, I set this ViewHolder as the tag of the View. I distinguish according to the viewType again and set the text and Listeners of the Views inside the ViewHolder. Then I return the view.
When the convertView is not null I reuse the View. I get the ViewHolder with the getTag() method, set the text and Button state according to the model class and return the view at the end of the method.
My problem is that the answering state is correctly saved in the SparseArray, but it is not displayed correctly. When I answer a question in the first rows of my list and then scroll down, answered questions appear at the end of the list. By scrolling up and down I can mess up the answering states completely. But while this is happening, the states in the sparseArray are always correct.
Am I missing something here?
I suppose there is something wrong with the code. When you use the same cell and when the view is not null you should reuse the same holder and not the convertView. Something like that:
ViewHolder viewHolder = new ViewHolder();
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
.
.
.
}
else {
viewHolder = convertView.getTag();
}
I've some problems with my ListView custom adapter (and its newly implemented viewHolder). I have a ListView with a checkbox for each item (nothing new here). The problem is, if there is more than 9 items in my list, when I check the first checkbox, the tenth will be automatically checked (same for the second with the eleventh) just like if there were one listener for both item (and I beleive it's the case in some way).
I read about the position issue with listView, view recycling and the ViewHolder way to solve it here: How can I make my ArrayAdapter follow the ViewHolder pattern?
But I probably made something wrong because it's not working...
public class PresenceListAdapter extends SimpleAdapter {
private LayoutInflater inflater;
private List<Integer> ids;
private List<String> statuts;
public PresenceListAdapter (Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to, List<Integer> ids, List<String> statuts)
{
super (context, data, resource, from, to);
inflater = LayoutInflater.from (context);
this.ids = ids;
this.statuts= statuts;
}
#Override
public Object getItem (int position)
{
return super.getItem (position);
}
#Override
public View getView (int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
if (convertView == null)
{
convertView = inflater.inflate (R.layout.list_text_checkbox, null);
holder = new ViewHolder();
holder.btn = (Button) convertView.findViewById(R.id.btnRetard);
holder.check = (CheckBox) convertView.findViewById(R.id.checkPresent);
if (statuts.get(position).equals("P")) {
Drawable img = inflater.getContext().getResources().getDrawable(android.R.drawable.presence_online);
holder.btn.setCompoundDrawablesWithIntrinsicBounds( img, null, null, null );
holder.btn.setEnabled(true);
holder.check.setChecked(true);
}
else if(statuts.get(position).equals("R"))
{
Drawable img = inflater.getContext().getResources().getDrawable(android.R.drawable.presence_away);
holder.btn.setCompoundDrawablesWithIntrinsicBounds( img, null, null, null );
holder.btn.setEnabled(true);
holder.check.setChecked(true);
}
else
{
Drawable img = inflater.getContext().getResources().getDrawable(android.R.drawable.presence_invisible);
holder.btn.setCompoundDrawablesWithIntrinsicBounds( img, null, null, null );
holder.check.setChecked(false);
}
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
int id = ids.get(position);
if(id != 0)
{
holder.check.setTag(id);
holder.btn.setTag(id);
}
return super.getView (position, convertView, parent);
}
static class ViewHolder {
Button btn;
CheckBox check;
}
And my listener:
public void changerPresent(View v) {
CheckBox checkPresent = (CheckBox) v;
int idPersonne = (Integer) checkPresent.getTag();
View parent = (View)v.getParent();
Button btn = (Button) parent.findViewById(R.id.btnRetard);
if(checkPresent.isChecked()) {
gestion.updatePresence(idPersonne, idSeance, "P");
btn.setEnabled(true);
setBtnRetardPresent(btn);
}
else
{
gestion.updatePresence(idPersonne, idSeance, "A");
btn.setEnabled(false);
setBtnRetardAbsent(btn);
}
}
I would appreciate any help at this point, I'm working on this for hours now.
Thank you very much.
Here's how I made it work:
First, you need a separate array for your checked state. It has to be the same size as your adapter's getCount().
Then on your getView, your checkbox's setOnCheckedChangedListener MUST PRECEED your checkbox.setChecked statements.
example:
holder.checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
isChecked[position] = isChecked;
}
});
holder.checkBox.setChecked(isChecked[position]);
You should set CheckedBox state outside the initialization of ViewHolder, like the following code:
if (convertView == null) {
viewHolder = new ViewHolder();
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.checkedBox.setChecked();
BTW: use SparseBooleanArray instead of two list to store CheckedBox state.
The problem is because of the fact that listview recycles it views
so in getView() method
if (convertView == null)
{
........
}
else
{
holder = (ViewHolder) convertView.getTag();
}
// Uncheck needed boxes here... You need to implement your logic
if( 'position' is checked earlier)
holder.check.setChecked(true);
else
holder.check.setChecked(false);
You need to write the code to manage the state of view if the convert is not null, because it is a already used view which may be having checked check boxes.