I have a list of Leagues that that I want to display the number of bowlers for in each entry. For Example:
I want to display a count of the number of bowlers in each list under each League name in the list. For Example:
This is meant to be a quick view about each League.
I tried to accomplish this with the following code:
DatabaseHelper
//Getting Number of Bowlers in League
public String leagueBowlerCount(String leagueId)
{
SQLiteDatabase db = this.getReadableDatabase();
String countQuery = "SELECT * FROM " + Bowler.TABLE_NAME + " WHERE " + Bowler.COLUMN_LEAGUE_ID + " = '" + leagueId + "'";
Cursor cursor = db.rawQuery(countQuery, null);
int count = 0;
if(null != cursor)
if(cursor.getCount() > 0){
cursor.moveToFirst();
count = cursor.getInt(0);
}
cursor.close();
db.close();
return String.valueOf(count);
}
League Adapter
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView id;
public TextView name;
public TextView baseScore;
public TextView basePercentage;
public TextView bowlerCount;
TextView timestamp;
public TextView buttonViewOption;
MyViewHolder(View view) {
super(view);
if (!(itemView instanceof AdView)) {
id = view.findViewById( R.id.tvLeagueId);
name = view.findViewById(R.id.tvLeagueName );
baseScore = view.findViewById( R.id.tvBaseScore);
basePercentage = view.findViewById(R.id.tvBaseScorePercentage);
bowlerCount = view.findViewById(R.id.tvNumberOfBowlers);
timestamp = view.findViewById(R.id.timestamp);
buttonViewOption = view.findViewById(R.id.buttonViewOptions);
}
}
}
public LeagueAdapter(Context context, List<League> leaguesList) {
this.context = context;
this.leaguesList = leaguesList;
mainActivity = (Activity) context;
inflater = LayoutInflater.from(context);
}
public LeagueAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
AdView adview;
MyViewHolder holder;
if (viewType == AD_TYPE) {
adview = new AdView(mainActivity);
adview.setAdSize( AdSize.BANNER);
// this is the good adview
adview.setAdUnitId(mainActivity.getString(R.string.admob_ad_id));
float density = mainActivity.getResources().getDisplayMetrics().density;
int height = Math.round(AdSize.BANNER.getHeight() * density);
AbsListView.LayoutParams params = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, height);
adview.setLayoutParams(params);
// dont use below if testing on a device
// follow https://developers.google.com/admob/android/quick-start?hl=en to setup testing device
AdRequest request = new AdRequest.Builder().build();
adview.loadAd(request);
holder = new MyViewHolder(adview);
}else{
View view = inflater.inflate(R.layout.listview_league, parent, false);
holder = new MyViewHolder(view);
}
return holder;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
if(position % 10 != 5) {
League league = leaguesList.get(position);
int id = league.getId();
String leagueId = String.valueOf(id);
holder.id.setText(leagueId);
holder.name.setText(league.getName());
holder.baseScore.setText(league.getBasisScore());
holder.basePercentage.setText(league.getBaseScorePercentage());
holder.bowlerCount.setText(db.leagueBowlerCount(leagueId));
holder.timestamp.setText(formatDate(league.getTimestamp()));
holder.buttonViewOption.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//creating a popup menu
PopupMenu popup = new PopupMenu(context, holder.buttonViewOption);
//inflating menu from xml resource
popup.inflate(R.menu.league_options_menu);
//adding click listener
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.profile:
//Log.d("leagueId", String.valueOf(position));
//int leagueId = league.getId();
((MainActivity) context).openDialog(true, leaguesList.get(position), position);
break;
case R.id.delete:
((MainActivity) context).deleteLeague(position);
break;
}
return false;
}
});
//displaying the popup
popup.show();
}
});
}
}
I have been messing around with this for a number of days, I cannot figure out why this will not work. Any assistance would be greatly appreciated. I am thinking that there is probably a much easier way of accomplishing this that I am not aware of.
In the logcat I am seeing the following message:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String ca.vogl.r.tenpinbowlingcompanion.database.DatabaseHelper.leagueBowlerCount(java.lang.String)' on a null object reference.
As was pointed out below, the error seems to be happening in leagueBowlerCount(), which is listed above.
After making making the following addition to the onBindViewHolder : db = new DatabaseHelper (mainActivity). I am seeing values where I should be but they are not correct. See images below.
Test League 1 (there are three bowlers, one is hidden by the test ad)
Test League 2 (there is only 1 bowler)
Test League 3 (there are three bowlers, one is hidden by the test ad)
So basically you should be seeing a 3 for Test League 1, a 1 for Test League 2 and a 3 for Test League 3
So it now seems that the problem is with the leagueBowlercount function that I wrote. It is not getting the counts that are associated only to the individual league Id
I believe that your issue is that you are returning the id of the first selected bowler rather than the row count.
That is you, after checking the number of rows is greater than 0, move to the first row and then use count = cursor.getInt(0); which will be the value stored in the first column of the first row that has been extracted.
try using :-
public String leagueBowlerCount(String leagueId)
{
String rv = "0";
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query(Bowler.TABLE_NAME,new String[]{"count(*)"},Bowler.COLUMN_LEAGUE_ID + "=?",new String[]{leagueId},null,null,null);
if (cursor.moveToFirst()) {
rv = String.valueOf(cursor.getLong(0));
}
cursor.close();
db.close();
return rv;
}
This uses the aggregate function count to extract the number of rows for the respective league.
Note the above code is in-principle code, it has not been tested or run and may therefore have some errors.
Alternatively you could use :-
public String leagueBowlerCount(String leagueId)
{
SQLiteDatabase db = this.getReadableDatabase();
String countQuery = "SELECT * FROM " + Bowler.TABLE_NAME + " WHERE " + Bowler.COLUMN_LEAGUE_ID + " = '" + leagueId + "'";
Cursor cursor = db.rawQuery(countQuery, null);
int count = cursor.getCount();
cursor.close();
db.close();
return String.valueOf(count);
}
In regard to your code :-
int count = 0;
if(null != cursor)
if(cursor.getCount() > 0){
cursor.moveToFirst();
count = cursor.getInt(0);
}
cursor.close();
Checking for a null Cursor is useless, as a Cursor returned from an SQLiteDatabase method, such as rawQuery, will never be null. Instead a valid, perhaps empty, Cursor will be returned.
Additionally checking if a Cursor has rows using the getCount method and then using moveToFirst is not needed as just using if (cursor.moveToFirst) {.....} is sufficient as if there are no rows the moveToFirst method will return false, as the move cannot be actioned.
Related
I am trying to create a filter for a ListView of Employees.
I am collecting the spinner value in the MainActivity which works as it is displayed using Toast
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
//first, we have to retrieve the item position as a string
// then, we can change string value into integer
String item_position = String.valueOf(position);
positonInt = Integer.valueOf(item_position);
Toast.makeText(MainActivity.this, "value is "+ positonInt, Toast.LENGTH_SHORT).show();
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
Later in the MainActivity I set the adapter by calling the methodgetEmployeeList
List<Employee> employeeList = dataBaseHelper.getEmployeeList(positonInt);
adaptor = new EmployeeAdaptor(MainActivity.this, employeeList);
empListView.setAdapter(adaptor);
I am trying to achieve this by calling the method below, depending the Spinner position value is passed to the method getEmployeeList() which is then used in a switch statement to create the query to populate the ListView
public List<Employee> getEmployeeList(int spinnerPostion) {
switch (spinnerPostion) {
case 1: spinnerPostion = 0;
query = "SELECT * FROM " + EMP_TABLE
+ " ORDER BY " + PROFIT + " DESC ";
break;
}
List<Employee> employeesList = new ArrayList<>();
//Referencing the active database to read infromation
SQLiteDatabase db = this.getReadableDatabase();
//Executing the query
// -Using a Cursor so we can iterate through all readable data in the db
Cursor cursor = db.rawQuery(query, null);
if (cursor.moveToFirst()) {
//If there are results loop through and create a new Item
//for every item in the db
do {
int employeeID = cursor.getInt(0);
String employeeName = cursor.getString(1);
String employeeSecondName = cursor.getString(2);
int profit = cursor.getInt(3);
Employee menu_emp = new Employee(employeeID, employeeName, employeeSecondName, profit);
employeesList.add(menu_emp);
} while (cursor.moveToNext());
} else {
// Empty List contains no Items
}
//Closing connection to the database and cursor
cursor.close();
db.close();
return employeesList;
}
The method getEmployeeList() works without the switch statement and populates the list when I just set String query but I am getting errors when I try to do this using the switch statement.
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.dropit/com.example.dropit.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.String.trim()' on a null object reference
Are you sure the value you are getting from the Spinner is 1
or you if it's 0 change it to
case 0:
{
spinnerPostion = 0;
query = "SELECT * FROM " + EMP_TABLE
+ " ORDER BY " + PROFIT + " DESC ";
break;
}
Illustration
Please see image first..
How can i get value "Test 1" and after that i want update column (favorite) in sqlite database to "Yes"?
I was searching to find answer for my case but there is no match with my problem, or maybe I am the one who doesn't understand.. :D
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_channel_list);
channelList = this.findViewById(R.id.channelList);
mDB = openDB();
if (mDB != null) {
mCsr = mDB.query(CHANNELTABLE,
new String[]{KEY_NO + " AS _id",
KEY_NAME, KEY_CATEGORY, KEY_LOGO, KEY_SERVER1, KEY_SERVER2, KEY_SERVER3, KEY_SERVER4, KEY_SERVER5, KEY_LIKE
},
null,null,null,null,null);
mSCA = new SimpleCursorAdapter(this,R.layout.custom_listview,mCsr,
new String[]{KEY_NAME, KEY_CATEGORY, KEY_LOGO, KEY_LIKE},
new int[]{R.id.channel_name, R.id.category, R.id.logo, R.id.like},0);
mSCA.setViewBinder(new SimpleCursorAdapter.ViewBinder(){
public boolean setViewValue(final View view, Cursor cursor, int columnIndex){
if(view.getId() == R.id.logo){
channelName = cursor.getString(3);
int resID = getResources().getIdentifier(channelName, "drawable", getPackageName());
//Toast.makeText(Channel_List_Act.this, cursor.getString(9), Toast.LENGTH_LONG).show();
((ImageView)view.findViewById(R.id.logo)).setImageDrawable(getResources().getDrawable(resID, getApplicationContext().getTheme()));
return true;
}
if(view.getId() == R.id.like){
liked = cursor.getString(9);
final int resID = getResources().getIdentifier(liked, "drawable", getPackageName());
((ImageView)view.findViewById(R.id.like)).setImageDrawable(getResources().getDrawable(resID, getApplicationContext().getTheme()));
((ImageView)view.findViewById(R.id.like)).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
I AM STUCK IN HERE, OR MAYBE NOT HERE?
}
});
return true;
}
return false;
}
});
channelList.setAdapter(mSCA);
} else {
Toast.makeText(this,"Unable to open Database.",Toast.LENGTH_LONG).show();
}
}
this is weird to answer my own question.. :D
//This is for getting Textview and Imageview from custom_listview.xml
RelativeLayout vwParentRow = (RelativeLayout)v.getParent();
TextView child = (TextView)vwParentRow.getChildAt(1);
ImageView btnChild = (ImageView)vwParentRow.getChildAt(3);
//This is for changing image to favorited or no
assert (R.id.like == btnChild.getId());
Integer integer = (Integer) btnChild.getTag();
integer = integer == null ? 0 : integer;
switch(integer) {
case R.drawable.yes:
btnChild.setImageResource(R.drawable.no);
btnChild.setTag(R.drawable.no);
//This is to update database
mDB.execSQL("UPDATE Channel_Info SET Favourite='no' WHERE name='" + child.getText() + "'");
break;
case R.drawable.no:
default:
btnChild.setImageResource(R.drawable.yes);
btnChild.setTag(R.drawable.yes);
mDB.execSQL("UPDATE Channel_Info SET Favourite='yes' WHERE name='" + child.getText() + "'");
break;
}
Get the ID of a drawable in ImageView
I am forgot.
Forgot
I want to make a to-do list app, and I wanted to delete the item in the list by tapping the checkbox.
I tried to make a "deleteTask"(as you see in the code) method in the database class. Also, you can see the "populateListView"
method, it provides data from the database into listview, I use it to refresh after each time a task got deleted from the database.
public void deleteTask(String task) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_NAME, COL2 , new String[]{task});
}
public void populateListView() {
try {
mDataBaseHelper = new DataBaseHelper(MainActivity.this);
data = mDataBaseHelper.getData();
mArrayList = new ArrayList<>();
if (data.getCount() != 0) {
while (data.moveToNext()) {
mArrayList.add(data.getString(1));
ListAdapter listAdapter = new ArrayAdapter(MainActivity.this, R.layout.list_items, R.id.checkBox, mArrayList);
list = (ListView) findViewById(R.id.myListId);
list.setAdapter(listAdapter);
}
mDataBaseHelper.close();
} else {
toastMessage("the Database is empty");
}
}catch(Exception e){
Log.e(TAG, "populateListView: error"+e.getStackTrace() );
}
}
when the application gets started, I tapped the item that I want to delete, but I see that the items start to be deleted by order from above!
one by one each time I tapped any checkbox.
You want :-
public void deleteTask(String task) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_NAME, COL2 + "=?" , new String[]{task});
}
If you weren't trapping the error by using the try/catch using db.delete(TABLE_NAME, COL2 , new String[]{task}); you would get an exception along the lines of :-
java.lang.IllegalArgumentException: Too many bind arguments. 1 arguments were provided but the statement needs 0 arguments.
However
Assuming that the issue with deleting rows sequentially rather than according to the checked item(s), is likely due to the handling of the checked items. However, as the code for this is not provided it would only be guess work to know where in the code you are going wrong.
One thing is that you do not want to be creating a new listadapter instance every time you populate the ListView.
As a hint to handling a ListView, but deleting an item when it is long-clicked based upon the COL2 value, perhaps consider the following which has been based upon your code (but deletes according to long clicking an item) :-
public void populateLisView() {
mDataBaseHelper = new DataBaseHelper(this); //<<<<<<<<<< NOTE 1
list = (ListView) this.findViewById(R.id.myListId); //<<<<<<<<<< NOTE 1
data = mDataBaseHelper.getData(); //<<<<<<<<<< get the data to be listed
if (listadapter == null) { //<<<<<<<<<< Only need to instantiate one adapter when it has not bee instantiated
listadapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,android.R.id.text1,data); // for convenience using a stock layout
list.setAdapter(listadapter);
//<<<<<<<<<<< add the onItemLongClick listener
list.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
mDataBaseHelper.deleteTaskByCol2(data.get(position)); //<<<<<<<<<< gets the value of the item according to it's position in the list
populateLisView(); //<<<<<<<<<< as the item has been deleted then refresh the Listview
return true; // flag the event as having been handled.
}
});
//<<<<<<<<<<< If the Adapter has been instantiated then refresh the ListView's data
} else {
listadapter.clear(); // Clear the data from the adapter
listadapter.addAll(data); // add the new changed data to the adapter
listadapter.notifyDataSetChanged(); // tell the adapter that the data has changed
}
}
NOTE 1
you would typically instantiate these variables once.
Check the comments
You may wish to edit your question to include how you are handling the check events.
The Full Working Example
DatabaseHelper.java
Note this may differ from yours a little
public class DataBaseHelper extends SQLiteOpenHelper {
public static final String DBNAME = "mydb";
public static final int DBVERSION = 1;
public static final String TABLE_NAME = "mytable";
public static final String COL1 = "col1";
public static final String COL2 = "col2";
SQLiteDatabase db;
private static final String CRT_MYTABLE_SQL = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME +
"(" +
COL1 + " TEXT, " +
COL2 + " TEXT" +
")";
public DataBaseHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
db = this.getWritableDatabase();
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CRT_MYTABLE_SQL);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public long addMytableRow(String col1, String col2) {
ContentValues cv = new ContentValues();
cv.put(COL1,col1);
cv.put(COL2,col2);
return db.insert(TABLE_NAME,null,cv);
}
public ArrayList<String> getData() {
ArrayList<String> rv = new ArrayList<>();
Cursor csr = db.query(TABLE_NAME,null,null,null,null,null,null);
while (csr.moveToNext()) {
rv.add(csr.getString(csr.getColumnIndex(COL2)));
}
csr.close();
return rv;
}
public void deleteTaskByCol2(String task) {
db.delete(TABLE_NAME,COL2 + "=?",new String[]{task});
}
}
MainActivity.java
i.e. an example activity that is based upon your code, but according to the above :-
public class MainActivity extends AppCompatActivity {
DataBaseHelper mDataBaseHelper;
ArrayList<String> data;
ListView list;
ArrayAdapter<String> listadapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addSomeTestData();
populateLisView();
}
private void example001() {
}
public void populateLisView() {
mDataBaseHelper = new DataBaseHelper(this);
list = (ListView) this.findViewById(R.id.myListId);
data = mDataBaseHelper.getData();
if (listadapter == null) {
listadapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,android.R.id.text1,data);
list.setAdapter(listadapter);
list.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
//mDataBaseHelper.deleteTaskWrong(data.get(position)); // ooops
mDataBaseHelper.deleteTaskByCol2(data.get(position));
populateLisView();
return true;
}
});
} else {
listadapter.clear();
listadapter.addAll(data);
listadapter.notifyDataSetChanged();
}
}
private void addSomeTestData() {
if (mDataBaseHelper == null) {
mDataBaseHelper = new DataBaseHelper(this);
}
if (DatabaseUtils.queryNumEntries(mDataBaseHelper.getWritableDatabase(),DataBaseHelper.TABLE_NAME) > 0) return;
mDataBaseHelper.addMytableRow("Test1","Test1");
mDataBaseHelper.addMytableRow("Test2","Test2");
mDataBaseHelper.addMytableRow("Test3","Test3");
mDataBaseHelper.addMytableRow("Test4","Test4");
}
}
Note AddSomeTestData adds some data for testing/demonstration.
Result
When first run :-
After LongClicking Test 2
i.e. the long clicked item has been removed (from the list and the database) and the list refreshed.
Try to replace
db.delete(TABLE_NAME, COL2 , new String[]{task});
By
db.delete(TABLE_NAME, COL2 + " = ?" , new String[]{task});
I am setting a new activity named Dispatch Report, which has two Spinners: CustomerSpinner and LotSpinner.
LotSpinner shows all Lots in Dispatch Table instead of showing only those Lots which are related to the Customer selected in the first Spinner.
I have fetched CustomerSpinner Value from Dispatch Table. In LotSpinner also fetched Lot numbers from Dispatch Table, but not Filtered according to customer selection.
DispatchReportActivity.Java
// Fetching customer from dispatch table
private void loadCustomerNameDispatch() {
DatabaseHelper db = new DatabaseHelper( getApplicationContext() );
List<String> lables1 = db.getFirmNAmeMoveStock();
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, lables1);
dataAdapter .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// attaching data adapter to spinner
spinCustomer.setAdapter(dataAdapter);
spinCustomer.setOnItemSelectedListener(this);
}
// Fetching lot from dispatch table
private void loadLotbyCustomerDispatch() {
// database handler
DatabaseHelper db = new DatabaseHelper(getApplicationContext());
// Spinner Drop down elements
List<String> lables = db.getLotbyCustomer();
// 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
spinLotbyCustomer.setAdapter(dataAdapter);
}
DATABASEHELPER.Java
//Get firm name in Dispatch Stock Report screen
public List < String > getFirmNAmeMoveStock() {
List < String > labels = new ArrayList < String > ();
// Select all query
String selectQuery = "SELECT * FROM " + Table_Inventory;
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(3));
Set < String > set = new HashSet < >(labels);
labels.clear();
labels.addAll(set);
} while ( cursor . moveToNext ());
}
// Closing connection
cursor.close();
db.close();
// Returning lables
return labels;
}
// Method to get Lot No. in Dispatch Stock Report Activity
public List < String > getLotbyCustomer() {
List < String > labels1 = new ArrayList < String > ();
// Select all query
String selectQuery = "SELECT * FROM " + Table_StockDispatch;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
labels1.add(cursor.getString(4));
Set < String > set = new HashSet < >(labels1);
labels1.clear();
labels1.addAll(set);
} while ( cursor . moveToNext ());
}
// Closing connection
cursor.close();
db.close();
// Returning lables
return labels1;
}
There will be multiple customers, and each customer could have multiple Lots, so I want the second spinner to show only those Lots which are relevant to the customer selected in the first Spinner.
I'd suggest utilising Cursor's and Cursor adapters which can make matter simpler as :-
there is no need for intermediate arrays (one of your issues is that String arrays do not provide sufficient information)
CursorAdapters are designed to handle id (albiet a requirement that these exists in the Cursor with the column name _id (see the use of BaseColumns._ID below)).
The following is a basic example of related spinners based loosely upon your requirements.
First the DatbaseHelper DatabaseHelper.java
Two tables are defined/created Customers and Lots, methods to add data for each exist as do methods to extract a list from each of the tables. The lots extracted are based upon the customer to which they reference/belong to/associate with.
public class DatabaseHelper extends SQLiteOpenHelper {
public static final String DBNAME = "mydb";
public static final int DBVERSION = 1;
public static final String TBL_CUSTOMER = "customer";
public static final String TBL_LOT = "lot";
public static final String COL_CUSTOMER_ID = BaseColumns._ID; //<<<<<<<<<< column name is _id (needed for Cursor Adapter)
public static final String COL_CUSTOMER_NAME = "customer_name";
public static final String COL_LOT_ID = BaseColumns._ID; //<<<<<<<<<< column name is _id (needed for Cursor Adapter)
public static final String COL_LOT_NAME = "lot_name";
public static final String COL_LOT_CUSTOMERREFERENCE = "customer_refererence";
SQLiteDatabase mDB;
public DatabaseHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
mDB = this.getWritableDatabase(); //<<<<<<<<<< get the database connection (force create when constructing helper instance)
}
#Override
public void onCreate(SQLiteDatabase db) {
String crt_customer_table_sql = "CREATE TABLE IF NOT EXISTS " + TBL_CUSTOMER + "(" +
COL_CUSTOMER_ID + " INTEGER PRIMARY KEY, " +
COL_CUSTOMER_NAME + " TEXT UNIQUE " +
")";
String crt_lot_table_sql = "CREATE TABLE IF NOT EXISTS " + TBL_LOT + "(" +
COL_LOT_ID + " INTEGER PRIMARY KEY, " +
COL_LOT_NAME + " TEXT, " +
COL_LOT_CUSTOMERREFERENCE + " INTEGER " +
/*?????????? OPTIONAL IF FOREIGN KEYS ARE TURNED ON
"REFERENCES " + TBL_CUSTOMER + "(" +
COL_CUSTOMER_ID +
")" +
*/
")";
db.execSQL(crt_customer_table_sql);
db.execSQL(crt_lot_table_sql);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public long addCustomer(String name) {
ContentValues cv = new ContentValues();
cv.put(COL_CUSTOMER_NAME,name);
return mDB.insert(TBL_CUSTOMER,null,cv);
}
public long addLot(String name, long customer_reference) {
ContentValues cv = new ContentValues();
cv.put(COL_LOT_NAME,name);
cv.put(COL_LOT_CUSTOMERREFERENCE,customer_reference);
return mDB.insert(TBL_LOT,name,cv);
}
public Cursor getCustomers() {
return mDB.query(TBL_CUSTOMER,null,null,null,null,null,COL_CUSTOMER_NAME);
}
public Cursor getLotsPerCustomer(long customer_id) {
String whereclause = COL_LOT_CUSTOMERREFERENCE + "=?";
String[] whereargs = new String[]{String.valueOf(customer_id)};
return mDB.query(TBL_LOT,null,whereclause,whereargs,null,null,COL_LOT_NAME);
}
}
Note the above is pretty straight-forward. However, it would obviously need to be adapted to suit you App.
The second code is the activity that utilises the above and incorporates the 2 linked/related spinners where the selectable Lots are as per those lots associated with the currently selected customer.
The layout used for the activity is very basic, it just has two spinners. The spiners use the stock Simple_List_Item_2 layout (2 has been used to allow the all important ID's to be viewed (typically the user would not be shown the ID's)).
In short whenever a selection is made in the Customer spinner the Lot spinner is managed (setup or refreshed) based upon the customer id which is used to select the related/reference lots.
public class MainActivity extends AppCompatActivity {
Context mContext;
DatabaseHelper mDBHlpr;
SimpleCursorAdapter mCustomerSCA, mLotSCA;
Spinner mCustomerList, mLotList;
Cursor mCustomers, mLots;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
mDBHlpr = new DatabaseHelper(this);
mCustomerList = this.findViewById(R.id.customer_list);
mLotList = this.findViewById(R.id.lot_list);
addTestingDataIfNoData(); //Go and add some testing data if there is none
manageCustomerSpinner();
}
private void manageCustomerSpinner() {
mCustomers = mDBHlpr.getCustomers();
if (mCustomerSCA == null) {
mCustomerSCA = new SimpleCursorAdapter(
this,
android.R.layout.simple_list_item_2,
mCustomers,
new String[]{
DatabaseHelper.COL_CUSTOMER_NAME,
DatabaseHelper.COL_CUSTOMER_ID
},
new int[]{
android.R.id.text1,
android.R.id.text2
},
0
);
mCustomerList.setAdapter(mCustomerSCA);
mCustomerList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
manageLotSpinner(id); //<<<<<<<<<< WHENEVER CUSTOMER IS SELECTED THE LOT SPINNER IS MANAGED >>>>>>>>>>
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
} else {
mCustomerSCA.swapCursor(mCustomers);
}
}
private void manageLotSpinner(long id) {
mLots = mDBHlpr.getLotsPerCustomer(id);
if (mLotSCA == null) {
mLotSCA = new SimpleCursorAdapter(
this,
android.R.layout.simple_list_item_2,
mLots,
new String[]{
DatabaseHelper.COL_LOT_NAME,
DatabaseHelper.COL_LOT_ID
},
new int[]{
android.R.id.text1,
android.R.id.text2
},
0
);
mLotList.setAdapter(mLotSCA);
} else {
mLotSCA.swapCursor(mLots);
}
}
private void addTestingDataIfNoData() {
if (DatabaseUtils.queryNumEntries(mDBHlpr.getWritableDatabase(),DatabaseHelper.TBL_CUSTOMER) < 1) {
mDBHlpr.addCustomer("Fred");
mDBHlpr.addCustomer("Mary");
mDBHlpr.addCustomer("Sue");
mDBHlpr.addCustomer("Alan");
mDBHlpr.addLot("Lot001",2); // Lot for mary
mDBHlpr.addLot("Lot002",1); // Lot for fred
mDBHlpr.addLot("Lot003",4); // Lot for ala
mDBHlpr.addLot("Lot004",3); // Lot for sue
mDBHlpr.addLot("Lot005",3); // Lot for sue
mDBHlpr.addLot("Lot006",3); // Lot for use
mDBHlpr.addLot("Lot007",2); // Lot for mary
mDBHlpr.addLot("Lot008",2); // Lot for mary
mDBHlpr.addLot("Lot009",2); // Lot for mary
mDBHlpr.addLot("Lot0010",2); // Lot for mary
mDBHlpr.addLot("Lot0020",1); // Lot for Fred
mDBHlpr.addLot("Lot00130",4); // Lot for Alan
mDBHlpr.addLot("Lot00130",3); // Lot for Sue
}
}
}
Result Example
Initial
Alan is initial selection due to sort order
After Selecting Mary
Note Lot names, as used, are not really suited to sorting
I'm using an ExpandableListView inside one of my activities and populating the child and group views (separate .xml files with a few textviews for each) using a custom SimpleCursorAdapter. I'm looking to have the following functionality: When a group is clicked, the list of children populates and an additional header (two textviews) populates in position 0 of the child list to act as the titles of each column of data the child displays.
Here's the code for my SimpleCursorAdapter and a snippet of the relevant code in my activity:
SimpleCursorAdapter:
public class PayeeCursorAdapter extends SimpleCursorTreeAdapter {
private final String LOG_TAG = getClass().getSimpleName();
private PayeeActivity mActivity;
protected final HashMap<Integer, Integer> mGroupMap;
// No cursor is added to the adapter so that it only runs when the CursorLoader runs, instead of every time the activity does
public PayeeCursorAdapter(
Context context, // The activity where the adapter will be running
int groupLayout, // The .xml layout file for the group layout
int childLayout, // The .xml layout file for the child layout
String[] groupFrom, // String of column names in the cursor that is the data for each group item
int[] groupTo, // The ID of the views in the group layout that display the column from the groupFrom String[]
String[] childrenFrom, // String of column names in the cursor that is the data for each child item
int[] childrenTo) { // The ID of the views in the child layout that display the column from the childFrom String[]
super(context, null, groupLayout, groupFrom, groupTo, childLayout, childrenFrom, childrenTo);
mActivity = (PayeeActivity) context;
mGroupMap = new HashMap<Integer, Integer>();
}
#Override
protected Cursor getChildrenCursor(Cursor groupCursor) {
int groupPos = groupCursor.getPosition();
int groupId = groupCursor.getInt(groupCursor.getColumnIndex(BillMeContract.PayeeEntry._ID));
Log.d(LOG_TAG, "getChildrenCursor() for groupPos " + groupPos);
Log.d(LOG_TAG, "getChildrenCursor() for groupId " + groupId);
mGroupMap.put(groupId, groupPos);
Loader<Cursor> loader = mActivity.getSupportLoaderManager().getLoader(groupId);
if(loader != null && !loader.isReset()) {
mActivity.getSupportLoaderManager().restartLoader(groupId, null, mActivity);
} else {
mActivity.getSupportLoaderManager().initLoader(groupId, null, mActivity);
}
return null;
}
public HashMap<Integer, Integer> getGroupMap(){
return mGroupMap;
}
}
Activity:
ExpandableListView expandablePayeeListView = (ExpandableListView) findViewById(R.id.payee_exp_list);
mAdapter = new PayeeCursorAdapter(
this,
R.layout.list_group_payee,
R.layout.list_item_payee_tx,
new String[] {BillMeContract.PayeeEntry.COL_NAME},
new int[] {R.id.payee_list_header},
new String[] {BillMeContract.TransactionEntry.COL_DATE,
BillMeContract.TransactionEntry.COL_PAYMENT,
BillMeContract.TransactionEntry.COL_TYPE},
new int[] {R.id.payee_list_item_date,
R.id.payee_list_item_amount,
R.id.payee_list_item_type});
mAdapter.setViewBinder(new SimpleCursorTreeAdapter.ViewBinder() {
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if(columnIndex == 4) {
int type = cursor.getInt(columnIndex);
TextView textView = (TextView) view;
if(type == BillMeContract.TransactionEntry.TYPE_CASH) {
textView.setText(R.string.tx_spinner_type_cash);
} else if (type == BillMeContract.TransactionEntry.TYPE_CHEQUE) {
textView.setText(R.string.tx_spinner_type_cheque);
} else if (type == BillMeContract.TransactionEntry.TYPE_E_TRANSFER) {
textView.setText(R.string.tx_spinner_type_e_transfer);
}
return true;
} else if(columnIndex == 3){
String cost = String.format(Locale.CANADA, "%.2f", cursor.getDouble(columnIndex));
cost = "$" + cost;
TextView textView = (TextView) view;
textView.setText(cost);
return true;
}
return false;
}
});
expandablePayeeListView.setAdapter(mAdapter);