Recyclerview checkbox are checked when not clicked - java

I have a Recyclerview with checkboxes. When a user checks a box, when they scroll down another box will already be checked. I understand why, because that box is being recycled which makes sense.
But I am having issues to set it so it does not do this:
My current onBindViewHolder:
#Override
public void onBindViewHolder(final ViewHolder holder, final int position){
holder.playerNameNumber.setText(playerData.get(position).name + "\n" +playerData.get(position).number );
holder.pickedPlayer.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View thisView){
PlayerDetails contact = new PlayerDetails();
int contactPos = position;
if(holder.pickedPlayer.isChecked()) {
contact.number = playerData.get(position).number;
contact.name = playerData.get(position).name;
playerListGame.addPlayer(contact);
String name = contact.name;
Toast.makeText(thisView.getContext(), name + " Added", Toast.LENGTH_LONG).show();
holder.pickedPlayer.setChecked(true);
}
else if(!holder.pickedPlayer.isChecked()){
contact.number = playerData.get(position).number;
contact.name = playerData.get(position).name;
for(PlayerDetails i : playerListGame.myPlayers){
if(i.name == contact.name && i.number == contact.number){
contactPos = playerListGame.myPlayers.indexOf(i);
}
}
playerListGame.removePlayer(contactPos);
String name = contact.name;
Toast.makeText(thisView.getContext(), name + " Removed", Toast.LENGTH_LONG).show();
}
}
});
}
Now I know that SetIsRecyclable is a big no no, though it does fix my problem:
public ViewHolder(View v){
super(v);
playerNameNumber = v.findViewById(R.id.playerDetails);
pickedPlayer =v.findViewById(R.id.
this.setIsRecyclable(false);
How do I avoid using such a method to resolve this issue?

holder.pickedPlayer.setOnCheckedChangeListener(null);
//define private boolean selected with setter and getter
holder.pickedPlayer.setChecked(playerData.isSelected());
holder.pickedPlayer.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
{
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
playerData.setSelected(isChecked);
if (isChecked)
{
Toast.makeText(context,"checked", Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(context,"not checked", Toast.LENGTH_LONG).show();
}
}
});

you have to use a boolean into your dataset also.so when user check or uncheck a box that time update the boolean value of respected position’s dataset and call notify item changed
arraylist.get(pos).setDataCheked(true);
notifyItemChanged(pos);

You need to set the current value of the checkbox just like you're setting the player name and number. I've assumed it's called something like picked in your playerData.
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.playerNameNumber.setText(playerData.get(position).name + "\n" +playerData.get(position).number );
holder.pickedPlayer.setChecked(playerData.get(position).picked);
// same onclick listener, removed for readability
}

Related

Android studio, recycle view

