I've been beating my head around trying to populate a ListView with data from an SQLite database and can't seem to figure it out. I've read countless tutorials and equally countless posts here, but I'm obviously not getting something critical. Was hoping someone could give me a hint as to why the following two pieces of code aren't working together, or if I should be looking at something else entirely. Any help would be appreciated. The result I'm getting is a force close.
Method that initiates populating ListView object
public void displayNurseRoster(){
listView = (ListView) findViewById(R.id.list);
// create instance of DbCommunicator
DbCommunicator rosterView = new DbCommunicator(this);
// open instance
rosterView.open();
// instantiate SimpleCursorAdapter instance and set value
SimpleCursorAdapter cursorAdapter;
cursorAdapter = rosterView.getRosterListViewAdapter(this);
// close database instance
rosterView.close();
// set adapter to listView
listView.setAdapter(cursorAdapter);
}
Method that returns SimpleCursorAdapter:
public SimpleCursorAdapter getRosterListViewAdapter (Context context) {
// method variables
int[] to = new int[] {R.id.rosterListLname};
// used ArrayList because of unpredictability of array length
List<String> dataArray = new ArrayList<String>();
String[] columns = new String[] {KEY_NURSE_ROWID, KEY_LNAME};
// create cursor
Cursor cursor = sqldb.query(NURSE_TABLE, columns, null, null, null,
null, KEY_LNAME);
int iLname = cursor.getColumnIndex(KEY_LNAME);
cursor.moveToFirst();
String result = "";
while(!cursor.isAfterLast()){
result = result
+ cursor.getString(iLname) + "\n";
dataArray.add(result);
}
// convert ArrayList to String array for use with SimpleCursorAdapter
String [] from = (String[]) dataArray.toArray();
SimpleCursorAdapter adapter = new SimpleCursorAdapter(context,
R.layout.edit_roster, cursor, from, to);
return adapter;
}
Okay, I got it to work and wanted to share to help out anyone else struggling with this and 2) to get feedback on my fix in case anyone with more experience has a better suggestion.
I actually wanted to show three items in the list view, so here's the code that worked. One of the problems I was having was that I was extending ListActivity, but found in another StackOverflow post that that is not a good idea when there is only one list view to worry about.
package com.deadEddie.staffingmanagement;
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
public class EditRoster extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_roster);
displayNurseRoster();
}
public void displayNurseRoster(){
ListView listView = (ListView) findViewById(R.id.list);
int[] to = new int[] {
R.id.rosterListLname,
R.id.rosterListFname,
R.id.rosterListMI };
String[] from = new String [] {
DbCommunicator.KEY_LNAME,
DbCommunicator.KEY_FNAME,
DbCommunicator.KEY_MI};
// create instance of DbCommunicator
DbCommunicator rosterView = new DbCommunicator(this);
// open instance
rosterView.open();
// get & manage cursor
Cursor cursor = rosterView.getRosterCursor(this);
startManagingCursor(cursor);
// instantiate cursor adaptor
ListAdapter adapter = new SimpleCursorAdapter(this,
R.layout.nurse_list, cursor, from, to);
cursor.moveToNext();
// set adapter to listView
listView.setAdapter(adapter);
}// displayNurseRoster()
}
Here's the method in my DbCommunicator class. What finally made the difference was that I was not creating an instance of SQLiteQueryBuilder and using that, but instead I was using sqldb.query(...), as shown above. I'm not sure what the difference is, but it finally did the trick. If anyone would like to share, please do.
public Cursor getRosterCursor (Context context) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(NURSE_TABLE);
Cursor cursor = queryBuilder.query(sqldb, new String[] {
KEY_NURSE_ROWID,
KEY_LNAME,
KEY_FNAME,
KEY_MI},
null, null, null, null, null);
return cursor;
}
A couple other newbie lessons for anyone else out there:
1. Always use the "_id" field in the cursor. The cursor cannot function without that.
2. The while or for loop is not necessary for a simple list view. The cursor adapter handles that.
3. There is no need to call the moveToFirst() method on the cursor.
Hope that's helpful to someone.
Related
my first question is here
however since I was advised that questions should not change the original matter I created a new one.
I am saving user settings and I would like to save it in the list, I have had a look on setting by James however I found that that its not possible to save it in the list. So ia have decided to use Xamarin Essentials.
First I tried to save only a string value, which after some struggle I managed to work out and now I am trying to save an object
static void AddToList(SettingField text)
{
var savedList = new List<SettingField>(Preference.SavedList);
savedList.Add(text);
Preference.SavedList = savedList;
}
private void ExecuteMultiPageCommand(bool value)
{
var recognitionProviderSettings = new RecognitionProviderSettings
{SettingFields = new List<SettingField>()};
var set = new SettingField()
{
ProviderSettingId = "test",
Value = "test"
};
AddToList(set);
NotifyPropertyChanged("IsMultiPage");
}
and then the sterilization and des
public static class Preference
{
private static SettingField _settingField;
public static List<SettingField> SavedList
{
get
{
//var savedList = Deserialize<List<string>>(Preferences.Get(nameof(SavedList), "tesr"));
var savedList = Newtonsoft.Json.JsonConvert.DeserializeObject<SettingField>(Preferences.Get(nameof(SavedList), _settingField)) ;
SavedList.Add(savedList);
return SavedList ?? new List<SettingField>();
}
set
{
var serializedList = Serialize(value);
Preferences.Set(nameof(SavedList), serializedList);
}
}
static T Deserialize<T>(string serializedObject) => JsonConvert.DeserializeObject<T>(serializedObject);
static string Serialize<T>(T objectToSerialize) => JsonConvert.SerializeObject(objectToSerialize);
}
}
But Preferences.Get doesn't take object, is there any other way how can I save my setting to a object list? Please advise
I would recommend you to use SecureStorage. You can save your strings only into it. So the place where you have serilized your object as json. Just convert your json to string with .ToString() and save it into secure storage.
You may continue saving your serialized json object as string in Shared preferences but it is recommended to use SecureStorage Instead.
Just started learning Dapper. I have an ADO.NET background. Using a demo I downloaded, I can insert/delete data from a webform into a MySql table just fine. This, however, I have searched all morning on.
In retrieving a single row from the db by ID, it doesn't return a LIST<>, it seems to be just an object (using code from the demo I downloaded). The query works, I get the object back. It has the fields: "ProductID, Description and Price".
The only way I could get the values to those three fields was like this:
System.Reflection.PropertyInfo pi = Product.GetType().GetProperty("ProductID");
System.Reflection.PropertyInfo desc = Product.GetType().GetProperty("Description");
System.Reflection.PropertyInfo price = Product.GetType().GetProperty("Price");
int _ProductID = (int)(pi.GetValue(Product, null));
string _Description = (string)(desc.GetValue(Product, null));
decimal _Price = (decimal)(price.GetValue(Product, null));
This works and gets the correct values for the three fields.
I'm used to looping through DataTables, but I just think there is probably a better way to get those values.
Is this the correct way to do this or am I missing something? I did actually read documentation and mess with this all morning before asking, too.
Some of the things I looked at seem to be very complex. I thought Dapper was supposed to simplify things.
OK, Thanks Marc. It was difficult for me to see what was supposed to be in the Dapper class files and what was supposed to be in my code behind. The original demo way of getting a product by ID had the query as .FirstOrDefault();
I changed everything to return a List<> and it all worked. I'm sure my ADO.NET is showing, but this works. In Dapper class files:
public List<Product> ProductAsList(int Id)
{
return this._db.Query<Product>("SELECT * FROM Cart_product WHERE ProductID=#Id", new { Id = Id }).**ToList()**;
}
This is just getting one row that matched the ProductID.
In page codebehind:
protected void CartItemAdd(string ProductId) // passing it the selected ProductID
{
var results = cartservice.ProductAsList(Convert.ToInt32(ProductId));
// returns that one row using Dapper ProductAsList(ProductId)
int _ProductId = 0;
string Description = string.Empty;
decimal Price = 0;
// Loop through the list and get the value of each item:
foreach (Product obj in results)
{
_ProductId = obj.ProductID;
Description = obj.Description;
Price = obj.Price;
}
// Using Dapper to insert the selected product into the shopping cart (table):
String UserName = "jbanks";
cartitem = new CartItem();
cartitem.ProductID = _ProductId;
cartitem.Quantity = 1;
cartitem.Description = Description;
cartitem.Price = Price;
cartitem.Created = DateTime.Now;
cartitem.CreatedBy = UserName;
result = cartservice.AddCartItem(cartitem);
if (result)
{
lblMessage.Text = string.Empty;
lblMessage.Text = "Successfully added a cart item";
}
}
}
It does indeed look up the product from one table and insert a selected item into another table.
Thanks again!
The main Query<T> API returns an IEnumerable<T>, which often will be a List<T>; the AsList<T>() extension method can get it back to a list without a copy, but either way: they are just T, for whatever T you asked for. If you asked for Query<Product>, then: they should be Product instances:
var results = connection.Query<Product>(someSql, someArgs); // perhaps .AsList()
foreach (Product obj in results) { // "var obj" would be fine here too
// now just use obj.ProductID, obj.Description and obj.Price
}
If that didn't work: check that you used the <T> version of Query. There is a non-generic variant too, which returns dynamic. Frankly, you should almost always use the <T> version.
Note: I'm assuming that somewhere you have something like
class Product {
public int ProductID {get;set;}
public string Description {get;set;}
public decimal Price {get;set;}
}
Gridview is not populating any data from Sqlite database while saving the data in to database. Logcat is not generating any error also.
My DB is.
public Cursor getAllRows() {
SQLiteDatabase db = this.getReadableDatabase();
//String where = null;
Cursor c = db.rawQuery("SELECT * FROM " + DATAALL_TABLE, null);
if (c != null) {
c.moveToFirst();
}
return c;
}
Mainactivity.
public void populateListView() {
Cursor cursor = db.getAllRows();
String[] fromFieldNames = new String[] {
DBHelper.COURSES_KEY_FIELD_ID1, DBHelper.FIELD_MATERIALDESC, DBHelper.FIELD_MATERIALNUM
};
int[] toViewIDs = new int[] {
R.id.textView1, R.id.textView3, R.id.textView2
};
SimpleCursorAdapter myCursorAdapter;
myCursorAdapter = new SimpleCursorAdapter(getBaseContext(), android.R.layout.activity_list_item, cursor, fromFieldNames, toViewIDs, 0);
GridView myList = (GridView) findViewById(R.id.gridView1);
myList.setAdapter(myCursorAdapter);
}
Post to the honorable member #MikeT advise its works fine but need alignment ,
as is
expected format
Your issue, assuming that there is data in the table, is likely that R.id.textView1 .... 3 are nothing to do with the layout passed to the SimpleCursorAdapter. i.e. Your issue is likely to do with the combination of the layout passed to the SimpleCursorAdapter and the Id's of the views passed as the to id's.
If you were to use :-
gridview = this.findViewById(R.id.gridView1);
csr = DBHelper.getAllRows();
myCursorAdapter = new SimpleCursorAdapter(
getBaseContext(),
//android.R.layout.simple_list_item_2,
android.R.layout.activity_list_item,
csr,
new String[]{
SO50674769DBHelper.COURSES_KEY_FIELD_ID1
//SO50674769DBHelper.FIELD_MATERIALDESC,
//SO50674769DBHelper.FIELD_MATERIALNUM
},
new int[]{android.R.id.text1}, //<<<< ID of the available view
0
);
gridview.setAdapter(myCursorAdapter);
Then result would be along the lines of :-
Changing to use a different stock layout and 2 fields as per :-
gridview = this.findViewById(R.id.gridView1);
csr = DBHelper.getAllRows();
myCursorAdapter = new SimpleCursorAdapter(
getBaseContext(),
android.R.layout.simple_list_item_2,
//android.R.layout.activity_list_item, //<<<< Changed Layout
csr,
new String[]{
SO50674769DBHelper.COURSES_KEY_FIELD_ID1,
SO50674769DBHelper.FIELD_MATERIALDESC,
//SO50674769DBHelper.FIELD_MATERIALNUM
},
new int[]{android.R.id.text1, android.R.id.text2}, //<<<< ID's of the 2 views
0
);
gridview.setAdapter(myCursorAdapter);
Note The DatabaseHelper class is so named for my convenience.
Would result in :-
As such I suspect that you need to change the layout to a one of your own.
Additionally, as hinted at your getAllRows method is not at all ideal.
Checking for a null Cursor is useless as a Cursor returned from rawQuery will not be null. It may be empty in which case the Cursor getCount method would return 0 ().
moveToFirst is also a waste.
Simply have :-
public Cursor getAllRows() {
SQLiteDatabase db = this.getReadableDatabase();
return db.rawQuery("SELECT * FROM " + DATAALL_TABLE, null);
}
I have created a two column editable TableView which the user can edit and change the data thats inside each cell. Now my question is, once the user has changed some data around in each cell, how do I then save this data or print it out in a way to add to an SQL query like this example below
INSERT INTO table_name
VALUES (Value from table,Value from table,Value from table,...);
//Editable cell
PriceColumn.setCellFactory(TextFieldTableCell.forTableColumn());
PriceColumn.setOnEditCommit(
new EventHandler<CellEditEvent<NewCustomerList, String>>() {
#Override
public void handle(CellEditEvent<NewCustomerList, String> t) {
((NewCustomerList) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setPrice(t.getNewValue());
}
}
);
You could do this by getting the required values and then passing them onto the DAO class to execute the query on the DB. Example follows-
PriceColumn.setOnEditCommit(
new EventHandler<CellEditEvent<NewCustomerList, String>>() {
#Override
public void handle(CellEditEvent<NewCustomerList, String> t) {
((NewCustomerList) t.getTableView().getItems().get(t.getTablePosition().getRow())).setPrice(t.getNewValue());
String newPrice = t.getNewValue();
String uniqueIdentifier = t.getRowValue().getUniqueIdentifier(); //Unique identfier is something that uniquely identify the row. It could be the name of the object that we are pricing here.
daoObj.updatePrice(newPrice, uniqueIdentifier); //Call DAO now
}
}
);
And somewhere in the deep dark jungles of DAO class,
private final String updateQuery = "UPDATE <TABLE_NAME> SET <PRICE_COLUMN> = ? WHERE <UNIQUE_COLUMN> = ?"; //If you require multiple columns to get a unique row, add them in the where clause as well.
public void updatePrice(String newPrice, String uniqueIdentifier) {
PreparedStatement ps = con.prepareStatement(updateQuery); //con is the connection object
ps.setString(1,uniqIdentifier); //if a string
ps.setString(2,newPrice); //if a string
ps.executeQuery();
}
If this is not what you were expecting, then can you please clarify your requirement?
I am trying to make a simple mp3 player using flash. The songs are loaded using an XML file which contains the song list. I have "play" button with the instance name "PlayBtn". I have an actionscript file named "playctrl", the content of which are listed below:
package classes
{
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.events.*;
import flash.events.MouseEvent;
import flash.net.URLRequest;
public class playctrl
{
private var MusicLoading:URLRequest;
private var music:Sound;
private var sc:SoundChannel;
private var currentSound:Sound;
private static var CurrentPos:Number;
private var xml:XML;
private var songlist:XMLList;
private static var currentIndex:Number;
public function playctrl()
{
music = new Sound();
currentSound= music;
CurrentPos = 0;
currentIndex = 0;
}
public function success(e:Event):void
{
xml = new XML(e.target.data);
songlist = xml.song;
MusicLoading = new URLRequest(songlist[0].file);
music.load(MusicLoading);
}
public function playSong(e:Event):void
{
if(sc != null)
sc.stop();
sc = currentSound.play(CurrentPos);
trace("HELLO !!!");
}
}
}
I have a second file named "play.as", the content of which is listed below:
import classes.playctrl;
var obj:playctrl = new playctrl();
var XMLLoader:URLLoader = new URLLoader(); //XML Loader
XMLLoader.addEventListener(Event.COMPLETE, obj.success);
XMLLoader.load(new URLRequest("playlist.xml"));
PlayBtn.addEventListener(MouseEvent.CLICK, obj.playSong);
However on clicking the play button, I notice that the function playSong() is called 7-8 times(check by printing an error msg. inside the function) resulting in overlapped audio output and the player crashing as a result. The function should be called only once when the MouseEvent.CLICK is triggered. Please help ...
interestingly, sound object doesn't have a built-in "isPlaying" boolean property (strange), so you could just create your own.
var isPlaying:Boolean
function playSong():void
{
if(!isPlaying)
sound.play();
}
function stopSong():void
{
if(isPlaying)
{
channel.stop();
isPlaying = false;
}
just a note: by convention, class names are capitalized camel case while instance names are uncapitalized camel case. so your playctrl.as class file should (or could) be PlayCtrl.as, and your PlayBtn instance should (or could) be playBtn.
Edit:
The title of your question is a bit misleading, the answer I gave you is a solution to the question expressed in the title.
Looking at your code, I would look at separating the concerns, on one hand you want to load the song data, on the other hand you want to control the sounds. I would implement separate classes for each concern. If you create a separate class for your player control, you'll be able to dispatch event within that class without the event bubbling all over your app and calling your functions several times.
//Previous answer
You could do this by implementing a Boolean that would be set when the sound is stopped or played.
In any case here's another way to filter unwanted clicks
private function playSong(event:MouseEvent ):void
{
// set up a conditional to identify your button ,
// here's an example...
if( event.currentTarget.name is "PlayBtn" )
{
//do whatever
//then...
event.stopImmediatePropagation();
}
}
This being said, in your case , it sounds like a bit of a quick fix since a MouseEvent shouldn't trigger the play function several times...
It would make sense to debug your code in order to understand why several events are dispatched after a Mouse click
private var _isPlaying:Boolean;
public function playSong(e:Event):void
{
if(sc != null)
{
sc.stop();
_isPlaying = false;
}
if( !_isPlaying )
{
sc = currentSound.play(CurrentPos);
_isPlaying = true;
trace("HELLO !!!");
}
}