I have a listview that contains some data which I got from the web. Now I can make changes in the list item and once I make changes to the item, I am storing the updated value in the db. When i login in next time to the app, I am downloading the content from net and showing it in the listview with the changes that I have done last time. So my approach here is, I am querying the db for each item in the getview method of the list adapter to check for changes. Is it a good practice to do a db query for each item's getview method of the adapter? If not could you please suggest me some alternative. Thanks.
Never, really, never do that.
If you put your data download code in the getView method of the adapter it will make a network call for each row of the list.
Even worst, it will call it anytime that row appears on the screen, not only one time for row.
You should get all your data first, then use the adapter only to draw it.
You can at anytime call the db to check for changes and, if needed, notify the adapter to redraw the list to show the changes.
Hope this helps.
In Android development, any time you want to show a vertical list of items you will want to use a ListView which is populated using an Adapter to a data source. When we want the data for the list to be sourced directly from a SQLite database query we can use a CursorAdapter.
The CursorAdapter fits in between a Cursor (data source from SQLite query) and the ListView (visual representation) and configures two aspects:
Which layout template to inflate for an item
Which fields of the cursor to bind to views in the template
Creating the View Template
When we want to display a series of items into a list using a custom representation of the items, we need to use our own custom XML layout template for each item. We can simply create an XML layout template in res/layout/item_todo.xml representing a particular cursor row:
<?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>
Defining the Adapter
public class ViewAdapter extends BaseAdapter {
LayoutInflater mInflater;
public ViewAdapter() {
mInflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return favoriteList.size();
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.listitem,null);
}
final TextView nameText = (TextView) convertView.findViewById(R.id.nameText);
nameText.setText("Name : "+favoriteList.get(position).getName());
final TextView ageText = (TextView) convertView.findViewById(R.id.ageText);
ageText.setText("Age : "+favoriteList.get(position).getAge());
final Button edit = (Button) convertView.findViewById(R.id.edit);
edit.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
final Dialog dialog = new Dialog(context);
dialog.setContentView(R.layout.row);
dialog.setTitle("Add Data to Database");
final EditText name = (EditText) dialog.findViewById(R.id.name);
final EditText age = (EditText) dialog.findViewById(R.id.age);
Button Add = (Button) dialog.findViewById(R.id.Add);
Add.setText("Add");
Add.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(name.getText().toString() != null && name.getText().toString().length() >0 ){
if(age.getText().toString() != null && age.getText().toString().length() >0 ){
db.updateRow(favoriteList.get(position).getId(), name.getText().toString(), age.getText().toString());
favoriteList = db.getFavList();
listView.setAdapter(new ViewAdapter());
dialog.dismiss();
}else{
Toast.makeText(getApplicationContext(), "Please Enter the Age", Toast.LENGTH_LONG).show();
}
}else{
Toast.makeText(getApplicationContext(), "Please Enter the Name", Toast.LENGTH_LONG).show();
}
}
});
dialog.show();
}
});
final Button delete = (Button) convertView.findViewById(R.id.delete);
delete.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
db.removeFav(favoriteList.get(position).getId());
notifyDataSetChanged();
favoriteList = db.getFavList();
listView.setAdapter(new ViewAdapter());
}
});
return convertView;
}
}
Create database
DatabaseHandler.java
public class DatabaseHandler extends SQLiteOpenHelper {
//Database Version
private static final int DATABASE_VERSION = 1;
//Database Name
private static final String DATABASE_NAME = "Test";
//Table Name
private static final String TABLE_TEST = "TestTable";
//Column Name
private static final String KEY_ID = "id";
private static final String KEY_NAME = "name";
private static final String KEY_AGE = "age";
public DatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
//Create Table
#Override
public void onCreate(SQLiteDatabase db) {
String CREATE_CONTACTS_TABLE = "CREATE TABLE " + TABLE_TEST + "("
+ KEY_ID + " INTEGER PRIMARY KEY," + KEY_NAME + " TEXT,"
+ KEY_AGE + " TEXT" + ")";
db.execSQL(CREATE_CONTACTS_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_TEST);
onCreate(db);
}
//Insert Value
public void adddata(Context context,String movieId,String songId) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_NAME, movieId);
values.put(KEY_AGE, songId);
db.insert(TABLE_TEST, null, values);
db.close();
}
//Get Row Count
public int getCount() {
String countQuery = "SELECT * FROM " + TABLE_TEST;
int count = 0;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(countQuery, null);
if(cursor != null && !cursor.isClosed()){
count = cursor.getCount();
cursor.close();
}
return count;
}
//Delete Query
public void removeFav(int id) {
String countQuery = "DELETE FROM " + TABLE_TEST + " where " + KEY_ID + "= " + id ;
SQLiteDatabase db = this.getReadableDatabase();
db.execSQL(countQuery);
}
//Get FavList
public List<FavoriteList> getFavList(){
String selectQuery = "SELECT * FROM " + TABLE_TEST;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
List<FavoriteList> FavList = new ArrayList<FavoriteList>();
if (cursor.moveToFirst()) {
do {
FavoriteList list = new FavoriteList();
list.setId(Integer.parseInt(cursor.getString(0)));
list.setName(cursor.getString(1));
list.setAge(cursor.getString(2));
FavList.add(list);
} while (cursor.moveToNext());
}
return FavList;
}
}
Enojoys.... :)
It is better to use cursor adapter to bind the list view.You can use Loader to get the list updated even if there is a change in the data base.
onLoadFinished (Loader loader, D data) of the Loader call back would be monitor for changes to the data, and report them to you through new calls. You should not monitor the data yourself.
Related
I want to delete a row in my Database via a ListView. I already managed to display the data in a listview. I am working with a OnItemLongClickListener which then opens up a Dialog to delete the clicked item. I can't get the actual delete method to work.
The OnCLickListener method:
public boolean onItemLongClick(AdapterView<?> adapterView, View view, final int position, long id) {
build = new AlertDialog.Builder(ZeigeFaecherListe.this);
build.setTitle("Fach löschen?" + faecherListe.get(position));
build.setMessage("Willst du das Fach wirklich löschen?");
build.setNegativeButton("Abbrechen", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.cancel();
}
});
build.setPositiveButton("Löschen", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int i) {
myDb.loescheFach(position);
Toast.makeText(getApplicationContext(), faecherListe.get(position) + " gelöscht.", Toast.LENGTH_LONG).show();
myDb.zeigeFaecher();
dialogInterface.cancel();
}
});
AlertDialog alert = build.create();
alert.show();
return true;
}
});
The actual delete method:
public int loescheFach(int position){
SQLiteDatabase db = this.getWritableDatabase();
return db.delete("Fach_table","FACHID=?", new String[]{String.valueOf(position)});
}
You are confusing your list position with id, they might not match.
Change your myDb.loescheFach(position); to myDb.loescheFach(id);
You're passing the position of ArrayList to SQLite to delete statement. You should pass FACHID (in your case) as an argument to delete statement.
As your posted code is incomplete I assume it as below.
....
....
build.setPositiveButton("Löschen", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int i) {
// myDb.loescheFach(position);
// first get an id from ArrayList using position faecherListe.get(position)
// then pass that id to your method
myDb.loescheFach(id);
Toast.makeText(getApplicationContext(), faecherListe.get(position) + " gelöscht.", Toast.LENGTH_LONG).show();
myDb.zeigeFaecher();
dialogInterface.cancel();
}
});
....
....
First position will not equate to the rowid/id of the row in the table. Assuming that you have a id column defined as your_id_column INTEGER PRIMARY KEY (or with the AUTOINCREMENT keyword, that makes no real difference) and that you don't specify a value when inserting a row then the first id will likely be 1, then 2 then 3 ... etc.
However, position (3rd parameter passed to onItemClick/onItemLongClick) for the first item in the List will be 0, then 1 then 2 etc. So you will never actually delete correct row (clicking the first item will delete nothing, clicking the second item will delete the first) and when rows are deleted it gets even more out of sync.
The 4th parameter (long id) passed will be the rowid/id IF AND ONLY IF a CursorAdapter is used (i.e. a Cursor is the source data for the ListView). In which case the cursor MUST have a column named _id. However, it looks as though faecherListe is the source and as you use the get method that it is an ArrayList/List. As such the id passed will be the same value position except it is a long.
Now if faecherListe were an ArrayList<object_that_reflects_the_columns_of_the_table_including_id> then assuming a getter of getId you could use long retrieved_id = faecherListe.get(position).getId(). However, the comments :-
I guess you have ArrayList of String, that's why are getting String
value from ArrayList – Shashanth 10 hours ago
Ye
You simply have ArrayList and thus no means of ascertaining the id unless whatever data is held by the String is guaranteed to be unique.
Example
This is an example using object_that_reflects_the_columns_of_the_table_including_id in this case a Feacher class that just has a name and an id
Feacher.java
This is an entirely new class that allows you to have an ArrayList with all the relevant data/fields/columns (especially the id column).
i.e. you can an have ArrayList
public class Feacher {
private String name;
private long id;
public Feacher(String feacher_name) {
this(feacher_name,-1);
}
public Feacher(String feacher_name, long feacher_id) {
this.name = feacher_name;
this.id = feacher_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String toString() {
return name + " ID(" + String.valueOf(id) + ")";
}
}
DBHelper
This is the database helper (sub class of SQLiteOpenHelper) :-
public class DBHelper extends SQLiteOpenHelper {
public static final String DBNAME = "feacher.db";
public static final int DBVERSION = 1;
public static final String TB_FEACHER = "feacher";
public static final String COL_FEACHER_ID = BaseColumns._ID;
public static final String COL_FEACHER_NAME = "feacher_name";
SQLiteDatabase mDB;
public DBHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
mDB = this.getWritableDatabase();
}
#Override
public void onCreate(SQLiteDatabase db) {
String crtsql = "CREATE TABLE IF NOT EXISTS " + TB_FEACHER +
"(" +
COL_FEACHER_ID + " INTEGER PRIMARY KEY," +
COL_FEACHER_NAME + " TEXT" +
")";
db.execSQL(crtsql);
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
public long addFeacher(String feacher_name) {
ContentValues cv = new ContentValues();
cv.put(COL_FEACHER_NAME,feacher_name);
return mDB.insert(TB_FEACHER,null,cv);
}
public ArrayList<Feacher> getAllAsFeacherArrayList() {
ArrayList<Feacher> rv = new ArrayList<>();
Cursor csr = mDB.query(
TB_FEACHER,
null,
null,
null,
null,
null,
null
);
while (csr.moveToNext()) {
rv.add(new Feacher(
csr.getString(csr.getColumnIndex(COL_FEACHER_NAME)),
csr.getLong(csr.getColumnIndex(COL_FEACHER_ID))
)
);
}
return rv;
}
public int deleteFeacher(long id) {
return mDB.delete(
TB_FEACHER,
COL_FEACHER_ID + "=?"
,new String[]{String.valueOf(id)}
);
}
}
the deleteFeacher method to delete a row according to it's id,
the addFeacher method allowing some test data to be added.
the getAllAsFeacherArrayList returns all data as an ArrayList (note uses the constructor that sets the id)
MainActivity.java
The activity that has the ListView.
public class MainActivity extends AppCompatActivity {
ListView mListView;
ArrayAdapter<Feacher> mAdapter;
DBHelper mDBHlpr;
ArrayList<Feacher> mFeacherList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = this.findViewById(R.id.listview);
mDBHlpr = new DBHelper(this);
addDataForTesting();
displayAdapter();
}
private void displayAdapter() {
if (mFeacherList == null) {
mFeacherList = mDBHlpr.getAllAsFeacherArrayList();
} else {
mFeacherList.clear();
for (Feacher f: mDBHlpr.getAllAsFeacherArrayList()) {
mFeacherList.add(f);
mAdapter.notifyDataSetChanged();
}
}
if (mAdapter == null) {
mAdapter = new ArrayAdapter<Feacher>(this,android.R.layout.simple_list_item_1,mFeacherList);
mListView.setAdapter(mAdapter);
mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
mDBHlpr.deleteFeacher(mAdapter.getItem(i).getId());
displayAdapter();
return true;
}
});
}
}
private void addDataForTesting() {
mDBHlpr.addFeacher("Feacher 001");
mDBHlpr.addFeacher("Feacher 002");
mDBHlpr.addFeacher("Feacher 003");
}
}
Note for simplicity and also to avoid inadvertent deletion onItemLongClick has been used. The all important bit being mDBHlpr.deleteFeacher(mAdapter.getItem(i).getId()); which gets the id from the item (object, the Feacher) that was clicked according to the position.
activity_main.xml
A very basic layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<ListView
android:id="#+id/listview"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFAAAAFF"
>
</ListView>
</LinearLayout>
Result
here's a screen shot after 3rd run so 9 rows have been added and 2 rows have been deleted (note id's 2 and 5 are missing). The list will refresh after a deletion so the row disappears almost immediately the long-click is completed.
I have MyDBHandler with a getAllDetails method that queries the database using a cursor and returns a list.
What I don't know what to do now is how to output this list in a listview in another fragment. I was told to create two XML layouts and a custom adapter but I don't know how to this exactly!
MyDBHandler class
package com.astuetz.viewpager.extensions.sample;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.Cursor;
import android.content.Context;
import android.content.ContentValues;
import java.util.ArrayList;
import java.util.List;
public class MyDBHandler extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "detailsDB.db";
public static final String TABLE_DETAILS = "details";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_FIRSTNAME = "firstname";
public static final String COLUMN_SURNAME = "surname";
public static final String COLUMN_PHONE = "phone";
public static final String COLUMN_EMAIL = "email";
public static final String COLUMN_ADDRESS1 = "address1";
public static final String COLUMN_ADDRESS2 = "address2";
public static final String TABLE_KIN_DETAILS = "kindetails";
public static final String COLUMN_KIN_ID = "_id";
public static final String COLUMN_KIN_YOUREMAIL = "youremailkin";
public static final String COLUMN_KIN_FIRSTNAME = "firstnamekin";
public static final String COLUMN_KIN_SURNAME = "surnamekin";
public static final String COLUMN_KIN_PHONE = "phonekin";
public static final String COLUMN_KIN_EMAIL = "emailkin";
public static final String COLUMN_KIN_ADDRESS1 = "address1kin";
public static final String COLUMN_KIN_ADDRESS2 = "address2kin";
// Pass database information along to superclass
public MyDBHandler(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, DATABASE_NAME, factory, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
String query = " CREATE TABLE " + TABLE_DETAILS + "("
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_FIRSTNAME + " TEXT, "
+ COLUMN_SURNAME + " TEXT, "
+ COLUMN_PHONE + " TEXT, "
+ COLUMN_EMAIL + " TEXT, "
+ COLUMN_ADDRESS1 + " TEXT, "
+ COLUMN_ADDRESS2 + " TEXT "
+ ");";
String query2 = " CREATE TABLE " + TABLE_KIN_DETAILS + "("
+ COLUMN_KIN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_KIN_YOUREMAIL + " TEXT, "
+ COLUMN_KIN_FIRSTNAME + " TEXT, "
+ COLUMN_KIN_SURNAME + " TEXT, "
+ COLUMN_KIN_PHONE + " TEXT, "
+ COLUMN_KIN_EMAIL + " TEXT, "
+ COLUMN_KIN_ADDRESS1 + " TEXT, "
+ COLUMN_KIN_ADDRESS2 + " TEXT "
+ ");";
db.execSQL(query);
db.execSQL(query2);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(" DROP TABLE IF EXISTS " + TABLE_DETAILS);
db.execSQL(" DROP TABLE IF EXISTS " + TABLE_KIN_DETAILS);
onCreate(db);
}
//Add a new row to the database
public void addDetails(Details details) {
ContentValues values = new ContentValues();
values.put(COLUMN_FIRSTNAME, details.getFirstname());
values.put(COLUMN_SURNAME, details.getSurname());
values.put(COLUMN_PHONE, details.getPhone());
values.put(COLUMN_EMAIL, details.getEmail());
values.put(COLUMN_ADDRESS1, details.getAddress1());
values.put(COLUMN_ADDRESS2, details.getAddress2());
SQLiteDatabase db = getWritableDatabase();
db.insert(TABLE_DETAILS, null, values);
db.close();
}
public void addKinDetails(KinDetails kinDetails){
ContentValues values = new ContentValues();
values.put(COLUMN_KIN_YOUREMAIL, kinDetails.getyourEmailkin());
values.put(COLUMN_KIN_FIRSTNAME, kinDetails.getFirstnamekin());
values.put(COLUMN_KIN_SURNAME, kinDetails.getSurnamekin());
values.put(COLUMN_KIN_PHONE, kinDetails.getPhonekin());
values.put(COLUMN_KIN_EMAIL, kinDetails.getEmailkin());
values.put(COLUMN_KIN_ADDRESS1, kinDetails.getAddress1kin());
values.put(COLUMN_KIN_ADDRESS2, kinDetails.getAddress2kin());
SQLiteDatabase db = getWritableDatabase();
db.insert(TABLE_KIN_DETAILS, null, values);
db.close();
}
public List<Details> getAllDetails(){
//create a new list in which we put all persons
List<Details>detailsList = new ArrayList<>();
SQLiteDatabase db = getWritableDatabase();
String query = "SELECT * FROM " + TABLE_DETAILS;
//Cursor points to a location in your results
Cursor c = db.rawQuery(query, null);
//Move to the first row in your results
if (c != null) {
c.moveToFirst();
//Position after the last row means the end of the results
while (!c.isAfterLast()) {
//create new details object
Details details = new Details();
//Here use static declared on top of the class..don't use "" for the table column
details.set_id(c.getColumnIndex(COLUMN_ID));
details.setFirstname(c.getString(c.getColumnIndex(COLUMN_FIRSTNAME)));
details.setSurname(c.getString(c.getColumnIndex(COLUMN_SURNAME)));
details.setPhone(c.getString(c.getColumnIndex(COLUMN_PHONE)));
details.setEmail(c.getString(c.getColumnIndex(COLUMN_EMAIL)));
details.setAddress1(c.getString(c.getColumnIndex(COLUMN_ADDRESS1)));
details.setAddress2(c.getString(c.getColumnIndex(COLUMN_ADDRESS2)));
detailsList.add(details);
c.moveToNext();
}
c.close();
}
db.close();
//return our list of persons
return detailsList;
}
}
You need to first create layout files for your fragment and the listview rows.
For the fragment you can create a new blank fragment as follows :
Note: We are using a blank fragment because its good practise to learn since it gives you more control in complex situations.
In the fragment_details.xml paste the following code : Note change com.companyname to your app package name!
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.companyname.myapplication.FragmentDetails">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/frag_details_listView"
android:layout_weight="1" />
</LinearLayout>
Create a new xml layout file and name it row_details , this will be our custom row for our listview.
And in the file row_details.xml paste the following code :
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Large Text"
android:id="#+id/row_details_textview_name"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Small Text"
android:id="#+id/row_details_textview_id"
android:layout_marginLeft="10dp" />
</LinearLayout>
What we need now is a custom adapter which will take care of loading the data into our listview.
Create a new Java Class and name it DetailsAdapter, and paste the following code : Note see my comments in the code because it is very important to understand the concept behind the adapters in Android :
public class DetailsAdapter extends ArrayAdapter<Person> {
private Context context;
//Constructor
public DetailsAdapter(Context context, int resource, List<Person> objects) {
super(context, resource, objects);
this.context = context;
}
//The get view is the most crucial part of the adapter, here the listview asks the
//adapter for the row to display
#Override
public View getView(int position, View row, ViewGroup parent) {
//Get an instance of our holder
Holder holder;
//Check if this is the first time we are creating this row for the listview
if (row == null){
//Row was null and thus we need to get components from the row_details.xml
holder = new Holder();
//get the Android's layout inflater service which will read our row_details.xml
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//Fill our row view with the xml layout file
row = inflater.inflate(R.layout.row_details, null);
//Fill our holder with the text view components
holder.textViewName = (TextView)row.findViewById(R.id.row_details_textview_name);
holder.textViewId = (TextView)row.findViewById(R.id.row_details_textview_id);
//This is very imp! attach our holder to the row
row.setTag(holder);
}else{
//row was created before! thus get the holder object from the row tag
holder = (Holder)row.getTag();
}
//At this point we have our row, either created from new or got it from the row tag object
//we can now fill the data
//First get our object from the list which is in the position of the listview
//The position as you can see is passed to the getView method by the listview
Person person = getItem(position);
holder.textViewName.setText(person.getFirstname());
holder.textViewId.setText("ID: " + person.get_id());
//we are done formatting our row..return to listview to show
return row;
}
//A holder will be resposable to hold our components to improve listview performance
//We replicate the components we have in the row_details.xml
private class Holder{
TextView textViewName;
TextView textViewId;
}
}
At this point we are ready to rumble!!
In the FragmentDetails java class we declare a global private List of Details...we get an instance of our listview..we get the data..create a new DetailsAdapter and attach it to the listview..
public class FragmentDetails extends Fragment {
private List<Details>detailsList = new ArrayList<>();
private ListView mListView;
private DetailsAdapter adapter;
public FragmentDetails() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_details, container, false);
//get an instance of our listview
mListView = (ListView)rootView.findViewById(R.id.frag_details_listView);
//Get the data here!!
MyDBHandler dbHandler = new MyDBHandler(getActivity().getApplicationContext());
detailsList = dbHandler.getAllDetails();
//Initiate our adapter
adapter = new DetailsAdapter(getActivity().getApplicationContext(), R.layout.row_details, detailsList);
//set adapter to the listview
if(adapter != null){
mListView.setAdapter(adapter);
}
return rootView;
}
}
NOTE !!
In your MyDbHandler class include another constructor which takes only the context as a parameter like so :
public MyDBHandler(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
Your good to go ..run the project
With this procedure you can create any type of listviews in android
Create a fragment that extends ListFragment and implements LoaderManager.LoaderCallbacks<Cursor>. Like below:
public class MyFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {}
Implement the methods onCreateLoader(), onLoadFinished() and onLoaderReset().
In the onActivityCreated() method of your fragment, call getLoaderManager().initLoader() method.
You can remove getAllDetails() method from dbhelper and have it as a query in onCreateLoader() method in the fragment.
For more details, check below links:
https://developer.android.com/training/load-data-background/setup-loader.html
https://developer.android.com/training/load-data-background/handle-results.html
I suggest you use Recycler view, its more efficient.The RecyclerView widget is part of the v7 support library. You need to import the recyclerview library(refer to this link)
Refer to this tutorial for more details and better understanding recyclerview.
To start, within your mainActivity code create recyclerview and a customAdapter as below:
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.my_recycler_view);
RecyclerView.LayoutManager mLayoutManager;
dbHelper = new DBHelper(getActivity().getApplicationContext());
list = MyDBHandler.getAllDetails();
mLayoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
c = new CustomAdapter(list); //send the list to the adapter
recyclerView.setAdapter(c);
Within your main Layout code you should have this to show the recyclerview:
<android.support.v7.widget.RecyclerView
android:id="#+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:scrollbars="vertical"/>
Now you need to create a custom adapter to show the contents of Details Object. Please note that in the earlier code of the Activity the CustomAdapter is called and passed the list of Details object. We need to use this to show it in the view.
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.viewHolder> {
List<Details> records;
public class viewHolder extends RecyclerView.ViewHolder {
public TextView firstname, surname;
public viewHolder(View v) {
super(v);
firstname = (TextView) v.findViewById(R.id.first);
surname = (TextView) v.findViewById(R.id.last);
}
}
public CustomAdapter(List<Details> records) {
this.records = records;
}
#Override
public viewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.listrow, parent, false);
return new viewHolder(v);
}
#Override
public void onBindViewHolder(final viewHolder holder, final int position) {
Details rec = list.get(position);
holder.firstname.setText(rec.getFirstname());
holder.lastname.setText(rec.getSurname);
}
}
Now you need a layout file for the customadapter, create as below:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"
android:orientation="vertical">
<TextView
android:id="#+id/first"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="16dp"
android:textColor="#CC000000"
android:textSize="16sp"
android:text=""/>
<TextView
android:id="#+id/last"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="16dp"
android:textColor="#CC000000"
android:textSize="16sp"
android:text=""/>
</LinearLayout>
I am using this code , but i ma getting a Runtime exception. I did all the required effort and work to remove this error. But it not work for me . i am using DatabaseHandler. java and AndroidSpinnerFromSQLiteActivity.java class, and my xml is activity_databse_handler.xml.
public class AndroidSpinnerFromSQLiteActivity extends Activity implements OnItemSelectedListener {
// Spinner element
Spinner spinner;
// Add button
utton btnAdd;
// Input text
EditText inputLabel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_database_handler);
// Spinner element
spinner = (Spinner) findViewById(R.id.spinner);
// add button
btnAdd = (Button) findViewById(R.id.btn_add);
// new label input field
inputLabel = (EditText) findViewById(R.id.input_label);
// Spinner click listener
spinner.setOnItemSelectedListener(this);
// Loading spinner data from database
loadSpinnerData();
/**
* Add new label button click listener
* */
btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
String label = inputLabel.getText().toString();
if (label.trim().length() > 0) {
// database handler
DatabaseHandler db = new DatabaseHandler(getApplicationContext());
// inserting new label into database
db.insertLabel(label);
// making input filed text to blank
inputLabel.setText("");
// Hiding the keyboard
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(inputLabel.getWindowToken(), 0);
// loading spinner with newly added data
loadSpinnerData();
} else {
Toast.makeText(getApplicationContext(), "Please enter label name",
Toast.LENGTH_SHORT).show();
}
}
});
}
/**
* Function to load the spinner data from SQLite database
* */
public void loadSpinnerData() {
// database handler
DatabaseHandler db = new DatabaseHandler(getApplicationContext());
// Spinner Drop down elements
List<String> lables = db.getAllLabels();
// Creating adapter for spinner
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, lables);
// Drop down layout style - list view with radio button
dataAdapter
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// attaching data adapter to spinner
spinner.setAdapter(dataAdapter);
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position,
long id) {
// On selecting a spinner item
String label = parent.getItemAtPosition(position).toString();
// Showing selected spinner item
Toast.makeText(parent.getContext(), "You selected: " + label,
Toast.LENGTH_LONG).show();
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
}
===========================================================================================
public class DatabaseHandler extends SQLiteOpenHelper {
public DatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
// Creating Tables
#Override
public void onCreate(SQLiteDatabase db) {
try {
// Category table create query
String CREATE_CATEGORIES_TABLE = "CREATE TABLE " + TABLE_LABELS + "("+ KEY_ID + " INTEGER PRIMARY KEY," + KEY_NAME + " TEXT)";
db.execSQL(CREATE_CATEGORIES_TABLE);
}
catch (Exception e) {
// TODO: handle exception
}
}
// Upgrading database
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Drop older table if existed
db.execSQL("DROP TABLE IF EXISTS " + TABLE_LABELS);
// Create tables again
onCreate(db);
}
/**
* Inserting new lable into lables table
* */
// Database Version
private static int DATABASE_VERSION = 1;
// Database Name
private static String DATABASE_NAME = "spinnerExample";
// Labels table name
private static String TABLE_LABELS = "labels";
// Labels Table Columns names
private static String KEY_ID = "id";
private static String KEY_NAME = "name";
public void insertLabel(String label){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_NAME, label);
// Inserting Row
db.insert(TABLE_LABELS, null, values);
db.close(); // Closing database connection
}
* Getting all labels
* returns list of labels
* */
public List<String> getAllLabels(){
List<String> labels = new ArrayList<String>();
// Select All Query
String selectQuery = "SELECT * FROM " + TABLE_LABELS;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
labels.add(cursor.getString(1));
} while (cursor.moveToNext());
}
// closing connection
cursor.close();
db.close();
// returning lables
return labels;
}
}
===========================================================================================
<!-- Label -->
<!-- Input Text -->
<!-- Add Button -->
<!-- Select Label -->
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Select Label"
android:padding="8dip" />
<!-- Spinner Dropdown -->
<Spinner
android:id="#+id/spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:prompt="#string/spinner_title"
android:layout_marginTop="20dip"
android:layout_marginLeft="8dip"
android:layout_marginRight="8dip"
/>
</LinearLayout>
You don't want the application context for the db, you want the activity context, so your call should be:
DatabaseHandler db = new DatabaseHandler(this);
I've created a ListView from an SQLite database but am stuck on how to add a listener to each ListView item so that when an item is clicked I can display another page with more information on that item. The database is just a sample. Any help would be appreciated.
public class Database extends ListActivity {
private final String SAMPLE_DB_NAME = "myFriendsDb";
//private final String SAMPLE_TABLE_NAME = "friends";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayList<String> results = new ArrayList<String>();
SQLiteDatabase db = null;
try {
db = this.openOrCreateDatabase(SAMPLE_DB_NAME, MODE_PRIVATE, null);
db.execSQL("CREATE TABLE IF NOT EXISTS people" +
" (LastName VARCHAR, FirstName VARCHAR," +
" Country VARCHAR, Age INT(3));");
db.execSQL("INSERT INTO people" +
" Values ('Jones','Bob','UK',30);");
db.execSQL("INSERT INTO people" +
" Values ('Smith','John','UK',40);");
db.execSQL("INSERT INTO people" +
" Values ('Thompson','James','UK',50);");
Cursor c = db.rawQuery("SELECT FirstName, LastName FROM people", null);
if (c != null ) {
if (c.moveToFirst()) {
do {
String firstName = c.getString(c.getColumnIndex("FirstName"));
String lastName = c.getString(c.getColumnIndex("LastName"));
results.add("" + firstName + " " + lastName);
}while (c.moveToNext());
}
}
this.setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,results));
} catch (SQLiteException se ) {
Log.e(getClass().getSimpleName(), "Could not create or Open the database");
} finally {
if (db != null)
db.execSQL("DELETE FROM people");
db.close();
}
}
}
there are many ways to solve your problem. One possible solution is this: you simply need to implement protected method onListItemClick(ListView l, View v, int position, long id) in your ListActivity.
public class Database extends ListActivity {
//YOUR CODE ABOVE HERE...
public static final String SHOWITEMINTENT_EXTRA_FETCHROWID = "fetchRow";
public static final int ACTIVITY_SHOWITEM = 0; /*Intent request user index*/
#Override
protected void onListItemClick(ListView l, View v, int position, long id){
/*
position variable holds the position of item you clicked...
do your stuff here. If you want to send to another page, say another activity
that shows your stuff, you can always use an intent
example:
*/
Intent tmpIntent = new Intent(this, YourActivityForShowingItem.class);
tmpIntent.putExtra(SHOWITEMINTENT_EXTRA_FETCHROWID, position);
startActivityForResult(tmpIntent, ACTIVITY_SHOWITEM);
}
}
Alternately, you can access the ListView of your listActivity using getListView(), and call the setters for listeners or context menu as you would have done with a regular ListView object. For instance, this function that sets a listener using this approach:
private void setMyListListener(){
getListView().setOnItemClickListener(new OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> a, View v, int position, long id){
/*same fake code as above for calling another activity using an intent:*/
Intent tmpIntent = new Intent(this, YourActivityForShowingItem.class);
tmpIntent.putExtra(SHOWITEMINTENT_EXTRA_FETCHROWID, position);
startActivityForResult(tmpIntent, ACTIVITY_SHOWITEM);
}
});
}
This function can be called by your onCreate(...) function afterwards if you want your click listener to be configured the same way for the whole duration of your activity.
I am a beginner and I am studying the android at University.
Currently, I am using DBAdapter at the moment, and I am trying to show the data on layout from database by using ListView(if there are better way instead, advice me).
When I run application at the moment, makeText function will be called, and it will be shown data information.
Instead of showing the notification, I want to show some of values on ListView such as name, and quantity.
How can I get it? which ListView do I have to use it with database?
I want to know the handling database with ListView.
Please advice.
Code attached below:
FridgeDbAdapter.java
public class FridgeDbAdapter
{
public static final String KEY_ROWID = "_id";
public static final String KEY_CATEGORY = "category";
public static final String KEY_NAME = "name";
public static final String KEY_EXPIRED_DATE = "expired_date";
private static final String DATABASE_TABLE = "fridge_table";
private Context ctxt;
private SQLiteDatabase db;
private FridgeDatabaseHelper dbhelper;
public FridgeDbAdapter(Context ctxt)
{
this.ctxt = ctxt;
}
//DB databaseHelper
public class FridgeDatabaseHelper extends SQLiteOpenHelper
{
private static final String DATABASE_NAME = "fridge_db";
private static final int DATABASE_VERSION = 1;
private static final String DATBASE_CREATE =
"create table fridge_table (_id integer primary key autoincrement, "
+ "category text not null, name text not null, expired_date text not null);";
public FridgeDatabaseHelper(Context ctxt)
{
super(ctxt, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db)
{
db.execSQL(DATBASE_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVer, int newVer)
{
Log.w(FridgeDatabaseHelper.class.getName(),
"Upgrading database from version " + oldVer + " to "
+ newVer + ", which will delete the old data.");
db.execSQL("DROP TABLE IF EXISTS fridge_table");
//Method is called during creation of new database
onCreate(db);
}
}
//Open database
public FridgeDbAdapter open() throws SQLException
{
dbhelper = new FridgeDatabaseHelper(ctxt);
db = dbhelper.getWritableDatabase();
return this;
}
//Close database
public void close(){
dbhelper.close();
}
//Create a new item
public long insertItem(String category, String name, String expired_date)
{
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_CATEGORY, category);
initialValues.put(KEY_NAME, name);
initialValues.put(KEY_EXPIRED_DATE, expired_date);
return db.insert(DATABASE_TABLE, null, initialValues);
}
//update a item
public boolean updateItem(long rowId, String category,
String name, String expired_date)
{
ContentValues updateValues = new ContentValues();
updateValues.put(KEY_CATEGORY, category);
updateValues.put(KEY_NAME, name);
updateValues.put(KEY_EXPIRED_DATE, expired_date);
return db.update(DATABASE_TABLE, updateValues, KEY_ROWID + "=" + rowId,
null) > 0;
}
//delete a item
public boolean deleteItem(long rowId){
return db.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
}
//return cursor over the list of all items in the database
public Cursor fetchAllItems(){
return db.query(DATABASE_TABLE, new String[]{KEY_ROWID, KEY_CATEGORY,
KEY_NAME, KEY_EXPIRED_DATE},
null, null, null, null, null);
}
//return a cursor positioned at the defined item
public Cursor fetchItem(long rowId) throws SQLException{
Cursor mCursor = db.query(true, DATABASE_TABLE,
new String[]{KEY_ROWID, KEY_CATEGORY, KEY_NAME,
KEY_EXPIRED_DATE}, KEY_ROWID + "=" + rowId, null, null, null, null, null);
if(mCursor != null){
mCursor.moveToFirst();
}
return mCursor;
}
}
Fridge.java
public class Fridge extends Activity{
//Button goBack;
Button button1;
TextView text;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fridge);
FridgeDbAdapter db = new FridgeDbAdapter(this);
//Open database
db.open();
//Get all items
Cursor c = db.fetchAllItems();
if(c.moveToFirst())
{
do
{
//Call displayItem method in below
DisplayItem(c);
} while (c.moveToNext());
}
else
{
Toast.makeText(this, "No item found", Toast.LENGTH_SHORT).show();
}
db.close();
}
public void DisplayItem(Cursor c){
Toast.makeText(this, "id: " + c.getString(0) + "\n" +
"category: " + c.getString(1) + "\n" +
"name: " + c.getString(2) + "\n" +
"expired date: " + c.getString(3),
Toast.LENGTH_SHORT).show();
}
}
fridge.xml
<?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="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fridger page" />
<LinearLayout
android:id="#+id/fridge_List_View"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</LinearLayout>
<Button
android:id="#+id/add_Btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add item" />
</LinearLayout>
Formula you might wanna consider using it's the Loader API, it's great because it help you handle your cursor and query efficiently.
Loaders make it easy to asynchronously load data in an activity or
fragment.
They are available to every Activity and Fragment.
They provide asynchronous loading of data.
They monitor the source of their data and deliver new results when the content changes.
They automatically reconnect to the last loader's cursor when being recreated after a configuration change. Thus, they don't need to
re-query their data.
Implements LoaderCallbacks in your ListView or ListFragment, then override the methods onCreateLoader, onFinishedLoader, onRestartLoader. Initialize your loader with the LoaderManager, getLoaderManager().initLoader, and enjoy.
The only disadvantage that I've seen it's that is usefull if you have a ContentProvider, but even if you don't you can try some solutions in SO, like Loader without ContentProvider, In general i've learned that ContentProvider makes your life so much easier with the cursors, granted!.
public class ShowNotificationAdapter extends SimpleCursorAdapter{
Cursor dataCursor;
LayoutInflater mInflater;
Context context;
int layoutType;
DatabaseHelper db_helper;
public CustomAdapter(Context context, int layout, Cursor dataCursor, String[] from,
int[] to) {
super(context, layout, dataCursor, from, to);
this.context=context;
this.dataCursor = dataCursor;
mInflater = LayoutInflater.from(context);
db_helper=new DatabaseHelper(context);
db_helper.open();
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if(convertView==null)
{
convertView = mInflater.inflate(R.layout.custom_listitem, null);
holder = new ViewHolder();
holder.name=(TextView)convertView.findViewById(R.id.name);
holder.quantity=(TextView)convertView.findViewById(R.id.quantity);
holder.otherInfo=(TextView)convertView.findViewById(R.id.otherInfo);
}
else
{
holder=(ViewHolder)convertView.getTag();
}
dataCursor.moveToPosition(position);
String nameString=Integer.toString(dataCursor.getString(dataCursor.getColumnIndexOrThrow("name")));//column name of "name"
holder.name.setText(nameString);
String quantityString=dataCursor.getString(dataCursor.getColumnIndexOrThrow("quantity")); //column name of "quantity"
holder.quantity.setText(quantityString);
String otherInfoString=dataCursor.getString(dataCursor.getColumnIndexOrThrow("field_name"));
holder.otherInfo.setText(quantityString);
return convertView;
}
static class ViewHolder
{
TextView name;
TextView quantity;
TextView otherInfo;
}
#Override
public Object getItem(int position) {
return position;
}
}
You want to use a ListActivity.
And you will need to create a cursor and query the data in the fillData method.
Here's a decent tutorial on how to do that:
http://www.vogella.de/articles/AndroidSQLite/article.html
Use ListView and SimpleCursor adapter.
Example code can be found at:
http://thinkandroid.wordpress.com/2010/01/09/simplecursoradapters-and-listviews/
http://www.vogella.de/articles/AndroidListView/article.html
You can use a custom_adapter using SimpleCursorAdapter,for getting your database info. displayed onto the listview.
CustomAdpater.class:
public class CustomAdpater extends SimpleCursorAdapter{
Cursor dataCursor;
LayoutInflater mInflater;
Context context;
int layoutType;
DatabaseHelper db_helper;
public CustomAdapter(Context context, int layout, Cursor dataCursor, String[] from,
int[] to) {
super(context, layout, dataCursor, from, to);
this.context=context;
this.dataCursor = dataCursor;
mInflater = LayoutInflater.from(context);
db_helper=new DatabaseHelper(context);
db_helper.open();
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if(convertView==null)
{
convertView = mInflater.inflate(R.layout.custom_listitem, null);
holder = new ViewHolder();
holder.name=(TextView)convertView.findViewById(R.id.name);
holder.quantity=(TextView)convertView.findViewById(R.id.quantity);
holder.otherInfo=(TextView)convertView.findViewById(R.id.otherInfo);
}
else
{
holder=(ViewHolder)convertView.getTag();
}
dataCursor.moveToPosition(position);
String nameString=Integer.toString(dataCursor.getString(dataCursor.getColumnIndexOrThrow("name")));//column name of "name"
holder.name.setText(nameString);
String quantityString=dataCursor.getString(dataCursor.getColumnIndexOrThrow("quantity")); //column name of "quantity"
holder.quantity.setText(quantityString);
String otherInfoString=dataCursor.getString(dataCursor.getColumnIndexOrThrow("field_name"));
holder.otherInfo.setText(quantityString);
return convertView;
}
static class ViewHolder
{
TextView name;
TextView quantity;
TextView otherInfo;
}
#Override
public Object getItem(int position) {
return position;
}
}
custom_listitem.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:id="#+id/name"
/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:id="#+id/quantity"
/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:id="#+id/otherInfo"
/>
</LinearLayout>
MainActivity.class:
...
ListView listview=(ListView)findViewById(R.id.listview);
Cursor cursor=<get data to be passed to custom adapter>;
String[] from={"name","quantity"};//field names of 'name' and 'quantity'
int[] to={R.id.name,R.id.quantity};
CustomAdapter ca=new CustomAdapter(context,R.layout.custom_listitem,cursor,from,to);
listview.setAdapter(ca);
ca.notifyDataSetChanged();
...