I have a RecyclerView with items and all of them have a checkbox. The point is that you can only set one checkbox as checked (something like favourite). Everything works fine until there are enough items that you need to scroll the RecyclerView. But, when there's enough items to scroll, then some of the item's names disappear and none of the checkboxes work except one and it is all stuck. Also, I get the following exception:
"Cannot call this method while RecyclerView is computing a layout or scrolling"
This is what I am doing:
holder.getBinder().cbIsFavourite.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (mListener == null) {
return;
}
if (isChecked) {
for (Peem peemTmp : mPeemList) {
if (peemTmp.isFavourite()) {
peemTmp.setFavourite(false);
}
}
peem.setFavourite(true);
PeemAdapter.this.notifyDataSetChanged();
} else {
peem.setFavourite(false);
removeFavouriteShops(peem);
}
mListener.onFavouriteChecked(peem);
}
});
The error occurs on this line PeemAdapter.this.notifyDataSetChanged();
If I remove that line it is all okay, but multiple checkboxes can be selected which is not the idea.
Why not use RadioButton and RadioGroup ?
Else try to call notifyDataSetChanged() in a post Runnable, it will be called after layout computing. Something like this:
myView.post(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
});
EDITED:
holder.getBinder().cbIsFavourite.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (mListener == null) {
return;
}
if (isChecked) {
for (Peem peemTmp : mPeemList) {
if (peemTmp.isFavourite()) {
peemTmp.setFavourite(false);
}
}
peem.setFavourite(true);
holder.getBinder().cbIsFavourite.post(new Runnable() {
#Override
public void run() {
PeemAdapter.this.notifyDataSetChanged();
}
});
} else {
peem.setFavourite(false);
removeFavouriteShops(peem);
}
mListener.onFavouriteChecked(peem);
}
});
Related
I have a program which creates a list of to-dos that allows the user to set a date/time for the app to notify them about it. There's a checkbox that says 'Notify me' which is supposed to schedule the notification. But, in the main RecyclerView of the list, there is also a toggle switch that allows the user to turn off/on the notification after they've saved it. Problem is, the toggle switch doesn't seem to be changing the notification state.
holder.notifSwitch.setChecked(journalModel.isNotify());
if(journalModel.getJournalDateNotify().getTime() > System.currentTimeMillis())
{
holder.notifSwitch.setVisibility(View.VISIBLE);
} else {
holder.notifSwitch.setVisibility(View.INVISIBLE);
}
holder.notifSwitch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!journalModel.isNotify())
{
createJournalFunction.updateJournal(realm, journalModel.getRealmJournalNo(),true);
} else
{
createJournalFunction.updateJournal(realm, journalModel.getRealmJournalNo(), false);
}
}
});
And here is the code for updating the Realm object:
public boolean updateJournal (Realm realm, final int realmJournalNo, final boolean isNotify){
success = false;
try{
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
final TblJournal tblJournal = realm.where(TblJournal.class).equalTo("realmJournalNo", realmJournalNo).findFirst();
tblJournal.setNotify(isNotify);
success = true;
}
});
}catch (RealmException e){
Log.i("INFO","update Retail Exception: "+e.toString());
success = false;
}finally {
return success;
}
}
As per your code your are not updating journalModel.isNotify= true or false in on click. journalModel.setIsNotify(true):/journalModel.setIsNotify(false):
holder.notifSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// status="true"; //edit here
switch_btn.setChecked(true);
} else {
// status="false"; //edit here
switch_btn.setChecked(false);
}
}
});
Everything looks fine, you just need to modify your onClick method.
holder.notifSwitch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
CheckBox checkbox = (CheckBox)v;
createJournalFunction.updateJournal(realm, journalModel.getRealmJournalNo(),checkbox.isChecked());
}
});
Checkbox is selected randomly in Recyclerview when using notifyDatasetChange. Here is my code.
public void onBindViewHolder(final ViewHolder holder, int position) {
SplitObject object=splitObjects.get(position);
holder.textView_memberName.setText(object.getMember_name());
holder.textView_splitAmount.setText(amount/splitObjects.size()+"");
if(holder.checkBox.isChecked())
{
Debug.e("checked"+position);
}
else
{
Debug.e("Unchecked"+position);
}
holder.checkBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
notifyDataSetChanged();
}
});
}
I am not able to figure out why notifyDataSetChanged function is not working properly.
Use checkChangeListener instead of onClickListener
holder. checkBox.setOnCheckedChangeListener(null);
//if true, your checkbox will be selected, else unselected
holder.checkBox.setChecked(pass the boolean from your list);
holder.checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//set your object's last status
yourarrayobject.setSelected(isChecked);
}
});
I think check box selected when you scroll the recyleview,YOu can please Viewholder concept,So add viewHolder.CheckBox.setChecked(isItemChecked); to onBindViewHolder and it will work. You will have to replace isItemChecked with your logic of retrieving the actual state for the item at the position...
Your notifyDataSetChanged() needs to run on the UI Thread. I had faced similar difficulties where the adapter wasn't getting properly updated. Try doing this in your OnClickListener:
#Override
public void onClick(View v) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
});
}
This is my code. What can ba a cause?
holder.favorite.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
addFavorite(famous);
} else {
deleteFavorite(famous);
}
}
});
When isChecked=true, firstly is run addFavorite() and after that deleteFavorite().
When isChecked=false, firstly is run deleteFavorite() and after that addFavorite().
Don't know why...
EDIT: When I scroll down/up my ListView it calls these methods too... Weird...
private void deleteFavorite(final FamousTop40Ranking famous) {
DeleteFavoriteData data = new DeleteFavoriteData(famous.getId());
FavoriteDeleteApi.Factory.getInstance().deleteFavorite(data.getData())
.enqueue(new Callback<StatusInfoModel>() {
#Override
public void onResponse(Call<StatusInfoModel> call, Response<StatusInfoModel> response) {
showToast(mActivity, "Famous deleted from your Favorites list.");
famous.setFollowersCountry(famous.getFollowersCountry() - 1);
famous.setFollowersWorld(famous.getFollowersWorld() - 1);
notifyDataSetChanged();
}
#Override
public void onFailure(Call<StatusInfoModel> call, Throwable t) {
Log.d("deleteFavorite", mActivity.getString(R.string.something_went_wrong) + t.getMessage());
}
});
}
private void addFavorite(final FamousTop40Ranking famous) {
FavoriteCountApi.Factory.getInstance().countFavorites()
.enqueue(new Callback<CountFavoriteModel>() {
#Override
public void onResponse(Call<CountFavoriteModel> call, Response<CountFavoriteModel> response) {
if (response.isSuccessful()) {
if (response.body().getCount() < 20) {
FavoriteAddApi.Factory.getInstance().addFavorite(String.valueOf(famous.getId()))
.enqueue(new Callback<StatusInfoModel>() {
#Override
public void onResponse(Call<StatusInfoModel> call, Response<StatusInfoModel> response) {
showToast(mActivity, "Famous added from your Favorites list.");
famous.setFollowersCountry(famous.getFollowersCountry() + 1);
famous.setFollowersWorld(famous.getFollowersWorld() + 1);
notifyDataSetChanged();
}
#Override
public void onFailure(Call<StatusInfoModel> call, Throwable t) {
Log.d("addFavorite", mActivity.getString(R.string.something_went_wrong) + t.getMessage());
}
});
} else {
showToast(mActivity, mActivity.getString(R.string.reached_max_favorites));
}
}
}
#Override
public void onFailure(Call<CountFavoriteModel> call, Throwable t) {
Log.d("countFavorite", mActivity.getString(R.string.something_went_wrong) + t.getMessage());
}
});
}
getView() method:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if(convertView == null) {
LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.list_view_ranking_famous_single_item, parent, false);
}
final FamousTop40Ranking famous = famousModelList.get(position);
holder = new ViewHolder(convertView);
holder.name.setText(famous.getName());
if (mTab == 0) { // RankingFamousMainFragment.TAB_FAMOUS
holder.followers.setText(String.valueOf(famous.getFollowersWorld()));
} else {
holder.followers.setText(String.valueOf(famous.getFollowersCountry()));
}
if (famous.getIsFavorite().get(0).getFavorite().equals("1")) {
holder.favorite.setChecked(true);
} else {
holder.favorite.setChecked(false);
}
Glide
.with(mActivity)
.load(famous.getPhoto())
.fallback(R.drawable.bg_gradient)
.error(R.drawable.bg_gradient)
.centerCrop()
.crossFade()
.into(holder.photo);
holder.favorite.setOnCheckedChangeListener(null);
holder.favorite.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
addFavorite(famous);
} else {
deleteFavorite(famous);
}
}
});
convertView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showToast(mActivity, mActivity.getString(R.string.famous_clicked, position));
}
});
return convertView;
}
In getView. Move setonCHecked(null) before the if/else.
The reason this is hapening is getView might get called alot of times, in this case i think it is called because when you change state(check/uncheck) it redraws the button. And in your getView you are calling setChecked which triggers the listener. Setting the listener to null before calling holder.favorite.setChecked(true/false) wont double trigger it.
About up/down issue- It is the same, when the view is of the screen it is deleted. When it appears again it calls getView whichi triggers everything due to holder.favorite.setChecked(true) activating the onCheckChangedLIstener
As an example:
holder.favorite.setOnCheckedChangeListener(null);
holder.favorite.setChecked(true);
holder.favourite.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// handle your logic
}
});
I may be late for the party!.. but this is how I fix a double trigger on radio Buttons, in my case I have a RadioGroup, that helps with the behavior of a group of radio buttons.
When you click on a specific radio button you expect to trigger lambda expression in "setOnCheckedChangeListener", which is normal.
But when you "reset" the group and need that no radio button be selected, you usually call either:
radioGroup.clearCheck()
or
radioGroup.check(-1)
But when you do that, the listener receives 2 calls, the first is for the radio button that is going to be "unchecked",
the second call with the radio button id equals -1.
so what ai have done, and work to me is:
radioGroup.setOnCheckedChangeListener(RadioGroup.OnCheckedChangeListener { group, checkedId ->
//If not -1, means that is a radio button that was been clicked (don't know yet is checked or not)
if (checkedId > 0) {
//Get radio button element
val radio: RadioButton = group.findViewById(checkedId)
// Preguntar por si esta checked == true ?
if (radio.isChecked) {
when (checkedId) {
R.id.rb_1 -> {
viewModel.onSetPv(1)
}
R.id.rb_2 -> {
viewModel.onSetPv(2)
}
else -> false
}
}
}
})
Best regards!
Actually, it's not an issue! Why? Imagine you have a radio-group, and you select one of the options; if you rotate the device, the selected option should be stay selected. (It save the state. In fact, it works just like view-model)
But, when it could be a problem? At first state, when you didn't choose any option. How to solve it? Just add if (checkedId > 0) before you check the ids:
radioGroup.setOnCheckedChangeListener((radioGroup, checkedId) -> {
if (checkedId > 0) {
// here check the ids
}
});
The other situation that you can face a problem is, when you use a same radio-group for multitasks (like quiz app). In this situation, what I did was using view-model. I let the view-model take care of saving state and right after I check the Ids, I add radioGroup.check(-1); example:
radioGroup.setOnCheckedChangeListener((radioGroup, checkedId) -> {
if (checkedId > 0) {
// here check the ids
// use viewModel
radioGroup.check(-1);
}
});
in the below code i am trying to use a checkbox. but at run time it is never clickable, i mean when i click on the check box nothing happen or change and the checkbox remains always unchecked
why that is happening?
code:
mcbComputations.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mcbComputations.isChecked()) {
mATComputations = new ATComputations();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mATComputations.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
mATComputations.execute();
}
} else {
mATComputations.cancel(true);
}
When dealing with a Checkbox, rather use an onCheckChangedListener:
mcbComputations.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (mcbComputations.isChecked()) {
mATComputations = new ATComputations();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mATComputations.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
mATComputations.execute();
}
} else {
mATComputations.cancel(true);
}
}
});
Use
setOnCheckedChangeListener
instead of
setOnClickListener
for CheckBox
I added two check box in the on create method
checkBox1 = (CheckBox) findViewById(R.id.checkBox1);
checkBox2 = (CheckBox) findViewById(R.id.checkBox2);
checkBox1.setOnCheckedChangeListener(this) ;
checkBox2.setOnCheckedChangeListener(this) ;
the main function of the check boxes that when ischeck() a picture will be added to the mainlayout and when uncheck the picture will be removed >> I used the code bellow, the first check box is working fine the second check box when I do check it shows the pics and then they I can remove them even with uncheck ... where is the wrong in my code ??
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// TODO Auto-generated method stub
if(checkBox1.isChecked())
{
......
mapOverlays.add(custom);
}
else {
mapOverlays.remove(custom) ;
}
if (checkBox2.isChecked())
{
....
mapOverlays.add(custom2);
}
else
{
mapOverlays.remove(custom2) ;
}
}
}
You are handling second checkbox checking differently. May be the code should look like this?
if (checkBox2.isChecked())
{
...
mapOverlays.add(custom2);
}
else
{
mapOverlays.remove(custom2);
}
Upd: if your code looks like in the current edit, then issue is the declaring custom2 variable in the if block. You are deleting not added mapOverlay, but another one declared somewhere else.
Just replace
if (checkBox2.isChecked())
{
MapItemizedOverlay custom2 = ...
by
if (checkBox2.isChecked())
{
custom2 = ...
Upd2: there is yet another issue with your onCheckedChanged() method. First if-else runs not only on checkBox1 check/uncheck but also on checkBox2 check/uncheck. Same for the second if-else.
Try to rewrite method:
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.equals(checkBox1)) {
// first if-else
} else if (buttonView.equals(checkBox2)) {
// second if-else
}
}
or even better:
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.getId() == R.id.checkBox1) {
if (isChecked) {
...
mapOverlays.add(custom);
} else {
mapOverlays.remove(custom);
}
} else if (buttonView.getId() == R.id.checkBox2) {
if (isChecked) {
...
mapOverlays.add(custom2);
} else {
mapOverlays.remove(custom2);
}
}
}
You need to add checkedchangelistener to checkbox 2 as well.
checkBox2.setOnCheckedChangeListener(this) ;