1

Добрый день. Реализовано некое подобие магазина. Есть шаблон содержащий ImageView и два TextView. В эти View загоняются данные из БД с помощью CursorLoader. При клике на View в БД обновляется поле товара, которое обозначает, что товар был куплен (ноль или единица). До этого момента все работает хорошо. А как мне обрабатывать данные в момент загрузки магазина? Необходимо сделать так: если в БД у товара стоит единица, значит View не кликабельна.

public class ShopActivity extends AppCompatActivity implements LoaderCallbacks<Cursor>{
    SimpleCursorAdapter scAdapter;
    ListView lvData;
    DBHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_shop2);
        dbHelper = new DBHelper(this);

        String[] from = new String[] { DBHelper.ITEMS_IMAGE, DBHelper.ITEMS_NAME, DBHelper.ITEMS_COST };
        int[] to = new int[] { R.id.radioImageIV, R.id.radioDescriptionTV, R.id.radioCostTV}; 

        scAdapter = new SimpleCursorAdapter(this, R.layout.shop_item_radio, null, from, to, 0);

        lvData = (ListView) findViewById(R.id.lvData);

        lvData.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                view.setClickable(false);

                new ProgramManager(Shop2Activity.this).setBought(view); //обновляет запись в БД
            }
        });

        lvData.setAdapter(scAdapter); 
        getSupportLoaderManager().initLoader(0, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
        return new MyCursorLoader(this, dbHelper);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        scAdapter.swapCursor(cursor);
    }

   static class MyCursorLoader extends CursorLoader {
        DBHelper dbHelper;

        public MyCursorLoader(Context context, DBHelper db) {
            super(context);
            this.dbHelper = db;
        }

        @Override
        public Cursor loadInBackground() {
            SQLiteDatabase db = dbHelper.getReadableDatabase();
            Cursor cursor = db.query(DBHelper.ITEMS_TABLE_NAME, null, null, null, null, null,null);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return cursor;
        }

    }

shop_item_radio.xml

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:animateLayoutChanges="true"
    android:background="@drawable/border_shop"
    android:fillViewport="true"
    android:isScrollContainer="true">

    <ImageView
        android:id="@+id/radioImageIV"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_marginBottom="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:background="@drawable/border"
        android:padding="4dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/radio" />

    <TextView
        android:id="@+id/radioDescriptionTV"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="4dp"
        android:layout_marginTop="8dp"
        android:textAlignment="textStart"
        android:textColor="#004D40"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/radioImageIV"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/radioCostTV"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:drawableLeft="@drawable/ic_money_24dp"
        android:text="10000"
        android:textAlignment="gravity"
        android:textColor="#004D40"
        android:textSize="18sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="@+id/radioDescriptionTV" />
</android.support.constraint.ConstraintLayout>
R1zen
  • 311
  • Если Вас не затруднит, поделитесь примером. Все примеры, которые я нашел содержат deprecated методы – R1zen Feb 14 '18 at 14:06
  • Колонка isBought, Вот весь SQL запрос на создание БД

    CREATE TABLE "items" ( "_id" INTEGER PRIMARY KEY AUTOINCREMENT, "itemName" TEXT, "itemDescriptions" TEXT, "itemCost" INTEGER, "itemImage" TEXT, "isBought" INTEGER );

    – R1zen Feb 14 '18 at 14:42
  • В этот шаблон подставляю данные, по нему же и кликаю для покупки R.layout.shop_item_radio – R1zen Feb 14 '18 at 14:49
  • это отдельный xml файл, в котором располагаются элементы.Добавил листинг в вопрос – R1zen Feb 14 '18 at 14:54
  • Да, смысл в том, что элемент нельзя купить больше 1 раза – R1zen Feb 14 '18 at 15:01

2 Answers2

2

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

public class ShopAdapter extends SimpleCursorAdapter {

        private Context mContext;
        private int layout;
        private Cursor cr;
        private final LayoutInflater inflater;

        public ShopAdapter(Context context, int layout, Cursor c, String[] from,int[] to, int flag) {
            super(context, layout, c, from, to, flag);
            this.layout=layout;
            this.mContext = context;
            this.inflater=LayoutInflater.from(context);
            this.cr=c;
        }

        @Override
        public View newView (Context context, Cursor cursor, ViewGroup parent) {
                return inflater.inflate(layout, null);
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            super.bindView(view, context, cursor);

            ConstraintLayout item =(ConstraintLayout)view.findViewById(R.id.item);
            // здесь вместо  "isBought" можно вставить константу из контракта, типа DBHelper.IS_BOUGHT
            int column=cursor.getColumnIndexOrThrow("isBought");  

            item.setEnabled(cursor.getInt(column) != 1); // кликабельно если содержимое колонки не = 1
        }

}

В разметке айтема корневому лэйауту нужно присвоить ID:

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"

    android:id = "@+id/item"

    ...     >


</android.support.constraint.ConstraintLayout>

в активити используем наш адаптер вместо стандартного:

String[] from = new String[] { DBHelper.ITEMS_IMAGE, DBHelper.ITEMS_NAME, DBHelper.ITEMS_COST };
int[] to = new int[] { R.id.radioImageIV, R.id.radioDescriptionTV, R.id.radioCostTV}; 