I have some error in android studio.
When i try to add symbol ('*') to special item, it also add the symbol to the item that placed in +14 from the first.
I will glad if someone have solution for this problem.
For more information you can check this link, where i describe the problem.
https://youtu.be/CJFkt-Cck1A
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {
private static final String TAG = "RecyclerViewAdapter";
private Context context;
private List<WorkDayItem> workDayItemList;
//Complete
public RecyclerViewAdapter(Context context, List<WorkDayItem> workDayItemList) {
this.context = context;
this.workDayItemList = workDayItemList;
}
//Complete
//Here we create view- itemWorkday and inflate it by layout- item_one_work_day
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemWorkDay = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_one_work_day, parent, false);
MyViewHolder myViewHolder = new MyViewHolder(itemWorkDay);
return myViewHolder;
}
#Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
Log.d(TAG, "onBindViewHolder: called." + (position + 1) + "\n" + workDayItemList.get(position).toString());
final WorkDayItem workDayItem = workDayItemList.get(position);
String dateStart = (String) DateFormat.format("dd.MM", workDayItem.getDateStart());
String timeStart = (String) DateFormat.format("HH:mm", workDayItem.getDateStart());
String timeEnd = (String) DateFormat.format("HH:mm", workDayItem.getDateEnd());
//Convert data from firebase String format to int hours and minutes format.
Double convertingDataFromFirebase;
try {
convertingDataFromFirebase = Double.parseDouble(new DecimalFormat("##.##").format(workDayItem.getCount_hours()));
} catch (NumberFormatException e) {
convertingDataFromFirebase = 0.0;
}
int hours = convertingDataFromFirebase.intValue();
convertingDataFromFirebase = (convertingDataFromFirebase - convertingDataFromFirebase.intValue()) * 60;
int minutes = convertingDataFromFirebase.intValue();
//Check if current item have description
if (workDayItemList.get(position).getDesc().length() > 2) {
Log.i(TAG, "TESTER: desc dote added");
holder.doteOfDesc.setVisibility(View.VISIBLE);
}
holder.dayPosition.setText((position + 1) + "");
holder.dateStart.setText(dateStart);
holder.timeStart.setText(timeStart);
holder.timeEnd.setText(timeEnd);
holder.countOfHours.setText(hours + ":" + minutes);
//On click on current hold open alert dialog with some functions
holder.parentLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
initializeAlertDialogForItem(workDayItem, holder);
}
});
}
//initialize data about current work day and have button for changing information
private void initializeAlertDialogForItem(final WorkDayItem workDayItem, final MyViewHolder holder) {
//Initialize alert dialog
final AlertDialog alertDialog = new AlertDialog.Builder(context).create();
View itemWork = LayoutInflater.from(context)
.inflate(R.layout.ad_item_desc, null, false);
alertDialog.setView(itemWork);
alertDialog.show();
//initialize alert dialog buttons and views
final ImageButton change = alertDialog.findViewById(R.id.itemAD_Edit);
final ImageButton delete = alertDialog.findViewById(R.id.itemAD_Delete);
TextView description = alertDialog.findViewById(R.id.itemADDescription);
TextView date = alertDialog.findViewById(R.id.itemADDate);
TextView from = alertDialog.findViewById(R.id.itemADFrom);
TextView to = alertDialog.findViewById(R.id.itemADTO);
String timeStart = (String) (DateFormat.format("HH:mm", workDayItem.getDateStart()));
String timeEnd = (String) (DateFormat.format("HH:mm", workDayItem.getDateEnd()));
String dateStart = (String) (DateFormat.format("dd.MM.yyyy", workDayItem.getDateStart()));
date.setText(dateStart);
from.setText(timeStart);
to.setText(timeEnd);
description.setText(workDayItem.getDesc());
//Change button
change.setOnClickListener(new View.OnClickListener() {
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
public void onClick(View v) {
AlertDialogReport userReport = new AlertDialogReport(context, "replace-remove", workDayItem);
userReport.initializeAlertDialog();
alertDialog.dismiss();
}
});
delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Delete data from firebase
Login.fc.databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
String itemTime = "" + workDayItem.getDateStart().getTime();
String firebaseTime = "" + snapshot.child("dateStart").child("time").getValue();
if (itemTime.equals(firebaseTime)) {
Login.fc.databaseReference.child(snapshot.getKey()).removeValue();
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
//Delete data from SqLiteDatabase
MySQLDataBase dataBase = new MySQLDataBase(context);
dataBase.deleteItem(workDayItem);
//Finish with alert dialog and notify user
alertDialog.dismiss();
Toast.makeText(context, R.string.item_deleted, Toast.LENGTH_SHORT).show();
holder.parentLayout.removeAllViews();
}
});
}
//Complete
#Override
public int getItemCount() {
return workDayItemList.size();
}
//Complete
//Here we catch our view and getting reference between view and our objects
public class MyViewHolder extends RecyclerView.ViewHolder {
private LinearLayout parentLayout;
private TextView doteOfDesc, dayPosition, dateStart, timeStart, timeEnd, countOfHours;
public MyViewHolder(View view) {
super(view);
doteOfDesc = view.findViewById(R.id.itemDote);
dayPosition = view.findViewById(R.id.itemDayPosition);
dateStart = view.findViewById(R.id.itemDateStart);
timeStart = view.findViewById(R.id.itemStartHour);
timeEnd = view.findViewById(R.id.itemEndHour);
countOfHours = view.findViewById(R.id.itemCountOfHours);
parentLayout = view.findViewById(R.id.itemWorkDay);
}
}
}
Try adding this:
//Check if current item have description
if (workDayItemList.get(position).getDesc().length() > 2) {
Log.i(TAG, "TESTER: desc dote added");
holder.doteOfDesc.setVisibility(View.VISIBLE);
}
else if( workDayItemList.get(position).getDesc().length() < 2
&& holder.doteOfDesc.getVisibility()== View.VISIBLE ){
holder.doteOfDesc.setVisibility(View.GONE);
}
The reason why this is happening is that RecyclerView creates as many ViewHolders as its needed to cover a whole screen plus few extra ( 12 in your case) then reuses them via rebinding values to views. And you set doteOfDescto View.VISIBLE in 2. ViewHolder, but never set it back to View.GONE, thats why every time that ViewHolder is reused it will have doteOfDesc visible.
The Prettier version:
Boolean hasDescription = workDayItemList.get(position).getDesc().length() > 2;
holder.doteOfDesc.setVisibility( hasDescription ? View.VISIBLE : View.GONE);

