Reputation: 3403
I have a listview
to which radio buttons are being added dynamically.
But it behaves very buggy. The radio buttons
are getting displayed in different rows after scrolling, some are disappearing etc. I have posted the adapter here. Please give your valuable suggestions on how to do this.
public class CheckTentativePlanNSaveProductLoanEmiListAdapter extends ArrayAdapter<AddSanctionListRowDS> {
private final Activity mContext;
private final int mResource;
private final ArrayList<CheckTentativePlanNsaveListRowDs> mAddSanctionListRowDS;
private EditText mAddSanctionRowValueET;
private Calendar mMyCalendar;
private ArrayList<String> mProductDetailsScreenEnteredDetails;
public CheckTentativePlanNSaveProductLoanEmiListAdapter(Activity context, int resource, ArrayList<CheckTentativePlanNsaveListRowDs> iAddSanctionListRowObj, ArrayList<String> iAddSanctionEnteredValues)
{
super(context, resource);
this.mContext = context;
this.mResource = resource;
this.mAddSanctionListRowDS = iAddSanctionListRowObj;
//This depicts the values when entered or changed...
this.mProductDetailsScreenEnteredDetails = iAddSanctionEnteredValues;
}
@Override
public int getCount() {
return mAddSanctionListRowDS.size();
}
public View getView(final int position,View convertView,ViewGroup parent) {
SaveProductListViewHolder saveProductListViewHolderObj;
//View is creating...
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(mResource, parent, false);
saveProductListViewHolderObj = new SaveProductListViewHolder();
saveProductListViewHolderObj.key_tv = (TextView) convertView.findViewById(R.id.id_check_tentative_plan_n_save_product_non_loan_row_key_tv);
saveProductListViewHolderObj.value_et = (EditText) convertView.findViewById(R.id.id_check_tentative_plan_n_save_product_non_loan_value_et);
LinearLayout etParentLl = (LinearLayout) convertView.findViewById(R.id.id_check_tentative_plan_n_save_product_et_non_loan_rb_ll);
saveProductListViewHolderObj.et_parent_ll = etParentLl;
RadioGroup.OnCheckedChangeListener onCheckedChangeListener = new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
for (int i = 0; i < ((LinearLayout) group.getParent()).getChildCount(); i++) {
RadioGroup sisterRadioGroup = (RadioGroup) ((LinearLayout) group.getParent()).getChildAt(i);
if (group != sisterRadioGroup) {
sisterRadioGroup.setOnCheckedChangeListener(null);
sisterRadioGroup.clearCheck();
sisterRadioGroup.setOnCheckedChangeListener(this);
}
}
}
};
final CheckTentativePlanNsaveListRowDs currentRowDsObj = mAddSanctionListRowDS.get(position);
if (currentRowDsObj.getmRadioBtnData().size() > 0) {
//Set layout width = fill parent
//Layout height = wrap content
//Orientation will be vertical
LinearLayout radioGrpParentLl = new LinearLayout(mContext);
LinearLayout.LayoutParams paramsRadioGrpParentLl = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
radioGrpParentLl.setLayoutParams(paramsRadioGrpParentLl);
radioGrpParentLl.setOrientation(LinearLayout.VERTICAL);
float halfOfRadioCount = (float) currentRowDsObj.getmRadioBtnData().size() / 2;
int noOfRadioGroupsNeeded = (int) Math.ceil(halfOfRadioCount);
for (int i = 0; i < noOfRadioGroupsNeeded; i++) {
final RadioGroup radioGroup = new RadioGroup(mContext);
RadioGroup.LayoutParams paramsRadio = new RadioGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
radioGroup.setOrientation(RadioGroup.HORIZONTAL);
radioGroup.setLayoutParams(paramsRadio);
//Add 2 Radio Buttons...
//Left Radio Button...
RadioButton leftRadioButtonInCurrentRadioGroup = new RadioButton(mContext);
RadioGroup.LayoutParams paramsLeftRadioBtnInCurrentRadioGroup = new RadioGroup.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT);
leftRadioButtonInCurrentRadioGroup.setLayoutParams(paramsLeftRadioBtnInCurrentRadioGroup);
paramsLeftRadioBtnInCurrentRadioGroup.weight = 1f;
leftRadioButtonInCurrentRadioGroup.setTypeface(Typeface.DEFAULT_BOLD);
leftRadioButtonInCurrentRadioGroup.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
//If checked, set the value...
int currentIndex = 0;
for (int i = 0; i < position; i++) {
if (currentRowDsObj.ismIsInputTypeNumeric() || currentRowDsObj.ismIsInputTypeText() || currentRowDsObj.ismIsDatePickerToBeShown()) {
currentIndex++;
}
if (currentRowDsObj.getmRadioBtnData().size() != 0) {
//A space allocated for unit...
currentIndex++;
}
}
if (currentRowDsObj.ismIsInputTypeText() || currentRowDsObj.ismIsInputTypeNumeric() || currentRowDsObj.ismIsDatePickerToBeShown()) {
//If current position has an allocation for entering value..
//Increment a space for that as well..
currentIndex++;
}
mProductDetailsScreenEnteredDetails.remove(currentIndex);
mProductDetailsScreenEnteredDetails.add(currentIndex, buttonView.getText().toString());
} else {
//Do nothing..
}
}
});
String leftRadionBtnLabel = currentRowDsObj.getmRadioBtnData().get(2 * i).getLabel();
leftRadioButtonInCurrentRadioGroup.setText(leftRadionBtnLabel);
radioGroup.addView(leftRadioButtonInCurrentRadioGroup);
if (((2 * i) + 1) < currentRowDsObj.getmRadioBtnData().size()) {
//At the end there is a chance that only one radio button has to be shown..
//The above condition avoids trying to add..
//Right Radio Button...
RadioButton rightRadioButtonInCurrentRadioGroup = new RadioButton(mContext);
RadioGroup.LayoutParams paramsRightRadioBtnInCurrentRadioGroup = new RadioGroup.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT);
rightRadioButtonInCurrentRadioGroup.setLayoutParams(paramsLeftRadioBtnInCurrentRadioGroup);
paramsRightRadioBtnInCurrentRadioGroup.weight = 1f;
rightRadioButtonInCurrentRadioGroup.setTypeface(Typeface.DEFAULT_BOLD);
String rightRadioBtnLabel = currentRowDsObj.getmRadioBtnData().get((2 * i) + 1).getLabel();
rightRadioButtonInCurrentRadioGroup.setText(rightRadioBtnLabel);
rightRadioButtonInCurrentRadioGroup.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
//If checked, set the value...
int currentIndex = 0;
for (int i = 0; i < position; i++) {
if (currentRowDsObj.ismIsInputTypeNumeric() || currentRowDsObj.ismIsInputTypeText() || currentRowDsObj.ismIsDatePickerToBeShown()) {
currentIndex++;
}
if (currentRowDsObj.getmRadioBtnData().size() != 0) {
//A space allocated for unit...
currentIndex++;
}
}
if (currentRowDsObj.ismIsInputTypeText() || currentRowDsObj.ismIsInputTypeNumeric() || currentRowDsObj.ismIsDatePickerToBeShown()) {
//If current position has an allocation for entering value..
//Increment a space for that as well..
currentIndex++;
}
mProductDetailsScreenEnteredDetails.remove(currentIndex);
mProductDetailsScreenEnteredDetails.add(currentIndex, buttonView.getText().toString());
} else {
//Do nothing..
}
}
});
radioGroup.addView(rightRadioButtonInCurrentRadioGroup);
radioGroup.setOnCheckedChangeListener(onCheckedChangeListener);
}
radioGrpParentLl.addView(radioGroup);
}
//Add RadioButton Group to the Parent Linear Layout..
saveProductListViewHolderObj.et_parent_ll.addView(radioGrpParentLl);
}
convertView.setTag(saveProductListViewHolderObj);
}else{
saveProductListViewHolderObj = (SaveProductListViewHolder)convertView.getTag();
}
final CheckTentativePlanNsaveListRowDs currentRowDsObj = mAddSanctionListRowDS.get(position);
saveProductListViewHolderObj.key_tv.setText(currentRowDsObj.getmEnterDetailsKey());
saveProductListViewHolderObj.value_et.setHint(currentRowDsObj.getmEnterDetailsValue());
saveProductListViewHolderObj.value_et.setFocusable(true);
//Based on the input type...
//Set the text change listener...
if(currentRowDsObj.ismIsInputTypeNumeric()){
saveProductListViewHolderObj.value_et.setInputType(InputType.TYPE_CLASS_NUMBER);
saveProductListViewHolderObj.value_et.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
int currentIndex = 0;
for(int i=0;i<position;i++){
if(currentRowDsObj.ismIsInputTypeNumeric()||currentRowDsObj.ismIsInputTypeText()||currentRowDsObj.ismIsDatePickerToBeShown()) {
currentIndex++;
}
if(currentRowDsObj.getmRadioBtnData().size()!=0) {
//A space allocated for unit...
currentIndex++;
}
}
mProductDetailsScreenEnteredDetails.remove(currentIndex);
mProductDetailsScreenEnteredDetails.add(currentIndex,s.toString());
}
});
}else if(currentRowDsObj.ismIsInputTypeText()){
saveProductListViewHolderObj.value_et.setInputType(InputType.TYPE_CLASS_TEXT);
saveProductListViewHolderObj.value_et.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
int currentIndex = 0;
for(int i=0;i<position;i++){
if(currentRowDsObj.ismIsInputTypeNumeric()||currentRowDsObj.ismIsInputTypeText()||currentRowDsObj.ismIsDatePickerToBeShown()) {
currentIndex++;
}
if(currentRowDsObj.getmRadioBtnData().size()!=0){
//A space allocated for unit...
currentIndex++;
}
}
mProductDetailsScreenEnteredDetails.remove(currentIndex);
mProductDetailsScreenEnteredDetails.add(currentIndex,s.toString());
}
});
}else if(currentRowDsObj.ismIsDatePickerToBeShown()){
//Here whether number or text...
//Doesnot matter...
saveProductListViewHolderObj.value_et.setInputType(InputType.TYPE_CLASS_NUMBER);
saveProductListViewHolderObj.value_et.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
//Calculate position dynamically here...
int currentIndex = 0;
for(int i=0;i<position;i++){
if(currentRowDsObj.ismIsInputTypeNumeric()||currentRowDsObj.ismIsInputTypeText()||currentRowDsObj.ismIsDatePickerToBeShown()) {
currentIndex++;
}
if(currentRowDsObj.getmRadioBtnData().size()!=0){
//A space allocated for unit...
currentIndex++;
}
}
mProductDetailsScreenEnteredDetails.remove(currentIndex);
mProductDetailsScreenEnteredDetails.add(currentIndex,s.toString());
}
});
}else{
saveProductListViewHolderObj.value_et.setVisibility(View.GONE);
}
return convertView;
}
static class SaveProductListViewHolder {
public TextView key_tv;
public EditText value_et;
public LinearLayout et_parent_ll;
}
}
Upvotes: 0
Views: 84
Reputation: 31015
EDIT: Now that I am parsing your code a little more deeply, I think you have an even bigger problem because you are dynamically adding radio groups to your list item. Dynamically adding/removing views from a list item is going to give you a headache.
Here's what I mean: The model for your list item is currentRowDsObj
which you are getting here:
final CheckTentativePlanNsaveListRowDs currentRowDsObj =
mAddSanctionListRowDS.get(position);
Then you have something in your model that determines how many radio groups you are going to add:
float halfOfRadioCount =
(float) currentRowDsObj.getmRadioBtnData().size() / 2;
int noOfRadioGroupsNeeded =
(int) Math.ceil(halfOfRadioCount);
So let's say your list item at position 0 has mRadioBtnData.size() == 4. You are going to add two radio groups to the list item.
But now suppose the user is scrolling and they reach position 10. Position 0 has scrolled off the screen so it is recycled. The list item at position 10 has mRadioBtnData.size() == 6 so you should have three radio groups. getView
for position 10 receives the recycled view from position 0. But your recycled view only has two groups from last time!
You are going to have to think a lot harder about how to achieve this. Here are a couple options:
What's the maximum number of radio buttons an item might have? If it's only, say, 5, you could have five different item view types. You could have a different layout to inflate for each item view type. Then you would override getItemViewType()
to return a different type value based on the number of radio buttons you have. The way this works is that if the list view knows that both position 0 and position 10 have an item view type == 2, then when getView
is called for position 10 it will only get a recycled view from position 0 or another position with the same item view type. So you can count on the recycled view being just the way you want.
I've written code where I've called getChildCount()
on the recycled view group and if it had fewer views than I needed I added them. If it had more views than I needed I removed the extra ones. That's pretty ugly and I wouldn't recommend it.
Also, can you simplify your logic by using a GridLayout
or a FlowLayout
so that you don't have to count how many rows to add?
Another point is that RadioGroup
is a LinearLayout
so that it wants all your radio buttons to be in a single row or column. Since you have a grid of radio buttons, I think you will end up having to do your own deselecting of previously selected buttons. I've written code that does this too, when the radio buttons themselves were list items inside a RecyclerView
.
Hopefully some of that helped. Rethink your design and take another swing at the code; if you still need help, post another question.
This is happening because of view recycling.
You are changing the list item view from within the event handler, and that will never work.
With an adapter, you always need to change the Model from the Controller (event handler) and update the View from the Model (getView).
As far as I can tell, you don't seem to have any data structure that models the radio button state, so you need to add that to the adapter. Use that data to set the checked state of the view in getView
, then update the data from the onCheckedChangedListener
and call notifyDataSetChanged
on the adapter.
As a rule, anything inside a list item view that can change has to have some kind of corresponding model data.
Upvotes: 1