Why Scrolling Up and Down In GridView Changes Registries in SharedPreferences? - java

My question is similar to this and this. But I cannot apply the solutions in those questions to my case.
My case is as follows: I have GridView consists of just CheckBoxes. I set setOnCheckedChangeListener in my Adapter's getView method, so that as soon as the status of CheckBox is changed, current status of the CheckBox is registered to SharedPreferences. And in getView also, I'm reading the current status of the CheckBox and set its status as so. Yet scrolling down and back up makes the checked boxes unchecked.
How can I solve this problem?
Here is my custom adapter:
public class KnowledgeItemAdapter extends ArrayAdapter<KnowledgeItem> {
private String currentKey;
public KnowledgeItemAdapter(Activity context, ArrayList<KnowledgeItem> knowledgeItems){
super(context, 0, knowledgeItems);
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View knowledgeItemView = convertView;
if (knowledgeItemView == null){
knowledgeItemView = LayoutInflater.from(getContext()).inflate(R.layout.item_knowledge, parent, false);
}
knowledgeItemView.setClickable(true);
knowledgeItemView.setFocusable(true);
knowledgeItemView.setBackgroundResource(android.R.drawable.menuitem_background);
final KnowledgeItem currentKnowledgeItem = getItem(position);
final CheckBox knowledgeCheckBox = (CheckBox) knowledgeItemView.findViewById(R.id.item_knowledge_check_box);
final SharedPreferences prefs = getContext().getSharedPreferences("com.example.myapplication", Context.MODE_PRIVATE);
currentKey = currentKnowledgeItem.getPrefsKey();
Boolean status = prefs.getBoolean(currentKey, false);
knowledgeCheckBox.setText(currentKnowledgeItem.getText());
knowledgeCheckBox.setChecked(status);
knowledgeCheckBox.setOnCheckedChangeListener( //this);
new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
prefs.edit().putBoolean(currentKnowledgeItem.getPrefsKey(), isChecked).apply();
}
}
);
return knowledgeItemView;
}
}

OK I've figured out the answer finally. I've changed the onCheckedChangeListener with onClickListener.
knowledgeItemView.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.e("On Click Listener", currentKey);
if(knowledgeCheckBox.isChecked()){
knowledgeCheckBox.setChecked(false);
prefs.edit().putBoolean(currentKey, false).apply();
} else {
knowledgeCheckBox.setChecked(true);
prefs.edit().putBoolean(currentKey, true).apply();
}
}
}
);
And do not forget to change the clickable xml attribution of the checkbox to false. Otherwise click events of checkbox and view conflict.

Related

How to avoid RadioGroup checked twice in RecyclerView

