4

There is an really weird thing happening with my listview. I am creating an ListView with buttons and an editText.

It's disposed like this: [Button] [EditText] [Button], The buttons works like an "incrementer" and "decrementer" updating the numerical value of EditText in 1 unit per click. The problem is, when I click in an button, almost every time an editText of another list view element is changed (the editText of the clicked item is also changed). And if I click in a button of this erroneous changed item, it also changes the editText of the first one. They basically have the same reference of buttons and editText, although they have textViews with data, and this data is different between they.

To accomplish that I created and custom adapter:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if(convertView == null) {
            convertView = mInflater.inflate(R.layout.lastproduct_row, null);
            holder = new ViewHolder();
            holder.btnAddQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_add_qtd);
            holder.btnSubQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_sub_qtd);
            holder.etQuantidade = (EditText) convertView.findViewById(R.lastproduct_row.et_quantidade);                

            TextView tv;

            holder.tvList = new TextView[PRODUCTROW_INT_KEY.length];

            for(int i = 0; i < PRODUCTROW_INT_KEY.length; i++) {
                tv = (TextView) convertView.findViewById(PRODUCTROW_INT_KEY[i]);
                holder.tvList[i] = tv;                  
            }

            convertView.setTag(holder);
        }
        else {
            holder = (ViewHolder) convertView.getTag();
        }

        HashMap<String, String> hm = productsList.get(position);
        String key = hm.get(CODIGO_KEY);

        for(int i = 0; i < PRODUCTROW_INT_KEY.length; i++) {
            holder.tvList[i].setText(hm.get(PRODUCTROW_STR_KEY[i]));
        }

        holder.btnAddQtd.setTag(key+QTD_FLAG+ADD_ACTION);
        holder.btnSubQtd.setTag(key+QTD_FLAG+SUB_ACTION);
        holder.btnAddQtd.setOnClickListener(handle);
        holder.btnSubQtd.setOnClickListener(handle);

        if(novosEstoques.containsKey(key)) {
            holder.etQuantidade.setText(MyParseFunctions.parseCentesimal(novosEstoques.get(key).getQuantidade()));
        }

        return convertView;
    }

    class ViewHolder {
        private TextView []tvList;
        private Button btnAddQtd, btnSubQtd;
        private Button btnAddQtVol, btnSubQtVol;
        private EditText etQuantidade, etQtVolume;
    }

I added onClick listenners to the buttons, setting their tags with my listView element ID (concatenated with another informations). Then in my event listener I just get the button parent View (an LinearLayout) and get the EditText from that using getViewAt():

    @Override
    public void onClick(View v) {
        String tag = (String) v.getTag();

        if(tag.contains(QTD_FLAG)) {
            String []info = ((String) v.getTag()).split(QTD_FLAG);
            float qtd;
            LinearLayout ll = (LinearLayout) v.getParent();
            ll.setBackgroundColor(Color.rgb(0, 128, 30));
            EditText et = (EditText) ll.getChildAt(2);

            qtd = Float.parseFloat(et.getText().toString().replace(",", "."));

            if(info[1].equals(ADD_ACTION)) {
                qtd++;

            }
            else if(info[1].equals(SUB_ACTION)) {
                if(qtd > 0)
                    qtd--;
            }

            Log.d("TESTE", "MODIFICAR KEY = "+info[0]);
            et.setText(qtd+"");
        }           
    }

I'm using an setBackgroundColor in this example to confirm that the LinearLayout instance is duplicated in the lisView. When I click an Button, it's painted in 2 different list view item.

Anyone can point me what could be doing this? I have found people with an duplicated ListView item, I don know if that is my case, cause I have TextView's inside my ListView, and they are not equal, only the LinearLayout portion with buttons and editText is "shared".


I make some changes in my getView method and it's working now! It seems that every time the getView method is called i have not guarantee at all that my editTexts will be filled properly and I didn't realize that. So every getView call I make I set the editText value, if the user edit an ET value, I store it in a HashMap to restore in getView, if there is no entry in HashMap for the given editText, then I set it to the default value (zero):

    ...
    if(convertView == null) {
            holder = new ViewHolder();
            holder.btnAddQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_add_qtd);
            holder.btnSubQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_sub_qtd);
            holder.etQuantidade = (EditText) convertView.findViewById(R.lastproduct_row.et_quantidade); 

            //Now it is easier to get etQuantidade reference in button
            //click handle, I just have to do:
            //    public onClick(View v) {
            //        EditText etButtonAssociated = (EditText) v.getTag();
            //        ...
            //    }
            holder.btnAddQtd.setTag(holder.etQuantidade);
            holder.btnSubQtd.setTag(holder.etQuantidade);

            holder.btnAddQtd.setOnClickListener(handle);
            holder.btnSubQtd.setOnClickListener(handle);
            ...
    }
    else {
        ...
    }
    holder.etQuantidade.setTag(key);

    if(novosEstoques.containsKey(key)) {
        holder.etQuantidade.setText(MyParseFunctions.parseCentesimal(novosEstoques.get(key).getQuantidade()));
    }
    else {
        holder.etQuantidade.setText("0");
    }

    return convertView;
IPValverde
  • 2,019
  • 2
  • 21
  • 38

1 Answers1

0

Israel,

After looking over your code, I was wondering about an implementation decision you have made. Since each Button is "bound" to a particular EditText, have you considered setting the Tag of those Buttons to the EditText? The Tag may be any Object including a UI element. This is especially useful for dynamic UI elements, such as a runtime populated list.

Since this is handled in your Adapter you wouldn't have to worry about duplicate Parents and such. Additionally, you could avoid having to worry about "finding" the control in your onClick() because you would have it (It's the tag). I'm not sure exactly what your project needs are, but this seems like a potentially viable solution, unless you need those Buttons to accomplish other tasks.

Note of Caution Just make sure that you erase the Tags' references to the EditText when you are done. Otherwise, you run the risk of leaking some memory.

FuzzicalLogic

Fuzzical Logic
  • 12,947
  • 2
  • 30
  • 58
  • Hi man, thanks for the tips! It make the code better, but the duplicated reference stand still =/ I put my EditText as the button's tag, and put an id as editText tag (I need this id stored for each listView element). And when i start my activity and change some editText value (usually the first ET) then another one changes it's value too (usually an ET element that is outside the current view when I edit the first one). So i put my id as an tag in each EditText and shows it in Toast when it's edited. For my surprise the id's are different! So I think the problem somewhere is in my Adapter. – IPValverde Oct 17 '11 at 12:29
  • Sounds like it. At least that gives you an idea of where to start. I'll look over your Adapter really quickly... see if anything stands out. – Fuzzical Logic Oct 17 '11 at 14:59
  • Israel, would you consider posting your updated code? In particular your getView() and onClick()? – Fuzzical Logic Oct 17 '11 at 15:14
  • Oh I got it! It seems kind of dirty but I get it working, I think that is the usually way to do that. I'll update the question with the improvement that make it work. – IPValverde Oct 17 '11 at 18:12
  • Glad you found a solution. Its not "dirty", its the standard MVC (Model, View, Controller) design pattern. Your View (List) doesn't know or care where the data comes from, so its not "bound" in the same way. Your Adapter applies your Model (data) to the View (List). But the (Model) is actually controlled by some other structure (Hashmap, array, database, etc). Seems a little awkward at first, but you'll get it more and more. – Fuzzical Logic Oct 18 '11 at 17:27