How to sum checked items data values in RecyclerView

My problem is how to sum all checked items values, and how to manage their changed at check/uncheck events?
This is my method for check box toggle (check/uncheck) event.
holder.itemCheckBox.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
if (((CheckBox) v).isChecked())
{
double price = Double.parseDouble(catalogDatabases.get(position).getPriceItem());
holder.totalPrice = holder.totalPrice + price;
holder.listener.respond(holder.totalPrice);
}
else {
}
}
});
So first of all don't rely on the view as data.
I'll explain in your code
if (((CheckBox) v).isChecked())
You relying on view's isChecked method.
This may cause a bug because of android list recycling mechanism.
You can read about it in the link below
How ListView's recycling mechanism works
TL;DR views are being recycled so when you scroll your list.
So for example the user checked item number 3 and then scrolled the list down item number 13 for example may also be shown as checked even tho it isn't .
So when onClick triggers we need to save the checked state in some list
After the theoretical intro i'll show it in code.
//Here you'll need to create some boolean array or list to store
//checked not checked positions lets call it checked
boolean[] checkedArr = new boolean[catalogDatabases.size()];
// catalogDatabases.size represents your data set size
// note that all items will be false initially
#Override
public void onBindViewHolder(#NonNull final MyViewHolder holder, final int position) {
/**
* Other views binding.......
*/
holder.itemCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
checkedArr[position] = isChecked;
sumAllCheckedAndNotify();
}
}
);
}
(I decided to do the calculation on every check it more straight forward from making an update on the event)
private void sumAllCheckedAndNotify() {
double sum = 0;
for (int i = 0; i < checkedArr.length; i++) {
if(checkedArr[i]) {
sum += Double.parseDouble(catalogDatabases.get(i).getPriceItem());
}
}
// pudate the listener
listener.respond(sum, selectedCount);
}
Store totalPrice inside adapter and add/subtract value based on CheckBox state like below:
class YourAdapter extends ... {
double totalPrice = 0;
...
#Override
public void onBindViewHolder(#NonNull RecyclerHolder holder, int position) {
holder.itemCheckBox.setChecked(catalogDatabases.get(position).isSelected());
holder.itemCheckBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
double price = Double.parseDouble(catalogDatabases.get(position).getPriceItem());
boolean isSelected = !catalogDatabases.get(position).isSelected();
catalogDatabases.get(position).setSelected(isSelected);
if (isSelected) {
totalPrice += price;
} else {
totalPrice -= price;
}
holder.listener.respond(totalPrice);
notifyDataSetChanged();
}
});
}
}
Update your Model to hold checked/ unchecked state to persist data
class CatalogDatabase { //Assume this is your model class
private boolean isSelected
....
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
....
}

onSuggestionClick(int position) in SuggestionListener gives absolute position of ArrayList