scAdapter = new ShopAdapter(this, R.layout.shop_item_radio, null, from, to, 0);
pavlofff
  • 36,765
  • Большое Вам спасибо! Чуть-чуть подправил адаптер и заработало. Добавил рабочий листинг в вопрос. – R1zen Feb 15 '18 at 06:56
  • 1
    @R1zen эта строка if (column == 1){ view.setClickable(false); cost.setText("Куплено"); должна включать явные указания для обоих вариантов (что выводить, когда равно 1 и когда не равно 1), иначе в айтемах будет хаос при прокрутке: if (column == 1){ view.setClickable(false); cost.setText("Куплено"); } else { view.setClickable(true); cost.setText("НЕ куплено");} – pavlofff Feb 15 '18 at 07:01
  • else { view.setClickable(true); }

    Сделал вот так. Ведь данные для cost изначально приходят из String[] from = new String[] { DBHelper.ITEMS_IMAGE, DBHelper.ITEMS_NAME, DBHelper.ITEMS_COST}; int[] to = new int[] { R.id.radioImageIV, R.id.radioDescriptionTV, R.id.radioCostTV};

    – R1zen Feb 15 '18 at 07:07
0

Оставлю тут рабочий вариант адаптера, может кому-то пригодится. Спасибо @pavlofff ShopAdapter.java

public class ShopAdapter extends SimpleCursorAdapter implements View.OnClickListener{

        private Context mContext;
        private int layout;
        private Cursor cr;
        private final LayoutInflater inflater;


        public ShopAdapter(Context context, int layout, Cursor c, String[] from,int[] to, int flag) {
            super(context, layout, c, from, to, flag);
            this.layout=layout;
            this.mContext = context;
            this.inflater=LayoutInflater.from(context);
            this.cr = c;
        }

        @Override
        public View newView (Context context, Cursor cursor, ViewGroup parent) {
                return inflater.inflate(layout, null);
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            super.bindView(view, context, cursor);

            ConstraintLayout item = view.findViewById(R.id.item);
            item.setOnClickListener(this);
            TextView cost = view.findViewById(R.id.radioCostTV);
            int img = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.ITEMS_COST_ICON));
            int column = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.ITEMS_IS_BOUGHT));
            if (column == 1){
                view.setClickable(false); // кликабельно если содержимое колонки не = 1
                cost.setText("Куплено");
                //если раскоментить строку ниже, начинает работать странно. При покупке любого предмета, иконка меняется и у первого (при этом он кликабелен). `Пришлось тянуть иконки из БД.
                //cost.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_check_circle_black_24dp, 0, 0, 0);
            }
            cost.setCompoundDrawablesWithIntrinsicBounds(img, 0, 0, 0);
        }   
            else {
                view.setClickable(true);
                //cost.setText("НЕ куплено");
            }
    @Override
    public void onClick(View v) {
            //обработчик кликов пришлось делать в адаптере
        TextView cost = v.findViewById(R.id.radioCostTV);
        if (GameActivity.hero.getMoney() >= Integer.parseInt(cost.getText().toString())) {
            new ProgramManager(mContext).setBought(v);

            cost.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_check_circle_black_24dp, 0, 0, 0);
            cost.setText("Куплено");
            v.setClickable(false);
        }
        else
            Toast.makeText(mContext, "no money", Toast.LENGTH_SHORT).show();
    }
}
R1zen
  • 311
  • вот это //cost.setText("НЕ куплено"); нужно раскомментировать и если никакого текста не нужно то устанавливать пустую строку cost.setText(""); иначе у вас периодически будет появляться "куплено" где вы этого не ждали. И вообще на код с else студия должна указывать вам на недостижимый компонент, потому что в скобках вы напутали и else не относится к if – pavlofff Feb 15 '18 at 07:20
  • Чтобы картинка не вела себя странно, вам нужно так же указывать оба варианта, что должно быть на месте картинки, и в блоке if и в блоке else – pavlofff Feb 15 '18 at 07:27
  • Да, действительно помогло. Однако возникла другая проблема. Когда покупаешь 1 предмет, его иконка обновляется, текст меняется все хорошо. Можно выйти из экрана магазина, зайти заново и все будет хорошо. Но если купить предмет, прокрутить ListView вниз (чтобы этот предмет скрылся из виду) и потом прокрутить обратно вверх, картинка сбивается возвращается на старую, как и текст, а item снова кликабелен. Т.е. решается только закрытием активити. Я так понимаю надо как-то обновлять данные в курсоре? – R1zen Feb 15 '18 at 08:57
  • да, конечно. недостаточно изменить вид на экране, нужно сохранить новое состояние в БД, а затем обновить курсор, чтобы он имел актуальные данные, ведь вы строите список на основе данных курсора. Учитывайте, что то, что пропало при скролле с экрана и не сохранено в постояное хранилище - уничтожено. я бы рекомендовал вам посмотреть в сторону livedata, livemodel и room - новые инструменты гугл, если в них разобраться они существенно облегчают разработку – pavlofff Feb 15 '18 at 09:04
  • посмотрите этот ответ, там в общем то в коде есть все, что вам может понадобиться для правильной реализации. и работа с изменяемыми данными и обновление и сохранение (на примере избранного, что по сути то же, что надо и вам), но с новыми инструментами гугл из комментария выше логика несколько измениться конечно – pavlofff Feb 15 '18 at 09:09
  • Спасибо Вам! Буду изучать дальше – R1zen Feb 15 '18 at 09:31
  • Что-то я совсем запутался. Если я делаю обработчик кликов в Activity в таком случае игнорируются мои установки clicable(false) в адаптере. А как мне передать инфу об изменении курсора в Activity? – R1zen Feb 15 '18 at 10:42