Android - Get each EditTexts values of ListView with custom ArrayAdapter
Asked Answered
F

2

5

I've got a ListView with a customer ArrayAdapter and a custom ListItem-XML. At the bottom of this ListView I've added a FooterView with a Button. Now I want to get all the values of the three EditTexts in each of my ListItems when I click this Button.

Here below is my code:

activity_checklistresult.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

result_inner_view.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:gravity="center">

    <TextView
        android:id="@+id/tv_result_amount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/default_margin" />
    <EditText
        android:id="@+id/et_result_amount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/default_margin"
        android:inputType="number" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center_vertical"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_result_product_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:ellipsize="end"
            android:layout_margin="@dimen/default_margin" />
        <AutoCompleteTextView
            android:id="@+id/actv_search_product"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:singleLine="true"
            android:ellipsize="end"
            android:layout_margin="@dimen/default_margin"
            android:inputType="textAutoComplete" />

    </LinearLayout>

    <TextView
        android:id="@+id/tv_result_price"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/default_margin" />
    <EditText
        android:id="@+id/et_result_price"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/default_margin"
        android:inputType="numberDecimal" />

</LinearLayout>

ResultListActivity.java:

public class ChecklistResultActivity extends ListActivity
{
    private MyResultAdapter adapt;
    private Button confirmButton;
    private ChecklistResultActivity rActivity;

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_checklistresult);

        // Set my Custom ArrayAdapter for this ListActivity
        setAdapter();

        rActivity = this;
    }

    private void setAdapter(){
        adapt = new MyResultAdapter(this, R.layout.result_inner_view, Controller.getInstance().getOrderedProducts());
        setListAdapter(adapt);

        // Add the confirm button at the end of the list
        addFooterConfirmButton();
    }

    private void addFooterConfirmButton(){
        ListView lv = (ListView)findViewById(android.R.id.list);
        FrameLayout footerLayout = (FrameLayout) getLayoutInflater().inflate(R.layout.result_footer_view, null);
        confirmButton = (Button)footerLayout.findViewById(R.id.confirm_result_button);
        confirmButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ...

                // Get all EditText values (and compare them with the Default Value)
            }
        });
        lv.addFooterView(footerLayout);
    }

    ...

MyResultAdapter.java:

public class MyResultAdapter extends ArrayAdapter<OrderedProduct>
{
    private Context context;
    private int layoutResourceId;
    private LayoutInflater inflater;

    public MyResultAdapter(Context c, int layoutResourceId, List<OrderedProduct> objects){
        super(c, layoutResourceId, objects);
        this.layoutResourceId = layoutResourceId;
        this.context = c;
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        View view = convertView;
        if(view == null)
            view = inflater.inflate(layoutResourceId, parent, false);

        ...

        return view;
    }
}

In the ResultListActivity at // Get all EditText values (and compare them with the Default Value) I want to loop through all the OrderedProducts and compare the default values of those, with the new filled-in values of the two EditTexts and the AutoCompleteTextView. So how can I get those values from within the onClick of the Footer-Button?

Thanks in advance for the responses.

Frederickfredericka answered 30/5, 2014 at 11:4 Comment(2)
What is happening now? Are you getting only the last value?Maury
use edit text change listener on edit text as you will enter value into edit text at that time save value into object according to position after that you can get all values from that object.Hawser
C
14

Your adapter has to save text on change for each created EditText :

  1. create a map to save values
  2. Define and set a generic TextWatcher for every EditText
  3. set a Tag on the editText (using its position or any id you want) we'll use this tag as the key in the hashMap
  4. create a method to retrieve the value for a given EditText

Adapter :

public class MyResultAdapter extends ArrayAdapter<OrderedProduct>
{
    ...

    private HashMap<String, String> textValues = new HashMap<String, String>();

    ...

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

        View view = convertView;
        boolean convertViewWasNull = false;
        if(view == null){
            view = inflater.inflate(layoutResourceId, parent, false);
            convertViewWasNull = true;
        }


        myEditText1 = findViewById...
        myEditText2 = findViewById...

        if(convertViewWasNull ){

            //be aware that you shouldn't do this for each call on getView, just once by listItem when convertView is null
            myEditText1.addTextChangedListener(new GenericTextWatcher(myEditText1));
            myEditText2.addTextChangedListener(new GenericTextWatcher(myEditText2));
        }

        //whereas, this should be called on each getView call, to update view tags.
        myEditText1.setTag("theFirstEditTextAtPos:"+position);
        myEditText2.setTag("theSecondEditTextAtPos:"+position);
    }


    private class GenericTextWatcher implements TextWatcher{

       private View view;
       private GenericTextWatcher(View view) {
            this.view = view;
        }

        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}

        public void afterTextChanged(Editable editable) {

            String text = editable.toString();
            //save the value for the given tag :
            MyResultAdapter.this.textValues.put(view.getTag(), editable.toString());
        }
    }

    //you can implement a method like this one for each EditText with the list position as parameter :
    public String getValueFromFirstEditText(int position){
          //here you need to recreate the id for the first editText
         String result = textValues.get("theFirstEditTextAtPos:"+position);
         if(result ==null)
              result = "default value";

         return result;
    }

    public String getValueFromSecondEditText(int position){
          //here you need to recreate the id for the second editText
         String result = textValues.get("theSecondEditTextAtPos:"+position);
         if(result ==null)
              result = "default value";

         return result;
    }

}
Circumvallate answered 30/5, 2014 at 11:46 Comment(2)
Though I changed some small things to make it more dynamic (like only one getValueOfEditText method with a String as parameter and such), this does indeed solve my problem. Thanks and accepted as answer.Frederickfredericka
Quote: "//be aware that you shouldn't do this for each call on getView, just once by listItem when convertView is null" I just found out that this isn't true, you should also call this when you are scrolling up or down in the ListView, since this resets the saved values of the EditTexts to their default values.Frederickfredericka
L
4

You can store all edittext values and get them when needed, you can store it in afterTextChange or onTextChange, it's up to you.

public class MyResultAdapter extends ArrayAdapter<OrderedProduct>{
    ...

    private final String[] valueList;

    public MyResultAdapter(Context c, int layoutResourceId, List<OrderedProduct> objects){
        super(c, layoutResourceId, objects);

        ...

        valueList = new String[objects.size()];

    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        View view = convertView;
        if(view == null)
            view = inflater.inflate(layoutResourceId, parent, false);

        ...

        //for edittext add text watcher listener

        final pos = position;

        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) {
                 valueList[pos] = s.toString();
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        return view;
    }

    public String[] getValueList(){
        return valueList;
    }
}
Lothair answered 30/5, 2014 at 11:36 Comment(4)
How do you manage several EditText on the same line ? in your case you've got just one. Moreover be carefull you a calling 'new TextWatcher()' on each 'getView' call, that can be very heavy and is really bad for list performances.Circumvallate
this is an example to user to understand the approach, it's not the fully answer of course, rest is up to him to adjust his own implementation and I disagree with the performance point. Listview already is recycle and handle performances issues. And what performance issue do you think it can cause?Lothair
You are creating to many new TextWatcher even when its not needed since you've got a convertView with an already set TextWatcher to reuse.Circumvallate
I see your point now, but as I said I'm just giving an approach and it's about how you can have a listener to watch. I didn't implement all code etc and no intention to do that. this initialize is when your convertview is null. You can have holder pattern to keep reference etc etc. But this all up to user to implement.Lothair

© 2022 - 2024 — McMap. All rights reserved.