Как удалить строку из ListView с помощью CursorAdapter

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

Я не знаю, как реализовать удаление элемента моего ListView!

Вот проект: https://github.com/gdurelle/Listify

Прямо сейчас это стремится показать список списков элементов.

Я использую кастом CursorAdapter чтобы показать мой список элементов, и у меня уже есть (некрасивая) кнопка уничтожения, но я не знаю, как заставить его удалить фактический элемент из списка (и базы данных).

Я использую ActiveAndroid для управления базой данных способом ActiveRecord.

Плюс: я не уверен, что использовать или не использовать getView(), bindView()и / или newView()...

Я создал проблему, чтобы запомнить это и сослаться на этот вопрос здесь: https://github.com/gdurelle/Listify/issues/1

public class ListifyCursorAdapter extends CursorAdapter {
  public String content;
  public ListifyCursorAdapter(Context context, Cursor cursor) {
    super(context, cursor, 0);
  }
  // The newView method is used to inflate a new view and return it, you don't bind any data to the view at this point.
  @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
      return LayoutInflater.from(context).inflate(R.layout.element_line, parent, false);
    }
  // The bindView method is used to bind all data to a given view such as setting the text on a TextView.
  @Override
  public void bindView(View view, Context context, Cursor cursor) {
  // Find fields to populate in inflated template
    TextView tvBody = (TextView) view.findViewById(R.id.element_content);
    // Extract properties from cursor
    content = cursor.getString(cursor.getColumnIndexOrThrow("content"));
    // Populate fields with extracted properties
    tvBody.setText(content);
  }
}

И в моей основной деятельности:

 Cursor cursor = ListifyElement.fetchResultCursor();
 adapter = new ListifyCursorAdapter(this, cursor);
 listView.setAdapter(adapter);

Я думал, может быть, о:

Button delete_button = (Button) listView.findViewById(R.id.delete_button);

с чем-то вроде ListifyElement.load(ListifyElement.class, the_id_of_the_element).delete(); где the_id_of_the_element будет идентификатором БД элемента, полученного каким-либо образом из нажатия на кнопку delete_button в пользовательском интерфейсе...

ОБНОВЛЕНИЕ:

@Override
public void bindView(View view, Context context, final Cursor cursor) {
    // Find fields to populate in inflated template
    TextView tvBody = (TextView) view.findViewById(R.id.element_content);
    // Extract properties from cursor
    content = cursor.getString(cursor.getColumnIndexOrThrow("content"));
    // Populate fields with extracted properties
    tvBody.setText(content);

    Button delete_button = (Button) view.findViewById(R.id.delete_button);
    delete_button.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View v){
            System.out.println(cursor.getColumnName(0)); // Id
            System.out.println(cursor.getColumnName(1)); // ListifyContainer
            System.out.println(cursor.getColumnName(2)); // content
            System.out.println(cursor.getColumnIndexOrThrow("Id")); // 0
            ListifyElement.load(ListifyElement.class, cursor.getColumnIndexOrThrow("Id")).delete();
            notifyDataSetChanged();
        }
    });

Я получаю эту ошибку, когда нажимаю кнопку удаления:

java.lang.NullPointerException: Attempt to invoke virtual method 'void com.gdurelle.listify.models.ListifyElement.delete()' on a null object reference

5 ответов

Контекстное меню относительно просто реализовать в виде списка. Оно (контекстное меню) активируется при длительном нажатии пункта. Мой совет заключается в том, чтобы добавить намерение к пункту меню, чтобы вы могли сохранить идентификатор элемента из своего пользовательского адаптера и использовать его для выполнения чего угодно.

public class MainActionBarTabListFragment extends ListFragment {

@Override
public void onActivityCreated(Bundle savedState) {
    super.onActivityCreated(savedState);
    registerForContextMenu(getListView());
}


@Override
public void onCreateContextMenu(ContextMenu menu, View v,
        ContextMenuInfo menuInfo) {

    AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
        String name = adapter2.getItem(info.position).get_name();
        menu.setHeaderTitle(name);
        MenuInflater inflater = this.getActivity().getMenuInflater();
        inflater.inflate(R.menu.menulistitem, menu);
        MenuItem mi = menu.findItem(R.id.action_context_delete);
        Intent i = new Intent();
        i.putExtra("id", adapter2.getItem(info.position).get_id());
        mi.setIntent(i);
    }
}

@Override
public boolean onContextItemSelected(MenuItem item) {

    switch (item.getItemId()) {
    case R.id.action_context_delete:
        Intent i = item.getIntent();
        if (i != null) {
            Bundle b = i.getExtras();
            if (b != null) {
                int id = b.getInt("id");
                Uri deleteIdUri = ContentUris.withAppendedId(
                        RidesDatabaseProvider.CONTENT_URI, id);
                context.getContentResolver()
                        .delete(deleteIdUri, null, null);
                return true;
            }
        }
    }
    return super.onContextItemSelected(item);
}

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:yourapp="http://schemas.android.com/apk/res-auto" >

    <item
        android:id="@+id/action_context_delete"
        android:icon="@drawable/action_about"
        android:orderInCategory="100"
        android:showAsAction="ifRoom"
        android:title="delete"
        yourapp:showAsAction="ifRoom"/>

