Android database - curser and adapter - android

Quite new to Android development, can manage the more simple tasks but never dealt with databases before. I have managed to get so far, however, when trying to output all results from the databases, which has been created, i am only receiving references to the record not the actual values. Debugging shows that the values are being assigned correctly, however, are showing the object reference and not the values them selves. What i see is a list similar to:
com.example.testdb.myapplication.Films#42ff37f0
Please see below for my code:
// Getting All Films
public List<Films> getAllFilms() {
List<Films> filmList = new ArrayList<Films>();
// Select All Query
String selectQuery = "SELECT * FROM film_table";
SQLiteDatabase db = new MyDBHandler(this).getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
ArrayAdapter<Films> adapter = new ArrayAdapter<Films>(MainActivity.this, android.R.layout.simple_list_item_1,filmList);
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
Films film = new Films();
film.setID(Integer.parseInt(cursor.getString(0)));
film.setfilmName(cursor.getString(1));
film.setFilmActors(cursor.getString(2));
film.setFilmDirectors(cursor.getString(3));
film.setfilmDescription(cursor.getString(4));
film.setfilmFave(cursor.getString(5));
ListView listView = (ListView) findViewById(android.R.id.list);
listView.setAdapter(adapter);
filmList.add(film);
} while (cursor.moveToNext());
}
// return Film list
return filmList;
}
I would very much appreciate any input which may guide me in the right direction. I have tried many methods to no avail.
Thank you in advance.

Okay, I have to guess a little bit here because you don't have more code posted.
You say you have a list that is showing the output of the default toString() method for your Films object.
I am guessing your list adapter is using the default getView() method, since the default behavior is to print the toString() output for the item.
If you want to see the database values of your Films object formatted into a list, you have to extend a list adapter class like BaseAdapter or create a class that implements ListAdapter, then override the getView() method to create a View that contains your values.
Have a look at ListAdapter.getView() and see if that will solve your issue. If not, you'll have to post more code and be more specific about what's happening.

You are setting the adapter before you are retrieving the values from database, hence there would be no data in the adapter. Moved setting the adapter after retrieving values from DB and after setting them in Films object.
public List<Films> getAllFilms() {
List<Films> filmList = new ArrayList<Films>();
// Select All Query
String selectQuery = "SELECT * FROM film_table";
SQLiteDatabase db = new MyDBHandler(this).getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
Films film = new Films();
film.setID(Integer.parseInt(cursor.getString(0)));
film.setfilmName(cursor.getString(1));
film.setFilmActors(cursor.getString(2));
film.setFilmDirectors(cursor.getString(3));
film.setfilmDescription(cursor.getString(4));
film.setfilmFave(cursor.getString(5));
filmList.add(film);
} while (cursor.moveToNext());
}
ArrayAdapter<Films> adapter = new ArrayAdapter<Films>(MainActivity.this, android.R.layout.simple_list_item_1,filmList);
ListView listView = (ListView) findViewById(android.R.id.list);
listView.setAdapter(adapter);
return filmList;
}
Also add a custom adapter to display the value from the class Film:
public class MyClassAdapter extends ArrayAdapter<Films> {
private static class ViewHolder {
private TextView filmname;
private TextView actors;
................
}
public MyClassAdapter(Context context, int textViewResourceId, ArrayList<Films> items) {
super(context, textViewResourceId, items);
}
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(this.getContext())
.inflate(R.layout.film_layout, parent, false);
viewHolder = new ViewHolder();
viewHolder.itemView = (TextView) convertView.findViewById(R.id.ItemView);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
Films item = getItem(position);
if (item!= null) {
// My layout has only one TextView
// do whatever you want with your string and long
viewHolder.itemView.setText(String.format("%s", item.getfilname));
}
return view;
}
}
Layout for the listrow(film_layout.xml)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/filname"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
.................... //Form how each row should be displayed
</RelativeLayout>
Tutorial how to create a custom listview here

Sorry i didn't post sooner. In the end i fixed the issue using the following code, which outputs into the list similar to you answer, however, without the floater element...
String[] columnName = new String[]
{MyDBHandler.COLUMN_NAME, MyDBHandler.COLUMN_ACTORS, MyDBHandler.COLUMN_DIRECTORS,MyDBHandler.COLUMN_DESCRIPTION, MyDBHandler.COLUMN_FAVE};
int[] displayName = new int[]
{R.id.filmNameEdit, R.id.filmActors, R.id.filmDirectors, R.id.filmDescription, R.id.filmFave};
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.activity_listview,cursor, columnName, displayName);
ListView listView = (ListView) findViewById(android.R.id.list);
listView.setAdapter(adapter);
Thanks for all the suggestions. This turned out to be the most simple answer for me.