My code is:
ArrayList<String> autocompleteArray = new ArrayList<>();
autocompleteArray.add("apple");
autocompleteArray.add("apples");
autocompleteArray.add("banana");
autocompleteArray.add("bananas");
autocompleteArray.add("orange");
SearchView.SearchAutoComplete searchAutoComplete = searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
searchAutoComplete.setDropDownAnchor(R.id.main_toolbar);
searchAutoComplete.setThreshold(1);`searchAutoComplete.setDropDownAnchor(R.id.main_toolbar);
searchAutoComplete.setThreshold(1);`
final ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(), R.layout.search_autocomplete, autocompleteArray);
searchAutoComplete.setAdapter(adapter);
searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
#Override
public boolean onSuggestionSelect(int position) {
Log.i("search_tag",autocompleteArray.get(position));
searchView.setQuery(autocompleteArray.get(position),true);
return true;
}
#Override
public boolean onSuggestionClick(int position) {
Log.i("search_tag",autocompleteArray.get(position));
searchView.setQuery(autocompleteArray.get(position),true);
return true;
}
});
When I click on apple, the output in log is "apple" ,"apples" when I click on apples.
But when I type "ba" only "banana" and "bananas" is shown[as expexted] and when i click on "banana" ,"apple" is the output and also when i click on "bananas" , "apples" is the output. It seems like "position" is giving the absolute position in arraylist and not the filtered array.
How can I get the position of the filtered arraylist?
Instead of adding suggestionListener try below
searchAutoComplete.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
String queryString = (String)adapterView.getItemAtPosition(position);
searchView.setQuery(queryString,true);
Toast.makeText(view.getContext(), "item clicked " + queryString, Toast.LENGTH_LONG).show();
}
});
According to the documentation, the position is absolute
https://developer.android.com/reference/android/widget/SearchView.OnSuggestionListener.html#onSuggestionClick(int)
In order to use the data, check below example
searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
#Override
public boolean onSuggestionSelect(int position) {
return true;
}
#Override
public boolean onSuggestionClick(int position) {
Cursor cursor= searchView.getSuggestionsAdapter().getCursor();
cursor.moveToPosition(position);
String suggestion =cursor.getString(2);//2 is the index of col containing suggestion name.
searchView.setQuery(suggestion,true);//setting suggestion
return true;
}
});

getTag, setTag checkbox issue, how can I get my activity to post checked boxes? The other values are posting ok

