EditText in a List Adapter, how to save the value?

谁说胖子不能爱 提交于 2020-01-02 04:58:05

问题


So, I have a custom adapter with two EditText fields on every row.

I've gotten most of the stuff working properly, except saving the values inside the ArrayList.

This is the code I've done so far:

private void holderTitleSavedOnScroll(final int position, IZUICartViewHolder holder) {
    if (!(position == (variantArrayList.size() - 1)) && holder.title != null) {
        holder.title.setText(variantArrayList.get(position).getVariantTitle());
        final int finalPosition = position;
        holder.title.setOnFocusChangeListener(new OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                    final EditText newVariant = (EditText) v;
                    variantArrayList.get(finalPosition).setVariantTitle(newVariant.getText().toString());
            }
        });
    }
}

So this actually does what I want, it saves the value when the focus has changed. Except for one problem, it only saves the value when the focus has changed.

Which is great most of the time, except when a user actually presses a button that makes the whole view disappear. The focus never changed, and the value does not get set.

So I'm guessing you are all thinking, yeah let's just call addOnTextChangedListener and attach a TextWatcher, by adding something like this:

        holder.title.setText(variantArrayList.get(position).getVariantTitle());
        final int finalPosition = position;
        final EditText holderTitle = (EditText) holder.title;

        if (holderTitle.getTag() != null) {
            final TextWatcher textWatcher = 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) {
                        variantArrayList.get(finalPosition).setVariantTitle(s.toString());
                }

                @Override
                public void afterTextChanged(Editable s) {

                }
            };


            holder.title.addTextChangedListener(textWatcher);
            holder.title.setTag(true);
        }

Nope, that won't work either. Sure it actually saves the value, but it also messes stuff up when you scroll since the listview reuses the cell it thinks it the value from one cell is in the other cell and then sets the value from the ArrayList.

I've tried different things like checking focus when changing values and stuff, but it does not work (for more or less obvious reasons).

Is there any creative solutions to solve this?

UPDATE (With more code):

The TextWatcher approach as suggested:

My getView method (lots of other irrelevant code for this issue in here):

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;
    IZUICartViewHolder holder;
    LayoutInflater inflater = ((Activity) context).getLayoutInflater();

    if (v == null) {

        holder = new IZUICartViewHolder();
        int type = getItemViewType(position);

        switch (type) {
            case TYPE_EDIT:
                v = inflater.inflate(R.layout.iz_ui_modify_product_cell, parent, false);
                holder.title = (EditText) v.findViewById(R.id.iz_prod_modify_variant_title);
                holder.title.setHint(addVariantPlaceholder);
                holder.deleteButton = v.findViewById(R.id.click_remove);
                holder.price = (EditText) v.findViewById(R.id.iz_prod_modify_price);
                holder.price.setHint(pricePlaceholder);
                holder.price.setText(String.valueOf(0.0));
                break;

        }

        v.setTag(holder);

    } else {
        holder = (IZUICartViewHolder) v.getTag();
    }

    hideDeleteButton(holder, position);
    holderTitleSavedOnScroll(position, holder);
    holderPriceSavedOnScroll(position, holder);

    v.setTag(holder);
    return v;
}

The holderTitleSavedOnScroll method

private void holderTitleSavedOnScroll(final int position, IZUICartViewHolder holder) {
    if (!(position == (variantArrayList.size() - 1)) && holder.title != null) {

        holder.title.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                v.requestFocus();
            }
        });


        final int finalPosition = position;
        final EditText holderTitle = (EditText) holder.title;

        if (holderTitle != null) {
            holder.title.setText(variantArrayList.get(position).getVariantTitle());
        }

        holderTitle.addTextChangedListener(new EditVariantTextWatcher(variantArrayList.get(finalPosition)));

    }
}

The TextWatcher class:

public class EditVariantTextWatcher implements TextWatcher {

private IZUIProductVariantContainer variantContainer;

protected EditVariantTextWatcher(IZUIProductVariantContainer variantContainer) {
    this.variantContainer = variantContainer;
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {

}

@Override
public void afterTextChanged(Editable s) {
    variantContainer.setVariantTitle(s.toString());
}
}

回答1:


This can be done via careful use of TextWatchers.

Include a reference to the current TextWatcher in your ViewHolder. When a view is recycled, remove the existing TextWatcher and add a new one, keyed to the current position.

Here is a complete working example, including state saving to allow testing navigating away:

public class EditTextListActivity extends ListActivity {

    private static final String SAVED_STATE_KEY = "saved_state_key";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        EditTextAdapter editTextAdapter = new EditTextAdapter(this, R.layout.main);
        setListAdapter(editTextAdapter);