Related

How to pass the data in the database to a spinner

I'm creating a method to read all the information in the database and view it through a spinner here is the code i tried for this function
public Spinner loadArtist(){
SQLiteDatabase DB = getReadableDatabase();
String[] projection = {
ArtistMaster.Artist.ARTIST_NAME};
Cursor cursor = DB.query(
ArtistMaster.Artist.TABLE_ARTIST,
projection,
null,
null,
null,
null,
null);
Spinner itemIds = new ArrayList<>();
while(cursor.moveToNext()) {
long itemId = cursor.getLong(
cursor.getColumnIndex(ArtistMaster.Artist.ARTIST_NAME));
itemIds.setAdapter(itemId);
}
cursor.close();
return itemIds;
}
but it gives me an error in this line Spinner itemIds = new ArrayList<>();
Should i declare it as a list instead of spinner
itemIds should be defined as an ArrayList<Long>. A Spinner is a UI element and an ArrayList is a data structure. You will most likely need to map the data to your UI using an adapter of some sort, eg. an ArrayAdapter:
Spinner spinner = ... // findViewById, new Spinner() etc.
ArrayList<Long> itemIds = new ArrayList<>();
//... fill array with artist IDs
spinner.setAdapter(
new ArrayAdapter(
this, // Context, Activity etc.,
android.R.layout.simple_list_item_1, // Spinner TextView item resource ID
itemIds // Data set.
));
By default, ArrayAdapter will call Object#toString() on each data object in the collection.
I'd suggest that it would be easier if you used a Cursor Adpater as they are designed to be used with a Cursor and there is no need to generate arrays.
SimpleCursorAdapter being that, a simple but still pretty flexible adapter for use with Cursors.
The only issue is that a Cursor Adapter requires a column name specifically _id (BaseColumns._ID resolves to this (as used below)).
First have the following member variables (obviously names can be what you wish)
:-
Cursor mCursor;
SimpleCursorAdapter mAdapter;
Spinner spinner;
SQLiteDatabase db;
In the onCreate Method of the activity have
:-
spinner = this.findViewById(R.id.?????); //
db = ???????? (as per your existing code)
manageSpinner();
Have a method
:-
private void manageSpinner() {
mCursor = db.query(
ArtistMaster.Artist.ARTIST_NAME,
new String[]{"*","rowid AS " + BaseColumns._ID}, //<<<<<<<< adds _ID column (unless the table is a WITHOUT ROWID table, pretty unlikely)
null,null,null,null,null
);
if (mAdapter == null) {
mAdapter = new SimpleCursorAdapter(
this,
android.R.layout.simple_list_item_1,
mCursor,
new String[]{"the_column"}, // column(s) from which to extract data
new int[]{android.R.id.text1}, // layout views into which the data is placed
0
);
spinner.setAdapter(mAdapter);
// You want want to do something when an Item in the spinner is clicked (this does nothing as it is)
spinner.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//........... do your stuff here
// notes
// id will be the id of the row
// cursor will be positioned, so you can access data from the cursor if needed
}
});
} else {
mAdapter.swapCursor(mCursor);
}
}
Override the activity's onResume (refresh the spinner when returning to activity as underlying data may have changed) and onDestroy (to close the Cursor) methods using
:-
#Override
protected void onDestroy() {
super.onDestroy();
mCursor.close();
}
#Override
protected void onResume() {
super.onResume();
manageSpinner();
}

SQLiteDatabase Change Two Text Views in a ListView From 2 Table Columns

