I've created a ListView and bind it to an adaptor to display data from a table. It works great. I can select on a row and it display the product name that I'm selecting. Now I've added an ImageView in the layout that will act as delete buttons on each row. My problem is I can't figure out how to add the code to make it so that when a user select the ImageView (delete button), it will delete the row. I've searched and found a lot of articles on this topic and tried a lot of them and none really work my code. Do I need to create a getView function? Also I've tried inserting the getTag(), but I couldn't make it work. Can you help me with a sample code that may work with my simple code or point me to the right direction? Here is my code:
private void displayListView() {
prodinputHelper = new DBAdaptorProductInput(this);
prodinputHelper.open();
Cursor cursor = prodinputHelper.fetchAllProductInput();
// The desired columns to be bound
String[] columns = new String[] {
DBAdaptorProductInput.KEY_PRODUCTTYPE,
DBAdaptorProductInput.KEY_PRODUCTNAME,
DBAdaptorProductInput.KEY_MANUFACTURER,
DBAdaptorProductInput.KEY_VISC40,
DBAdaptorProductInput.KEY_VISC100,
DBAdaptorProductInput.KEY_VI,
DBAdaptorProductInput.KEY_DEN15C,
DBAdaptorProductInput.KEY_VISCTEXT,
DBAdaptorProductInput.KEY_BASEOILTYPE,
DBAdaptorProductInput.KEY_BASEOIL,
DBAdaptorProductInput.KEY_ADDITIVES,
DBAdaptorProductInput.KEY_OTHERADDITIVES,
DBAdaptorProductInput.KEY_THICKENER,
DBAdaptorProductInput.KEY_NLGI,
DBAdaptorProductInput.KEY_COMMENT,
DBAdaptorProductInput.KEY_PACKAGES,
DBAdaptorProductInput.KEY_AREA,
};
// the XML defined views which the data will be bound to
int[] to = new int[] {
R.id.code,
R.id.name,
R.id.manufacturer,
R.id.visc40,
R.id.visc100,
R.id.viscindex,
R.id.den15c,
R.id.visctext,
R.id.baseoiltype,
R.id.baseoil,
R.id.additives,
R.id.otheradditives,
R.id.thickener,
R.id.nlgi,
R.id.comments,
R.id.packages,
R.id.area,
};
// create the adapter using the cursor pointing to the desired data
//as well as the layout information
dataAdapter = new SimpleCursorAdapter(
this, R.layout.activity_product_review_info, cursor, columns, to, 0);
ListView listView = (ListView) findViewById(R.id.listView1);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
//SetOnItemClickListener for the ListView
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listView, View view,
int position, long id) {
// Get the cursor, positioned to the corresponding row in the result set
Cursor cursor = (Cursor) listView.getItemAtPosition(position);
// Get the Customer Name from this row in the database.
String countryCode = cursor.getString(cursor.getColumnIndexOrThrow("ProductName"));
Toast.makeText(getApplicationContext(), countryCode, Toast.LENGTH_SHORT).show();
}
});
}
You need to have a custom adapter that extends BaseAdapter or SimpleCursorAdapter.
In the adapter's getView() method set the onClickListener for your ImageView.
Related
I have a ListView backed by a SQLite db table which displays a song title (TextView) and a button (ImageButton) in the list item XML for each row. I setup an onClick() event for the button to take action on the song on the row they clicked on.
The ListView correctly displays all contents from the db table using a SimpleCursorAdapter.
Here's the question: when I click on the ImageButton it correctly calls the onClick event for the button with parameter (View view); how do I get to the Cursor row data for the row in which the button was clicked from the View passed into the event? I need the row _ID value in order to act on the correct song. I also have access to the dbAdapter in a Class field if I can get there from that object.
Notes: When I add an ImageButton to the Item List the onItemClickListener no longer fires if I click on the row containing the song or on the button.
And please, if there is a better design pattern to give the user the functionality of selecting a ListView item and performing an action on it, let me know. My intention is to eventually add 2-3 buttons per row for Delete, Info, Play, etc.
// Load ListView with previously downloaded files
dbHelper = new DBHelper(this);
// Create Cursor holding db data
Cursor cursor = dbHelper.fetchData();
// Map db columns to view ids
String[] columns = new String[]{
DBContract.Songs.COLUMN_NAME_NAME,
DBContract.Songs.COLUMN_NAME_LOADED_DATETIME
};
int[] to = new int[]{
R.id.songName,
R.id.songDateLoaded
};
// Create the dbAdapter
dbAdapter = new SimpleCursorAdapter(this, R.layout.songs, cursor, columns, to, 0);
// Assign the adapter to the ListView
ListView listView = findViewById(R.id.songsListView);
listView.setAdapter(dbAdapter);
// Anonymous OnItemClickListener
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {...
With Mike's persistent and patient assistance, I implemented the solution below.
The key is using a setViewBinder() on the SimpleCursorAdapter and then assigning the db row key to the Tag property of the ImageButton. Then, in XML, define an onClick() event and in that event, you now have access to the db row key from the view.getTag() method.
// Bind the Cursor record _ID to the ImageButton Tag property;
// So when it is called, we can delete the record with the Tag property value.
dbAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue(View view, Cursor cursor, int i) {
if (view.getId() == R.id.songName) {
final long id = cursor.getLong(cursor.getColumnIndex(DBContract.Songs._ID));
final ImageButton delete = ((View)view.getParent()).findViewById(R.id.deleteSongButton);
delete.setTag(id);
}
return false;
}
});
public void DeleteSong(View view) {
final long id = (long) view.getTag();
dbHelper.RemoveSong(id);
dbHelper.fetchSongsAndUpdateAdapter(dbAdapter);
Toast.makeText(this, "Song removed!", Toast.LENGTH_LONG).show();
}
Thanks Mike!
I am quite new to Android development. I managed to get data saved to SQLite database. Now, what I want is to view these data when I call viewData(). I have viewData() which shows data as a Toast as I made it as a sample. Now I need these data to show on a new activity using a ListView, but the number of data to show is depending on how many data is in the database at the moment, If user saved 10 items then I want all the 10 items to shown up. How can I do it?
I hope my question is clear.
Thanks in advance.
you could use ListView
declare it in your layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="#+id/list"
android:layout_height="wrap_content"
android:layout_width="match_parent">
</ListView>
</LinearLayout>
in yor activity declare a globar var:
ListView listView;
and onCreate
listView = (ListView) findViewById(R.id.list);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, android.R.id.text1, values);
// Assign adapter to ListView
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id){
// ListView Clicked item index
int itemPosition = position;
// ListView Clicked item value
String itemValue = (String) listView.getItemAtPosition(position);
}
});
datos can be an array that you can populate with data that you extract from your data base and that's the most simple way to show it. if you want to customizise your listView you can create a custom adapter, or in other way the newest element that replace listView is ReciclerView. I hope tihs help you
You can use a SimpleCursorAdapter:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ListView answerList=(ListView)findViewById(R.id.answerList);
Cursor mCursor = getData();
startManagingCursor(mCursor);
// now create a new list adapter bound to the cursor.
// SimpleListAdapter is designed for binding to a Cursor.
ListAdapter adapter = new SimpleCursorAdapter(this, // Context.
android.R.layout.two_line_list_item,
mCursor, // Pass in the cursor to bind to.
// Array of cursor columns to bind to.
new String[] {"_id", "answer"};
// Parallel array of which template objects to bind to those
// columns.
new int[] { android.R.id.text1,android.R.id.text2 });
// Bind to our new adapter.
answerList.setAdapter(adapter);
}
private Cursor getData() {
String sq = "Select _id, answer from foo";
Cursor c = db.rawQuery(sql);
return c;
}
I will try to give an in-depth answer to this.
Whenever you want to fetch and display a list of data from the database, you can use a ListView, GridView, Spinner, etc for it.
You can use a CursorAdapter which can make the job of querying and displaying data much more simple and easy.
Here is a basic visual representation of it,
Step 1
Firstly, you need to create a database. As mentioned in your question, it is clear that you know how to create a database and put some data into it. So I am not going into the depths of it.
Step 2
We need to define the layout to be used for the individual items in the ListView and save it as res/layout/item_todo.xml This is just a sample layout, you can design any kind of layout you want to.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<TextView
android:id="#+id/tvBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Study cursors"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/tvPriority"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="3"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
Step 3
Now we need to define an adapter. Here we are using a CursorAdapter which converts a Cursor (that you provide) into Views (defined by your layout).
There are two methods, newView and bindView which we need to override. The newView is responsible for inflating newViews for the first time and the bindView is responsible for binding the data to the Views.
public class TodoCursorAdapter extends CursorAdapter {
public TodoCursorAdapter(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.item_todo, 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.tvBody);
TextView tvPriority = (TextView) view.findViewById(R.id.tvPriority);
// Extract properties from cursor
String body = cursor.getString(cursor.getColumnIndexOrThrow("body"));
int priority = cursor.getInt(cursor.getColumnIndexOrThrow("priority"));
// Populate fields with extracted properties
tvBody.setText(body);
tvPriority.setText(String.valueOf(priority));
}
}
Step 4
Now as you can clearly see, that the constructor needs a Context and a Cursor. Now we need to query the database and retrieve the data into a Cursor and pass it to the adapter.
// TodoDatabaseHandler is a SQLiteOpenHelper class connecting to SQLite
TodoDatabaseHandler handler = new TodoDatabaseHandler(this);
// Get access to the underlying writeable database
SQLiteDatabase db = handler.getWritableDatabase();
// Query for items from the database and get a cursor back
Cursor todoCursor = db.rawQuery("SELECT * FROM todo_items", null);
Step 5
This is the last step where we need to instantiate the adapter and attach the ListView with the adapter to populate the data.
// Find ListView to populate
ListView lvItems = (ListView) findViewById(R.id.lvItems);
// Setup cursor adapter using cursor from last step
TodoCursorAdapter todoAdapter = new TodoCursorAdapter(this, todoCursor);
// Attach cursor adapter to the ListView
lvItems.setAdapter(todoAdapter);
I have a Spinner which is filled from a query using a SimpleCursorAdapter, this works fine... but now I need to put an option "Please Select" before all the items retrieved from the query, just for usability issues... but I'm not quite sure of how to do so... HELP please...
Here is my code...
private Spinner comboForm;
...
comboForm = (Spinner) findViewById(R.id.comboFormularios);
...
mDbH.abrir();
final Cursor cu = mDbH.consultaFormularios(idU);
if(cu.moveToFirst() == false){
cu.close();
mDbH.cerrar();
}else{
SimpleCursorAdapter adapter2 = new SimpleCursorAdapter(getApplicationContext(),R.layout.spinner,cu,new String[] {"nombre"},new int[] {R.id.textoCombo});
adapter2.setDropDownViewResource(R.layout.simple_spinner_dropdown_item);
comboForm.setAdapter(adapter2);
}
mDbH.cerrar();
...
comboForm.setOnItemSelectedListener(new OnItemSelectedListener(){
public void onItemSelected(AdapterView<?> parentView, View selectedItemView,int position, long id) {
idF = (int) id;
obtenerDatosRutas(idU,idF);
tabla.removeAllViews();
llenarTabla();
}
public void onNothingSelected(AdapterView<?> arg0) {}
});
Where mDbH is an instance of the class I'm using to manipulate the Database... as you can see the Spinner is filled up from Cursor resulting of the query consultaFormularios(idU)
When you create your cursor, one possible solution would be to use a SQL UNION and construct second SELECT that simply contains the label you need (adding hard-coded dummy fields for ordering).
Alternatively, and this is most likely the simplest solution. Instead of using a cursor adapter, use an array adapter and start by populating the array with your default value you want, then stick in all the items from your cursor. Something like,
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("Please select");
final Cursor cu = mDbH.consultaFormularios(idU);
while(cu.moveToNext()) {
arrayList.add(cu.getString(0)); // assuming you want a
//string from the first column
}
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, arrayList);
comboForm.setAdapter(spinnerArrayAdapter);
I have a ListView which is populated by a SQLite query in OnCreate using the following code which then sets up an OnItemClickListener.
ListView menuList = (ListView) findViewById(R.id.ListView_Menu);
String sql = "SELECT EXHIBITORS, ('Stand No. ' || STANDNO) AS STANDNO, _ID FROM EXHIBITOR ORDER BY EXHIBITORS";
cursor = myDbHelper.getReadableDatabase().rawQuery(sql, null);
startManagingCursor(cursor);
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.menu_item, cursor, FROM, TO);
menuList.setAdapter(adapter);
menuList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View itemClicked, int position, long id) {
However, I want to be able to filter the ListView and then amend the query so that the user can reduce the size of the ListView by say requesting all Exhibitors that start with the letter 'A'. How can I do this, I assume by using the above code again but how do I this and still keep the OnItemClickListener working?
CursorAdapter.changeCursor() will allow you to replace your query with one that filters the Exhibitor names.
I have a ListView that is fed by an SQLiteDB. On each row returned, I have a clickable TextView that onClick should delete the item on that row. So I need to find the sql rowid, so I can call deleteItem(rowid).
I've tried putting in an invisible TextView that I fill with the sql rowid as the ListView is populated, it works. But I set content view to R.layout.list_view and the invisible TextView is in R.layout.list_item (custom layout for each item line), so I can't seem to access it.
How do I go about making this work or is there a better way?
Here is the code that populates the listview:
private void fillData() {
// Get all of the notes from the database and create the item list
Cursor c = mDbHelper.fetchAllNotes();
startManagingCursor(c);
String[] from = new String[] { CommonDbAdapter.KEY_NOTES };
int[] to = new int[] { R.id.text1 };
// Now create an array adapter and set it to display using our row
SimpleCursorAdapter notes =
new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);
}
Here is my current click listener, I'm not sure how to differentiate between a click on the item, which returns the item to another activity and a click on the textview that would delete the item.
itemList = (ListView) findViewById(android.R.id.list);
itemList.setTextFilterEnabled(true);
itemList.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
Intent intent = new Intent();
Bundle b = new Bundle();
b.putString("TEXT", ((TextView)v.findViewById(R.id.text1)).getText().toString());
intent.putExtras(b);
setResult(SUCCESS_RETURN_CODE, intent);
finish();
}
});
I was originally trying to get the rowid and use a simple onClick set in the layout. But had problems getting the rowid.
public void clickDelete(View view) {
mDbHelper.deleteNote(rowid);
}
here is a quick way to get a click listener for just your textview:
replace your lines:
SimpleCursorAdapter notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);
with this:
setListAdapter(new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
final TextView t = (TextView)v.findViewById(R.id.text1);
t.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Hello " + t.hashCode(), Toast.LENGTH_SHORT).show();
}
});
return v;
}
});
now, your text1 view will have an onclick listener, and you can do whatever you want. you need to store the rowId somewhere accessible from inside the onClick method though.
this doesn't exactly answer the question i guess, but it gives you a click listener for an individual view in the listview.
you really should implement a custom CursorAdapter class to do something like this, although you could hack around and retrieve the rowId from somewhere
or you could instead use an invisible textview and add an onclick listener to it(like i've shown above), grab it's value(which is a rowId), and perform an operation.
Are you using a CursorAdapter? If so you can query the Cursor directly for the rowid.
You may want to use OnItemClickListener instead of an OnClickListener. This will set you up with the ListView, the position and the id. I believe if you are using a CursorAdapter that the id given to you is the actual rowid of the row in the database.
Edit: if using OnItemClickListener is not possible, you'll have to change to CursorAdapter from SimpleCursorAdapter to do this. In your bindView method you'll have access to the rowid of the View you are creating, and you can reference this in the OnClickListener you create.