</menu>

Давайте начнем с самого начала, зачем вам нужен ActiveAndroid? Я предлагаю избегать таких вещей, ИМХО. Вы перепутали свою логику, адаптер не должен изменять вашу базу данных. Установите прослушиватель для адаптера (т. Е. IDeleteListener с одним методом onDeleteRequested(long rowId)). Далее вам нужно передать rowId, что-то вроде:

delete_button.setTag(cursor.getLong(0));
delete_button.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View v){
            listener.onDeleteRequested((Long)v.getTag());
        }
    });

В вашем классе фрагмента / деятельности вы должны настроить слушателя на адаптер и работать с вашей базой данных. Я предлагаю вам использовать LoaderManager, он автоматически повторно запросит вашу базу данных при удалении и обработает жизненный цикл активности. Надеюсь, это поможет!

Я бы предложил вам использовать ArrayAdapter вместо. Тогда вы просто используете ArrayList (или любой другой массив) и отредактируйте его, и вызовите adapter.notifyDataSetChanged();и содержание ListView будет обновлять.

Объявить ArrayAdapter как это:

ArrayAdapter<String> adapter = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_list_item_1, yourStringArray);

где переменная называется yourStringArray это просто ArrayListиз Strings.

Затем добавьте адаптер к ListView, как это:

yourListView.setAdapter(adapter);

Затем вы можете изменить содержимое списка, отредактировав yourStringArray список и вызов adapter.notifyDataSetChanged();, Вот простой пример:

list.add("Hello!");
adapter.notifyDataSetChanged();

Наконец, чтобы удалить выбранный элемент при нажатии кнопки, вы можете сделать что-то вроде этого:

int selectedItemIndex = 0;

yourArrayList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        selectedItemIndex = position;
     }
});

yourDestroyButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        yourStringArray.remove(selectedItemIndex);
        adapter.notifyDataSetChanged();
    }
});

Целый onCreate Метод будет выглядеть примерно так:

// Called when the activity is created
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ArrayList<String> yourStringArray = new ArrayList<>();

    ArrayAdapter<String> adapter = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_list_item_1, yourStringArray);

    int selectedItemIndex = 0;

    ListView yourListView = (ListView) findViewById(R.layout.yourListViewID);
    yourListView.setAdapter(adapter);
    yourListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            selectedItemIndex = position;
        }
    });

    yourDestroyButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            yourStringArray.remove(selectedItemIndex);
            adapter.notifyDataSetChanged();
        }
    });
}

Если вы хотите Button в каждом ряду, вы должны добавить его в свой xml который вы раздувать в newView(), После этого вы должны установить OnClickListener на ваш Button внутри bindView(), Что-то вроде того:

public void bindView(View view, Context context, Cursor cursor) {
 // Find fields to populate in inflated template
TextView tvBody = (TextView) view.findViewById(R.id.element_content);
Button delete_button = (Button) view.findViewById(R.id.delete_button);
delete_button.setOnClickListener(new OnClickListener(){
     @Override
     public void onClick(View v){
       //As you're using ActiveAndroid
       new Delete().from(ListfyElement.class).where("yourCondition=?",yourCondition).execute();
       notifyDataSetChanged();

     } 
});
// Extract properties from cursor
content = cursor.getString(cursor.getColumnIndexOrThrow("content"));
// Populate fields with extracted properties
tvBody.setText(content);

}

Используйте код ниже, чтобы выполнить это:

ListView lv;
ArrayList<String> arr = new ArrayList<String>();
ArrayAdapter adapter;
int position;

@Override
protected void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.extra);

    for(int i=0;i<5;i++)arr.add("Hi @ "+i);

    lv = (ListView) findViewById(R.id.listViewBirthday);
    lv.setOnItemLongClickListener(this);
    adapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1, arr);
    lv.setAdapter(adapter);
}

@Override
public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {
    // TODO Auto-generated method stub
    super.onCreateContextMenu(menu, v, menuInfo);
    MenuItem it1=menu.add("Delete");
}

@Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int p, long arg3) 
{
    position = p;
    registerForContextMenu(lv);
    return false;
}

@Override
public boolean onContextItemSelected(MenuItem item) {
    // TODO Auto-generated method stub
    if(item.getTitle().equals("Delete"))
    {
        arr.remove(position);
        adapter.notifyDataSetChanged();
        Toast.makeText(getBaseContext(), "deleted", Toast.LENGTH_SHORT).show();
    }
    return true;
}
Другие вопросы по тегам