I've noticed RecyclerView "recycle" the views in the adapter but i want stop RecyclerView adapter duplicate the checked action in another item of the RecyclerView.
I have 10 items drawed into a RecyclerView, each one of them have a RadioGroup with 2 RadioButton within, but when i fired the check in the first item, for example, the number ten item have a checked too.
I was reading this question but i could'nt got it work.
Using Radio button with recyclerview in android
How to avoid this?
My adapter:
...
public class PreguntaAdapter extends RecyclerView.Adapter<PreguntaAdapter.ViewHolder> {
private Context context;
private ArrayList<VistaEncuestaActivity.PreguntasSiNo> preguntas;
public ArrayList<Respuestas> respuestas = new ArrayList<Respuestas>();
public PreguntaAdapter(Context context, ArrayList<VistaEncuestaActivity.PreguntasSiNo> preguntas) {
this.context = context;
this.preguntas = preguntas;
}
public ArrayList<Respuestas> getCheckedItems() {
return respuestas;
}
#NonNull
#Override
public PreguntaAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
Context context = viewGroup.getContext();
LayoutInflater layoutInflater = LayoutInflater.from(context);
View v = layoutInflater.inflate(R.layout.item_pregunta_sino, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull final PreguntaAdapter.ViewHolder viewHolder, int i) {
final VistaEncuestaActivity.PreguntasSiNo currentPregunta = preguntas.get(i);
//viewHolder.pregunta.setText(currentEncuesta.getString_pregunta());
viewHolder.pregunta.setText(currentPregunta.getQuestion());
viewHolder.myLinearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
viewHolder.setIsRecyclable(false);
}
#Override
public int getItemCount() {
return preguntas.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public SharedPreferences prefs;
private LinearLayout myLinearLayout;
public TextView pregunta;
public RadioGroup radio_group;
public ViewHolder(#NonNull View itemView) {
super(itemView);
pregunta = (TextView) itemView.findViewById(R.id.pregunta);
radio_group = (RadioGroup) itemView.findViewById(R.id.radio_group_pregunta);
prefs = (SharedPreferences) context.getSharedPreferences("logged", Context.MODE_PRIVATE);
myLinearLayout = (LinearLayout) itemView.findViewById(R.id.myLinearLayout);
}
}
}
Thanks all of you for your responses. Unfortunally any response helped me to solve this issue, i used this function and i added the getItemViewType function to my adapter and it's working well now:
#Override
public int getItemViewType(int position) {
return position;
}
And going to the official google docs i could find the next information:
Return the view type of the item at position for the purposes of view recycling. The default implementation of this method returns 0, making the assumption of a single view type for the adapter. Unlike ListView adapters, types need not be contiguous. Consider using id resources to uniquely identify item view types. https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter
This make sense at the time RecyclerView "recycle" the views but when this is use it in the adapter this have a different behavior treating each view as unique.
Thank you to all of you.
RadioGroup.getCheckedRadioButtonId() is the way to go. In conjunction with a SparseArray<Integer>.
In onBindViewHolder():
On each RadioGroup, set a RadioGroup.OnCheckedChangeListener(). Add the checked state to the a SparseArray or Map<Integer,Integer> mapping the index of the item position to the updated value in RadioGroup.OnCheckedChangeListener() onCheckedChanged.
So your onBindViewHolder would look something like this:
private SparseArray<Integer> mMapping = new SparseArray<>();
public void onBindViewHolder(#NonNull final PreguntaAdapter.ViewHolder viewHolder,final int position) {
final VistaEncuestaActivity.PreguntasSiNo currentPregunta = preguntas.get(i);
//viewHolder.pregunta.setText(currentEncuesta.getString_pregunta());
viewHolder.pregunta.setText(currentPregunta.getQuestion());
viewHolder.myLinearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
viewHolder.radio_group.setOnCheckedChangeListener(new OnCheckedChangeListener(){
void onCheckedChanged(RadioGroup group,int checked) {
mMapping.put(position,checked);
}
}};
// Check to see if a previous checked value exists and restore.
if(mMapping.get(position) != null && mMapping.get(position) != -1){
radio_group.check(mMapping.get(position));
}
viewHolder.setIsRecyclable(false);
}
onCheckedChanged(RadioGroup group, int checkedId)
You should give each RadioButton a unique ID, like radioButton_1, radioButton_2 in your layout file.

How to create an algorithm to set a multi checked items in a RecyclerView

I need to create an algorithm that set a checked item in a RecyclerView. I have a RecyclerView made with boxes with a TextView and an ImageView, I use the TextView to show nameItems from a List and the ImageView to show a check once the user clicks on it.
What I want to do is that every time the user clicks on an item the check appears and every time he clicks on a checked Item the check disappears.
I create an algorithm which uses a boolean variable (isChecked) set to false, every time user clicks on an item the variable is set to true and vice versa.
In this case, a user has to click on the next item of the list two times to let show the check.
How can I do it?
Thank you so much in advance
Here is my Adapter's class:
public class RecyclerTypeListViewAdapter extends RecyclerView.Adapter<RecyclerTypeListViewAdapter.TypeViewHolder> {
List<TipologiaEvento> eventType;
private boolean isChecked = false;
public RecyclerTypeListViewAdapter (List<TipologiaEvento> typeList){
this.eventType = typeList;
}
public static class TypeViewHolder extends RecyclerView.ViewHolder {
LinearLayout linearLayout;
TextView typeName;
ImageView check_icon;
TypeViewHolder (View view){
super(view);
linearLayout = view.findViewById(R.id.type_listed_linear_layout);
typeName = view.findViewById(R.id.event_type_text_view);
check_icon = view.findViewById(R.id.event_type_checked_icon);
}
}
#Override
public TypeViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.cell_type_listed, viewGroup, false);
TypeViewHolder tvh = new TypeViewHolder(view);
return tvh;
}
#Override
public void onBindViewHolder(final TypeViewHolder typeViewHolder, int position) {
typeViewHolder.typeName.setText(eventType.get(position).getDescrizione());
typeViewHolder.linearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(isChecked == false) {
typeViewHolder.check_icon.setVisibility(View.VISIBLE);
Toast.makeText(v.getContext(), "Aggiunto: " + typeViewHolder.typeName.getText().toString(), Toast.LENGTH_SHORT).show();
isChecked = true;
} else {
typeViewHolder.check_icon.setVisibility(View.INVISIBLE);
Toast.makeText(v.getContext(), "Rimosso: " + typeViewHolder.typeName.getText().toString(), Toast.LENGTH_SHORT).show();
isChecked = false;
}
}
});
}
}
Just take one boolean value in the model class of your list. and in bindviewholder.
just put the condition like below
if(list.get(position).ischecked()) {
action of checked
} else {
action of unchecked
}
and on the click of the item you just have to change the flag of object and notify the adapter.

