Как передать данные из адаптера в Activity через тэг View

276
09 января 2017, 20:46

Пробую передать данные в Activity способом из этого ответа. В массиве myStr сейчас всегда оказывается последний элемент ListView.

Код моего адаптера:

public class MyCursorAdapter extends CursorAdapter {
    private int layout; //нужен для создания объектов класса View
    String[] myStr = new String[3];
    //OnFavoriteListener callback;
    public MyCursorAdapter(Context context, int layout, Cursor c, int flags) {
        super(context, c, flags);
        this.layout = layout;
    }
    public static class ViewHolder {
        public TextView txtBukva;
        public TextView txtSlovo;
        public ImageButton btnIzbrannoe;
        public void setBtnIzbrannoe(ImageButton btnIzbrannoe) {
            this.btnIzbrannoe = btnIzbrannoe;
        }
    }
    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(layout, parent, false);
        return view;
    }
    @Override
    public void bindView(View view, Context context, final Cursor cursor) {
        final ViewHolder holder = new ViewHolder();
        String bukva = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_SLOVO)).substring(0, 1).toUpperCase();
        final String slovo = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_SLOVO));
        final String izbrannoe = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_IZBRANNOE));
        int _ID = cursor.getInt(cursor.getColumnIndex(Contract.Entry._ID));
        holder.txtBukva = (TextView) view.findViewById(R.id.txtBukva);
        holder.txtSlovo = (TextView) view.findViewById(R.id.txtSlovo);
        holder.btnIzbrannoe = (ImageButton) view.findViewById(R.id.btnIzbrannoe);
        holder.txtBukva.setText(bukva);
        holder.txtSlovo.setText(slovo);
        holder.btnIzbrannoe.setFocusable(false);
        if (izbrannoe.equals("1")) {
            holder.btnIzbrannoe.setImageResource(R.drawable.icon_star_yellow);
        } else if (izbrannoe.equals("0")) {
            holder.btnIzbrannoe.setImageResource(R.drawable.icon_star_outline_black);
        }
        myStr[0] = _ID + "";
        myStr[1] = slovo;
        myStr[2] = izbrannoe;
        holder.btnIzbrannoe.setTag(myStr);
    }
}

Как передать данные, соответствующие кликнутому айтему?

Вот такой код получился в MainActivity