I am in the process of changing someone else's code from listView to recyclerView, changing code in my activity and my adapter where required - the adapter is a big change, with OnCreateView, onBindViewHolder and so on...
As the question indicates, category, name, phone, address, comment, public_or_private are posting successfully to mySql db, but nothing for checkedContacts. I'd be very grateful if you could tell me how to fix the problem.
I am quite sure it has to do with something around this line:
//get the other data related to the selected contact - name and number
SelectPhoneContact contact = (SelectPhoneContact) checkbox.getTag();
I am not getting any errors but when I run in debug mode it gets to here and then jumps to:
} catch (Exception e) {
System.out.println("there's a problem here unfortunately");
e.printStackTrace();
}
I think it has something to do with setTag but I find it confusing and I'm not sure where it should be set (if, even, that is the problem).
I'm posting the relevant code for my activity, NewContact and the RecyclerViewAdapter, PopulistoCOntactsAdapter. Thanks for any help.
NewContact.java
//for the SAVE button
private void saveContactButton() {
save.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("you clicked it, save");
try {
System.out.println("we're in the try part");
int count = MatchingContactsAsArrayList.size();
for (int i = 0; i < count; i++) {
//for each Matching Contacts row in the listview
LinearLayout itemLayout = (LinearLayout)recyclerView.getChildAt(i); // Find by under LinearLayout
//for each Matching Contacts checkbox in the listview
CheckBox checkbox = (CheckBox)itemLayout.findViewById(R.id.checkBoxContact);
//get the other data related to the selected contact - name and number
SelectPhoneContact contact = (SelectPhoneContact) checkbox.getTag();
//if that checkbox is checked, then get the phone number
if(checkbox.isChecked()) {
Log.d("Item " + String.valueOf(i), checkbox.getTag().toString());
Toast.makeText(NewContact.this, contact.getPhone(), Toast.LENGTH_LONG).show();
// make each checked contact in selectPhoneContacts
// into an individual
// JSON object called checkedContact
JSONObject checkedContact = new JSONObject();
// checkedContact will be of the form {"checkedContact":"+353123456"}
checkedContact.put("checkedContact", contact.getPhone());
// Add checkedContact JSON Object to checkedContacts jsonArray
//The JSON Array will be of the form
// [{"checkedContact":"+3531234567"},{"checkedContact":"+353868132813"}]
//we will be posting this JSON Array to Php, further down below
checkedContacts.put(checkedContact);
System.out.println("NewContact: checkedcontact JSONObject :" + checkedContact);
}
}
} catch (Exception e) {
System.out.println("there's a problem here unfortunately");
e.printStackTrace();
}
//When the user clicks save
//post phoneNoofUserCheck to NewContact.php and from that
//get the user_id in the user table, then post category, name, phone etc...
//to the review table
StringRequest stringRequest = new StringRequest(Request.Method.POST, NewContact_URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
//response, this will show the checked numbers being posted
Toast.makeText(NewContact.this, response, Toast.LENGTH_LONG).show();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
//post these details to the NewContact.php file and do
//stuff with it
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
//post the phone number to php to get the user_id in the user table
params.put("phonenumberofuser", phoneNoofUserCheck);
//the second value, categoryname.getText().toString() etc...
// is the value we get from Android.
//the key is "category", "name" etc.
// When we see these in our php, $_POST["category"],
//put in the value from Android
params.put("category", categoryname.getText().toString());
params.put("name", namename.getText().toString());
params.put("phone", phonename.getText().toString());
params.put("address", addressname.getText().toString());
params.put("comment", commentname.getText().toString());
params.put("public_or_private", String.valueOf(public_or_private));
System.out.println("public_or_private is " + String.valueOf(public_or_private));
//this is the JSON Array of checked contacts
//it will be of the form
//[{"checkedContact":"+3531234567"},{"checkedContact":"+353868132813"}]
params.put("checkedContacts", checkedContacts.toString());
return params;
}
};
// Adding request to request queue
AppController.getInstance().addToRequestQueue(stringRequest);
//when saved, go back to the PopulistoListView class and update with
//the new entry
Intent j = new Intent(NewContact.this, PopulistoListView.class);
j.putExtra("phonenumberofuser", phoneNoofUserCheck);
NewContact.this.startActivity(j);
finish();
}
});
}
}
PopulistoContactsAdapter.java
public class PopulistoContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
//make a List containing info about SelectPhoneContact objects
public List<SelectPhoneContact> theContactsList;
Context context_type;
public class MatchingContact extends RecyclerView.ViewHolder {
//In each recycler_blueprint show the items you want to have appearing
public TextView title, phone;
public CheckBox check;
public Button invite;
public MatchingContact(final View itemView) {
super(itemView);
//title is cast to the name id, in recycler_blueprint,
//phone is cast to the id called no etc
title = (TextView) itemView.findViewById(R.id.name);
phone = (TextView) itemView.findViewById(R.id.no);
invite = (Button) itemView.findViewById(R.id.btnInvite);
check = (CheckBox) itemView.findViewById(R.id.checkBoxContact);
}
}
public class nonMatchingContact extends RecyclerView.ViewHolder {
//In each recycler_blueprint show the items you want to have appearing
public TextView title, phone;
public CheckBox check;
public Button invite;
public nonMatchingContact(final View itemView) {
super(itemView);
//title is cast to the name id, in recycler_blueprint,
//phone is cast to the id called no etc
title = (TextView) itemView.findViewById(R.id.name);
phone = (TextView) itemView.findViewById(R.id.no);
invite = (Button) itemView.findViewById(R.id.btnInvite);
check = (CheckBox) itemView.findViewById(R.id.checkBoxContact);
}
}
#Override
public int getItemViewType(int position) {
//for each row in recyclerview, get the getType_row, set in NewContact.java
return Integer.parseInt(theContactsList.get(position).getType_row());
}
public PopulistoContactsAdapter(List<SelectPhoneContact> selectPhoneContacts, Context context) {
//selectPhoneContacts = new ArrayList<SelectPhoneContact>();
theContactsList = selectPhoneContacts;
// whichactivity = activity;
context_type = context;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView;
//if getType_row is 1...
if (viewType == 1)
{
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
itemView = inflater.inflate(R.layout.recycler_blueprint, parent, false);
//itemView.setTag();
return new MatchingContact(itemView);
} else {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
itemView = inflater.inflate(R.layout.recycler_blueprint_non_matching, parent, false);
return new nonMatchingContact(itemView);
}
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {
//bind the views into the ViewHolder
//selectPhoneContact is an instance of the SelectPhoneContact class.
//We will assign each row of the recyclerview to contain details of selectPhoneContact:
//The number of rows will match the number of phone contacts
final SelectPhoneContact selectPhoneContact = theContactsList.get(position);
if (viewHolder.getItemViewType() == 1)
{
((MatchingContact) viewHolder).title.setText(selectPhoneContact.getName());
((MatchingContact) viewHolder).phone.setText(selectPhoneContact.getPhone());
CheckBox check = ((MatchingContact) viewHolder).check;
//get the number position of the checkbox in the recyclerview
//check.setTag(position);
}
else {
((nonMatchingContact) viewHolder).title.setText(selectPhoneContact.getName());
((nonMatchingContact) viewHolder).phone.setText(selectPhoneContact.getPhone());
}
}
#Override
public int getItemCount() {
return theContactsList.size();
}
}
And here is my SelectPhoneContact class:
public class SelectPhoneContact {
String phone;
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
boolean isMatching;
public boolean isMatching(){return isMatching;}
public void setIsMatchingContact(boolean isMatching){
this.isMatching = isMatching;
}
//*****************************************
//this is for the checkbox
boolean selected = false;
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected){
this.selected = selected;
}
String type_row;
public String getType_row() {
return type_row;
}
public void setType_row(String type_row) {
this.type_row = type_row;
}
}
I figured it out. This tutorial was very helpful: https://demonuts.com/2017/07/03/recyclerview-checkbox-android/
Here is the code I used for NewContact.java:
//for the SAVE button
private void saveContactButton() {
save.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("you clicked it, save");
try {
System.out.println("we're in the try part");
//loop through the matching contacts
int count = MatchingContactsAsArrayList.size();
for (int i = 0; i < count; i++) {
//for matching contacts that are checked...
if (PopulistoContactsAdapter.theContactsList.get(i).getSelected()) {
Toast.makeText(NewContact.this, PopulistoContactsAdapter.theContactsList.get(i).getPhone() + " clicked!", Toast.LENGTH_SHORT).show();
// make each checked contact in selectPhoneContacts
// into an individual
// JSON object called checkedContact
JSONObject checkedContact = new JSONObject();
// checkedContact will be of the form {"checkedContact":"+353123456"}
checkedContact.put("checkedContact", PopulistoContactsAdapter.theContactsList.get(i).getPhone());
// Add checkedContact JSON Object to checkedContacts jsonArray
//The JSON Array will be of the form
// [{"checkedContact":"+3531234567"},{"checkedContact":"+353868132813"}]
//we will be posting this JSON Array to Php, further down below
checkedContacts.put(checkedContact);
System.out.println("NewContact: checkedcontact JSONObject :" + checkedContact);
}
}
} catch (Exception e) {
System.out.println("there's a problem here unfortunately");
e.printStackTrace();
}
//When the user clicks save
//post phoneNoofUserCheck to NewContact.php and from that
//get the user_id in the user table, then post category, name, phone etc...
//to the review table
StringRequest stringRequest = new StringRequest(Request.Method.POST, NewContact_URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
//response, this will show the checked numbers being posted
Toast.makeText(NewContact.this, response, Toast.LENGTH_LONG).show();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
//post these details to the NewContact.php file and do
//stuff with it
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
//post the phone number to php to get the user_id in the user table
params.put("phonenumberofuser", phoneNoofUserCheck);
//the second value, categoryname.getText().toString() etc...
// is the value we get from Android.
//the key is "category", "name" etc.
// When we see these in our php, $_POST["category"],
//put in the value from Android
params.put("category", categoryname.getText().toString());
params.put("name", namename.getText().toString());
params.put("phone", phonename.getText().toString());
params.put("address", addressname.getText().toString());
params.put("comment", commentname.getText().toString());
params.put("public_or_private", String.valueOf(public_or_private));
System.out.println("public_or_private is " + String.valueOf(public_or_private));
//this is the JSON Array of checked contacts
//it will be of the form
//[{"checkedContact":"+3531234567"},{"checkedContact":"+353868132813"}]
params.put("checkedContacts", checkedContacts.toString());
return params;
}
};
// Adding request to request queue
AppController.getInstance().addToRequestQueue(stringRequest);
//when saved, go back to the PopulistoListView class and update with
//the new entry
Intent j = new Intent(NewContact.this, PopulistoListView.class);
j.putExtra("phonenumberofuser", phoneNoofUserCheck);
NewContact.this.startActivity(j);
finish();
}
});
}
}
And my PopulistoContactsAdapter.java:
public class PopulistoContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
//make a List containing info about SelectPhoneContact objects
public static List<SelectPhoneContact> theContactsList;
Context context_type;
public class MatchingContact extends RecyclerView.ViewHolder {
//In each recycler_blueprint show the items you want to have appearing
public TextView title, phone;
public CheckBox check;
public Button invite;
public MatchingContact(final View itemView) {
super(itemView);
//title is cast to the name id, in recycler_blueprint,
//phone is cast to the id called no etc
title = (TextView) itemView.findViewById(R.id.name);
phone = (TextView) itemView.findViewById(R.id.no);
invite = (Button) itemView.findViewById(R.id.btnInvite);
check = (CheckBox) itemView.findViewById(R.id.checkBoxContact);
}
}
public class nonMatchingContact extends RecyclerView.ViewHolder {
//In each recycler_blueprint show the items you want to have appearing
public TextView title, phone;
public CheckBox check;
public Button invite;
public nonMatchingContact(final View itemView) {
super(itemView);
//title is cast to the name id, in recycler_blueprint,
//phone is cast to the id called no etc
title = (TextView) itemView.findViewById(R.id.name);
phone = (TextView) itemView.findViewById(R.id.no);
invite = (Button) itemView.findViewById(R.id.btnInvite);
check = (CheckBox) itemView.findViewById(R.id.checkBoxContact);
}
}
#Override
public int getItemViewType(int position) {
//for each row in recyclerview, get the getType_row, set in NewContact.java
return Integer.parseInt(theContactsList.get(position).getType_row());
}
public PopulistoContactsAdapter(List<SelectPhoneContact> selectPhoneContacts, Context context) {
//selectPhoneContacts = new ArrayList<SelectPhoneContact>();
theContactsList = selectPhoneContacts;
// whichactivity = activity;
context_type = context;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView;
//if getType_row is 1...
if (viewType == 1)
{
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
itemView = inflater.inflate(R.layout.recycler_blueprint, parent, false);
//itemView.setTag();
return new MatchingContact(itemView);
} else {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
itemView = inflater.inflate(R.layout.recycler_blueprint_non_matching, parent, false);
return new nonMatchingContact(itemView);
}
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {
//bind the views into the ViewHolder
//selectPhoneContact is an instance of the SelectPhoneContact class.
//We will assign each row of the recyclerview to contain details of selectPhoneContact:
//The number of rows will match the number of phone contacts
final SelectPhoneContact selectPhoneContact = theContactsList.get(position);
if (viewHolder.getItemViewType() == 1)
{
((MatchingContact) viewHolder).title.setText(selectPhoneContact.getName());
((MatchingContact) viewHolder).phone.setText(selectPhoneContact.getPhone());
((MatchingContact) viewHolder).check.setText("Cheeckbox" + position);
((MatchingContact) viewHolder).check.setChecked(theContactsList.get(position).getSelected());
((MatchingContact) viewHolder).check.setTag(position);
((MatchingContact) viewHolder).check.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Integer pos = (Integer) ((MatchingContact) viewHolder).check.getTag();
Toast.makeText(context_type, theContactsList.get(pos).getPhone() + " clicked!", Toast.LENGTH_SHORT).show();
if (theContactsList.get(pos).getSelected()) {
theContactsList.get(pos).setSelected(false);
} else {
theContactsList.get(pos).setSelected(true);
}
}
});
// CheckBox check = ((MatchingContact) viewHolder).check;
//get the number position of the checkbox in the recyclerview
//check.setTag(position);
}
else {
((nonMatchingContact) viewHolder).title.setText(selectPhoneContact.getName());
((nonMatchingContact) viewHolder).phone.setText(selectPhoneContact.getPhone());
}
}
#Override
public int getItemCount() {
return theContactsList.size();
}
}