switch state is not stable in listview when scrolling

I'am new to android and i use sample for my listview. because of that i can't change the code. i have one switch button in each item of my Listview. when i scroll the Listview switches change state randomly. how can i make switch state stable?
my listview class adapter:
public class MyCustomCursorAdapter extends CursorAdapter {
LIGHTS calling_activity;
private DatabaseHelper dbHelper;
public MyCustomCursorAdapter(Context context, Cursor c) {
super(context, c, 0);
this.calling_activity = (LIGHTS) context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
return view;
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.adapter,parent,false);
}
#Override
public void bindView(final View view, Context context, Cursor cursor) {
((TextView)view.findViewById(R.id.id)).setText(cursor.getString(cursor.getColumnIndex(dbHelper._ID)));
((TextView)view.findViewById(R.id.KEYCODE)).setText(cursor.getString(cursor.getColumnIndex(dbHelper.TITLE)));
((TextView)view.findViewById(R.id.NAME)).setText(cursor.getString(cursor.getColumnIndex(dbHelper.DESC)));
ImageView option = view.findViewById(R.id.itemoption);
Switch thisswitch = view.findViewById(R.id.onoff);
thisswitch.setTag(cursor.getString(cursor.getColumnIndex(dbHelper._ID)));
thisswitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { calling_activity.myOnCheckedChangedHandler((String)buttonView.getTag(),isChecked);
}
});
option.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TextView itemID = (TextView) view.findViewById(R.id.id);
TextView itemTitle = (TextView) view.findViewById(R.id.KEYCODE);
TextView itemDesc = (TextView) view.findViewById(R.id.NAME);
String myId = itemID.getText().toString();
String myTitle = itemTitle.getText().toString();
String myDesc = itemDesc.getText().toString();
Intent intent = new Intent(calling_activity, ModifyActivity.class);
intent.putExtra("Id", myId);
intent.putExtra("Title", myTitle);
intent.putExtra("Desc", myDesc);
calling_activity.startActivity(intent);
}
});
}
}
and in my Lights activity :
#Override
public void myOnCheckedChangedHandler(String id, boolean check_status) {
Toast.makeText(
this,
"You changed the status for the row with an id of " + id +
" the status is now " + new Boolean(check_status).toString(),
Toast.LENGTH_SHORT).show();
String name = cursor.getString(cursor.getColumnIndex(dbHelper.DESC));
if(new Boolean(check_status).toString().equals("true")){
Toast.makeText(this,name +" is ON", Toast.LENGTH_SHORT).show();
}
}
Please manage through if/else case inside bindView at adapter, You need to check the switch condition like below :
// set the code into bind
if(switchCondition1 == 1)
{
//set the code as per condition wise
}else{
//
}
After change the state you also need to change the state into your list item and use notifydatasetChanged() method to refresh the list items.

Android: Changing one number picker cause other number picker to change