public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {
    private AutoCompleteTextView txtSearch;
    private Spinner spinner;
    private ListView list;
    private ImageButton btnClear;
    DBHeler db;
    private MySimpleCursorAdapter cursorAdapter;
    final private static int LOADER_RUS = 0; //0 - потому что в spinner первое значение имеет id 0
    final private static int LOADER_ENG = 1; //1 - потому что в spinner первое значение имеет id 1
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        db = new DBHeler(this);
        try {
            db.createDataBase();
            db.openDataBase();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        spinner = (Spinner) findViewById(R.id.spinner);
        txtSearch = (AutoCompleteTextView) findViewById(R.id.txtSearch);
        list = (ListView) findViewById(R.id.list);
        btnClear = (ImageButton) findViewById(R.id.btnClear);
        ArrayAdapter<?> adapter = ArrayAdapter.createFromResource(this, R.array.types, android.R.layout.simple_spinner_item);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(adapter);
        String[] from = new String[] {Contract.Entry.COLUMN_SLOVO, Contract.Entry.COLUMN_SLOVO, Contract.Entry.COLUMN_PEREVOD, Contract.Entry.COLUMN_IZBRANNOE};
        int[] to = new int[] {R.id.txtBukva, R.id.txtSlovo, R.id.txtPerevod, R.id.btnIzbrannoe};
        cursorAdapter = new MySimpleCursorAdapter(this, R.layout.item, null, from, to, 0);
        list.setAdapter(cursorAdapter);
        // инициализируем оба загрузчика
        getSupportLoaderManager().initLoader(LOADER_RUS, null, this);
        getSupportLoaderManager().initLoader(LOADER_ENG, null, this);
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            /*При выборе пункта в спинере обновление загрузчика происходит по нажатой позиции (а не по содержимому пункта списка) -
            третий аргумент колбэка с именем loadID в данном коде передает позицию выбранного пункта.
            Позиция может быть 0 или 1, ID наших загрузчиков так же 0 или 1 для русского и английского набора соответственно.*/
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int loadID, long l) {
                getSupportLoaderManager().getLoader(loadID).forceLoad(); // обновляем данные в курсоре
            }
            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {
            }
        });
    }
    @Override
    protected void onResume() {
        super.onResume();
        list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                Intent intent = new Intent(MainActivity.this, SlovoActivity.class);
                CharSequence strCharSequence = ((TextView)view.findViewById(R.id.txtSlovo)).getText();
                String str = strCharSequence.toString().toLowerCase().trim();
                String selectedItem = spinner.getSelectedItem().toString();
                if (selectedItem.equals("С русского на английский")) {
                    intent.putExtra("slovo", str);
                    intent.putExtra("type", "RU");
                    startActivity(intent);
                } else if (selectedItem.equals("С английского на русский")) {
                    intent.putExtra("slovo", str);
                    intent.putExtra("type", "EN");
                    startActivity(intent);
                }
            }
        });
        txtSearch.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                String[] from = new String[] {Contract.Entry.COLUMN_SLOVO, Contract.Entry.COLUMN_SLOVO, Contract.Entry.COLUMN_PEREVOD, Contract.Entry.COLUMN_IZBRANNOE};
                int[] to = new int[] {R.id.txtBukva, R.id.txtSlovo, R.id.txtPerevod, R.id.btnIzbrannoe};
                cursorAdapter = new MySimpleCursorAdapter(MainActivity.this, R.layout.item, null, from, to, 0); list.setAdapter(cursorAdapter);
            }
            @Override
            public void afterTextChanged(Editable editable) {
            }
        });
    }
    public void onFavoriteClick(View view) {
        Slovo data = (Slovo)view.getTag();
        long id = data.id;
        String slovo = data.word;
        String perevod = data.perevod;
        String bukva = data.simbol;
        boolean izbrannoe = data.favorite;
        //System.out.println(id + " " + slovo + " " + perevod + " " + bukva + " " + izbrannoe);
        if (izbrannoe == false) {
            ContentValues values = new ContentValues();
            values.put(Contract.Entry.COLUMN_IZBRANNOE, "1");
            // Вставляем новый ряд в базу данных и запоминаем его идентификатор
            long newRowId = db.database.update(Contract.Entry.TABLE_RUEN, values, Contract.Entry.COLUMN_SLOVO + "= ?", new String[]{slovo});
            izbrannoe = true;
            // Выводим сообщение в успешном случае или при ошибке
            if (newRowId == -1) { // Если ID  -1, значит произошла ошибка
                Toast.makeText(this, "Ошибка", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "Добавлено в избранное", Toast.LENGTH_SHORT).show();
                getSupportLoaderManager().getLoader(LOADER_RUS).forceLoad();
            }
        } else if (izbrannoe == true) {
            ContentValues values = new ContentValues();
            values.put(Contract.Entry.COLUMN_IZBRANNOE, "0");
            // Вставляем новый ряд в базу данных и запоминаем его идентификатор
            long newRowId = db.database.update(Contract.Entry.TABLE_RUEN, values, Contract.Entry.COLUMN_SLOVO + "= ?", new String[]{slovo});
            izbrannoe = true;
            // Выводим сообщение в успешном случае или при ошибке
            if (newRowId == -1) { // Если ID  -1, значит произошла ошибка
                Toast.makeText(this, "Ошибка", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "Удалено из избранного", Toast.LENGTH_SHORT).show();
                getSupportLoaderManager().getLoader(LOADER_RUS).forceLoad();
            }
        }
    }
    public void onClearBtnClick(View view) {
        txtSearch.setText("");
        btnClear.setVisibility(View.INVISIBLE);
    }
    protected void onDestroy() {
        super.onDestroy();
        db.close();
    }
    static class MyCursorLoader extends CursorLoader {
        Cursor cursor;
        DBHeler dbHeler;
        final int loaderID;
        public MyCursorLoader(Context context, DBHeler dbHeler, int id) {
            super(context);
            this.dbHeler = dbHeler;
            loaderID = id;
        }
        @Override
        protected Cursor onLoadInBackground() {
            switch (loaderID) {
                case LOADER_RUS:
                    cursor = dbHeler.getRuWords();
                    break;
                case LOADER_ENG:
                    cursor = dbHeler.getEnWords();
                    break;
            }
            return cursor;
        }
    }
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new MyCursorLoader(this, db, id);
    }
    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        cursorAdapter.swapCursor(data);
    }
    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        cursorAdapter.swapCursor(null);
    }
}
Answer 1

