Android обрабатывает множество полей EditText в ListView

#android #gridview #android-edittext #listadapter #textwatcher

#Android #gridview #android-edittext #listadapter #textwatcher

Вопрос:

Просто основной вопрос: если у меня есть несколько десятков полей EditText, которые являются частью ListAdapter, как отдельные поля EditText могут узнать, к какой строке они принадлежат?

В настоящее время я использую TextWatcher для прослушивания ввода текста. Я попытался расширить TextWatcher, чтобы я мог передавать позицию EditText конструктору TextWatcher.

Однако, когда появляется программная клавиатура, позиции, соответствующие различным полям EditText, меняются местами.

Как я могу отследить поля EditText до их правильного положения?

Я использую GridView для размещения элементов. Макет каждого элемента представляет собой ImageView с полем TextView и EditText под ним.

Текст для каждого EditText хранится в глобальном массиве строк, называемом strings. Изначально он пуст и обновляется моим классом TextWatcher.

 public void initList()
    {
        ArrayAdapter<String> listAdapter = new ArrayAdapter<String>(this, R.layout.shape, strings)
        {
            @Override
            public View getView(final int position, View convertView, ViewGroup parent)  {
                if (convertView == null)  {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.shape, null);
                }
                final String theData = getItem(position);
                final EditText editText = (EditText) convertView.findViewById(R.id.shape_edittext);
                editText.setText(theData);
                editText.addTextChangedListener(
                        new MyTextWatcher(position, editText)
                );

                ImageView image = (ImageView) convertView.findViewById(R.id.shape_image);
                image.setBackgroundResource(images[position]);

                TextView text = (TextView) convertView.findViewById(R.id.shape_text);
                if (gameType == SHAPES_ABSTRACT)
                    text.setText("Seq:");
                else
                    text.setVisibility(View.GONE);  

                return convertView;
            }

            @Override
            public String getItem(int position) {        return strings[position];       }
        };

        grid.setAdapter(listAdapter);
    }


private class MyTextWatcher implements TextWatcher {
    private int index;
    private EditText edittext;
    public MyTextWatcher(int index, EditText edittext) { 
               this.index = index; 
               this.edittext = edittext;    
        }
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
    public void onTextChanged(CharSequence s, int start, int before, int count) {}
    public void afterTextChanged(Editable s) {  strings[index] = s.toString();      }
    public void setIndex(int newindex) {  index = newindex;    }
}
  

Когда я нажимаю на первый EditText (см. Рисунок), EditText перемещается на тот, который находится под смайликом.

Показывает, как расположены поля EditText

Ответ №1:

Не принимая во внимание, хороший ли это дизайн пользовательского интерфейса, вот как вы могли бы это сделать:

 public class TestList
{
    public void blah()
    {
        ArrayAdapter<DataBucket> listAdapter = new ArrayAdapter<DataBucket>()
        {

            @Override
            public View getView(int position, View convertView, ViewGroup parent)
            {
                if (convertView == null)
                {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.testlayout, null);
                }

                final DataBucket dataBucket = getItem(position);
                final EditText editText = (EditText) convertView.findViewById(R.id.theText);
                editText.setText(dataBucket.getSomeData());
                editText.addTextChangedListener(new TextWatcher()
                {
                    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)
                    {
                        dataBucket.setSomeData(editable.toString());
                    }
                });

                return convertView;
            }
        };
    }

    public static class DataBucket
    {
        private String someData;

        public String getSomeData()
        {
            return someData;
        }

        public void setSomeData(String someData)
        {
            this.someData = someData;
        }
    }
}
  

‘DataBucket’ — это заполнитель. Вам нужно использовать любой созданный вами класс для хранения данных, которые вводятся и редактируются в редактируемом тексте. TextWatcher будет иметь ссылку на объект данных, на который ссылается. По мере прокрутки текстовые поля редактирования должны обновляться текущими данными, а изменения текста должны сохраняться. Возможно, вам захочется отслеживать, какие объекты были изменены пользователем, чтобы повысить эффективность обновления данных / сети.

