sqlite database on listview - android

I'm creating an android book that has 6 season whithin sqlite database but listview take several seconds to load data.
I know that I sholud implement onscroll methode to do this,but I don't know how manage it
my code is:
public class list_story extends ListActivity{
private database db;
private String[] Name;
private String sea;
Context c;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.list_story);
db = new database(this);
Bundle ex = getIntent().getExtras();
sea = ex.getString("sea");
refresh();
setListAdapter(new AA());
}
class AA extends ArrayAdapter<String>{
public AA(){
super(list_story.this,R.layout.row_list,Name);
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater in = getLayoutInflater();
View row = in.inflate(R.layout.row_list, parent,false);
txt.setText(Name[position]);
return (row);
}
}
private void refresh(){
db.open();
int s =db.Story_count("content", sea);
Name = new String[s];
Fav=new String[s];
Tasvir=new String[s];
Scientific=new String[s];
English=new String[s];
for (int i = 0; i < s; i++) {
Name[i]=db.Story_display(i, sea, "content", 1);
Fav[i]=db.Story_display(i, sea, "content", 4);
Tasvir[i]=db.Story_display(i, sea, "content", 5);
Scientific[i]=db.Story_display(i, sea, "content", 6);
English[i]=db.Story_display(i, sea, "content", 7);
}
db.close();
}
}

Basically every time you try to inflate a new view, you will literally inflate a new view. The problem with that is if you have 100 plus items to put into the list view, well that's 100+ views you've defined. That's extremely inefficient. Use the ViewHolder pattern for ListView, or use a the RecyclerView. Better yet use the RecyclerView. There's a lot of information on the web for both of those. Just look up "how to use ViewHolder in a list view"

If you are only using a single text field and a db i would recomend you to use a simpleCursorAdapter :
db = new DBAdapter(this);
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_1,
db.getAllTitles(),
new String[] { "title" },
new int[] { android.R.id.text1 });
ListView listView = (ListView) findViewById(R.id.list);
listView.setAdapter(adapter);
If you have further questions check this great example https://thinkandroid.wordpress.com/2010/01/09/simplecursoradapters-and-listviews/

Related

Where do I use notifydatasetchanged in my code? Need for my ListView to automatically update itself after list item deletion

