1

I am trying to put a button in a custom listview that should reset all the values of the widgets in that row (eg: unchecking all the radiobuttons). I have used something like this inside getView()

btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick() {
        editor.putBoolean("rb1"+position,false).apply();
        editor.putBoolean("rb2"+position,false).apply();
        rb1.setChecked(false);
        rb2.setChecked(false);
        et.setText("");
        notifyDataSetChanged();
    }
}

But the changes are not reflected immediately. When I click on the button, the radio buttons are not unchecked. Rather, the radiobutton in another row gets unchecked sometimes. But when I scroll down and again come back to that place, I see the change. Can anyone say how to solve this problem?

Note: Inside the getView() method, I have also mentioned that the radio buttons should be checked or unchecked based on the SharedPreferences values.

EDIT

I am writing the full code.

ArrayAdapter class

import android.app.Activity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;

public class MyListAdapter extends ArrayAdapter<String> {
    private final Activity activity;
    View rowView;
    TextView tv;
    RadioGroup rg;
    RadioButton rb1, rb2;
    EditText etQty;
    ImageView imageView;
    public MyListAdapter(Activity activity, String[] a) {
        super(activity, R.layout.activity_row, a);
        this.activity = activity;
    }
    public View getView(final int position, final View convertView, ViewGroup parent) {
        LayoutInflater inflater = activity.getLayoutInflater();
        if(convertView == null)
            rowView = inflater.inflate(R.layout.activity_row, parent, false);
        else
            rowView = convertView;
        tv = rowView.findViewById(R.id.textView);
        rg = rowView.findViewById(R.id.radioGroup);
        rb1 = rowView.findViewById(R.id.rbFB);
        rb2 = rowView.findViewById(R.id.rbCB);
        etQty = rowView.findViewById(R.id.etQty);
        imageView = rowView.findViewById(R.id.imageView);
        imageView.setClickable(true);
        rb1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                notifyDataSetChanged();
                Month.editor.putInt(Worker.name + Open.year + Month.month + (position+1) + "item",1).apply();
            }
        });
        rb2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                notifyDataSetChanged();
                Month.editor.putInt(Worker.name + Open.year + Month.month + (position+1) + "item",2).apply();
            }
        });
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Month.editor.putInt(Worker.name + Open.year + Month.month + (position+1) + "item",0).apply();
                Month.editor.putInt(Worker.name + Open.year + Month.month + (position+1) + "dz",0).apply();
                rb1.setChecked(false);
                rb2.setChecked(false);
                etQty.setText("");
                notifyDataSetChanged();
            }
        });
        etQty.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                if (!charSequence.toString().equals("")) {
                    Month.editor.putInt(Worker.name + Open.year + Month.month + (position + 1) + "dz", Integer.parseInt(charSequence.toString())).apply();
                }
            }

            @Override
            public void afterTextChanged(Editable editable) {

            }
        });
        tv.setText((position+1)+"/"+Month.month+"/"+"20"+Open.year);
        if (Month.settings.getInt(Worker.name + Open.year + Month.month + (position+1) + "item",0) == 1) {
            rb1.setChecked(true);
            notifyDataSetChanged();
        }
        else if (Month.settings.getInt(Worker.name + Open.year + Month.month + (position+1) + "item",0) == 2) {
            rb2.setChecked(true);
            notifyDataSetChanged();
        }
        if (Month.settings.getInt(Worker.name + Open.year + Month.month + (position+1) + "dz",0) != 0) {
            etQty.setText(Integer.toString(Month.settings.getInt(Worker.name + Open.year + Month.month + (position + 1) + "dz", 0)));
            notifyDataSetChanged();
        }
        return rowView;
    }
    @Override
    public int getViewTypeCount() {
        return getCount();
    }
    @Override
    public int getItemViewType(int position) {
        return position;
    }
    @Override
    public String getItem(int position) {
        return getItem(position);
    }
}

The MainActivity

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.ListView;

public class MainActivity extends AppCompatActivity {
    ListView listView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String[] a;
        if (Month.month == 1 || Month.month == 3 || Month.month == 5 || Month.month == 7 || Month.month == 8 || Month.month == 10 || Month.month == 12)
            a = new String[] {"a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a"};
        else if (Month.month == 4 || Month.month == 6 || Month.month == 9 || Month.month == 11)
            a = new String[] {"a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a"};
        else {
            if (Open.year % 4 == 0) {
                a = new String[] {"a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a"};
            }
            else {
                a = new String[] {"a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a"};
            }
        }
        MyListAdapter adapter = new MyListAdapter(this,a);
        listView = findViewById(R.id.listView);
        listView.setAdapter(adapter);
    }
}