* Редактировать *

Использовать позицию int вместо прямой ссылки на объект:

 ArrayAdapter<DataBucket> listAdapter = new ArrayAdapter<DataBucket>()
{

    @Override
    public View getView(final int position, View convertView, ViewGroup parent)
    {
        if (convertView == null)
        {
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.testlayout, null);
        }

        final DataBucket dataBucket = getItem(position);
        final EditText editText = (EditText) convertView.findViewById(R.id.theText);
        editText.setText(dataBucket.getSomeData());
        editText.addTextChangedListener(new TextWatcher()
        {
            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)
            {
                getItem(position).setSomeData(editable.toString());
            }
        });

        return convertView;
    }
};
  

* Отредактируйте еще раз *

Я чувствую себя обязанным сказать для потомков, что на самом деле я бы не стал кодировать это таким образом. Я бы предположил, что вам нужны немного более структурированные данные, чем массив строк, и вы поддерживаете массив строк снаружи, а также ArrayAdapter, так что это своего рода странная параллельная ситуация. Однако это будет работать нормально.

У меня есть мои данные в виде массива с одной строкой, а не в виде многомерного массива. Причина в том, что модель данных, поддерживающая GridView, представляет собой всего лишь простой список. Это может показаться нелогичным, но так оно и есть. GridView должен сам создавать макет, и если предоставить его собственным устройствам, он заполнит строку переменным количеством ячеек, в зависимости от объема имеющихся у вас данных и ширины вашего экрана (AFAIK).

Хватит болтать. Код:

 public class TestList extends Activity
{
    private String[] guess;

    //Other methods in here, onCreate, etc

    //Call me from somewhere else. Probably onCreate.
    public void initList()
    {
        ArrayAdapter<String> listAdapter = new ArrayAdapter<String>(this, /*some resourse id*/, guess)
        {

            @Override
            public View getView(final int position, View convertView, ViewGroup parent)
            {
                if (convertView == null)
                {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.testlayout, null);
                }

                final String theData = getItem(position);
                final EditText editText = (EditText) convertView.findViewById(R.id.theText);
                editText.setText(theData);
                editText.addTextChangedListener(
                        new MyTextWatcher(position)
                );

                return convertView;
            }
        };

        gridView.setAdapter(listAdapter);
    }

    class MyTextWatcher extends TextWatcher {
         private int position;

         public MyTextWatcher(int position) {
                 this.position = position;
         }

         public void afterTextChanged(Editable s) {
                 guess[position] = s.toString();
         }

     // other methods are created, but empty
    }
}
  

Комментарии:

1. было бы проще просто использовать теги или идентификаторы для идентификации редактируемых текстов? Я не знаю, как будет работать реализация, но можно ли это сделать простым способом?

2. Я не уверен, что это сложно, но я посмотрю.

3. Использует значение позиции, а не прямую ссылку на объект. Если вы имеете в виду более простой способ не использовать внутренние анонимные классы Java, я бы сказал, вам следует привыкнуть к «сложности» 😉

4. Это сложно в том смысле, что я, честно говоря, понятия не имею, что делает ваш код, я новичок в listadapters в целом. Для меня простое означало бы присвоение номера самому объекту EditText либо через тег / идентификатор (какими бы они ни были), либо расширение класса EditText для хранения дополнительной переменной, соответствующей строке, в которой он находится. Любой из них жизнеспособен?

5. Как я уже сказал, вам следует привыкнуть к этим конструкциям. Концептуально они проще, чем то, о чем вы говорите, как только вы привыкнете к ним, и вы увидите подобные вещи повсюду в Android. Я не совсем уверен, что именно вы хотите сделать, но расширение EditText в качестве альтернативы тому, что я делаю, кажется мне очень сложным. Не рекомендовал бы.

Ответ №2:

Чтобы отслеживать номер строки, каждый прослушиватель в EditText должен сохранять ссылку на элемент в списке и использовать getPosition(item) для получения позиции в ListView . В моем примере используется Button , но я думаю, что его можно применить к EditText .

 class DoubleAdapter extends ArrayAdapter<Double> {
    public DoubleAdapter(Context context, List<Double> list) {
        super(context, android.R.layout.simple_list_item_1, list);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_row, null);
        }

        // keep a reference to an item in a list
        final Double d = getItem(position);

        TextView lblId = (TextView) convertView.findViewById(R.id.lblId);
        lblId.setText(d.toString());

        Button button1 = (Button) convertView.findViewById(R.id.button1);
        button1.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // the button listener has a reference to an item in the list
                // so it can know its position in the ListView
                int i = getPosition(d);
                Toast.makeText(getContext(), ""   i, Toast.LENGTH_LONG).show();
                remove(d);
            }
        });

        return convertView;
    }
}
  

Комментарии:

1. Что произойдет, если элемент списка не является Double? Что произойдет, если несколько элементов будут иметь одинаковое значение? Я думаю, целью здесь было обновить значения в списке, а не удалить их, верно? Этот код был бы проблематичным, если бы данные не были двойными, и вы не были уверены, что никакие два значения не будут одинаковыми. Я не думаю, что мы это знаем.

2. Если элемент списка не является Double, то замените все Double в коде типом элемента. Кроме того, вы можете обновить значение элемента с помощью ссылки d .

3. Что, если у вас есть два (или более) элемента с одинаковым значением?

4. Если это один и тот же объект, то оба будут изменены. В противном случае будет изменен только один элемент.

5. Правильно. Если бы это был список значений, я сомневаюсь, что вы бы хотели, чтобы оба объекта были изменены.

Ответ №3:

Возможно, стоит подумать, нужно ли сохранять тексты редактирования в ячейках списка? Это кажется немного ненужным, когда пользователь будет редактировать только по одному за раз.

Хотя я не знаю, как разработано ваше приложение, я бы рекомендовал немного переосмыслить ваш пользовательский интерфейс, чтобы при нажатии на элемент списка отображался один текст для редактирования. Таким образом, вы можете просто получить ссылку на элементы списка, как обычно с помощью адаптера списка, сохранить ее, пока пользователь редактирует, и обновить, когда они закончат.

Комментарии:

1. Почему на него смотрят свысока из-за такого количества EditTexts? Я не думаю, что мой экземпляр хорошо подойдет для отображения только одного всплывающего окна. Это только внешний вид, или это также накладывает нагрузку на память телефона?

2. Основная причина заключается в том, что во время прокрутки ListViews перерабатывает каждый элемент, как только он скрыт с экрана, и использует этот переработанный вид для отображения следующего элемента из списка. Если переработанный вид содержит данные, как если бы ваш EditText содержал текст, эти данные, скорее всего, будут потеряны или неуместны при прокрутке вашего ListView.

Ответ №4:

я не уверен, что у вас хороший дизайн, так как содержимое EditText может вызвать проблемы (перетасовка содержимого, пропуск текста) при прокрутке вашего listview. подумайте о том, чтобы опробовать идею m6tt.

но если вы действительно хотите пойти своим путем, не могли бы вы опубликовать какой-нибудь код, конкретно вашего TextWatcher?

Ответ №5:

Я пытался решить эту проблему, и, как вы можете видеть, есть простой метод — я публикую ответ здесь, поскольку это может быть кому-то полезно.

Не удается получить позицию, когда просмотр списка -> редактировать текст имеет средство просмотра текста.

Это решение, которое сработало для меня :

В get view —

когда я добавляю прослушиватель text watcher для редактирования текста, я также добавил строку ниже

 edittext.setTag(R.id.position<any unique string identitiy>, position)
  

в вашем afterTextChanged —

  int position = edittext.getTag(R.id.position) 
  

Выдает правильный номер позиции, и вы можете вносить изменения на основе номера позиции.