        // Restore our state, if there is any
        if (savedInstanceState != null) {
            List<String> savedStrings = savedInstanceState.getStringArrayList(SAVED_STATE_KEY);
            for (String savedString : savedStrings)
                editTextAdapter.add(new ListItem(savedString));
        } else {
            // Add some empty items so that we can see it in action
            for (int i = 0; i < 30; i++)
                editTextAdapter.add(new ListItem(""));
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        ArrayList<String> arrayList = new ArrayList<String>();
        EditTextAdapter editTextAdapter = (EditTextAdapter) getListAdapter();
        for (int i = 0; i < editTextAdapter.getCount(); i++)
            arrayList.add(editTextAdapter.getItem(i).string1);
        outState.putStringArrayList(SAVED_STATE_KEY, arrayList);
    }

    /**
     * The object we have a list of, probably more complex in your app
     */
    static class ListItem {
        public String string1;

        ListItem(String string1) {
            this.string1 = string1;
        }
    }

    /**
     * ViewHolder which also tracks the TextWatcher for an EditText
     */
    static class ViewHolder {
        public TextView textView;
        public EditText editText;
        public TextWatcher textWatcher;
    }

    class EditTextAdapter extends ArrayAdapter<ListItem> {
        EditTextAdapter(Context context, int resource) {
            super(context, android.R.layout.simple_list_item_single_choice);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View rowView = convertView;
            if (rowView == null) {
                // Not recycled, inflate a new view
                rowView = getLayoutInflater().inflate(R.layout.main, null);
                ViewHolder viewHolder = new ViewHolder();
                viewHolder.textView = (TextView) rowView.findViewById(R.id.textview);
                viewHolder.editText = (EditText) rowView.findViewById(R.id.edittext1);
                rowView.setTag(viewHolder);
            }

            ViewHolder holder = (ViewHolder) rowView.getTag();
            // Remove any existing TextWatcher that will be keyed to the wrong ListItem
            if (holder.textWatcher != null)
                holder.editText.removeTextChangedListener(holder.textWatcher);

            final ListItem listItem = getItem(position);

            // Keep a reference to the TextWatcher so that we can remove it later
            holder.textWatcher = 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) {
                    listItem.string1 = s.toString();
                }

                @Override
                public void afterTextChanged(Editable s) {
                }
            };
            holder.editText.addTextChangedListener(holder.textWatcher);

            holder.editText.setText(listItem.string1);
            holder.textView.setText(Integer.toString(position));

            return rowView;
        }
    }
}

layout/main.xml:

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

    <TextView
        android:id="@+id/textview"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content" />

    <EditText
        android:id="@+id/edittext1"
        android:layout_width="0dp"
        android:layout_height="?android:attr/listPreferredItemHeightSmall"
        android:layout_weight="2"
        android:inputType="text" />

    <!-- This EditText is included to demonstrate problems with a naive approach. -->
    <EditText
        android:inputType="text"
        android:layout_width="0dp"
        android:layout_height="?android:attr/listPreferredItemHeightSmall"
        android:layout_weight="2" />
</LinearLayout>

Known bug: an EditText will lose initial focus when the keyboard comes up, see this answer for discussion of that problem.




回答2:


We can save value with the help of TextWatcher

Better to use A Model to store and retrieve the value of EditText.

public class RowData {

    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
} 

Next is TextWatcher Listener

public class EditTextWatcher implements TextWatcher {

    private RowData data;

    public EditTextWatcher(RowData data) {
        this.data = data;
    }

    @Override
    public void afterTextChanged(Editable s) {
        data.setValue(s.toString());
    }

    @Override
    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
            int arg3) {

    }

    @Override
    public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {

    }

}

Your Adapter Class

         public class YourAdapter extends ArrayAdapter<RowData> {
                private ArrayList<RowData> data;
                private Context context;
            public YourAdapter(Context context, ArrayList<RowData> data) {
                    super(context, 0, data);
                    this.data = data;
                }
            @Override
            public View getView(final int position, View convertView, ViewGroup parent) {
                View v = convertView;
                final RowData row = data.get(position);
                final EditText edittext = (EditText) v.findViewById(R.id.edittext);
                if (edittext != null)
                   edittext.setText(row.getValue());
                edittext.addTextChangedListener(new EditTextWatcher(row));


                ///////Your Code
                /////
        }
}

// edittext.addTextChangedListener(new EditTextWatcher(row));This line will store the data for each EditText, and even if you will scroll the ListView then while creating cell it will set the value from Model(RowData)

Try like this, Hope this will help you.



来源:https://stackoverflow.com/questions/19666752/edittext-in-a-list-adapter-how-to-save-the-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!