I have a listview populated with checkedtextview using custom adapter,the problem that I'm having is that the when I for example check one check boxe another check box in the list view is checked, now I fairly understand the concept of list view and how views are recycled, so what is that I'm doing wrong here?
and another issue that I'm having, is that the position of the check boxes keeps changing when scrolling
I have tries multiple solutions and keep on trying, and any help to understand this issue is surely appreciated
this the checkedtextview xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<CheckedTextView
android:id="@+id/checkedTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:checked="false"
android:drawableLeft="?android:attr/listChoiceIndicatorMultiple"
android:gravity="center"
android:paddingBottom="@dimen/virtical_padding"
android:textAppearance="?android:textAppearanceLarge"
tools:text="Service 1" />
<Button
android:id="@+id/plus_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/check_box_list_margin"
android:layout_marginLeft="@dimen/check_box_list_margin"
android:focusable="false"
android:text="@string/plus"
android:textColor="#000"
android:visibility="invisible" />
<TextView
android:id="@+id/quantity_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:text="@string/quantity_default"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="#000"
android:visibility="invisible" />
<Button
android:id="@+id/minus_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:text="@string/minus"
android:textColor="#000"
android:visibility="invisible" />
</LinearLayout>
this is the code for the OnItemClickListener
mServiceListView.setOnItemClickListener( new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//find the checkedTextView inside the relative view
ctv = (CheckedTextView) view.findViewById( R.id.checkedTextView );
mServiceQuantity = (TextView) view.findViewById( R.id.quantity_tv );
mMinusBtn = ( Button) view.findViewById( R.id.minus_btn );
mPlusBtn = (Button) view.findViewById( R.id.plus_btn );
((CheckedTextView) ctv).toggle();
Service service = (Service) parent.getItemAtPosition(position);
if (ctv.isChecked()) {
selectedServices.add(service.getServiceNum());
//make the quantity option visible
mMinusBtn.setVisibility( View.VISIBLE );
mPlusBtn.setVisibility( View.VISIBLE );
mServiceQuantity.setVisibility( View.VISIBLE );
} else {
//remove the service and quantity from table
selectedServices.remove( service.getServiceNum() );
selectedServicesHash.remove( service.getServiceNum() );
//the quantity option will be invisible
mMinusBtn.setVisibility( View.INVISIBLE );
mPlusBtn.setVisibility( View.INVISIBLE );
mServiceQuantity.setVisibility( View.INVISIBLE );
}
}
} );
and this is the code for the custom adapter
package com.example.android.bookkeepingapp;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CheckedTextView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import org.w3c.dom.Text;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
public class ServiceAdapterCheckBox extends ArrayAdapter<Service> {
private Hashtable<String, Integer> selectedServicesHash;
private Set<String> serviceNums;
public ServiceAdapterCheckBox(@NonNull Context context, int resource, List<Service> objects
, Hashtable<String, Integer> selectedServiceHash) {
super( context, resource, objects );
this.selectedServicesHash = selectedServiceHash;
}
public void setSelectedServicesHash(Hashtable<String, Integer> selectedServices) {
this.selectedServicesHash = selectedServices;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
//Find the current object
Service service = getItem(position);
//put all the services name of the hashtable in a list
serviceNums= selectedServicesHash.keySet();
final ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(getContext()).inflate(R.layout.service_item_check_box,
parent, false);
holder.textCheckBox = (CheckedTextView) convertView.findViewById(R.id.checkedTextView);
holder.minBtn = (Button) convertView.findViewById( R.id.minus_btn );
holder.plusBtn = (Button) convertView.findViewById( R.id.plus_btn );
holder.quantityTv = (TextView) convertView.findViewById( R.id.quantity_tv );
convertView.setTag(holder);
}
else {
// the getTag returns the viewHolder object set as a tag to the view
holder = (ViewHolder)convertView.getTag();
}
//supply the textViews with the correct data
assert service != null;
holder.textCheckBox.setText( service.getServiceName() );
//set the quantity value set by the user
holder.plusBtn.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
//holder.plusBtn.setFocusable( true );
int quantityValue = Integer.parseInt(holder.quantityTv.getText().toString());
quantityValue++;
holder.quantityTv.setText( String.valueOf( quantityValue ) );
}
} );
holder.minBtn.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
//holder.plusBtn.setFocusable( true );
int quantityValue = Integer.parseInt(holder.quantityTv.getText().toString());
if(quantityValue == 1){
quantityValue = 1;
}
else {
quantityValue--;
}
holder.quantityTv.setText( String.valueOf( quantityValue ) );
}
} );
//if the there are services in the hash table
if(serviceNums.size() != 0) {
Log.v(this.getContext().toString(), "there are services selected");
//put all the services name of the hashtable in a list
for (String s : serviceNums) {
if (service.getServiceNum().equals( s )) {
//set the check text with the current position to true
//holder.textCheckBox.setChecked( true );
//set the quantity text box to the correct value set by the user
//holder.quantityTv.setText(selectedServicesHash.get(s));
//set the quantity selectors to visible
holder.plusBtn.setVisibility( View.VISIBLE );
holder.minBtn.setVisibility( View.VISIBLE );
holder.quantityTv.setVisibility( View.VISIBLE );
}
}
}
else{
Log.v(this.getContext().toString(), "no the list is empty");
//set the quantity text box to the correct value set by the user
holder.quantityTv.setText("1");
}
return convertView;
}
public Hashtable<String, Integer> getSelectedServicesHash() {
return selectedServicesHash;
}
/**
* customizable toast
*
* @param message
*/
private void toastMessage(String message) {
Toast.makeText( getContext(), message, Toast.LENGTH_SHORT ).show();
}
private class ViewHolder {
protected CheckedTextView textCheckBox;
protected Button plusBtn;
protected Button minBtn;
protected TextView quantityTv;
}
}
all I want is to get the correct checked box with the correct position Thanks...
Because views are recycled in a ListView, you have to update the view state to what it should be in your adapter code. Otherwise the views will just have the checked state of an old view that is being reused.
So along with updating the text on the view, you need to set its checked state.