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

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

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

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

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

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

Текст для каждого EditText хранится в глобальном массиве строк, называемом строками. Изначально он пуст и обновляется моим классом 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


person Rob    schedule 13.11.2011    source источник


Ответы (5)


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

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;
    }
};

* Изменить еще раз *

Я чувствую себя обязанным сказать потомкам, что на самом деле я бы не кодировал это таким образом. Я предполагаю, что вам нужны немного более структурированные данные, чем массив String, и вы поддерживаете массив String снаружи, а также 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
    }
}
person Kevin Galligan    schedule 14.11.2011
comment
было бы проще просто использовать теги или идентификаторы для идентификации EditTexts? Я не знаю, как будет работать реализация, но можно ли это сделать простым способом? - person Rob; 14.11.2011
comment
Я не уверен, что это не просто, но я посмотрю. - person Kevin Galligan; 14.11.2011
comment
Использует значение позиции, а не прямую ссылку на объект. Если вы имеете в виду проще, как не использовать внутренние анонимные классы Java, я бы сказал, что вы должны привыкнуть к сложности;) - person Kevin Galligan; 14.11.2011
comment
Это сложно в том смысле, что я, честно говоря, понятия не имею, что делает ваш код, я новичок в listadapters в целом. Для меня простота означала бы присвоение номера самому объекту EditText либо через тег/идентификатор (какими бы они ни были), либо расширение класса EditText для хранения дополнительной переменной, соответствующей строке, в которой он находится. Любая из этих жизнеспособных? - person Rob; 14.11.2011
comment
Как я уже сказал, вы должны привыкнуть к этим конструкциям. Концептуально они проще, чем то, о чем вы говорите, как только вы к ним привыкнете, и вы увидите подобные вещи повсюду в Android. Я не совсем уверен, что вы хотите сделать, но расширение EditText в качестве альтернативы тому, что я делаю, звучит для меня очень сложно. Не рекомендовал бы. - person Kevin Galligan; 14.11.2011
comment
Позвольте мне попытаться объяснить. «DataBucket» — это просто то, что я придумал. Я не знаю, какой тип данных у вас есть в вашем списке. Возможно, опубликуйте свой код, и я смогу прокомментировать. Приведенный выше код делает следующее. Он создает новый ArrayAdapter и переопределяет встроенный getView. Этот метод вызывается с «позицией». Там мы получаем данные и EditText для позиции. Секретный соус — это TextWatcher. Мы создаем встроенный TextWatcher и ссылаемся на «position» в afterTextChanged. Не уверен, как объяснить это больше, чем это. Нужен конкретный код. - person Kevin Galligan; 14.11.2011
comment
Я только что добавил некоторые детали и немного кода, а также изображение того, с чем я работаю. Количество строк и столбцов элементов gridview может варьироваться от 3 до 100. Возможно, вы могли бы объяснить что-то с учетом этого контекста? Отличается ли установка TextWatcher прямо в getView() от подключения моей расширенной версии TextWatcher? - person Rob; 14.11.2011
comment
Могу ли я просто добавить еще один метод в свой TextWatcher, который позволяет сбрасывать строку и столбец частных переменных? В моем методе getView() я бы просто сбросил значения TextWatcher. Будет ли это работать? - person Rob; 14.11.2011
comment
Данные, поддерживающие GridView, по-прежнему являются ListAdapter. Чтобы иметь какой-либо контроль над значениями строк/столбцов, вам нужно указать количество столбцов в GridView, а затем в основном предположить, какие значения были помещены в какие строки/столбцы. Вероятно, есть способ сделать это динамически, но это будет некрасиво. Данные находятся в ListAdapter, поэтому я бы не стал с этим бороться и попытался использовать их как есть. Доступ к значениям по единственному значению int 'position'. Вы храните данные в массивах строк? Это несколько усложняет. Дай мне подумать... - person Kevin Galligan; 14.11.2011
comment
В любой момент времени я знаю, сколько строк и столбцов в сетке. Итак, учитывая позицию p, row = (int) p / numCols и col = (int) p % numCols. Затем я передаю строку и столбец в TextWatcher. Массив строк является глобальной переменной в моей деятельности. - person Rob; 14.11.2011
comment
ХОРОШО. Думаю, я бы продолжил, имеет ли значение, как GridView отображает данные? Его по-прежнему поддерживает единый список. Вы можете сделать это по-своему, но я чувствую, что вы добавляете сложности, которая никоим образом не улучшает приложение. - person Kevin Galligan; 14.11.2011
comment
У меня есть свои причины, но я могу переключиться. Тем временем я разместил больше кода в stackoverflow.com/questions/8130257/, и я был бы рад наградить вас правильным ответом на оба, если вы хотите попробовать это там. - person Rob; 15.11.2011
comment
Если вы посмотрите на приведенный выше код, он теперь очень точно имитирует ваш ответ. Текст по-прежнему смещается, если я сильно прокручиваю. - person Rob; 15.11.2011

