Пытаюсь использовать Custom CursorAdapter для моего списка задач, но у меня возникают проблемы

Я делаю простое приложение со списком задач и использую базу данных для хранения всего. Я написал собственный CursorAdapter, расширяющий ResourceCursorAdapter, для обработки данных. Все отображается нормально, но как только вы начинаете выходить за рамки этого, все становится отрывочным. Во-первых, моя реализация:

import android.app.AlertDialog;
import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.ResourceCursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.Filterable;
import android.widget.LinearLayout;
import android.widget.TextView;

/**
 * @author Dave Smith
 *
 */
public class TaskCursorAdapter extends ResourceCursorAdapter {

    /**
     * @param context
     * @param layout
     * @param c
     * @param from
     * @param to
     * @param flags
     */
    private Context context;
    private int layout, isCheckedFromDb, IdFromDb;
    private Cursor cursor;

    public TaskCursorAdapter(Context context, int layout, Cursor c,
            int flags) {
        super(context, layout, c, flags);
        this.context = context;
        this.layout = layout;
    }
    public TaskCursorAdapter(Context context, int layout){
        super(context, layout, null, 0);
        this.context = context;
        this.layout = layout;
    }
    public TaskCursorAdapter(Context context, int layout, Cursor c){
        super(context, layout, c, 0);
        this.context = context;
        this.layout = layout;
    }
    public TaskCursorAdapter(Context context, int layout, Cursor cursor, boolean autoRequery)
    {
        super(context, layout, cursor, autoRequery);
        this.context = context;
        this.layout = layout;
    }
    public View newView(Context context, Cursor cursor, ViewGroup parent){
        final LayoutInflater inflater = LayoutInflater.from(context);
        View v = inflater.inflate(layout, parent, false);
        return v;
    }
    public void bindView(View v, Context context, Cursor c){
        CheckBox taskCheckBox = (CheckBox)v.findViewById(R.id.taskCheckBox);
        TextView taskText = (TextView)v.findViewById(R.id.taskTitle);
        TextView taskNote = (TextView)v.findViewById(R.id.taskNote);
        final long assignedID = v.getID();
        //v.setClickable(true);     
        //LinearLayout holder = (LinearLayout)v.findViewById(R.id.container);
        //holder.setClickable(true);
        //IdFromDb = c.getInt(c.getColumnIndex(NagTasksDatabaseHelper.ID));
        taskText.setText(c.getString(c.getColumnIndex(NagTasksDatabaseHelper.TASK)));
        taskNote.setText(c.getString(c.getColumnIndex(NagTasksDatabaseHelper.NOTE)));
        //isCheckedFromDb = c.getInt(c.getColumnIndex(NagTasksDatabaseHelper.CHECKED));
        taskCheckBox.setChecked(c.getInt(c.getColumnIndex(NagTasksDatabaseHelper.CHECKED))==1);
        //cursor = c;
        taskCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                //Really Old method:
                /*NagTasksDatabaseHelper helper = new NagTasksDatabaseHelper(null);
                int idCol = cursor.getColumnIndex(NagTasksDatabaseHelper.ID); 
                int taskID = helper.getNewTaskId();
                if (idCol!=-1)
                {
                    taskID= cursor.getInt(idCol);
                    if (isChecked){
                        helper.checkOffTask(taskID);
                    } else {
                        helper.uncheckTask(taskID);
                    }
                }
                //TODO Solve how to use helper to change ISCHECKED in database
                helper.close();
                //Old method:
                NagTasksDatabaseHelper helper = new NagTasksDatabaseHelper(null);
                if (isChecked) {
                    helper.checkOffTask(IdFromDb);
                } else {
                    helper.uncheckTask(IdFromDb);
                }
                helper.close();*/
                //Current method:

                if (assignedID!=View.NO_ID) {
                    NagTasksDatabaseHelper helper = new NagTasksDatabaseHelper(buttonView.getContext());
                    if (isChecked) {
                        helper.checkOffTask(assignedID);
                    } else {
                        helper.uncheckTask(assignedID);
                    }
                    helper.close();
                } else {
                    AlertDialog.Builder builder = new AlertDialog.Builder(buttonView.getContext());
                    builder.setMessage("No ID found. Try something else.");
                    builder.setCancelable(true);
                    builder.create().show();
                }
            } 
        });
    }

}

Выпуск 1

EDIT: Проблема решена, хотя я не знаю, как это сделать.

В текущем состоянии я получаю сообщение об ошибке в консоли Eclipse, когда нажимаю «Выполнить». Это вообще не влияет на то, насколько хорошо он работает, но в предыдущей реализации, где я использовал макет, который имел только CheckBox и просто изменил текст CheckBox, эта ошибка не появлялась:

[2012-06-18 16:10:15 - ddms] null
java.lang.NullPointerException
    at com.android.ddmlib.JdwpPacket.writeAndConsume(JdwpPacket.java:213)
    at com.android.ddmlib.Client.sendAndConsume(Client.java:575)
    at com.android.ddmlib.HandleHello.sendHELO(HandleHello.java:142)
    at com.android.ddmlib.HandleHello.sendHelloCommands(HandleHello.java:65)
    at com.android.ddmlib.Client.getJdwpPacket(Client.java:672)
    at com.android.ddmlib.MonitorThread.processClientActivity(MonitorThread.java:317)
    at com.android.ddmlib.MonitorThread.run(MonitorThread.java:263)

