Wciąż nowy w Androidzie, a jeszcze bardziej w niestandardowym adapterze kursora, więc mam problem ze zrozumieniem, jak zapobiec ponownemu przetwarzaniu widoków przez mój widok listy, aby zapobiec wyświetlaniu danych wejściowych z jednego tekstu edycji w innym podczas przewijania. Widziałem w innym poście, że należy zmienić nazwę convertview, ale jak to zrobić, rysuję puste miejsce. Miałem nadzieję, że ktoś tutaj będzie mógł podać więcej szczegółów lub przykład, jak to zrobić w oparciu o kod, który napisałem do tej pory.

public class editview extends ListActivity {
    private dbadapter mydbhelper;
    private PopupWindow pw;
    public static int editCount;
    public static ListView listView;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mydbhelper = new dbadapter(this);
        mydbhelper.open();


        View footer = getLayoutInflater().inflate(R.layout.footer_layout, null);
        ListView listView = getListView();
        listView.addFooterView(footer);
        showResults();
        }

    //Populate view
    private void showResults (){
        Cursor cursor = mydbhelper.getUserWord();
        startManagingCursor(cursor);
        String[] from = new String[] {dbadapter.KEY_USERWORD};
         int[] to = new int[] {R.id.textType};
         ItemAdapter adapter = new ItemAdapter(this, R.layout.edit_row, cursor,
                        from, to);
            adapter.notifyDataSetChanged();
            this.setListAdapter(adapter);
            editCount = adapter.getCount();

    }


            //footer button
            public void onClick(View footer){
                    final MediaPlayer editClickSound = MediaPlayer.create(this, R.raw.button50);
                    editClickSound.start();
                    startActivity(new Intent("wanted.pro.madlibs.OUTPUT"));

                }

//custom cursor adapter
class ItemAdapter extends SimpleCursorAdapter {
    private LayoutInflater mInflater;
    private Cursor cursor;


    public ItemAdapter(Context context, int layout, Cursor cursor, String[] from,
            int[] to) {
        super(context, layout, cursor, from, to);
        this.cursor = cursor;
        mInflater = LayoutInflater.from(context);

    }


    static class ViewHolder {
        protected TextView text;
        protected EditText edittext;

    }

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


        ViewHolder holder;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.edit_row, null);


             holder = new ViewHolder();
            holder.text = (TextView) convertView.findViewById(R.id.textType);
            holder.edittext = (EditText) convertView.findViewById(R.id.editText);



            convertView.setTag(holder);

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

        }
        cursor.moveToPosition(position);
        int label_index = cursor.getColumnIndex("userword"); 
        String label = cursor.getString(label_index);

        holder.text.setText(label);

        return convertView;

    }

}

Zmieniłem to na

class ItemAdapter extends SimpleCursorAdapter {
    private LayoutInflater mInflater;
    private Cursor cursor;
    Map<Integer, String> inputValues = new HashMap<Integer, String>();
    public View getView(final int position, View convertView, ViewGroup parent) {
        ....

        ViewHolder holder;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.edit_row, null);


             holder = new ViewHolder();
            holder.text = (TextView) convertView.findViewById(R.id.textType);
            holder.edittext = (EditText) convertView.findViewById(R.id.editText);


