I've got "for each" loop that creates textView for every obiect from the list and add them to the linear layout. It works great. Then I want to hide all of the textViews when user clicks on toggle button but here I've got problem - only the last textView from the list is being hidden , rest of them is still visible. I tried to solve this problem with many solutions ( for example with getChild()), but nothing works.
final List<FilterItem> filterItemList = filterData.getFilterItemList();
for (FilterItem filterItem : filterItemList) {
final TextView filter = new TextView(MainPanelActivity.this);
filter.setText(filterItem.getFilterItemName());
filter.setTextColor(R.color.black);
linearLayout.addView(filter);
filter.setVisibility(View.GONE);
textLine.setOnCheckedChangeListener(
new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
filter.setVisibility(View.VISIBLE);
} else {
filter.setVisibility(View.GONE);
}
}
});
Note that youre setting a listener for textLine inside the for loop - so for each iteration you set a new listener that changes the visibility of the TextView created in the current iteraton.
Move textLine.setOnCheckedChangeListener() outside of the for loop; and inside onCheckedChanged - loop through all children of linearLayout and set the visibility for each child.
textLine.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
for (View v : linearLayout.getChildren()) {
if (v instanceof TextView) {
if (isChecked) {
v.setVisibility(View.VISIBLE);
} else {
v.setVisibility(View.GONE);
}
}
}
You could keep a list of TextViews when you create them. Then set the click listener outside the for-loop as Dmitri says, which would iterate through the list of TextViews and set the visibility to GONE.
private void setup() {
List<View> textViews = new ArrayList<>();
for (FilterItem filterItem : filterData.getFilterItemList()) {
View view = createTextViewFor(filterItem);
linearLayout.addView(filter);
textViews.add(view);
}
updateVisibility(textViews, View.GONE);
textLine.setOnCheckedChangeListener(
new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int visibility = isChecked ? View.VISIBLE : View.GONE;
updateVisibility(textViews, visibility);
}
}
);
}
private View createTextViewFor(FilterItem filterItem) {
TextView view = new TextView(MainPanelActivity.this);
view.setText(filterItem.getFilterItemName());
view.setTextColor(R.color.black);
view.addFilter(filterItem);
return view;
}
private void updateVisibility(List<View> views, int visibility) {
for (View view : views) {
view.setVisibility(visibility);
}
}
In case you want to display textView dynamically, I think putting textView inside RecyclerView will be a better idea. RecyclerView has its own Adapter which will make it easy to play with data and textViews. Give recyclerView a try. RecycerView for what you are trying to do can be learnt in few hours and sky is the limit for what you can do with recyclerView :)
Related
I'm trying to set up a checkbox in my recycler view, but I'm facing the common problem of random checks without my control. I check one item and some more random items are being checked at the same time.
I realise this is a common question here, but the solutions I found here don't seem to work. Among many others I was trying to follow different instructions in this thread:
Android RecyclerView checkbox checks itself
but what seems to be the problem is that functions isSelected and setSelected can't be resolved.
My best guess is that the reason for this complication might be that my onCheckedChange action is actually interfaced from the main activity, as you can see at the bottom of the code below. Is that what complicates the code (can't avoid it)?
public LocationRecyclerViewAdapter(List<IndividualLocation> styles,
Context context, ClickListener cardClickListener, OnCheckedChangeListener checkedChangeListener, int selectedTheme) {
this.context = context;
this.listOfLocations = styles;
this.selectedTheme = selectedTheme;
this.clickListener = cardClickListener;
this.onCheckedChangeListener = checkedChangeListener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int singleRvCardToUse = R.layout.single_location_map_view_rv_card;
View itemView = LayoutInflater.from(parent.getContext()).inflate(singleRvCardToUse, parent, false);
return new ViewHolder(itemView);
}
public interface ClickListener {
void onItemClick(int position);
}
public interface OnCheckedChangeListener {
void onCheckboxClick(int position);
}
#Override
public int getItemCount() {
return listOfLocations.size();
}
#Override
public void onBindViewHolder(final ViewHolder card, int position) {
final IndividualLocation locationCard = listOfLocations.get(position);
card.nameTextView.setText(locationCard.getName());
card.addressTextView.setText(locationCard.getAddress());
card.hoursTextView.setText(locationCard.getHours());
card.priceTextView.setText(locationCard.getPrice());
card.categoryTextView.setText(locationCard.getCategory());
card.cuisineTextView.setText(locationCard.getCuisine());
card.happyHourTextView.setText(locationCard.getHappyHour());
card.lunchDealTextView.setText(locationCard.getLunchDeal());
card.websiteTextView.setText("WEBSITE");
card.newPlaceTextView.setText(locationCard.getNewPlace());
card.websiteTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String url = locationCard.getWebsite();
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
context.startActivity(intent);
}
});
static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView nameTextView;
TextView addressTextView;
TextView priceTextView;
TextView hoursTextView;
TextView categoryTextView;
TextView cuisineTextView;
TextView happyHourTextView;
TextView lunchDealTextView;
TextView newPlaceTextView;
ConstraintLayout constraintUpperColorSection;
CardView cardView;
ImageView backgroundCircleImageView;
ImageView emojiImageView;
Button websiteTextView;
CheckBox checkBox;
ViewHolder(View itemView) {
super(itemView);
nameTextView = itemView.findViewById(R.id.location_name_tv);
addressTextView = itemView.findViewById(R.id.location_description_tv);
priceTextView = itemView.findViewById(R.id.location_price_tv);
hoursTextView = itemView.findViewById(R.id.location_hours_tv);
backgroundCircleImageView = itemView.findViewById(R.id.background_circle);
emojiImageView = itemView.findViewById(R.id.emoji);
constraintUpperColorSection = itemView.findViewById(R.id.constraint_upper_color);
categoryTextView = itemView.findViewById(R.id.location_type_tv);
cuisineTextView = itemView.findViewById(R.id.location_cuisine_tv);
happyHourTextView = itemView.findViewById(R.id.happyhour_tv);
lunchDealTextView = itemView.findViewById(R.id.lunchdeal_tv);
cardView = itemView.findViewById(R.id.map_view_location_card);
websiteTextView = itemView.findViewById(R.id.website);
newPlaceTextView = itemView.findViewById(R.id.new_place);
cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
clickListener.onItemClick(getLayoutPosition());
}
});
checkBox = itemView.findViewById(R.id.checkBox);
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
onCheckedChangeListener.onCheckboxClick(getLayoutPosition());
}
}});
}
#Override
public void onClick(View view) {
}
}
}
I'm sure there is some simple solution to this, but I've been cracking my head over this for way too long now. Thanks!
First of all, you have to check the checkbox programatically, for every item. Your model should hold the data, a simple boolean could do it.
The second is the trickiest one
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(buttonView.isPressed()){
if (isChecked ) {
onCheckedChangeListener.onCheckboxClick(holder.getAdapterPosition()());
}}
}});
You have to check if the checkbox is actually checked, in order to avoid confusions,
Inside your onBindViewHolder you have to set your checkbox either checked or unchecked. if true, your checkbox will be selected, else unselected,
because recycler view will be recycled every time you scroll, so you need to make sure you need to tell adapter that checkbox need to be checked or unchecked at that particular position.
why are you using getLayoutPosition() for getting checkbox position
use like this holder.getAdapterPosition()
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
onCheckedChangeListener.onCheckboxClick(holder.getAdapterPosition()());
}
}});
and move this listener to your onBindViewHolder()
Your data model should have a boolean variable isChecked. You can also give it an default value false. Now, in your adapter function onBindViewHolder, you should set value of your checkbox ex.
card.checkBox.setChecked(locationCard.getIsChecked())
Also, in your onCheckedChanged you should update your data
locationCard.setIsChecked(isChecked)
Now each time your RecyclerView Adapter reuses and list item, it will set the value of its checkbox correctly. Hope this helps.
In my Activity i have a list view and a check box.I created an adapter and do all stuff about it.In adapter i have 4 checkbox that loaded into list view.
I want to when user check this man checkBox in activity all checkBoxs on adapter is checked so in order to In activity first i find check box and finally i override setOnCheckedChangeListener method.
To find out number of view in adapter i am using TmpAdp.getCount().TmpAdp is my instance adapter.
This is a completed code :
chb_allCheck.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
for (int i = 0; i < TmpAdp.getCount(); i++) {
View view = Glist.getChildAt(i);
CheckBox ch_item = view.findViewWithTag(i);
// CheckBox ch_item = view.findViewById(R.id.chb_send_info); // this is not work like findViewWithTag
ch_item.setChecked(true);
}
TmpAdp.checkAll(TmpAdp.getCount());
TmpAdp.notifyDataSetChanged();
} else {
}
}
});
The problem is, when i checked mani check box (chb_allCheck), none of them of check boxs inside adapter is not checked ?
This is a part of adapter class and getView method:
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) Tmpcontext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.liststyle_saveed, parent, false);
BtnChangeToInPross = rowView.findViewById(R.id.BtnChangeToInPross);
TextView txtNosaziCodestr = rowView.findViewById(R.id.NosaziCodestr);
checkBox = rowView.findViewById(R.id.chb_send_info);
checkBox.setTag(position);
As you can see when i have clicked on Main check boxs, none of them of check box inside adapter is not checked
First of all make a Model class. In which you will take boolean isChecked.
Now in adapter class you will attach that model boolean to the list item checkbox.
checkbox.setChecked(model.isChecked());
When you check your main checkbox. Then just change isChecked boolean in your model list.
boolean isChecked = mainCheckBox.isChecked();
for(Model model : adapter.getList()){
model.setChecked(isChecked);
}
adapter.notifyDataSetChanged();
Here when you change checked status of model and notify your list. Then all your checkBox will be checked automatically, because these are attached to the model checked field.
It is logic, I did not write full code, you will make Model class, and will attach model by your list item.
Create a model class and make getter and setter method for check and uncheck Checkbox
Click of Main Checkbox in Activity
chb_allCheck.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean
for (int i = 0; i < yourList.size; i++) {
if(checked){
yourlist.get(i).setCheckBoxAdapter(true);
}
else{
yourlist.get(i).setCheckBoxAdapter(false);
}
}
TmpAdp.notifyDataSetChanged();
}
});
in yourAdapterClass
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
CheckBoxModelClass modelclass= getItem(position);
if(modelclass.getCheckbox()){
adapterCheckBox.setChecked(true);
}
else{
adapterCheckBox.setChecked(false);
}
return convertView;
}
Tank you of your guys, You have made good suggestions but let me complete my way.
My problem was TmpAdp.notifyDataSetChanged();.When i removed this, code work completally.
I have completed my code and works perfectly and it can be an additional way to Select
all checkbox.
chb_allCheck.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
for (int i = 0; i < TmpAdp.getCount(); i++) {
View view = Glist.getChildAt(i);
CheckBox ch_item = view.findViewById(R.id.chb_send_info);
ch_item.setChecked(true);
}
TmpAdp.checkAll(TmpAdp.getCount());
} else {
for (int i = 0; i < TmpAdp.getCount(); i++) {
View view = Glist.getChildAt(i);
CheckBox ch_item = view.findViewById(R.id.chb_send_info);
ch_item.setChecked(false);
}
TmpAdp.removeAll();
}
}
});
I have a RecyclerView which holds 10 views each having a CheckBox. Now, in my main activity when a menu button named "POST" is pressed I want to know whether all the CheckBox in each of the views of the RecyclerView is checked or not.
How can I implement this?
I advise you to pass additional variable isChecked inside every model of a list of models passed to RecyclerView.
Like that:
public class Model {
private boolean isChecked;
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean checked) {
isChecked = checked;
}
}
Then inside your RecyclerView ViewHolder, create a constructor:
public ListViewHolder(View view) {
super(view);
switchCompat = (SwitchCompat) view.findViewById(R.id.add_switch);
switchCompat.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
getItemAt(getLayoutPosition()).setChecked(isChecked);
}
});
}
Then to get all button states just iterate through the list of models in Your activity:
public boolean areAllChecked() {
for (int i = 0; i < adapter.getItemCount(); i++) {
Model model = adapter.getItemAt(i);
if (!model .isChecked()) {
return false;
}
}
return true;
}
Add a boolean isChecked in your model class (Item which you are adding to RecyclerView Adapter)
In your adapter onBindViewHolder implement onCheckedChangeListener for your checkbox and set isChecked appropriately.
implement a isChecked(int position) method in your adapter. It should return checked value of the items specified by position
in your fragment/activity iterate through adapter items and find out if all items are checked or not.
Track the state of each checkbox in the adapter.
To do so let it have an map that stores a boolean value bound to a unique key for each checkbox. And also let it implement the OnCheckedChangeListener.
MyAdapter extends RecyclerView.Adapter implements OnCheckedChangeListener {
private Map<Object, Boolean> checkboxStates = new HasMap<>();
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
checkboxStates.put(buttonView.getTag(), isChecked);
}
public Map<Object, Boolean> getCheckboxStates() {
//We don't want the caller to modify adapter state.
return Collections.unmodifiableMap(this.checkboxStates);
}
//other code
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
//your code
CheckBox cb;
//assuming that we have the reference to the cb view
//also assuming that cb has a unique identifiable tag assigned from the model
cb.setOnCheckedChangeListener(this);
if (this.checkboxStates.containsKey(cb.getTag()) {
cb.setChecked(this.checkboxStates.get(cb.getTag());
} else {
this.checkboxStates.put(cb.getTag(), cb.isChecked());
}
}
}
With this you can call the getCheckboxStates to get the states of each checkbox that was visible.
The key point here is that you need something unique identifiable for each item in the dataset that can be used as a tag for each checkbox that represents that item.
I have a custom ExpendableListViewAdapter. Like this:
Group
Child
Child
Child
Group
All his groups and childs have checkboxes.
I want parent CheckBox to dynamically check itself when all his children are checked.
How to implement this?
I've implemented selection of children then the parent element selected.
This is my method for it:
void selectGroup(boolean isSelected) {
for (Item itemChild : item.getItems()) {
if (isSelected) itemChild.isSelected = true;
else itemChild.isSelected = false;
itemChild.setSelected(isSelected);
}
notifyDataSetChanged();
}
And I summon it inside getGroupView:
holder.cbChild.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
item.isSelected = isChecked;
item.setSelected(isChecked);
if (getChildrenCount(groupPosition) != 0) {
selectGroup(item.isSelected);
}
}
});
And it works fine.
I've tried to implement parent selection as well.
Method:
public boolean isAllChildrenSelected(int groupPosition) {
int selectedNumber = 0;
for (int i = 0; i < getChildrenCount(groupPosition); i++) {
Item itemChild = getChild(groupPosition, i);
if (itemChild.isSelected) {
selectedNumber++;
}
}
if (selectedNumber == getChildrenCount(groupPosition)) {
notifyDataSetChanged();
return true;
} else return false;
}
And I summon this method inside my getGroupView as well:
if (getChildrenCount(groupPosition) != 0) {
if (isAllChildrenSelected(groupPosition)) {
holder.cbChild.setChecked(true);
}
}
And then, in getChildView inside setOnCheckedChangeListener for child CheckBox I summon notifyDataSetChanged();.
But I'm getting some strange behavior - for example, if group have 3 childs and I'm trying to select the 1st one, it selects the 2nd and the 3rd.
I think, this is because I putted notifyDataSetChanged(); in a wrong place, but I cannot figure out where exactly and how to fix it.
Well, it looks like I've figured it out.
Add this method:
private void selectGroupIfAllChildrenSelected() {
int numSelected = 0;
for (Child child: group.getChildrenList()) {
if (child.isSelected()) {
numSelected++;
}
if (group.getChildrenList().size() == numSelected) {
group.setSelected(true);
} else if (numSelected == 0) {
group.setSelected(false);
}
}
notifyDataSetChanged();
}
And summon it inside your getChildView method inside OnCheckedChangeListener:
holder.cbChild.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
child.setSelected(isChecked);
selectGroupIfAllChildrenSelected();
}
});
Also, don't forget to set to null your setOnCheckedChangeListener inside getChildView before you set a CheckBox state, otherwise you will get some strange behaviour:
holder.cbChild.setOnCheckedChangeListener(null);
holder.cbChild.setChecked(child.isSelected());
All this will also deselect your group CheckBox as soon as you deselect all the children.
My RecyclerView is a list with checkboxes and at the bottom of the page is a submit button. When I click on the button, the checkboxes should be disabled but the state of those boxes that are already checked should retain. And also, how to access the checkbox since it's in the RecyclerView.ViewHolder? Help please.
It's much better to have this as an attribute to the item that you're modeling.
So if the model item will have an "enabled" state that you can change.
public class Model {
private boolean isEnabled;
private boolean isChecked;
public void setEnabled(boolean enabled) {
isEnabled = enabled;
}
public void setChecked(boolean checked) {
isChecked = checked;
}
public boolean isEnabled() {
return isEnabled;
}
public boolean isChecked() {
return isChecked;
}
}
Then your ViewHolder will check this attribute every time you bind to it. Additionally, the ViewHolder itself will listen for changes to the checkbox on the View that it handles.
public class ModelViewHolder extends RecyclerView.ViewHolder implements CompoundButton.OnCheckChangeListener {
private CheckBox checkBox;
private Model boundItem;
public ModelViewHolder(View itemView) {
checkBox = (CheckBox) itemView.findItemById(R.id.checkBoxId);
checkBox.setOnCheckChangeListener(this);
}
public void bind(Model model) {
boundItem = model;
getItemView().setEnabled(model.isEnabled());
checkBox.setChecked(model.isChecked());
}
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
boundItem.setChecked(isChecked);
}
}
Now, what this allows is the state of the item will be consistent while the user scrolls (since Views in RecyclerItem are re-used). It also allows you to use notifyItemChanged(int position) more easily on the item whenever you enable/disable the Model item.
You will need to get your item in the list you passed to the adapter. If it is a custom adapter you can make a method to return your list, and code will be:
mAdapter.getList().get(4).setEnabled(false); //or equivalent
mAdapter.notifyDataSetChanged(); //or mRecycler.getAdapter().notifyDataSetChanged()
Change the Recyclerview background colour is Gray. In Recyclerview disable only work when some action is performed. if try to disable no action is performed, You have NULL Pointer exception.
You can try that:
RecyclerView rv=new RecyclerView(context);
rv.getChildAt(5).setEnabled(false); // disables the 6th element