Чтобы отслеживать номер строки, каждый слушатель в 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;
    }
}
person wannik    schedule 14.11.2011
comment
Что произойдет, если элемент списка не является двойным? Что произойдет, если несколько элементов имеют одинаковое значение? Я думаю, что целью здесь было обновить значения в списке, а не удалить их, верно? Этот код был бы проблематичным, если бы данные не были Double, и вы были уверены, что никакие два значения не будут одинаковыми. Я не думаю, что мы это знаем. - person Kevin Galligan; 14.11.2011
comment
Если элемент списка не является Double, то замените все Double в коде типом элемента. И вы можете обновить значение элемента по ссылке d. - person wannik; 14.11.2011
comment
Что делать, если у вас есть два (или более) элемента с одинаковым значением? - person Kevin Galligan; 14.11.2011
comment
Если это один и тот же объект, то оба будут изменены. В противном случае будет изменен только один элемент. - person wannik; 14.11.2011
comment
Верно. Если бы это был список значений, я сомневаюсь, что вы бы хотели, чтобы оба объекта были изменены. - person Kevin Galligan; 14.11.2011
comment
Не уверен, почему это не дает мне покоя, но я чувствую, что все детали этого не были ясны. Скажем, это был список учеников в классе, и значение в каждой строке — это их оценка, их рост или что-то еще. Очень вероятно, что более одного ученика будут иметь одинаковое значение, поэтому редактировать их по значению было бы совершенно неправильно. Вам нужно знать, что все значения разные, и что они по необходимости ВСЕГДА будут разными. Поскольку запрос здесь состоял в том, чтобы ИЗМЕНИТЬ значения в EditText, у вас есть несколько проблем. - person Kevin Galligan; 14.11.2011
comment
Проблема №1. Поскольку вы ищете по значению, если вы измените значение в текстовом поле, оно обновит список только в первый раз. Скажем, первое значение было 5,75. В вашем обработчике есть ссылка на 5.75. Измените это значение на 6.2. Это должно сработать. Теперь еще раз поменяй. Это не удастся. Адаптер все еще будет искать обновление 5.75. - person Kevin Galligan; 14.11.2011
comment
Проблема № 2 (также известная как БОЛЬШАЯ ПРОБЛЕМА) — это несколько значений, как уже упоминалось. Скажем, это просто список повторений, выполненных в тренажерном зале с поднятием тяжестей. Возможно, вы НИКОГДА не сделаете одинаковое количество повторений в подходе, но это не гарантируется. Итак, скажем, вы делаете кучу подходов и это: 10, 8, 6, 6. Однако вы действительно немного жульничаете, поэтому вы возвращаетесь и меняете последний на 5. Упс. Теперь он говорит 10, 8, 5, 5. Вернитесь назад и измените предпоследнюю цифру на 6 снова, и это будет 10, 8, 6, 6. Ваши пользователи будут сообщать о странных проблемах с данными, и вы спишете это на глупые пользователи. - person Kevin Galligan; 14.11.2011
comment
Теперь, когда я думаю об этом, это не имеет большого значения, верно? На адаптере нет метода обновления. Вы можете только удалить или вставить. Поскольку первоначальный вопрос заключался в том, как обновить значения на месте, делать это по ссылке на значение в любом случае не помогает. Вам нужно найти позицию, удалить, а затем вставить в эту позицию, что является своего рода беспорядком. В любом случае, послушайте, почему вы вызываете int i = getPosition(d); так или иначе? Если вам нужна позиция, вы можете просто напрямую ссылаться на «позицию». Установите его в финал. - person Kevin Galligan; 14.11.2011

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

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

person m6tt    schedule 14.11.2011
comment
Почему на так много EditText смотрят свысока? Я не думаю, что мой экземпляр хорошо подходит для появления только одного всплывающего окна. Это только внешний вид или еще и память телефона? - person Rob; 14.11.2011
comment
Основная причина заключается в том, что во время прокрутки ListViews перерабатывает каждый элемент, когда он скрыт с экрана, и использует это переработанное представление для отображения следующего элемента из списка. Если переработанное представление содержит данные, как если бы ваш EditText содержал текст, эти данные, скорее всего, будут потеряны или неуместны при прокрутке вашего ListView. - person josephus; 14.11.2011

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

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

person josephus    schedule 14.11.2011

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

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

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

В режиме просмотра -

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

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

в вашем afterTextChanged -

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

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

person Sreeram Sunkara    schedule 26.04.2017