I'm using an SQLiteDatabase to record 2 types of data under a single table (COL_TASK_TITLE, and COL_TASK_WEIGHT). I'm then using an ArrayAdapter to set the 2 TaskEntry's strings to their 2 relevant TextViews (characterNameDisplay, and characterWeightDisplay). These TextView are both part of the same ListView element (item_todo).
This is the method I'm calling to update the ListView:
public void updateUI() {
ArrayList<String> taskList = new ArrayList<>();
SQLiteDatabase db = mHelper.getReadableDatabase();
Cursor cursor = db.query(TaskContract.TaskEntry.TABLE,
new String[]{TaskContract.TaskEntry._ID, TaskContract.TaskEntry.COL_TASK_TITLE, TaskContract.TaskEntry.COL_TASK_WEIGHT},
null, null, null, null, null);
while (cursor.moveToNext()) {
int idx = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_TITLE);
taskList.add(cursor.getString(idx));
int idx1 = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_WEIGHT);
taskList.add(cursor.getString(idx1));
}
if (mAdapter == null) {
mAdapter = new ArrayAdapter<>(this,
R.layout.item_todo,
R.id.characterNameDisplay,
taskList);
mTaskListView.setAdapter(mAdapter);
} else {
mAdapter.clear();
mAdapter.addAll(taskList);
mAdapter.notifyDataSetChanged();
}
cursor.close();
db.close();
}
Currently, it is setting both TaskEntry's to just the characterNameDisplay TextView in seperate ListView segments.
My question is; how do I correctly set each TextView to their relevant TaskEntry's?
e.g. characterWeightDisplay to COL_TASK_WEIGHT; and characterNameDisplay to COL_TASK_TITLE
You are using a List<String> and adding both the task name and task weight as individual elements of this list. There is no way for ArrayAdapter (or anything else) to know that two adjacent items are related--everything looks like individual elements of the list.
You should create a class to encapsulate the attributes of a task:
class Task {
String name;
String weight;
// add more as needed
}
Then when you process the Cursor, you make a list of Tasks instead:
List<Task> taskList = new ArrayList<>();
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex(COL_TASK_TITLE));
String weight= cursor.getString(cursor.getColumnIndex(COL_TASK_WEIGHT));
Task task = new Task();
task.name = name;
task.weight = weight;
taskList.add(task);
}
This also means your adapter will be an ArrayAdapter<Task> instead. However, now this adapter probably does not have necessary logic to bind a Task to the layout you've defined for the list rows. At the very least, you would have to override getView() and implement that yourself.
In my opinion, it's worth not using ArrayAdapter because it's generally too simple for most applications; instead you should subclass BaseAdapter. Even better would be to use RecyclerView instead of ListView and extend RecyclerView.Adapter, since ListView is old and clunky.

View data from SQLite database using ListView on Android

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

Is is possible to add additional column to SimpleCursorAdapter

I have successfully used a SimpleCursorAdapter to list displayname and value (both in my DB) and displayed them in my list activity.
What I am was trying to do now was add a new view (ImageView) for each item in my list activity. It is supposed to look like this in the end.
Image_1_NotInDB -- DisplayName1FromDB -- DisplayName1FromDB
Image_2_NotInDB -- DisplayName2FromDB -- DisplayName2FromDB.
The image is going to different (based on DisplayName1FromDB). I don't think SimpleCursorAdapter is good anymore for this purpose.
I tried creating a customSimpleCursorAdapter extending SimpleCursorAdapter and tried to use 'newView' and 'bindView' methods to achieve. I pretty much followed this: Custom CursorAdapters.
The problem is; what Image I use is based on a value from DB (which I intended to pass in the constructor or customSimpleCursorAdapter)
public View newView(Context pContext, Cursor pCursor, ViewGroup pParent)
{
Cursor lCursor = getCursor();
final LayoutInflater inflater = LayoutInflater.from(pContext);
View lView = inflater.inflate(layout, pParent, false);
int lImage = "dog".equals(variable) ? R.drawable.dog : R.drawable.cat;
// "variable" is a member variable (set at the constructor)
ImageView lImageView = (ImageView) lView.findViewById(R.id.appImage);
if (lImageView != null)
{
lImageView.setImageResource(lImage);
}
return pParent;
}
public void bindView(View pView, Context pContext, Cursor pCursor)
{
int lImage = "dog".equals(variable) ? R.drawable.dog : R.drawable.cat;
// "variable" is a member variable (set at the constructor)
ImageView lImageView = (ImageView) lView.findViewById(R.id.appImage);
if (lImageView != null)
{
lImageView.setImageResource(lImage);
}
}
This is how I tried using the "customSimpleCursorAdapter"
String[] lDisplay = new String[] {KEY_NAME, KEY_TIME};
int[] lValues = new int[] {R.id.name, R.id.time};
CustomRowCursorAdapter lCursorAdapter = new CustomRowCursorAdapter(this, R.layout.row, lSTime, lDisplay, lValues, "MY IDEA WAS TO PASS THE ANIMAL NAME HERE, BUT NOT LUCK as I am not sure How ");
lCursorAdapter.newView(this, lSTime, getListView());
lCursorAdapter.bindView(getListView(), this, lSTime);
setListAdapter(lCursorAdapter);
Is ArrayAdapter the answer? If yes, could you share what parameters would you pass to it?
I recommend to go for custom adapter
Is ArrayAdapter the answer?
No you can not use the ArrayAdapter here. Because you need to display data from the database therefore you must use here a Custom CursorAdapter.
You could add an additional field to the projection from the database.
Here is an example of a projection I have used to format a cost based off of the a price of an item (stored in cents) and an associated unit.
public static final String CONCATE_COST
= "'$' || CASE WHEN SUBSTR(ROUND("+COLUMN_PRICE
+"/100.0, 2), LENGTH(ROUND("+COLUMN_PRICE
+"/100.0, 2))-1, 1)='.' THEN ROUND("+COLUMN_PRICE
+"/100.0, 2) || '0' else ROUND("+COLUMN_PRICE
+"/100.0, 2) end || "+COLUMN_UNIT;
Use that as a column and you can pair it with a field in your view using the normal mapping of column to R.id.*.

