【问题标题】:ListView with RadioButtons behaving buggy带有 RadioButtons 行为错误的 ListView
【发布时间】:2016-08-04 04:46:13
【问题描述】:

我有一个listview,正在向其中动态添加单选按钮。 但它的行为非常错误。 radio buttons 滚动后显示在不同的行中,有些正在消失等。我已经在此处发布了适配器。请就如何做到这一点提出宝贵的建议。

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;

    }
}

【问题讨论】:

  • getView 方法实在是太难理解了……我强烈建议做一些重构(也许你在做的时候自己发现了这个 bug)
  • 唯一的问题是我应该在哪里添加动态单选按钮到视图中?应该在 (convertView == null) 内部还是外部?

标签: android listview radio-button listview-adapter


【解决方案1】:

编辑:现在我正在更深入地解析您的代码,我认为您遇到了更大的问题,因为您正在动态地将单选组添加到列表项中。从列表项中动态添加/删除视图会让您头疼。

这就是我的意思:您的列表项的模型是currentRowDsObj,您在这里得到:

        final CheckTentativePlanNsaveListRowDs currentRowDsObj = 
                mAddSanctionListRowDS.get(position);

然后你的模型中有一些东西决定了你要添加多少个无线电组:

            float halfOfRadioCount =
                    (float) currentRowDsObj.getmRadioBtnData().size() / 2;

            int noOfRadioGroupsNeeded =
                     (int) Math.ceil(halfOfRadioCount);

假设您在位置 0 的列表项具有 mRadioBtnData.size() == 4。您将向列表项添加两个无线电组。

但是现在假设用户正在滚动并且他们到达位置 10。位置 0 已经滚出屏幕,所以它被回收了。位置 10 的列表项具有 mRadioBtnData.size() == 6,因此您应该有三个无线电组。位置 10 的 getView 接收位置 0 的回收视图。但是您的回收视图只有上次的两组!

您将不得不更加努力地思考如何实现这一目标。这里有几个选项:

  • 一个项目最多可以有多少个单选按钮?如果它只有 5 个,那么您可以有五种不同的项目视图类型。您可以为每个项目视图类型使用不同的布局。然后,您将覆盖 getItemViewType() 以根据您拥有的单选按钮的数量返回不同的类型值。其工作方式是,如果列表视图知道位置 0 和位置 10 都具有项目视图类型 == 2,那么当位置 10 调用 getView 时,它将得到回收从位置 0 或具有相同项目视图类型的另一个位置查看。所以你可以指望回收的视图是你想要的。

  • 我已经编写了代码,我在回收的视图组上调用了getChildCount(),如果它的视图比我需要的少,我添加了它们。如果它有比我需要的更多的视图,我删除了额外的视图。太丑了,不推荐。

另外,您能否通过使用GridLayoutFlowLayout 来简化您的逻辑,这样您就不必计算要添加多少行?

另一点是RadioGroupLinearLayout,因此它希望所有单选按钮位于单行或单列中。由于您有一个单选按钮网格,我认为您最终将不得不自己取消选择以前选择的按钮。当单选按钮本身是RecyclerView 中的列表项时,我也编写了执行此操作的代码。

希望其中的一些帮助。重新思考你的设计并再次尝试编写代码;如果您仍然需要帮助,请发布另一个问题。


这是由于视图回收而发生的。

您正在从事件处理程序中更改列表项视图,这将永远无法正常工作。

使用适配器,您总是需要从控制器(事件处理程序)更改模型并从模型(getView)更新视图。

据我所知,您似乎没有任何模拟单选按钮状态的数据结构,因此您需要将其添加到适配器中。使用该数据在getView 中设置视图的选中状态,然后更新来自onCheckedChangedListener 的数据并在适配器上调用notifyDataSetChanged

通常,列表项视图中可以更改的任何内容都必须具有某种对应的模型数据。

【讨论】:

  • 任何代码 sn-p 都会有很大帮助。建模是指向视图持有者添加单选按钮吗?
  • 通过建模我的意思是在适配器中有一个变量,如List&lt;Integer&gt;,其中整数是所选单选按钮的 id。但首先看看我更新的答案。
猜你喜欢
  • 1970-01-01
  • 2012-05-05
  • 2023-03-03
  • 2014-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多