I have created a listview. Each item in list view has two UI elements. One is a textview and other is a number picker. Now the issue is that if i click on first number picker to change value, the fourth one also changes and vice versa. Here is my getview function
private class ViewHolder {
public TextView name;
public NumberPicker numberPicker;
public CustomListener listener;
}
public View getView(final int position, #Nullable View convertView, #NonNull final ViewGroup parent) {
ViewHolder holder;
View listItem = convertView;
currentCell=getItem(position);
currentCell.setPosition(position);
if (listItem == null) {
LayoutInflater inflater = LayoutInflater.from(mContext);
listItem = inflater.inflate(R.layout.organ_item, parent, false);
}
holder = new ViewHolder();
holder.name = (TextView) listItem.findViewById(R.id.organName);
holder.numberPicker = (NumberPicker)
listItem.findViewById(R.id.numberPicker);
holder.numberPicker.setMinValue(1);
holder.numberPicker.setMaxValue(10);
holder.numberPicker.setOnValueChangedListener(holder.listener);
holder.numberPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
currentCell=getItem(position);
View parentRow = (View) picker.getParent();
ListView mListView=(ListView)parentRow.getParent().getParent();
ConstraintLayout constraintLayoutView = (ConstraintLayout) mListView.getChildAt(currentCell.getPosition());
RelativeLayout relativeLayout = (RelativeLayout)constraintLayoutView.getChildAt(0);
NumberPicker p = (NumberPicker) relativeLayout.getChildAt(1);
if(position==currentCell.getPosition())
{
p.setValue(newVal);
}
else
{
p.setValue(oldVal);
}
}
});
//Set the name
TextView organName = (TextView)listItem.findViewById(R.id.organName);
organName.setText(QuickMeditationScreenInfo.getInstance().getScreenNameFromIndex(currentCell.getOrgan()));
return listItem;
}
Also even if i comment out the onValueChangeListener even then the same behaviour occurs which i assume is the default behaviour of number picker in a list. I have spent multiple hours on it but couldn't figure out the solution. I have also debugged the code and when i change a value, the debugger comes into the onValueChange code only once.
You need set numberpicker default value every time
holder = new ViewHolder();
holder.name = (TextView) listItem.findViewById(R.id.organName);
holder.numberPicker = (NumberPicker) ;
holder.numberPicker.setValue(defaultValue);//like this
Try to handle click event into adapter using interface like below
for example make one interface into adapter class ..
private onItemClick onItemClick;
public void setOnItemClick(DisplayAllData.onItemClick onItemClick) {
this.onItemClick = onItemClick;
}
public interface onItemClick{
void onItemSelected(int position); // pass your data
}
In getView() method like number listner called all logical code into activity or fragment.
holder.mTvName.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onItemClick.onItemSelected(position);
}
});
In activity or fragment after adapter set into listview or recyclerview then
adapter not null then called below code..
allDataAdapter.setOnItemClick(new DisplayAllData.onItemClick() {
#Override
public void onItemSelected(int position) {
// here called all logical part
allDataAdapter.notifyDataSetChanged();
}
});

Checkbox holder using setOnCheckedChangeListener

I'm trying to make a listview with checkboxes, I have tried a code and it worked correctly but I faced a problem when trying to handle the checkbox states. The problem appears in this line of code
holder.chkBox.setOnCheckedChangeListener((MyActivity) context);
because the code I have tried is using Adapter.java file separated from the MainActivity.java file, but in my code all are in one file so I don't know how to use this line in my code, I have tried this
holder.chkBox.setOnCheckedChangeListener((this) context);
but also a wrong statement
The reason of using this line of holder is that when I checked a checkbox and scroll down through the others items, the checked checkbox is automatically unchecked, Could anyone tell me how to fix this problem?
Here is my Adapter Code
#Override public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder=null;
View itemView = convertView;
if (itemView == null){
LayoutInflater inflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
itemView = inflater.inflate(R.layout.item, null);
holder = new ViewHolder();
holder.textView1 = (TextView) itemView.findViewById(R.id.textView1);
holder.check1 = (CheckBox) itemView.findViewById(R.id.check1);
holder.check2 = (CheckBox) itemView.findViewById(R.id.check2);
itemView.setTag(holder);
}
else{
holder = (ViewHolder) itemView.getTag();
}
holder.check1.setTag(position);
final Item item = items.get(position);
holder.textView1.setText(item.getName());
holder.check1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
if(buttonView.isPressed()){
if (position != ListView.INVALID_POSITION) {
Item p = items.get(position);
p.setSelected(isChecked);
}
}
}
}
);
holder.check2.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
if(buttonView.isPressed()){
if (position != ListView.INVALID_POSITION) {
Item p = items.get(position);
p.setSelected(isChecked);
}
}
}
}
);
holder.check1.setChecked(item.isSelected());
holder.check2.setChecked(item.isSelected());
return itemView;
}
}private static class ViewHolder {
public TextView textView1;
public CheckBox check2,check1;
}}
First, make your activity implement OnCheckedChangedListener interface, which forces to implement the following method :
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
}
Now pass the reference of your activity to the your adapter as an argument of type OnCheckedChangeListener and name it listener :
MyAdapter myadapter = new MyAdapter(context, MyActivity.this,listdata);
Then, in your adapter class, set the listener for your checkbox like :
holder.chkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
if(buttonView.isPressed()){
//Write the logic to determine if the checkbox must be checked or not
}
}
}
);
The point here is using the isPressed() method of the checkbox.
It looks like in the example code you worked with, your activity implemented OnCheckedChangeListener, so you needed to pass the context. In your Adapter you can do it as follows:
holder.chkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
// logic
}
}
);
In the code where you had it working, you probably had an overridden onCheckedChanged method in your activity. Move the logic of that where I have // logic above.

Categories

Resources