And the Layout that is being inflated in the ListView

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="40dp"
        android:layout_height="40dp"
        app:layout_constraintBottom_toBottomOf="@+id/etQty"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.348"
        app:layout_constraintStart_toEndOf="@+id/etQty"
        app:layout_constraintTop_toTopOf="@+id/etQty"
        app:layout_constraintVertical_bias="0.0"
        app:srcCompat="@drawable/delete" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="110dp"
        android:layout_height="40dp"
        android:textSize="20sp"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.053"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.46" />

    <RadioGroup
        android:id="@+id/radioGroup"
        android:layout_width="130dp"
        android:layout_height="40dp"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="@+id/textView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.074"
        app:layout_constraintStart_toEndOf="@+id/textView"
        app:layout_constraintTop_toTopOf="@+id/textView"
        app:layout_constraintVertical_bias="0.0">

        <RadioButton
            android:id="@+id/rbFB"
            android:layout_width="60dp"
            android:layout_height="40dp"
            android:text="FB"
            android:textSize="20sp" />

        <RadioButton
            android:id="@+id/rbCB"
            android:layout_width="60dp"
            android:layout_height="40dp"
            android:layout_marginStart="10dp"
            android:text="CB"
            android:textSize="20sp" />
    </RadioGroup>

    <EditText
        android:id="@+id/etQty"
        android:layout_width="50dp"
        android:layout_height="40dp"
        android:ems="10"
        android:inputType="phone"
        android:textSize="20sp"
        android:textAlignment="center"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="@+id/radioGroup"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.118"
        app:layout_constraintStart_toEndOf="@+id/radioGroup"
        app:layout_constraintTop_toTopOf="@+id/radioGroup"
        app:layout_constraintVertical_bias="0.0" />


</androidx.constraintlayout.widget.ConstraintLayout>

The purpose of the ImageView is to uncheck all the radio buttons and set the text in the EditText to " " when clicked.

  • 3
    Could you please provide your adapter? The context on where you get your position and your radio buttons from are relevant here. Also, radio buttons are a separate widget. They will update without notifying the adapter. the adapter only needs to be notified if something in the dataset has changed which is not the case for the radio buttons, since you dont change their value in the dataset but rather directly set them unchecked. – Basti Oct 26 '20 at 18:47
  • 1
    I have written the full code. Please help me if possible. @Basti –  Oct 27 '20 at 17:52
  • please check this question @ShaistaNaaz – Samudra Ganguly Oct 27 '20 at 22:00

1 Answers1

0

You have a general problem with your View-Management.
getView(...) is called whenever the list needs a view to display the next item. This means, that all Views used within this method should only be accessed within this method. But you store them in class members. This is which causes the behavior that the view of one item causes another item to change. Because it was initialized when item1 was loaded, but in its onClick(...) callback the class member variables reference item2 because getView() was called multiple times before the click event was triggered. You have to work inside the scope of getView(...) if you want the Views that you work with to belong all to the same layout.

Another thing i noticed which surely causes errors is your implementation of some of the Adapters methods. Specifically

@Override
public int getViewTypeCount() {
    return getCount();
}
@Override
public int getItemViewType(int position) {
    return position;
}
@Override
public String getItem(int position) {
    return getItem(position);
}

Read up on their documentation, then implement them accordingly.
getItemViewType() surely isn't supposed to return a position.
getViewTypeCount() is supposed to return the count of different view types, not the count of items which getCount() returns.
getItem() is overriden and calls it self. So getItem() would run into an infinite loop until the Thread would crash due to a stack overflow.
These are all implemented wrongly. I suggest looking up the documentation of ArrayAdapter and maybe also some exmaple implementations. Then redo your adapter properly and all your issues will vanish as well.

Note that notifyDataSet() is supposed to inform the adapter about changes in the datamodel. So only call it if you actually changed data. Changing the checked-state of a RadioButton is not manipulating data!
You should rather set the flag in your datamodel to true/false, then notify the adapter about the change. This will cause getView(...) to be called where you then set the RadioButton enabled/disabled based on your datamodel.

Basti
  • 1,117
  • 12
  • 32
  • But everything else seems to be working fine. The only problem is that the states of the other widgets do not change when I click on the ImageView. But it changes properly when the ListView is scrolled down and back up. Is there any way to maually reset the listview when the ImageView is clicked? –  Oct 29 '20 at 05:39
  • The notify-methods of the adapter do exactly that. They tell the adapter that data has been changed. It then will rebuild all rows of the ListView that are mapped to the changed data (notifyDataSet tells the adapter to refresh all). Your problems result from a wrong implementation of the adapter. This is what causes the wrong behavior as i explained in my answer. Implement your adapter correctly and your issues will be fixed. – Basti Oct 29 '20 at 15:21
  • Making the variables local did the trick. Thanks buddy @Basti –  Oct 31 '20 at 07:09
  • @SusantaGanguly you are welcome. I still want to point out tho, that you might still run into more issues if you don't also fix the other issues in your implementation. If you are lucky it works without fixing them because you don't produce a case in which it would cause problems. But you should always aim for a clean implementation rather than one that only works for your scenario. – Basti Oct 31 '20 at 09:41