I am creating a RecyclerView which has items. Each item has a CheckBox, ImageButton and a TextView as stated below:
public class RecyclerViewViewHolder extends RecyclerView.ViewHolder {
RelativeLayout item;
TextView id;
TextView name;
ImageButton delete;
CheckBox checkBox;
public RecyclerViewViewHolder(View view) {
super(view);
item = (RelativeLayout) view.findViewById(R.id.layout_item_item);
id = (TextView) view.findViewById(R.id.layout_item_id);
name = (TextView) view.findViewById(R.id.layout_item_name);
delete = (ImageButton) view.findViewById(R.id.layout_item_delete);
checkBox = (CheckBox) view.findViewById(R.id.layout_item_checkbox);
}
}
I want the checkbox view to be checked only if a certain value in a column of a database is equal to 1. If the value is 0, it should not be checked. I have started off with the code below:
SQLiteDatabaseAdapter database = new SQLiteDatabaseAdapter(context);
database.open();
Cursor cursor = database.getAllItems(table);
if (cursor.moveToFirst()) {
do {
if (cursor.getInt(2) == 0) {
viewHolder.checkBox.setChecked(false);
}
else {
viewHolder.checkBox.setChecked(true);
}
} while (cursor.moveToNext());
}
database.close();
This code works but it checks the first row in the database and if the checked column is 1, it changes every single CheckBox to be checked. I only want the CheckBox associated with one particular row to be checked, not every single one. How do I exactly do that?
The problem is that you are iterating through every row in the cursor. Instead, you need to go to the row for the current position by calling cursor.moveToPosition().
In pseudocode, the steps you should follow are
Open the database.
Get the cursor.
Move the cursor to the correct row.
Retrieve the value from the cursor.
Set the Checkbox to the correct state.
Close the cursor.
I leave the details as an exercise for the reader.
I recommend that you create a HashMap<> and link the IDs and the checked states. Then, you can reference from the HashMap<> and get the checked states.
HashMap<Integer, Integer> hashMap = new HashMap<Integer, Integer>();
Then, in your onBindViewHolder function, you can use the following to check you CheckBox:
if (hashMap.get(position) == 0) {
// not checked
}
else {
// checked
}
Make sure you add the IDs and checked states from your SQLite database.
Related
I generated a table which has checkboxes at the end of every row. I want to sum all of values in the specific row if the checkbox is selected in that row. Therefore I need to check if the checkbox is checked in that specific row. So far I have this:
btnCalculate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try {
int sum = 0;
for(int i=0; i<rowNumber; i++) {
if(cb.isChecked()) {
sum = sum + rowSums[i];
}
}
txtResult.setText(String.valueOf(sum));
} catch (Exception e) { }
}
});
Normally, I can access the values of all cells withing a row and I am able to sum all of those values inside the row. However, when I add if statement inside the sum, I always get 0. I know what is the problem. I know I have to check if the checkbox is checked in that specific row. But I don't know how to do that.
By the way I also set IDs for every checkboxes inside the row. For example, ID of the first row's checkbox is 0 and the second row's is 1 and so on. So I just have to access that specific checkbox and check if it is checked.
EDIT (SOLVED)
I stored every checkboxes inside a CheckBox array. Then in my for loop, I called that specific CheckBox and it worked fine.
private CheckBox cb;
private CheckBox [] cbs;
In the for loop:
cbs[i] = cb;
And my for loop:
for(int i=0; i<rowNumber; i++) {
if(cbs[i].isChecked()) {
sum = sum + rowSums[i];
}
}
You can create a listener for the checkboxes so that when any checkbox is checked/unchecked add/delete the corresponding data to the calculation.
OR:
You can give all the checkboxes ids, and then store the ids in an array in the class like:
//as a class attribute
Int[]checkboxesIds = new array[] {ids in xml};
//in the loop
CheckBox ch = findViewById(ids[i])
ch.ischecked();
but its wrong for large of data.
I have a delete Row function as according:
public boolean removeData(int position) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_NAME, COL_ID+"="+position, null);
return true;
}
This function deletes a row according to its unique ID.
How can I change this so that after deleting a row, all rows below that one will be moved up to fill the empty space in the database?
That's against the design principle of a relational database. The rows are not ordered in a predictable way. So after delete you can only be sure that the deleted record appears to be away, but you have no control on the physical locations of any record, including which record(s), if any, now cover the space of the deleted one.
Querying data is another topic. You can specify a sort order, available as a parameter with the query methods. When querying your table, the results will appear exactly as you want it: If previously your results were Adam, Eve, Jack, Michael, then after deleting Jack, the result will be Adam, Eve, Michael.
The interplay between the displayed list, the domain objects behind that list, and the database is a different topic. Here are a few code snippets I use for a similar task. The basic idea is, when reading the objects that will be displayed, to include the database id with the object. So, if I read a list of products, the the domain class Product will have an id field that gets set with the database id when reading it.
To get the domain object displayed at a specific list position (e.g. the one where a user hit a delete button), the code fragment is.
public void onClick(View view) {
Product product = (Product) ProductList.this.products.get(ProductAdapter.this.listView.getPositionForView((View) view.getParent()));
... now do whatever is necessary to delete the product, probably
calling a DAO class that deletes the object based on its id,
not the list position
ProductAdapter.this.notifyDataSetChanged();
}
Solved this by removing the row in the database by the text of the TextView in the ListView instead of removing by the position of the TextView.
Now looks like this:
//Erasebutton listener
final Button eraseButton = (Button) findViewById(R.id.eraseButton);
assert eraseButton != null;
eraseButton.setOnClickListener(new View.OnClickListener() { //erasebutton onclick
public void onClick(View eraseButton) {
SparseBooleanArray checked = questionList.getCheckedItemPositions();
for(int i = questionList.getCount() - 1; i >= 0; i--)
{
if(checked.get(i)) {
//What to do with selected listitems
TextView tv = (TextView) questionList.getChildAt(i).findViewById(R.id.checkedTextView1);
db.removeData(tv.getText().toString());
}
}
checked.clear();
Cursor newCursor = db.getData();
adapter.swapCursor(newCursor);
}
});
And removeData function now looks likte this:
public boolean removeData(String question) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_NAME, COL_QUESTION+"='"+question+"'", null);
return true;
}
Currently I can check/uncheck the checkboxes by groupPosition and childPosition, and it's working fine. I save the groupPosition and childPosition to the SQLite Database.
When I added the delete option, my checkbox checked states are off/messed up.
For example, I have the following:
Group1,
Child1, Child2, Child3
I check all 3 of them and save the checked positions to the SQLite Database.
The checked positions would be 0,0 and 0,1 and 0,2 in the Database.
After I deleted Child1,
Child2 becomes position 0, Child3 becomes position 1 which would mess up the checked states.
I am trying to find a way to save the group name and child name instead of the positions to the SQLite Database, then load those names in Oncrete method.
MainActivity:
if(category_array.get(groupPosition).subcategory_array.get(childPosition).selected) {
category_array.get(groupPosition).subcategory_array.get(childPosition).selected = false;
try{
MySQLITE_DATABASE.deleteRows(groupPosition, childPosition);
}
catch (Exception e) {}
}
else
{
category_array.get(groupPosition).subcategory_array.get(childPosition).selected = true;
MySQLITE_DATABASE.addRow(groupPosition, childPosition);
}
Adapter code:
private List<Category> mGroupCollection;
if(mGroupCollection.get(groupPosition).subcategory_array.get(childPosition).selected) {
childHolder.checkBox.setBackgroundResource(R.drawable.checkbox_checked);
} else {
childHolder.checkBox.setBackgroundResource(R.drawable.checkbox_unchecked);
}
Date Holder Classes:
public class Category {
public String category_name = null;
public String cat_SelectedChildCount_name;
public ArrayList<SubCategory> subcategory_array = new ArrayList<SubCategory>();
}
//==============================
public class SubCategory {
public String subcategory_name = null;
public boolean selected = false;
}
I want to save the group name and child name instead of positions to the SQLite Database and then load them in OnCreate method.
I have tried this but it is not working:
String Group_Name = category_array.get(Integer.parseInt(groupPosition)).category_name;
String Child_Name = category_array.get(Integer.parseInt(groupPosition)).
subcategory_array.get(Integer.parseInt(childPosition)).subcategory_name;
if(category_array.get(Group_Name).subcategory_array.get(Child_Name).selected) {
category_array.get(Group_Name).subcategory_array.get(Child_Name).selected = false;
MySQLITE_DATABASE.DeleteRow(Group_Name, Child_Name);
}
else {
category_array.get(Group_Name).subcategory_array.get(Child_Name).selected = true;
MySQLITE_DATABASE.AddRow(Group_Name, Child_Name);
}
I know that I need to change the code in the adapater, data holder classes, and mainactivity in order to make it work, but I am out of ideas. I have been thinking and thinking, but nothing works...
Can someone please please guide me on this?
I am sorry for the long code.
Thank you and thank you.
Try any of the following if it suit's your requirement.
If you want to use the position strictly (May be there is no unique option)
Keep the DB design like
| id | parent_position | child_position |
Assume you have N childs. If you delete a child (Suppose it's 0). do this
Update all the child's with (child_position = child_position - 1) whose position is greater than
the deleted child of the parent. So the position remains unchanged.
Personally i would suggest don't use the position if a delete option is there. Option that i found, But don't know your use case
If you are loading the list from the database use the database primarykey as the key to save it in the SQLite database on selection/unselection. In this case you don't want to know the parent position as well.
I have a listview with a custon adapter. I the row's layout, I have a text and a checkbox.
When I load the listview, I get the data from a database and it has one colunm that determine if the row is cheched or not. When I load the list, its ok, the rows that has to stay checked, stays checkd, and the others no. The problem is: when I unckheck a row ans roll the list down and up, when I return to the start, the row that I had unchecked, returns checked again, how can I resold this problem:
The getView() code below:
public View getView(int index, View view, ViewGroup parent) {
if (view == null) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
view = inflater.inflate(R.layout.linha_acessorios, parent, false);
}
final AcessoriosItensLista acessorios = (AcessoriosItensLista)getItem(index);
final ImageView imgAcessorio = (ImageView)view.findViewById(R.id.imgAcessorioLista);
final CheckBox cb = (CheckBox)view.findViewById(R.id.cbListaAcessorios);
TextView tvNome = (TextView) view.findViewById(R.id.tvNomeAcessoriosLinha);
tvNome.setText(acessorios.getNomeAcessorio());
final Integer iditem = Integer.valueOf(acessorios.getId());
boolean ch = acessorios.isChecked();
final Integer position = Integer.valueOf(index);
if(ch){
if(!checked.contains(iditem)){
checkedPositions.add(position);
checked.add(iditem);
}
}
cb.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(checked.contains(iditem)){
checked.remove(iditem);
checkedPositions.remove(position);
}
if (((CheckBox) v).isChecked()) {
checkedPositions.add(position);
checked.add(iditem);
int id = context.getResources().getIdentifier("acc_gold_"+acessorios.getId(), "drawable", context.getPackageName());
imgAcessorio.setBackgroundResource(id);
}
else if(checkedPositions.contains(position)) {
checkedPositions.remove(position);
checked.remove(iditem);
int id = context.getResources().getIdentifier("acc_"+acessorios.getId(), "drawable", context.getPackageName());
imgAcessorio.setBackgroundResource(id);
}
}
});
if(checkedPositions.contains(position)){
cb.setChecked(true);
int id = context.getResources().getIdentifier("acc_gold_"+acessorios.getId(), "drawable", context.getPackageName());
imgAcessorio.setBackgroundResource(id);
} else {
cb.setChecked(false);
int id = context.getResources().getIdentifier("acc_"+acessorios.getId(), "drawable", context.getPackageName());
imgAcessorio.setBackgroundResource(id);
}
return view;
}
My guess is that probably you're unchecking that CheckBox but you're not saving its status anywhere, so when that row disappears from the screen by scrolling and you scroll down again, it loads the data again from the database and it's checked in it. I don't know how you're handling your ArrayAdapter extension, but I recommend saving the constructor's ArrayList as an instance inside the class, updating that value inside of it on uncheck, and call notifyDataSetChanged().
---- EDIT ----
To store the ArrayList inside your class, you'll have to create a separate class (with the two fields you're working on), for example:
class MyRow {
CheckBox cb;
TextView tv;
}
So when you declare your custom adapter in your Activity, you'll have to declare previously an ArrayList with some initial elements (or even empty):
ArrayList<MyRow> myList = new ArrayList<MyRow>();
MyRow row1 = new MyRow();
row1.cb.isChecked(...);
row1.tv.setText(...);
myList.add(row1);
Then you call the constructor of your adapter class, something like this:
MyArrayAdapter adapter = new MyArrayAdapter(context, R.layout.your_layout, myList);
So when you pass it to the constructor of your adapter class, you save a copy of it in that class:
public class MyArrayAdapter extends ArrayAdapter {
final private ArrayList<MyRow> myContent;
...
MyArrayAdapter(Context context, int my_layout, ArrayList<MyRow> myContent_) {
...
myContent = myContent_
}
}
So now, any content you change (like for example checking/unchecking a checkbox) you have to save its state in the myContent array. You would find that item by getItem(position) in your getView() method and make the changes you need. After it, you just have to call the notifyDataSetChanged(); method and it will automatically display the changes in your ListView.
It's almost as it your list items are being re-redered or recreated when they go off screen, now the easiest and obvious solution here is to trigger an event when your checkbox is clicked so make an onclick event in your adapter that is triggered when the checkbox is checked or unchecked and updates the data source.
I have implemented custom list view with two rows (name and number) and it is checkable.
The list view has multiple select option.
Whenever user searches for a name, cursor will returned the new list of items. I can't keep track for items which has been selected earlier once adapter gets changed with the new cursor items.
For example user searches for name "Jo" it returns 10 items, in which i have selected 2 rows. Once i remove the search, the cursor and adapter gets changed. I am not able to mark the items checked.
I want to override default checkable items based on position( have to write own which has to make items checkable based on _id(contact id))
( I tried overriding onFinishInflate method. But it didn't help).
Any help appreciated.
Thanks in advance.
what you need is an object to have your check-box data persist your adapter and listview. A hashmap of boolean arrays should suffice.
private HashMap<String, boolean[]> contactMap;
I'd imagine that you could load this in some database method or something and you could have the person's name, like "Jo", as an identifier if need be. the array indexes would correspond to the checkboxes in each listview row as they appear. Then in your adapter which i'd imagine is a CursorAdapter, you could have the following:
private boolean[] contactObj;
public void setContactObj(boolean[] contactObj) {
this.contactObj = contactObj;
notifyDataSetChanged();
}
public boolean[] getContactObj() {
return contactObj;
}
#Override
public void bindView(View view, Context context, Cursor c) {
final int position = c.getPosition();
final CheckBox cb = (CheckBox) view.findViewById(R.id.checkbox);
cb.setChecked(contactObj[position]);
cb.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (cb.isChecked()) {
contactObj[position] = true;
} else {
contactObj[position] = false ;
}
}
});
}
Basically, you have an adapter that only has capacity for one boolean[] which is able to adjust the checkboxes in your listview, modify as the boolean[] as the checkbox is being clicked and then return it in the event that you still need it.