I know this has been discussed extensively on StackOverflow, and on the internet in general, but I can't find any good, solid information that helps me with my situation. At least none that I can understand and translate for my situation.
I have data in the database that I am retrieving with a Cursor. I am sticking this data into a ListView within a Fragment. On each line of my list, there is a button I use to delete that item. This all works, but I cannot figure out how to use notifydatasetchanged so that the ListView automatically refreshes itself, and the user can see that the deletion took place.
I have the List embedded in a SwipeRefreshLayout and the list item does disappear when I swipe it after deletion. However, what I want is for the list to automatically update on its own, which is what I thought the notifydatasetchanged was supposed to provide. Is this not the case? Is my understanding of notifydatasetchanged 100% incorrect?
Anyway, here is what I have:
My Home.java fragment:
public class Home extends Fragment{
private MainActivity mainActivity;
private View rootView;
private SwipeRefreshLayout personSwipeRefresh;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
rootView = inflater.inflate(R.layout.home, container, false);
personSwipeRefresh = (SwipeRefreshLayout) rootView.findViewById(R.id.person_swipe_refresh);
mainActivity = (MainActivity)getActivity();
return rootView;
}
#Override
public void onViewCreated(View rootView, Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
drawThePersonView();
personSwipeRefresh.setColorSchemeColors(Color.parseColor(Constants.RED), Color.parseColor(Constants.ORANGE), Color.parseColor(Constants.YELLOW), Color.parseColor(Constants.GREEN), Color.parseColor(Constants.BLUE), Color.parseColor(Constants.INDIGO), Color.parseColor(Constants.VIOLET));
personSwipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
personSwipeRefresh.setRefreshing(false);
drawThePersonView();
}
});
}
#Override
public void onResume() {
super.onResume();
drawThePersonView();
}
public void drawThePersonView(){
Context context = getContext();
DatabaseHelper myDBHelper = new DatabaseHelper(context);
Cursor personCursor = myDBHelper.getPersonCursor();
String[] fromColumns = {"_id","personNumber","location","status"};
int[] toViews = {R.id.person_number_textview, R.id.personNumber_textview, R.id.person_location_textview, R.id.person_status_textview};
CustomSimpleCursorAdapter mySimpleCursorAdapter = new CustomSimpleCursorAdapter(context, R.layout.person_layout, personCursor, fromColumns, toViews, 0, mainActivity);
ListView myListView = (ListView) rootView.findViewById(R.id.person_row);
// Draw the list
myListView.setAdapter(mySimpleCursorAdapter);
myDBHelper.close();
}
}
My CustomSimpleCursorAdapter.java
public class CustomSimpleCursorAdapter extends SimpleCursorAdapter {
private Context context;
public CustomSimpleCursorAdapter(Context context, int layout, Cursor cursor, String[] from, int[] to, int flags) {
super(context, layout, cursor, from, to, flags);
this.context = context;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent){
DatabaseHelper dbHelper = new DatabaseHelper(context);
View row = super.getView(position, convertView, parent);
cursor.moveToPosition(position);
final Integer ID = cursor.getInt(cursor.getColumnIndex("_id"));
final String PERSON_NUMBER = cursor.getString(cursor.getColumnIndex("personNumber"));
// Make the delete button clickable.
Button deletePersonButton = (Button)row.findViewById(R.id.remove_person);
deletePersonButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DatabaseHelper databaseHelper = new DatabaseHelper(context);
databaseHelper.deletePerson(PERSON_NUMBER);
}
});
return row;
}
}
Here is something I did try that I thought would work, but it did not:
Button deletePersonButton = (Button)row.findViewById(R.id.remove_person);
final CustomSimpleCursorAdapter csca = (CustomSimpleCursorAdapter)this;
...
databaseHelper.deletePerson(PERSON_NUMBER);
csca.notifydatasetchanged();
Thank you for taking the time to read this.
Have a good day. :-)
UPDATE: I found out my issue is that my cursor does not refresh when I delete from the database. If I had 5 items in the list before deletion, the cursor.getCount() says 5, but after I delete the item from the database, the `cursor.getCount()`` still says 5. How do I get my cursor to refresh from within my custom SimpleCursorAdapter?

Dynamic row layout for ListView

I have a ListView where I send data from a database using SimpleCursorAdapter, but I need to have dynamic row layout, because every table can have different number of columns.
Let's say I create TextViews according to the number of columns:
public int getColumnNumbers() {
int i = 0;
cursor = db.rawQuery("PRAGMA table_info("+DATABASE_TABLE_NAME+")", null);
if (cursor.moveToFirst()) {
do {
i++;
} while (cursor.moveToNext());
}
cursor.close();
return i;
}
........
int rowsNumber = getColumnNumbers();
textViews = new TextView[rowsNumber];
for(i = 0; i < rowsNumber; i++) {
textViews[i] = new TextView(this);
textViews[i].setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
What I'm basically looking for, is a way to get these TextViews passed to CursorAdapter's (or other adapter's) argument int[] to
I'm pretty new to this, so I would appreciate any help or advice.
EDIT:
I'm adding my implementation of bindViewmethod, which I made with help of the links provided here, in case someone would have to face similar issue in the future.
#Override
public void bindView(View view, Context context, Cursor cursor) {
int i, count = myHelper.getColumnNumbers();
String[] columnNames = myHelper.getColumnNamesString();
String text;
LinearLayout layout = (LinearLayout) view.findViewById(R.id.linear_layout_horizontal);
for(i = 0; i < (count-1); i++) {
TextView textView = new TextView(context);
textView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 1f));
text = cursor.getString(cursor.getColumnIndexOrThrow(columnNames[i+1]));
textView.setText(text);
layout.addView((TextView)textView);
}
}
EDIT 2:
I found out yesterday that the implementation mentioned above works until you need to scroll. After scrolling, ListView gets deformed.
So again, in case someone would like to do something similar, I'm adding my whole Adapter class.
public class DatabaseCursorAdapter extends CursorAdapter {
private DatabaseHelper myHelper;
private int count;
private String[] columnNames;
public DatabaseCursorAdapter(Context context, Cursor cursor) {
super(context, cursor, 0);
myHelper = new DatabaseHelper(context);
count = myHelper.getColumnNumbers();
columnNames = myHelper.getColumnNamesString();
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = LayoutInflater.from(context).inflate(R.layout.text_select, parent, false);
LinearLayout layout = (LinearLayout) view.findViewById(R.id.linear_layout_horizontal);
int i;
for(i = 0; i < count; i++) {
TextView textView = new TextView(context);
textView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1f));
textView.setTag(Integer.valueOf(i));
layout.addView(textView);
}
return view;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
int i;
String text;
for(i = 0; i < count; i++) {
TextView textView = (TextView) view.findViewWithTag(Integer.valueOf(i));
text = cursor.getString(cursor.getColumnIndexOrThrow(columnNames[i]));
textView.setText(text);
}
}
}
In the and, as it is with most issues, the solution is pretty easy. Technique is almost the same as if you would use a static layout, except instead of using findViewById, you just tag elements of your layout and use findViewWithTag method.
You need to look at good tutorial, a link is
Populating a ListView with a CursorAdapter . The web page has some nice explanations.
Look at TodoCursorAdapter and the bindView method to check which column/data is available from the database. Snippet of code from tutorial:
#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);
// Extract properties from cursor
String body = cursor.getString(cursor.getColumnIndexOrThrow("body"));
// Populate fields with extracted properties
tvBody.setText(body);
}
I think this is a simple code design.
Code in the webpage to populate data onto Listview:
// 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);
You can implement an ArrayAdapter instead of CursorAdapter. This makes sense if your app is not continually interfacing with the database. The link is Using an ArrayAdapter with ListView . It is the same website.
In this case, look at getView method instead of the similar bindView.

ListActivity ListView completed load

I want to set the background color depending on the data for the row of the ListView. I implemented a ListActivity but I dont know how to get notified the load is completed so that I can access the rows of the ListView.
public class RouteList extends ListActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.route_list);
CommuteDb db = new CommuteDb(this);
Cursor cursor = db.getRouteList();
ListAdapter adapter = new SimpleCursorAdapter(this, // Context.
R.layout.route_row, //row template
cursor, // Pass in the cursor to bind to.
new String[] { BaseColumns._ID, "Name" }, //Columns from table
new int[] { R.id.id, R.id.name }, //View to display data
0); //FLAG_REGISTER_CONTENT_OBSERVER
setListAdapter(adapter);
ListView listView = (ListView) findViewById(android.R.id.list);
Log.i("RouteList","listView.getChildCount()=" + listView.getChildCount()); //returns always 0
//Loop will not execute because no row yet
for (int i=0; i < listView.getChildCount(); i++) {
View rowView = listView.getChildAt(i);
Log.i("RouteList",rowView.getClass().getName());
rowView.setBackgroundColor(0x88ff0000);
}
If I execute this loop later (for instance on user's request) I am able to get each row and assign the color I want. However I need to do this automatically after data are loaded in ListView.
Thanx for the hint guys.
It works after modifying the code this way:
I added a custom adapter (RouteAdapter) derived from SimpleCursorAdapter:
private class RouteAdapter extends SimpleCursorAdapter {
public RouteAdapter(Context context, int layout, Cursor c,
String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//Let the default getView do the job (inflate the row layout and set views to their values from database
View view = super.getView(position, convertView, parent);
//Get the resulting view (route_row) and do custom loading
TextView isOffer = (TextView) view.findViewById(R.id.isOffer);
if (isOffer.getText().equals("0"))
view.setBackgroundColor(0x8800ff00); //seek=green
else
view.setBackgroundColor(0x88ff0000); //offer=red
return view;
}
}
Then in my original code I just replaced SimpleCursorAdapter by RouteAdapter:
ListAdapter adapter = new RouteAdapter(this, // Context.

Multiple ListViews in ExpandableListView or nested ListViews

I have been trying to import multiple Listviews inside another "mother" Listview.
I have a class where the ListViews are created and then the Activity class which call these views. The only thing I managed to do is fill the "mother" with ListPerms# entries(the objects I guess)
Here is my class for multiple ListViews
public ListPerms(Context context, String a, int docid) {
super(context);
table=a;
did=docid;
list= (ListView) findViewById(R.id.specific_perm_list);
getdata(list,ar_list);
}
private void getdata( ListView list, ArrayList<String> arlist){
Database openHelper = new Database(getContext());
myDB = openHelper.getReadableDatabase();
myDB=SQLiteDatabase.openDatabase("data/data/com.example.login2/databases/aeglea", null, SQLiteDatabase.OPEN_READONLY);
try{fill(list,arlist);}catch(Exception e){Log.e("NAT EXISTANT","THIS->"+e);}
}
private void fill( ListView list, ArrayList<String> arlist){
Cursor temp = null;
Cursor buffer = null;
String type_from_table = null;
String[] items = null;
if(table=="med") {type_from_table = "medication";}
if(table=="test") {type_from_table = "test";}
if(table=="all") {type_from_table = "allergy";}
if(table=="proc") {type_from_table = "procedure";}
if(table=="cond") {type_from_table = "condition";}
if(table=="vacc") {type_from_table = "vaccine";}
temp = fetchOption("SELECT * FROM permission WHERE did="+did+" AND type='"+type_from_table+"'");
if(temp.getCount()>0){
buffer = fetchOption("SELECT * FROM user_"+table+" WHERE id="+temp.getString(temp.getColumnIndex("fileid")));
items = new String[] {buffer.getString(buffer.getColumnIndex("name"))};
arlist.addAll( Arrays.asList(items) );
listAdapter = new ArrayAdapter<String>(getContext(), R.layout.item, arlist);
for(int i=1;i<temp.getCount();i++){
temp.moveToNext();
buffer = fetchOption("SELECT * FROM user_"+table+" WHERE id="+temp.getString(temp.getColumnIndex("fileid")));
listAdapter.add(buffer.getString(buffer.getColumnIndex("name")));
}
list.setAdapter(listAdapter);
}else{
items = new String[] { "None."};
arlist.addAll( Arrays.asList(items) );
listAdapter = new ArrayAdapter<String>(getContext(), R.layout.item, arlist);
list.setAdapter(listAdapter);
}
}
And the Activity class follows where(now) I have an ExpandableListView, but I'm really noob to understand how ELV works
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.docp);
general = (ExpandableListView) findViewById(R.id.Tot_perm_list);
//random data
}
protected void onResume(){
super.onResume();
ListPerms me = new ListPerms(getApplicationContext(),"med",did);
ListPerms te = new ListPerms(getApplicationContext(),"test",did);
ListPerms all = new ListPerms(getApplicationContext(),"all",did);
ListPerms proc = new ListPerms(getApplicationContext(),"proc",did);
ListPerms cond = new ListPerms(getApplicationContext(),"cond",did);
ListPerms vacc = new ListPerms(getApplicationContext(),"vacc",did);
me.setActivated(isChild());
te.setActivated(isChild());
all.setActivated(isChild());
proc.setActivated(isChild());
cond.setActivated(isChild());
vacc.setActivated(isChild());
list.add(me);
list.add(te);
list.add(all);
list.add(proc);
list.add(cond);
list.add(vacc);
general.setAdapter(listAdapter);
general.addChildrenForAccessibility(list);
//what do I do
}
Advice on how to do ListView{LV,Lv ... } or ExpandableListView{LV, LV ....}
Per my understanding it's not a good idea to incorporate two or more list views into one. You'll definitely have issues with scrolling (of course, some dirty hacks can help to fix it) and probably, performance will be way worst than for single list.
I would suggest You to go with ExpandableList and ExpandableListAdapter. To make lists into one You will need to compose it's adapters.
e.g. it could looks like the following:
...
// Your implementation of ExpandableListAdapter
// To store all 'child' adapters, it should be filled by the owner
SparseArray<BaseAdapter> mChildAdapters = new SparseArray<BaseAdapter>();
#Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
final BaseAdapter childAdapter = mChildAdapters.get(groupPosition);
View view = null;
if (childAdapter != null) {
view = childAdapter.getView(childPosition, convertView, parent);
}
return view;
}
#Override
public int getChildrenCount(int groupPosition) {
int childrenCount = 0;
final BaseAdapter childAdapter = mChildAdapters.get(groupPosition);
if (childAdapter != null) {
childrenCount = mChildAdapter.getCount();
}
return childrenCount;
}
...
Please note, the above code is just a draft, but I hope the idea is clear from it. The only trick You've might face with this approach - convertView You've received in one childAdapter might be from another, so some (quite trivial) checks should be applied before reusing convertView.
Also, expandable list would give you nice possibility to provide every list with the header (via groupView).

CheckedTextViews will randomly appear checked in a list if I click one further up the list

Ok, so this has been somewhat addressed alot on this site, however I do not believe the exact problem with what my code uses. I am filling a listView with CheckedTextViews which works completely. However when I click on an item it gets checked but when I scroll up and down random rows are also checked. I realize it must have something to do with how the ListView keeps track of the items. I am running into some errors at the moment. I attempted to fill a hashmap with the list of the rows so I can keep track which one is set to true and which are false. However I am not positive where to implement the map and try to fill it.
Here is my OnCreate
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewmenu);
//Get table name of menu clicked.
Bundle extras = getIntent().getExtras();
tableName = extras.getString("table");
// map each contact's name to a TextView in the ListView layout
String[] from = new String[] { "name" };
int[] to = new int[] { R.id.toppingCheckedTextView };
for(int i=0; i< from.length; i++){
map.put(i, false);
}
contactAdapter = new SimpleCursorAdapter(
ViewToppingListing.this, R.layout.toppings_list_item, null, from, to);
setListAdapter(contactAdapter); // set contactView's adapter
}
I attempt to place the map in the onCreate to fill it however it complains about a nullpointer.
Here is where I tried using the OnListItemClick method
#Override
protected void onListItemClick(ListView arg0, View arg1, int arg2, long arg3){
final int index = arg2 - arg0.getFirstVisiblePosition();
View v = arg0.getChildAt(index);
CheckedTextView ctv = (CheckedTextView) v.findViewById(R.id.toppingCheckedTextView);
if((Boolean)map.get(index) == true){
ctv.setChecked(true);
ctv.setVisibility(View.VISIBLE);
} else{
ctv.setVisibility(View.GONE);
}
}
I have read alot on this, and it seems that alot of solutions involves using getView(), however I don't know if that applies to my situation. Any help would be greatly appreciated!
First of all do you need a SimpleCursorAdapter? You set the adapter with a null cursor:
contactAdapter = new SimpleCursorAdapter(
ViewToppingListing.this, R.layout.toppings_list_item, null, from, to); // the third parameter is the cursor and you set it to null!
The behavior you see it's because of the ListView is recycling views and yes you'll have to implement your own adapter and override bindView(). The code bellow is based on another answer to a similar question maybe you'll want to look at it( Getting the selected View from ListView ). Here is an example:
public class TestCursorAdapter extends ListActivity {
MySimpleAdapter adapter;
private HashMap<Long, Boolean> positionHide = new HashMap<Long, Boolean>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] columns = new String[] { "_id", "name" };
MatrixCursor mc = new MatrixCursor(columns); // cursor for testing
for (int i = 1; i < 35; i++) {
long id = i;
mc.addRow(new Object[] { id, "Name" + i });
}
String[] from = new String[] { "name" };
int[] to = new int[] { R.id.checked_text };
adapter = new MySimpleAdapter(this,
R.layout.adapter_mysimpleadapter_row, mc, from, to);
setListAdapter(adapter);
}
private class MySimpleAdapter extends SimpleCursorAdapter {
public MySimpleAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
super.bindView(view, context, cursor);
CheckedTextView ctv = (CheckedTextView) view
.findViewById(R.id.checked_text);
long pos = cursor.getLong(0); // the id from the cursor
if (positionHide.get(pos) == null) {
ctv.setChecked(false);
// we don't have this id in the hashmap so the value is by
// default false, the TextView is GONE
} else {
// we have the value in the Hashmap so see what it is and set
// the textview visibility from this value
Boolean tmp = positionHide.get(pos);
if (tmp.booleanValue()) {
ctv.setChecked(true);
} else {
ctv.setChecked(false);
}
}
}
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Boolean tmp = positionHide.get(id);
if (tmp == null) {
// if null we don't have this key in the hashmap so
// we add it with the value true
positionHide.put(id, true);
} else {
positionHide.put(id, !tmp.booleanValue());
// if the value exists in the map then inverse it's value
}
adapter.notifyDataSetChanged(); // notify the adapter that something has
// changed
}
}

Categories

Resources