Search code examples
javaandroidlistviewandroid-listviewcustom-arrayadapter

How to make changes in a custom listview programmatically that should immediately reflect?


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.


Solution

  • 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.