[2012-06-18 16:10:15 - ddms] null
java.lang.NullPointerException
    at com.android.ddmlib.JdwpPacket.writeAndConsume(JdwpPacket.java:213)
    at com.android.ddmlib.Client.sendAndConsume(Client.java:575)
    at com.android.ddmlib.HandleHello.sendHELO(HandleHello.java:142)
    at com.android.ddmlib.HandleHello.sendHelloCommands(HandleHello.java:65)
    at com.android.ddmlib.Client.getJdwpPacket(Client.java:672)
    at com.android.ddmlib.MonitorThread.processClientActivity(MonitorThread.java:317)
    at com.android.ddmlib.MonitorThread.run(MonitorThread.java:263)

Выпуск 2

ListFragment, который использует этот CursorAdapter, не выполняет свой метод OnListItemClick(). Точно так же я зарегистрировал ListView ListFragment для использования ContextMenu (с registerForContextMenu()), но долгое нажатие не вызывает контекстное меню.

Выпуск 3

Раскомментирование taskCheckBox.setOnCheckedChangeListener обычно приводит к плохим последствиям при установленном флажке. Как правило, он генерирует исключение нулевого указателя и аварийно завершает работу, потому что не знает, для чего _id вызывать checkOffTask() или uncheckTask(). (РЕДАКТИРОВАТЬ: изменения, внесенные в последнее редактирование... по-прежнему не делают то, что я хочу, но, по крайней мере, не падает. Также раздражает, что getItemId() запрашивает позицию и я не знаю, как получить это для данного элемента в ListView из курсора, иначе я бы использовал это.)


Может ли кто-нибудь помочь мне решить эти проблемы? Я до сих пор в основном не понимаю, как работают CursorAdapters, несмотря на -something-read">просить помощи по этому вопросу раньше.

EDIT: я должен отметить, что в моем приложении есть другой список, в котором используется очень слегка измененный ResourceCursorAdapter (см. связанный вопрос), и ни одна из этих проблем там не возникла. (Хотя у проблемы 3 нет причин для появления, так как это простой список строк.)


person MowDownJoe    schedule 18.06.2012    source источник
comment
Почему вы использовали ResourceCursorAdapter вместо CursorAdapter?   -  person Macarse    schedule 19.06.2012
comment
@Macarse: Потому что он справляется с инфляцией ресурса макета для меня? Честно говоря, изначально я использовал SimpleCursorAdapter, но на самом деле я не воспользовался преимуществами, которые дало мне использование SimpleCursorAdapter. Итак, я спустился по звену в цепочке.   -  person MowDownJoe    schedule 19.06.2012
comment
Попробуйте CursorAdapter напрямую.   -  person Macarse    schedule 20.06.2012
comment
@Macarse: Не могли бы вы опубликовать ответ с примером кода? Честно говоря, мне нравится, что ResourceCursorAdapter справляется с инфляцией за меня, но если вы думаете, что у вас есть что-то, что решает мои проблемы, покажите мне.   -  person MowDownJoe    schedule 20.06.2012


Ответы (2)


ВЫПУСК 1:

Пропущу, так как решено.

ВОПРОС 2:

Как отмечалось в предыдущем вопросе, если представление имеет фокусируемый объект, оно невозможно сделать все представление интерактивным из-за ошибки в SDK. Итак, я перешел к расширенному XML и добавил атрибут android:focusable="false" к каждому элементу. Элементы списка теперь кликабельны и кликабельны.

ВОПРОС 3:

Как заметил хороший парень на Reddit, я был на правильный путь со старым методом, но нужно было использовать локальный final long вместо параметра.

person MowDownJoe    schedule 26.06.2012

Сначала случайные предложения. Не используйте getColumnIndex или, по крайней мере, кэшируйте это где-нибудь. Индексы всегда соответствуют порядку в проекции, поэтому общий шаблон состоит в том, чтобы просто использовать это (поэтому, если ваша проекция ['_id', 'something', 'else'] , затем объявите INDX_ID = 0; INDX_SOMETHING = 1 и т. д. ..)

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

Пометьте представление идентификатором строки или позицией в bindView (как отмечено на Reddit, это не v.getID()). Вы можете сделать это, используя checkBoxView.setTag(). Это позволяет вам просто произвольно помечать данные в представлении.

Затем зарегистрируйте прослушиватель изменений проверки в новом представлении — в идеале, передавайте статический класс вместо анонимного внутреннего, но, по крайней мере, вы ограничите частоту их создания, делая это в newView. Обратный вызов будет использовать v.getTag() для получения идентификатора строки (или чего-либо еще, что вы поместите в bindView) и может использовать его для выполнения любой работы, которую вы хотите.

person John Reck    schedule 28.06.2012
comment
Это на самом деле очень умно и то, что я никогда не рассматривал. То, что я сделал, отлично работает, и я, вероятно, пока буду придерживаться этого, но я определенно буду помнить об этом на будущее. - person MowDownJoe; 29.06.2012