When i am scrolling ListView position of the item changes.My ListView contain 2 Button and 2 TextView. These 2 Button are initially invisible. when user Long click on item i just set the visibility.Its working but when i scroll the ListView ,it changes the position.Why?
I am setting this visibility and invisibility code inside of my adapter class..
here is my code :::
#Override
public View getView(int position, View convertView, ViewGroup parent) {
holder = new ViewHolder();
if (convertView == null) {
convertView = lInflater.inflate(R.layout.message_row, null);
holder.messageDeleteButton = (Button) convertView
.findViewById(R.id.delete_button);
holder.messageTextView = (TextView) convertView
.findViewById(R.id.message);
holder.timeTextView = (TextView) convertView
.findViewById(R.id.time);
holder.copyMessageButton = (ImageButton) convertView
.findViewById(R.id.copy_bt);
holder.messageDeleteButton
.setOnClickListener(new OnClickListener() {
#Override
public void onClick(final View v) {
System.out.println("Delete clicked");
}
});
convertView.setTag(R.layout.message_row, holder);
} else {
holder = (ViewHolder) convertView.getTag(R.layout.message_row);
}
convertView.setTag(position);
holder.messageDeleteButton.setTag(position);
if (mMessageData.get(position).isVisible())
holder.messageDeleteButton.setVisibility(View.VISIBLE);
else
holder.messageDeleteButton.setVisibility(View.GONE);
holder.messageTextView.setText(mMessageData.get(position)
.getMessage_text());
holder.messageTextView.setTypeface(Typeface.createFromAsset(
mContext.getAssets(), "Existence-Light.ttf"));
holder.timeTextView.setText(mMessageData.get(position)
.getMessage_date());
holder.timeTextView.setTypeface(Typeface.createFromAsset(
mContext.getAssets(), "Existence-Light.ttf"));
convertView.setOnTouchListener(mOnTouchListener);
convertView.setOnClickListener(mOnClickListener);
// convertView.setOnLongClickListener(mLongClick);
return convertView;
}
This is my OnTouchListiner code it will working fine.But when i scroll the ListView the buttons positions will changed..
class MyTouchListener implements OnTouchListener {
#SuppressWarnings("deprecation")
private final GestureDetector gestureDetector = new GestureDetector(
new GestureListener());
#Override
public boolean onTouch(View v, MotionEvent event) {
int touch = 0;
ViewHolder holder = (ViewHolder) v.getTag(R.layout.message_row);
animation = new Animater();
int action = event.getAction();
int position = (Integer) v.getTag();
pos = position;
view = v;
switch (action) {
case MotionEvent.ACTION_DOWN:
action_down_x = (int) event.getX();
Log.d("action", "ACTION_DOWN - ");
touch = 0;
break;
case MotionEvent.ACTION_MOVE:
Log.d("action", "ACTION_MOVE - ");
action_up_x = (int) event.getX();
difference = action_down_x - action_up_x;
calcuateDifference(holder, position);
touch = 1;
break;
case MotionEvent.ACTION_UP:
Log.d("action", "ACTION_UP - ");
// calcuateDifference(holder, position);
action_down_x = 0;
action_up_x = 0;
difference = 0;
// touch = 2;
break;
}
return gestureDetector.onTouchEvent(event);
}
public GestureDetector getGestureDetector() {
return gestureDetector;
}
}
In this gestuer class when i tap on ListView just display one more Button.This also be changed the position
public class GestureListener extends SimpleOnGestureListener{
#SuppressLint("NewApi")
#Override
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub
super.onLongPress(e);
final ViewHolder hold = (ViewHolder) view.getTag(R.layout.message_row);
hold.copyMessageButton.setVisibility(View.VISIBLE);
hold.copyMessageButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
System.out.println("hello you click me..");
});
}
}
What is the wrong on my code..Any one please help me...
Android ListView use Adapters to recycle views that's why ListView position changes while scrolling to conserve memory.See the Google I/O presentation given by Romain Guy to understand how adapters work. http://www.youtube.com/watch?v=N6YdwzAvwOA
Also check the below links,
http://strangevikas.wordpress.com/tag/how-adapter-works-in-android-android-and-adapters/
http://www.edureka.in/blog/what-are-adapters-in-android/
You just need to do some modification in your code like:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = new ViewHolder();
if (convertView == null) {
convertView = lInflater.inflate(R.layout.message_row, null);
holder.messageDeleteButton = (Button) convertView
.findViewById(R.id.delete_button);
holder.messageTextView = (TextView) convertView
.findViewById(R.id.message);
holder.timeTextView = (TextView) convertView
.findViewById(R.id.time);
holder.copyMessageButton = (ImageButton) convertView
.findViewById(R.id.copy_bt);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.messageDeleteButton.setTag(mMessageData.get(position));
holder.messageDeleteButton
.setOnClickListener(new OnClickListener() {
#Override
public void onClick(final View v) {
// message data can be retrieved here like
MessageData mData = (MessageData) v.getTag();
System.out.println("Delete clicked");
}
});
if (mMessageData.get(position).isVisible())
holder.messageDeleteButton.setVisibility(View.VISIBLE);
else
holder.messageDeleteButton.setVisibility(View.GONE);
holder.messageTextView.setText(mMessageData.get(position)
.getMessage_text());
holder.messageTextView.setTypeface(Typeface.createFromAsset(
mContext.getAssets(), "Existence-Light.ttf"));
holder.timeTextView.setText(mMessageData.get(position)
.getMessage_date());
holder.timeTextView.setTypeface(Typeface.createFromAsset(
mContext.getAssets(), "Existence-Light.ttf"));
return convertView;
}
Related
Can you help me out, I'm learning! ;-)
I have two buttons "+" and "-" and I want them to increase or decrease the amount by one up or one down. How do I make sure that it will only have effect on the right textview using an adapter.
Now all the buttons only effects the first one. I know I have to get the position/id of the array. But I don't know how.
btw
Merk = Brand
Aantal = Amount
public class ListViewDemo2 extends Activity {
private ArrayList<String> data = new ArrayList<String>();
int aantal = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view_demo2);
ListView lv = (ListView) findViewById(R.id.bestel_lijst);
lv.setAdapter(new MyListAdapter(this, R.layout.list_item, data));
generateListContent();
}
private void generateListContent(){
for (int i =0; i < 55; i++){
data.add("this is row number: " + i);
}
}
#Override
public boolean onCreateOptionsMenu (Menu menu){
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings){
return true;
}
return super.onOptionsItemSelected(item);
}
private class MyListAdapter extends ArrayAdapter<String>{
private int layout;
public MyListAdapter(Context context, int resource, List<String> objects) {
super(context, resource, objects);
layout = resource;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder mainViewholder = null;
if(convertView == null){
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(layout, parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.Merk = (TextView) convertView.findViewById(R.id.Merk);
viewHolder.Aantal = (TextView) convertView.findViewById(R.id.Aantal);
viewHolder.btnup = (Button) convertView.findViewById(R.id.Bup);
viewHolder.btndown = (Button) convertView.findViewById(R.id.Bdown);
viewHolder.btndown.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
aantal = aantal-1;
displayAantal(aantal);
}
});
viewHolder.btnup.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
aantal = aantal+1;
displayAantal(aantal);
}
});
convertView.setTag(viewHolder);
}
else {
mainViewholder = (ViewHolder)convertView.getTag();
mainViewholder.Merk.setText(getItem(position));
}
return convertView;
}
}
public class ViewHolder {
TextView Merk;
TextView Aantal;
Button btnup;
Button btndown;
}
public void displayAantal(int aantal) {
TextView aantalView = (TextView) findViewById(R.id.Aantal);
aantalView.setText(String.valueOf(aantal));
}
}
your problem seems to be in your displayAantal(int) method, in particular, in this line:
TextView aantalView = (TextView) findViewById(R.id.Aantal);
You do not need to re-initialize that field here, since it is already initialized inside getView():
viewHolder.Aantal = (TextView) convertView.findViewById(R.id.Aantal);
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder mainViewholder = null;
if(convertView == null){
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(layout, parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.Merk = (TextView) convertView.findViewById(R.id.Merk);
viewHolder.Aantal = (TextView) convertView.findViewById(R.id.Aantal);
viewHolder.btnup = (Button) convertView.findViewById(R.id.Bup);
viewHolder.btndown = (Button) convertView.findViewById(R.id.Bdown);
convertView.setTag(viewHolder);
}
else {
mainViewholder = (ViewHolder)convertView.getTag();
}
String str = getItem(position);
mainViewholder.Merk.setText(str);
viewHolder.btndown.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
//just handle the data ,then call adapter.notifyDataSetChanged();
}
});
viewHolder.btnup.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
//just handle the data ,then call adapter.notifyDataSetChanged();
}
});
return convertView;
}
I am creating a submit form by getting data from server, this form may have different views (EditText, CheckBox, RadioButton, RatingBar) according what the server suggest. I have successfully created that view in adapter but every time I scroll down or up the user's answer get cleared (like checked Item get unchecked automatically, EditText get cleared etc) and also the views get automatically exchanged their position from first to last or just get swapped. I don't know how to solve that, here is my getView method:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = context.getLayoutInflater();
rowView = inflater.inflate(R.layout.customratinglist, parent, false);
final ViewHolder holder = new ViewHolder();
holder.tvQuestion = (TextView) rowView.findViewById(R.id.txtquestion);
holder.tvQuestion.setTag(list.get(position));
holder.layOut = (LinearLayout) rowView.findViewById(R.id.lnQuestion);
holder.layOut.setTag(position);
rowView.setTag(holder);
} else {
rowView = convertView;
((ViewHolder) rowView.getTag()).layOut.setTag(position);
}
final ViewHolder viewHolder = (ViewHolder) rowView.getTag();
viewHolder.tvQuestion.setTypeface(font);
viewHolder.tvQuestion.setText(list.get(position).getQuestion());
viewHolder.layOut.removeAllViews();
switch (list.get(position).getAns_type_id()) {
case "1":
ratingBar = new RatingBar(context);
ratingBar.setNumStars(5);
ratingBar.setStepSize(0.1f);
ratingBar.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
viewHolder.layOut.addView(ratingBar);
ratingBar.setOnRatingBarChangeListener(new RatingBar.OnRatingBarChangeListener() {
#Override
public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) {
answerSetter((int) (viewHolder.layOut.getTag()), String.valueOf(rating));
}
});
break;
case "2":
editText = new EditText(context);
editText.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT));
editText.setTextColor(Color.BLACK);
viewHolder.layOut.addView(editText);
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
answerSetter((int) (viewHolder.layOut.getTag()), String.valueOf(s));
}
#Override
public void afterTextChanged(Editable s) {
}
});
break;
}
return rowView;
}
The solution is a using these two methods :
#Override
public int getItemViewType(int position) {
return Integer.parseInt(list.get(position).getAns_type_id());
}
#Override
public int getViewTypeCount() {
return 2;
}
After that you can use getItemViewType method inside of getView method with position which is passed as param in getView method.
For more info please take a look for this tutorial
Here is documentation for these methods.
u can try like this,
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
// TODO Auto-generated method stub
LayoutInflater inflater = getActivity().getLayoutInflater();
View row;
switch (Maintain_List.get(position))
{
case 1:
row = inflater.inflate(R.layout.row, parent, false);
TextView tv = (TextView) row.findViewById(R.id.row_title);
tv.setText("" + sliding_menu_text.get(position));
ImageView iv1 = (ImageView) row.findViewById(R.id.row_image);
iv1.setImageResource(sliding_menu_icon.get(position));
break;
case 2:
row = inflater.inflate(R.layout.row2, parent, false);
break;
case 3:
row = inflater.inflate(R.layout.row3, parent, false);
TextView tv2 = (TextView) row.findViewById(R.id.tv_of_row3);
tv2.setText("" + sliding_menu_text.get(position));
break;
case 4:
row = inflater.inflate(R.layout.refresh_category, parent, false);
final Button b = (Button) row.findViewById(R.id.refresh_btn_of_refresh_category);
final WebView wv = (WebView) row.findViewById(R.id.wv_of_refresh_category);
b.setVisibility(View.VISIBLE);
b.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
NetworkCheck nc = new NetworkCheck(getActivity());
isInternetPresent = nc.isConnectingToInternet();
if (!isInternetPresent)
{
// Ntwork not avilabale
Toast.makeText(getActivity(), "Connection Error !!", Toast.LENGTH_SHORT).show();
}
else
{
// Ntwork Avilable
wv.loadUrl("file:///android_asset/m.html");
wv.setBackgroundColor(Color.TRANSPARENT);
wv.setVisibility(View.VISIBLE);
b.setVisibility(View.INVISIBLE);
new get_List_of_Drawer().execute();
}
}
});
break;
default:
row = null;
break;
}
return row;
}
works fine for me...!!!
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.
Hello at all the community,
i've this adapter with 2 button and 2 textview.
#Override
public View getView(int position, View view, ViewGroup parent) {
if(view == null) {
holder = new Holder();
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.my_adapter, null);
holder.result = (TextView)view.findViewById(R.id.description);
holder.value = (TextView)view.findViewById(R.id.value);
holder.add = (Button)view.findViewById(R.id.add);
holder.subtract = (Button)view.findViewById(R.id.subtract);
myObject = getItem(position);
holder.result.setText(myObject.result);
holder.value.setText(myObject.value);
view.setTag(holder);
} else {
holder = (Holder)view.getTag();
}
holder.add.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
// TODO Auto-generated method stub
}
});
return view;
}
Now my question is: if I want that when the user press on add button set the text of the textview with (for example) 5 how can i do this? If I put into the onCLick method
holder.result.setText("My text") set the text of the last textview and not the correspond textview of the selected row item (I disabled the click on the listview with):
#Override
public boolean isEnabled(int position) {
return false;
}
Is there any solution for my problem?
You should put your code in the getView method inside the adapter and remember to use references to the current Button/TextVeiw so that each Button would correspond to that specific TextView
P.S. I found something with your code check this out:
#Override
public View getView(int position, View view, ViewGroup parent) {
if(view == null) {
holder = new Holder();
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.my_adapter, null);
holder.result = (TextView)view.findViewById(R.id.description);
holder.value = (TextView)view.findViewById(R.id.value);
holder.add = (Button)view.findViewById(R.id.add);
holder.subtract = (Button)view.findViewById(R.id.subtract);
view.setTag(holder);
} else {
holder = (Holder)view.getTag();
}
myObject = getItem(position);
holder.result.setText(myObject.result);
holder.value.setText(myObject.value);
final TextView tv = holder.result;
holder.add.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
tv.setText("bla bla");
}
});
return view;
}
You mixed something up, the getItem has to be below the view creation stuff. First create the view, then set the values.
#Override
public View getView(int position, View view, ViewGroup parent) {
if(view == null) {
holder = new Holder();
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.my_adapter, null);
holder.result = (TextView)view.findViewById(R.id.description);
holder.value = (TextView)view.findViewById(R.id.value);
holder.add = (Button)view.findViewById(R.id.add);
holder.subtract = (Button)view.findViewById(R.id.subtract);
view.setTag(holder);
} else {
holder = (Holder)view.getTag();
}
myObject = getItem(position);
holder.result.setText(myObject.result);
holder.value.setText(myObject.value);
holder.add.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
//What should happen here?
}
});
return view;
}
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();
}