Android ListView Checkbox Selection

I kind of have a 2 part question here.
1) How can I populate my ListView so that strings are what is displayed, but when items are selected, a non-visible id value (contact id from phone contacts) is the value that is actually used?
2) I have a ListView that's using multipleChoice mode for item selections. It's popluated with names from my contacts list. When I select an item in the ListView, I want that selected item to trigger a call to my SqLite routine to store the value into a database record. How do I make this event fire when an item is checked in the listview?
This is my layout XML;
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView
android:id="#+id/lvContacts"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:choiceMode="multipleChoice"
/>
</LinearLayout>
And here is the code I am using to populate my ListView;
private void fillData() {
final ArrayList<String> contacts = new ArrayList<String>();
// Let's set our local variable to a reference to our listview control
// in the view.
lvContacts = (ListView) findViewById(R.id.lvContacts);
String[] proj_2 = new String[] {Data._ID, Phone.DISPLAY_NAME, CommonDataKinds.Phone.TYPE};
cursor = managedQuery(Phone.CONTENT_URI, proj_2, null, null, null);
while(cursor.moveToNext()) {
// Only add contacts that have mobile number entries
if ( cursor.getInt(2) == Phone.TYPE_MOBILE ) {
String name = cursor.getString(1);
contacts.add(name);
}
}
// Make the array adapter for the listview.
final ArrayAdapter<String> aa;
aa = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_multiple_choice,
contacts);
// Let's sort our resulting data alphabetically.
aa.sort(new Comparator<String>() {
public int compare(String object1, String object2) {
return object1.compareTo(object2);
};
});
// Give the list of contacts over to the list view now.
lvContacts.setAdapter(aa);
}
I had hoped to be able to use something like the onClick event for each checked item, but have made not one bit of progress.
Any help would be so very appreciated. Thanks.
I think solution for you problem is using custom list adapter witch each item include contact name and contact ID, here is detail:
1) Try to create custom contact item bean include 2 properties: contactID, contactName
public class contactItem{
private long contactID;
private String contactName;
//...
}
Create CustomContactAdapter:
public class CustomContactAdapter extends ArrayAdapter<contactItem>{
ArrayList<contactItem> itemList = null;
//Constructor
public CustomContactAdapter (Context context, int MessagewResourceId,
ArrayList<contactItem> objects, Handler handler) {
//Save objects and get LayoutInflater
itemList = objects;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
final ReceiveMailStruct contact= items.get(position);
if (contact!= null) {
view = inflater.inflate(R.layout.layout_display_contact_item, null);
//Set view for contact here
}
}
}
Search custom adapter for more information
2)To handler click event at ListView you must register an handler for listitem:
Step1: Register handler to List item:
lvContacts.setOnItemClickListener(new HandlerListClickEvent());
Step2: Implement proccess when item click (check/uncheck)
class HandlerListClickEvent implements OnItemClickListener {
public void onItemClick( AdapterView<?> adapter, View view, int position, long id ) {
//Get contact ID here base on item position
}
Hope it help,
Regards,

Categories

Resources