Поскольку у вас данные разного рода (число и строки), то разумнее использовать объект-модель для передачи данных. Кроме того, конкретно в вашем случае из-за ограничения готовой БД, которая хранит состояние избранного в виде строки, проведем полезное преобразование - логично это состояние представлять в виде boolean. Состояние true - в избранном, false - не в избранном, это сильно упростит логику обработки.

Создадим класс-модель (там где классы активити и проч.):

public class Data {
    long id;
    String word;
    String simbol;
    boolean favorite;
    public Data(long id, String word, String simbol, String favorite) {
        this.id = id;
        this.word = word;
        this.simbol = simbol;
        this.favorite = (favorite.equals("1"))? true:false;
    }
}

Теперь в адаптере заполняем эту модель и отправляем через тег (только необходимый для демонстрации код из адаптера):

public class MyCursorAdapter extends CursorAdapter {
    public static class ViewHolder {
        public TextView txtBukva;
        public TextView txtSlovo;
        public ImageButton btnIzbrannoe;
        public ViewHolder(View view) {
          txtBukva = (TextView) view.findViewById(R.id.txtBukva);
          txtSlovo = (TextView) view.findViewById(R.id.txtSlovo);
          btnIzbrannoe = (ImageButton) view.findViewById(R.id.btnIzbrannoe);
        }
    }
    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(layout, parent, false);
        ViewHolder viewHolder = new ViewHolder(view);
        view.setTag(viewHolder);
        return view;
    }
    @Override
    public void bindView(View view, Context context, final Cursor cursor) {
        ViewHolder holder = (ViewHolder) view.getTag();
        String bukva = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_SLOVO)).substring(0, 1).toUpperCase();
        final String slovo = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_SLOVO));
        final String izbrannoe = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_IZBRANNOE));
        long _ID = cursor.getLong(cursor.getColumnIndex(Contract.Entry._ID));
        holder.txtBukva.setText(bukva);
        holder.txtSlovo.setText(slovo);
        holder.btnIzbrannoe.setFocusable(false);
        if (izbrannoe.equals("1")) {
            holder.btnIzbrannoe.setImageResource(R.drawable.icon_star_yellow);
        } else {
            holder.btnIzbrannoe.setImageResource(R.drawable.icon_star_outline_black);
        }
        // через конструктор устанавливаем значения для передачи в Activity
        holder.btnIzbrannoe.setTag(new Data(_ID,slovo,bukva,izbrannoe);
    }
}

Получаем в Activity:

public void onFavoriteClick(View view) {
      Data data = (Data)view.getTag();
      long id = data.id;
      String slovo = data.word;
      String bukva = data.simbol;
      boolean izbrannoe = data.favorite;
}

Если данные планируется использовать за пределами кликера, то выносим локальные переменные в поля класса.

PS: Так же исправил использование паттерна ViewHolder. Назначение паттерна - кэшировать ссылки на объекты айтема (у вас эти ссылки получают каждый раз при биндинге в айтем и холдер в таком виде бесполезен). Дело в том, что получение ссылки на виджет из разметки методом findViewById() довольно ресурсоемкая операция, чтобы выполнять ее при создании каждого айтема, поэтому эти ссылки получают один раз при создании разметки, а затем хранят в холдере. Выше методы newView() и bindView() без сокращений кода.

PPS: значение ID из БД рекомендуется получать в long (а не int) так как это число может быть очень большим, рекомендую исправить (в примере внес исправления).

READ ALSO
Как в TextView записать химическую формулу?

Как в TextView записать химическую формулу?

Как в TextView записать химическую формулу с верхними и нижними символами?

270
Табы на чистом CSS

Табы на чистом CSS

Для реализации табов использую :target, но при загрузке страницы нужно чтобы первый блок был видимБлоки будут выводится динамически

438