Best solution for Checkbox in LinearLayout

In my LinearLayout, there's a variable number of CheckBoxes. In a question I had a month ago someone said it´s better to add checkboxes dynamicly instead of make them not visible.
Integer[] count = new Integer[]{1,2,3,4,5,6,7,8,9,10};
size = mFrageList.get(position).getAuswahlList().size();
for (int i = 0; i < size; i++) {
cBox = new CheckBox(this);
cBox.setText(mFrageList.get(position).getAuswahlList().get(i));
cBox.setId(count[i]);
cBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
antwortencode[position] += "" + buttonView.getId();
frageBeantworten.setText("Antwort :"+antwortencode[position]+" abgeben");
} else {
String id = Integer.toString(buttonView.getId());
antwortencode[position] = antwortencode[position].replaceAll(id,"");
if(!antwortencode[position].isEmpty() || antwortencode[position]!= "") {
frageBeantworten.setText("Antwort :" + antwortencode[position] + " abgeben");
} else {
frageBeantworten.setText("Keine Checkbox(en) gewählt");
}
}
}
});
antworten.addView(cBox);
Currently, I'm able to save a string with the checked checkboxes, if I un-check a checkbox, it deletes it's value out of the string.
If I update the activity, the string is saved, and the checkboxes get a new List from the mFrageList.get(position)getAuswahlList(); and fill a new string in the "antwortencode" List with their values.
If I go back to the last position, I have the string which was generated but the checkboxes aren't checked anymore. But they have the Strings from the old position. that means everything is saved except the state of the checkboxes. I cant set a cBox.setChecked(isChecked) or buttonView.setChecked(isChecked) or buttonView.setChecked(buttonView.isChecked()) or something which is nearly the same in syntax.
I don't know what I can do besides declaring 10 Checkboxes in a xml file to talk to them one by one and set the VISIBLE.false if the auswahlList.get(position).isEmpty().
IMPORTANT: My XML is a Scrollable Activity because the size of the content overextended the screen. Thats why i didn´t and can´t use a Listview. So i need a solution that uses a LinearLayout
The truth is, you should actually use a ListView. As long as you reuse a layout multiple times - do it.
There are 2 options:
ListView as root - add other contents of your layout as different types of view
ListView inside a scrollable layout - there are many lightweight implementations of ListView that allow it to wrap content, e.g. https://github.com/paolorotolo/ExpandableHeightListView
The other thing is how to maintain the state of Checkboxes - use model classes. It's extremely easy with a ListView as it forces you to use an Adapter which provides methods to iterate over all positions.
Example of an adapter:
public class CheckableItemAdapter extends BaseAdapter {
private List<Pair<Integer, Boolean>> items = new ArrayList<>();
public void setItems(List<Pair<Integer, Boolean>> items) {
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
ViewHolder holder;
if (convertView == null) {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_checkable, parent, false);
holder = new ViewHolder(view);
view.setTag(holder);
} else {
view = convertView;
holder = (ViewHolder) view.getTag();
}
Pair<Integer, Boolean> item = items.get(position);
holder.itemCheck.setChecked(item.second);
return view;
}
static class ViewHolder {
CheckBox itemCheck;
public ViewHolder(View itemView) {
itemCheck = (CheckBox) itemView.findViewById(R.id.check);
}
}
}
I´ve managed to solve my problem alone, and now i want to share it, even if it isn´t the best example of programming.
Integer[] count = new Integer[]{1,2,3,4,5,6,7,8,9,10}; //maximum of 10 Checkboxes
size = mFrageList.get(position).getAuswahlList().size();
for (int i = 0; i < size; i++) {
cBox = new CheckBox(this);
cBox.setText(mFrageList.get(position).getAuswahlList().get(i));
cBox.setId(count[i]);
try{ //this is where the magic happens
if(antwortencode[position] != ""){ //cause i won´t want null in my db i´ve set "" as standard string in my activity for the List<String>
String code = antwortencode[position];
char[] c = code.toCharArray();
for(int j=0;j<=c.length;j++){
int x = c[j] -'0'; // 'char 1' - 'char 0' = Integer 1 , lol
if(cBox.getId()== x){ //compare them
cBox.toggle(); //if it fits, toggle
}
}
}
} catch (Exception e){
e.printStackTrace();
} //and here it ends
cBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
antwortencode[position] += "" + buttonView.getId();
frageBeantworten.setText("Antwort :"+antwortencode[position]+" abgeben");
} else {
String id = Integer.toString(buttonView.getId());
antwortencode[position] = antwortencode[position].replaceAll(id,"");
if(!antwortencode[position].isEmpty() || antwortencode[position]!= "") {
frageBeantworten.setText("Antwort :" + antwortencode[position] + " abgeben");
} else {
frageBeantworten.setText("Keine Checkbox(en) gewählt");
}
}
}
});
antworten.addView(cBox);
Ty for the answers and for the correction of my question.
Nostramärus

Categories

Resources