Search code examples
javaandroidlistviewbuttonandroid-adapter

When clicking on Button on ListView item, the state of the last item changes


Hey so the goal is that when the button cancel of a list item is clicked, the state of the item must be shown as "Canceled" and the button goes black. However, when pressing on the button the button does go black but the list item doesn't change state. Instead, the last button of the view list changes state. Can you help me out?

Adapter class:

package com.example.myappfinal.Adapter;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.drawable.DrawableCompat;

import com.example.myappfinal.Logic.Appointment;
import com.example.myappfinal.R;

import java.util.ArrayList;


public class AppointmentListAdapter extends ArrayAdapter<Appointment> {

    private String state,date,time;
    private TextView stateT,dateT,timeT;
    private Context mContext;
    int mRecource;


    public AppointmentListAdapter(@NonNull Context context, int resource, @NonNull ArrayList<Appointment> objects) {
        super(context, resource, objects);
        mContext=context;
        mRecource=resource;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {


        state=getItem(position).getState();
        String service=getItem(position).getService().getName();
        date = getItem(position).getDate();
        time = getItem(position).getStartTime();

        LayoutInflater inflater = LayoutInflater.from(mContext);
        convertView = inflater.inflate(mRecource,parent,false);

        stateT = (TextView) convertView.findViewById(R.id.state);
        TextView serviceT = (TextView) convertView.findViewById(R.id.service);
        TextView dateT = (TextView) convertView.findViewById(R.id.date);
        TextView timeT = (TextView) convertView.findViewById(R.id.time);


        stateT.setText(state);
        serviceT.setText(service);
        dateT.setText(date);
        timeT.setText(time);

        Button button =(Button)convertView.findViewById(R.id.button);
        if(state.equals("Canceled")){
            Drawable buttonDrawable = button.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            DrawableCompat.setTint(buttonDrawable, Color.BLACK);
            button.setBackground(buttonDrawable);
        }

            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //do something
                    if(state.equals("Active")) {
                        getItem(position).cancel();
                        state = getItem(position).getState();
                        stateT.setText(state);

                        Drawable buttonDrawable = button.getBackground();
                        buttonDrawable = DrawableCompat.wrap(buttonDrawable);
                        DrawableCompat.setTint(buttonDrawable, Color.BLACK);
                        button.setBackground(buttonDrawable);
                    }

                }

            });

        return convertView;

    }


Solution

  • The problem is that stateT is a class member in the adapter that you override for each row's getView call, so after the list is populated it always refers to the last row. You should not save the views (e.g. stateT) as class members. Every call to getView will override the prior values held there.

    For your case you should probably do something like this (hard to say for sure without the full code)

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    
        // Don't re-inflate unless necessary
        if( convertView == null ) {
            LayoutInflater inflater = LayoutInflater.from(mContext);
            convertView = inflater.inflate(mRecource,parent,false);
        }
    
        // Get the state of the current row
        Appointment appt = getItem(position);
        String state = appt.getState();
        String service = appt.getService().getName();
        String date = appt.getDate();
        String time = appt.getStartTime();
            
        // These should NOT be class members
        TextView stateT = convertView.findViewById(R.id.state);
        TextView serviceT = convertView.findViewById(R.id.service);
        TextView dateT = convertView.findViewById(R.id.date);
        TextView timeT = convertView.findViewById(R.id.time);
    
        // Set the row's values
        stateT.setText(state);
        serviceT.setText(service);
        dateT.setText(date);
        timeT.setText(time);
    
        Button button = convertView.findViewById(R.id.button);
        
        if(state.equals("Canceled")){
            Drawable buttonDrawable = button.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            DrawableCompat.setTint(buttonDrawable, Color.BLACK);
            button.setBackground(buttonDrawable);
     
            // may also want to disable the button
            button.setEnabled(false);
        }
        else {
            button.setEnabled(true);
    
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // get the current row state - since it may have been
                    // updated since the listener was created
                    String rowState = appt.getState();
                    
                    if(rowState.equals("Active")) {
                        appt.cancel();
                        String newState = appt.getState();
    
                        // stateT here refers to just this row's TextView now
                        stateT.setText(newState);
    
                        Drawable buttonDrawable = button.getBackground();
                        buttonDrawable = DrawableCompat.wrap(buttonDrawable);
                        DrawableCompat.setTint(buttonDrawable, Color.BLACK);
                        button.setBackground(buttonDrawable);
    
                        // may also want to disable the button
                        button.setEnabled(false);
                    }
                }
            });
        }
    
        return convertView;
    }