            convertView.setTag(holder);

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

        }
        cursor.moveToPosition(position);
        int label_index = cursor.getColumnIndex("userword"); 
        String label = cursor.getString(label_index);

        holder.text.setText(label);
        String oldText =  inputValues.get(position);
        holder.edittext.setText(oldText == null ? "" : oldText); 
        holder.edittext.addTextChangedListener(new TextWatcher(){
            public void afterTextChanged(Editable editable) {
                inputValues.put(position, editable.toString());
            }

Ale jest to recykling po tym, jak cały tekst edycji zawiera dane. Próbowałem użyć holder.edittext.setText(oldText), ale ten sam efekt.

Start filling in fields

Finish filling in fields

Scroll up.

5
maebe 25 luty 2012, 02:01

2 odpowiedzi

Najlepsza odpowiedź

Po pierwsze, naprawdę nie chcesz, aby widok listy mógł ponownie przetwarzać swoje widoki. Zobacz recykling to ogromna optymalizacja. Aby uzyskać wiele naprawdę dobrych informacji na temat list, zobacz wykład Google IO: http://www.youtube .com/watch?v=wDBM6wVEO70

Biorąc to pod uwagę, poprawnie zidentyfikowałeś swój problem: masz znacznie mniej tekstów edycji niż elementów na liście. Podczas przewijania listy te EditTexts są przetwarzane, więc widzisz te same dane wejściowe w kółko.

Zasadniczo to, co musisz zrobić, to zapisać dane wejściowe dla swoich EditTexts w jakiejś strukturze danych (HashMap, jeśli będą edytować tylko kilka wartości, może List, jeśli będą zmieniać większość wartości, albo zadziała), która mapuje pozycję na wejście. Możesz to zrobić, dodając textChangedListener do swoich tekstów edycji w getView:

@Override
public View getView(final int position, View convertView, ViewGroup parent){
    ...
    cursor.moveToPosition(position);
    int label_index = cursor.getColumnIndex("userword");
    String label = cursor.getString(label_index);

    holder.text.setText(label);

    //clear whatever text was there from some other position
    //and set it to whatever text the user edited for the current 
    //position if available
    String oldText = yourMapOfPositionsToValues.get(position);
    holder.setText(oldText == null ? "" : oldText); 

    //every time the user adds/removes a character from the edit text, save 
    //the current value of the edit text to retrieve later
    holder.edittext.addTextChangedListener(new TextWatcher(){
        @Override
        public void afterTextChanged(Editable editable) {
            yourMapOfPositionsToValues.put(position, editable.toString());
        }
        ....
    };

    return convertView;
}

Za każdym razem, gdy użytkownik zakończy edycję, możesz przeglądać strukturę danych i robić cokolwiek z tymi wartościami.

Edytować:

Zmieniłem onTextChanged na afterTextChanged, ponieważ używałem tego wcześniej i wiem, że to działa. Należy pamiętać, że funkcja afterTextChanged jest wywoływana za każdym razem, gdy zmienia się LITERA, a nie tylko po zakończeniu wpisywania słowa przez użytkownika. Jeśli użytkownik wpisze "pies", po TextChanged zostanie wywołane trzy razy, najpierw przez "d", potem przez "do", a następnie przez "pies".

HashMap jest prosty: Map yourMapOfPositionsToValues ​​= new HashMap();

Aby dodać lub zaktualizować element: yourMap.put(position, someText); aby pobrać element: twojaMapa.get(pozycja);

Jeśli hashmapy nie mają sensu, poświęć trochę czasu na ich badanie. Stanowią niezwykle ważną strukturę danych.

Twoja implementacja TextWatchera jest nieprawidłowa. Struktura danych nie powinna należeć do jednego widoku, ale raczej do aktywności lub adaptera. Wydaje Ci się, że pozycje nie są stabilne, ponieważ Twoja lista należy do każdego widoku. Same pozycje są stabilne, o ile nie zmienią się dane bazowe, kursor zwróci te same dane za każdym razem dla tej samej pozycji. Jednak tekst edycji jest używany w wielu różnych pozycjach.

Utwórz hashmap jako zmienną instancji, którą zademonstrowałem powyżej w konstruktorze twojego adaptera. Następnie dodaj dokładnie TextWatcher, który napisałem pierwotnie, nie ma potrzeby stosowania nazwanej klasy, anonimowość jest prostsza. Twój kod powinien działać.

4
Sam Judd 25 luty 2012, 11:10

Rozwiązaniem tego problemu jest usunięcie dodanego textwatchera przed ustawieniem tekstu. W przeciwnym razie poprzedni textwatcher w tym widoku będzie nadal wywoływany razem z nowym textwatcherem. Przechowuj textwatcher jako znacznik w EditText, aby go śledzić.

Object oldWatcher = viewHolder.quantitySold.getTag();
    if(oldWatcher != null){
        viewHolder.quantitySold.removeTextChangedListener((CustomTextWatcher)oldWatcher);
    } 
    String oldText =  inputValues.get("key"+position);
    Log.d(TAG, "oldText: "+oldText+" position: "+position);
    viewHolder.quantitySold.setText(oldText == null ? "" : oldText);
    CustomTextWatcher watcher = new CustomTextWatcher(
            cursor.getString(SKUFragment.COL_NAME),
            cursor.getInt(SKUFragment.COL_ID),
            cursor.getDouble(SKUFragment.COL_UNIT_PRICE),
            position
    ) {
        @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) {
            if (s != null) {
                int quantity = 0;
                if (!TextUtils.isEmpty(s.toString())) {
                    quantity = Integer.parseInt(s.toString());
                    inputValues.put("key"+mPosition, "" + quantity);
                }else{
                    inputValues.put("key"+mPosition, "");
                }
                double value = quantity * skuPrice;
                mListener.onQuantityChanged(skuName+", position: "+mPosition, skuId, quantity, value);
            }
        }
    };
    viewHolder.quantitySold.setTag(watcher);
    viewHolder.quantitySold.addTextChangedListener(watcher);
1
SimuMax 4 czerwiec 2015, 05:38