-2

I started a small Android project to re-learn a bit of Android development, and I'm already stuck...

I do not know how to implement the deletetion of an element of my ListView!

Here is the project: https://github.com/gdurelle/Listify

Right now it aims at showing a list of lists of elements.

I use a custom CursorAdapter to show my list of elements, and I already have a (ugly) destroy button, but I do not know how to make it delete an actual element from the list (and the database).

I use ActiveAndroid to manage the database in the ActiveRecord way.

Plus: I'm not sure wether or not to use getView(), bindView(), and/or newView()...

I created an issue to remember this and reference this question here: 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);
  }
}

And in my MainActivity :

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

I was thinking maybe about a:

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

with something like ListifyElement.load(ListifyElement.class, the_id_of_the_element).delete(); where the_id_of_the_element would be the DB's id of the element retrieived somehow from the click on it's delete_button in the UI...

UPDATE:

@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();
        }
    });

I get this error when I click the delete button:

java.lang.NullPointerException: Attempt to invoke virtual method 'void com.gdurelle.listify.models.ListifyElement.delete()' on a null object reference
Daniel Kvist
  • 3,032
  • 5
  • 26
  • 51
gdurelle
  • 2,079
  • 22
  • 38
  • What is your question? – barq Jan 31 '15 at 21:38
  • The question is in the title... How to make the click on the delete button of a row of the list make the row and its database field be deleted ? – gdurelle Jan 31 '15 at 21:53
  • The code is given via the github link. I did not put it here as it involves too many different classes and files to be as easily understandable as simply browing github or even cloning the repo in AndroidStudio. – gdurelle Jan 31 '15 at 21:56
  • Show a concise snippet of code that demonstrates your problem. – barq Jan 31 '15 at 22:00
  • @gdurelle: where you are calling `ListifyElement.delete()` method? – ρяσѕρєя K Feb 05 '15 at 07:28

5 Answers5

0

If you want a Button in each row, you should add it to your xml which you inflate in the newView(). After that, you should set OnClickListener to your Button inside bindView(). Something like that:

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);

}

vinitius
  • 3,212
  • 2
  • 17
  • 22
  • That's where I ended being stuck : how to get the associated object with the view ? like: ```Delete().from(ListfyElement.class).where("id=?",v.getTheObjectiD).execute();``` or ```ListifyElement.load(ListifyElement.class, v.getTheObjectId).delete();``` – gdurelle Feb 02 '15 at 21:06
  • Retrieve your `id` from the `cursor` and perform the deletion , for instance: ListfyElement.load(ListfyElement.class,cursor.getString(cursor.getColumnIndexOrThrow("id"))).delete(); – vinitius Feb 02 '15 at 21:12
  • ```java.lang.NullPointerException: Attempt to invoke virtual method 'void com.gdurelle.listify.models.ListifyElement.delete()' on a null object reference``` – gdurelle Feb 05 '15 at 17:09
0

Use below code to perform this--

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;
}
Akshay Paliwal
  • 3,718
  • 2
  • 39
  • 43
0

The context menu is relatively simple to implement on a listview. It (context menu) is activated on a long press of the item. My tip is to add an intent to the menu item so you can preserve the item id from your custom adapter and use it to perform whatever you want.

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>
danny117
  • 5,581
  • 1
  • 26
  • 35
0

lets start from the beginning, why do you need to use ActiveAndroid? I suggest to avoid such things, IMHO. You've mixed your logic, adapter should not change your database. Set a listener to adapter (i.e. IDeleteListener with single method onDeleteRequested(long rowId)). Next you need to pass rowId, something like:

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

In your fragment/activity class you should set a listener to adapter and work with your database. I suggest you to use LoaderManager this will automatically re-query your db on delete and handle life cycle of activity. Hope that helps!

Viktor Yakunin
  • 2,927
  • 3
  • 24
  • 39
  • I use ActiveAndroid because it's makes it easy to CRUD on DB without hassle. – gdurelle Feb 10 '15 at 21:42
  • All ORMs are cool at first glance but after you use it in real project you'll see that Android platform is tightly coupled with ContentProvider and Cursor interface. If you want to make things rock than use https://github.com/hamsterksu/Android-AnnotatedSQL - create a single interface (database schema) and take all the best from the platform. At the moment you'll have a lot of problems in your code. – Viktor Yakunin Feb 11 '15 at 12:48
0

I would suggest you to use an ArrayAdapter instead. Then you are just using an ArrayList (or any other array) and edit it, and call adapter.notifyDataSetChanged();, and the content of the ListView will update.

Declare the ArrayAdapter like this:

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

where the variable called yourStringArray is just an ArrayListof Strings.

Then, add the adapter to the ListView, like this:

yourListView.setAdapter(adapter);

Then, you can modify the contents of the list, by editing the yourStringArray list, and calling adapter.notifyDataSetChanged();. Here is a simple example:

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

Finally, to remove the selected item when the button is clicked, you can do something like this:

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();
    }
});

The whole onCreate method would look something like this:

// 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();
        }
    });
}
user229044
  • 232,980
  • 40
  • 330
  • 338
Daniel Kvist
  • 3,032
  • 5
  • 26
  • 51
  • I guess i'd better start reading this : https://github.com/codepath/android_guides/wiki/Using-an-ArrayAdapter-with-ListView then. I'm not really comfortable with all types of adapters so I do not know wich one is better for what... – gdurelle Feb 10 '15 at 21:40
  • @gdurelle I find the `ArrayAdapter` pretty easy to use. You just have an array of all the data to present in the list, and edit it to change the list contents. I also find it very easy to extend, but that is ofcourse only what I think. I would really suggest it anyways. Good luck! – Daniel Kvist Feb 11 '15 at 06:17