android - 滚动出屏幕后如何从回收 View 中的 View 中获取值

标签 android android-recyclerview android-viewholder

我有一个包含 RecyclerView 的 fragment 。这个 RecyclerView 将包含几种不同类型的 View (EditText、Seekbar、CheckBox 等),它将作为一种表单工作。我还有一个执行保存的按钮,它应该转到 RecyclerView 的每一行并获取用户输入的值。

我还有一个适配器,它根据我想在 RecyclerView 上添加的 View 类型来扩展不同的布局。 在这个适配器中,我有一个“getValuesFromViews”方法,它根据 View 类型读取用户输入。

public String getValuesFromViews(int position,ViewHolder vHolder){
 //...
}

在我的 fragment 中,在 For 循环期间,在使用 findViewHolderForAdapterPosition 获取 ViewHolder 之后,我调用了此方法

我的问题是,当我滚动我的 RecyclerView 时,我丢失了屏幕上未显示的位置的 ViewHolder,因此 findViewHolderForAdapterPosition 返回 null。

如何从屏幕上未显示的 View 中读取值?

编辑 这是我目前的适配器(仍在开发中)。

public class ParamsAdapter extends RecyclerView.Adapter<ParamsAdapter.ViewHolder> {
    private final android.support.v4.app.FragmentManager fragmentManager;
    private final Fragment fragment;
    private Context context;
    private View view1;


    ParamsAdapter.ViewHolder viewHolder1;
    List<UserConfigurableParametersResponseMainItem1> listParams;

    public ParamsAdapter(Context context1, List<UserConfigurableParametersResponseMainItem1> list, android.support.v4.app.FragmentManager fmanager, Fragment frag){
        context = context1;
        listParams = list;
        fragmentManager = fmanager;
        fragment = frag;
    }

    public List<UserConfigurableParametersResponseMainItem1> getListParams() {
        return listParams;
    }

    public class ViewHolder extends RecyclerView.ViewHolder{

        @BindView(R.id.tv_param_title)
        TextViewMPOS tvParamTitle;
        @BindView(R.id.iv_question)
        ImageView ivQuestion;
        @Nullable @BindView(R.id.et_param)
        View param;
        @BindView(R.id.param_loader)
        AVLoadingIndicatorView loader;
        @BindView(R.id.iv_status)
        ImageView iv_status;
        @BindView(R.id.iv_error)
        ImageView iv_error;
        @BindView(R.id.tv_param_error)
        TextViewMPOS tvError;
        @BindView(R.id.iv_reset)
        ImageView ivReset;

        public ViewHolder(View v){
            super(v);
            ButterKnife.bind(this,v);

        }
    }

    @Override
    public int getItemViewType(int position) {
        return position;
    }

    @Override
    public ParamsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){

        view1 = createViews(parent,viewType);
        viewHolder1 = new ParamsAdapter.ViewHolder(view1);

        return viewHolder1;
    }

    private View createViews(ViewGroup parent, int viewType) {
        ConfigurableParameterWebControlCode op = listParams.get(viewType).getParamWbCntrlCd();
        switch (op){
            case NUMERO:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_num,parent,false);
            case DOUBLE:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_double,parent,false);
            case EMAIL:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_email,parent,false);
            case TEXTBOX:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_text,parent,false);
            case TEXAREA:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_textarea,parent,false);
            case SLIDER:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_slider,parent,false);
            case CHECKBOX:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_num,parent,false);
            case DROPDOWN:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_drop,parent,false);
            case RADIOBUTTON:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_num,parent,false);
            case DATA:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_data,parent,false);
            case DATAHORA:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_datahora,parent,false);
            case TEMPO:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_tempo,parent,false);
/*            case PHONE:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_phone,parent,false);*/
            case CHECKGROUP:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_checkgroup,parent,false);
        }

        return null;
    }

    @Override
    public void onBindViewHolder(final ParamsAdapter.ViewHolder holder, final int position){

        final int index = getLanguageIndex(position);
        holder.tvParamTitle.setText(listParams.get(position).getUsrCnfgrblPrmtrsRspnItms().get(index).getLablTxt());
        holder.ivQuestion.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(context != null)
                    MyDialogBuilder.createSingleButtonDialog(context,R.drawable.ic_warningcor,listParams.get(position).getUsrCnfgrblPrmtrsRspnItms().get(index).getHlprTxt(),R.string.ok,null,-1).show();
            }
        });
        holder.ivReset.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                clearView(holder,position);
            }
        });

        holder.iv_error.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(context != null)
                    MyDialogBuilder.createSingleButtonDialog(context,R.drawable.ic_errorcor,listParams.get(position).getUsrCnfgrblPrmtrsRspnItms().get(index).getErrTxt(),R.string.ok,null,-1).show();
            }
        });


        switch (listParams.get(position).getParamWbCntrlCd()){

            case NUMERO:
            case DOUBLE:
            case EMAIL:
            case TEXTBOX:
            case TEXAREA:
            case TELEFONE:
                EditTextMPOS editTextAux = (EditTextMPOS)holder.param;
                if (editTextAux != null) {
                    editTextAux.setText(listParams.get(position).getParamVl());
                }
                break;
            case SLIDER:
                AppCompatSeekBar seekbarAux = (AppCompatSeekBar)holder.param;
                seekbarAux.setMax(Integer.parseInt(listParams.get(position).getParamVldtnMax()));
                seekbarAux.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                    @Override
                    public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                        holder.tvParamTitle.setText(listParams.get(position).getUsrCnfgrblPrmtrsRspnItms().get(index).getLablTxt() +"   "+i);
                    }

                    @Override
                    public void onStartTrackingTouch(SeekBar seekBar) {

                    }

                    @Override
                    public void onStopTrackingTouch(SeekBar seekBar) {

                    }
                });
                break;
            case CHECKBOX:
                break;
            case DROPDOWN:
                break;
            case RADIOBUTTON:
                break;

            case DATA:
                if (holder.param != null) {
                    holder.param.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            MyDialogBuilder.DialogDateNoHoursPicker(context,fragmentManager,fragment, DatePickerFragment.DATE_END,"# ",0);
                        }
                    });
                }
                break;
            case DATAHORA:
                if (holder.param != null) {
                    holder.param.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            MyDialogBuilder.DialogDatePicker(context,fragmentManager,fragment, DatePickerFragment.DATE_END,"##",0);
                        }
                    });
                }
                break;
            case TEMPO:
                if (holder.param != null) {
                    holder.param.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            MyDialogBuilder.DialogTimePicker(context,fragmentManager,fragment, DatePickerFragment.DATE_END,"###",0);
                        }
                    });
                }
                break;
            case CHECKGROUP:
                LinearLayout linearLayout = (LinearLayout)holder.param;
                List<String> checkList = new ArrayList<String>(Arrays.asList(listParams.get(position).getParamVldtnRgExp().split(";")));
                for(int i=0;i<checkList.size();i++){
                    CheckBox checkBox = new CheckBox(context);
                    checkBox.setText(checkList.get(i));
                    LinearLayout.LayoutParams newParams = new LinearLayout.LayoutParams(
                            RelativeLayout.LayoutParams.WRAP_CONTENT,
                            RelativeLayout.LayoutParams.WRAP_CONTENT);
                        newParams.weight = 1;
                        checkBox.setLayoutParams(newParams);

                    linearLayout.addView(checkBox,i);
                }
                break;
        }
    }

    private void clearView(ViewHolder holder, int position) {
        switch (listParams.get(position).getParamWbCntrlCd()){
            case NUMERO:
            case DOUBLE:
            case EMAIL:
            case TEXTBOX:
            case TEXAREA:
            case TELEFONE:
                EditTextMPOS auxView = (EditTextMPOS)holder.param;
                if (auxView != null) {
                    auxView.setText("");
                    auxView.setHint(listParams.get(position).getDfltVl());
                }
                listParams.get(position).setUseDefault(true);
                break;
            case CHECKBOX:
                break;
            case DROPDOWN:
                break;
            case RADIOBUTTON:
                break;
            case DATA:
                if (holder.param != null) {
                    holder.param.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            MyDialogBuilder.DialogDateNoHoursPicker(context,fragmentManager,fragment, DatePickerFragment.DATE_END,"# ",0);
                        }
                    });
                }
                break;
            case DATAHORA:
                if (holder.param != null) {
                    holder.param.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            MyDialogBuilder.DialogDatePicker(context,fragmentManager,fragment, DatePickerFragment.DATE_END,"##",0);
                        }
                    });
                }
                break;
            case TEMPO:
                if (holder.param != null) {
                    holder.param.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            MyDialogBuilder.DialogTimePicker(context,fragmentManager,fragment, DatePickerFragment.DATE_END,"###",0);
                        }
                    });
                }
                break;
            case CHECKGROUP:
                break;
        }
    }

    private int getLanguageIndex(int position) {
        int index = 0;
        String lang = HelperSharedPreferences.getSharedPreferencesString(context,HelperSharedPreferences.LANG,"pt-PT");
        for (int i=0; i<listParams.get(position).getUsrCnfgrblPrmtrsRspnItms().size();i++) {
            if(listParams.get(position).getUsrCnfgrblPrmtrsRspnItms().get(i).getLangCd().equals(lang)) {
                index = i;
                break;
            }
        }
        return index;
    }

    public String getValuesFromViews(int position,ViewHolder vHolder){
        String result = new String();
        if(listParams.get(position).isUseDefault()) {
            result = null;
        }
        else {
            switch (listParams.get(position).getParamWbCntrlCd()) {
                case NUMERO:
                case DOUBLE:
                case EMAIL:
                case TEXTBOX:
                case TEXAREA:
                case TELEFONE:
                    EditTextMPOS auxView = (EditTextMPOS) vHolder.param;
                    if (auxView != null) {
                        result = auxView.getText().toString();
                    }
                    break;
                case CHECKBOX:

                    break;
                case CHECKGROUP:
                    LinearLayout linearLayout = (LinearLayout)vHolder.param;
                    for(int i = 0;i<linearLayout.getChildCount();i++){
                        if(((CheckBox)linearLayout.getChildAt(i)).isChecked()){
                            result += ((CheckBox)linearLayout.getChildAt(i)).getText() + ";";
                        }
                    }
                    break;

            }
        }
        Logging.log("RESULT ",result);
        return result;
    }


    public void setStatus(final int position, ViewHolder vHolder,int status){
        if (status == 1) {
            vHolder.iv_status.setVisibility(View.VISIBLE);
            vHolder.iv_error.setVisibility(View.INVISIBLE);
            vHolder.param.setBackgroundResource(R.drawable.edit_text_background);
        } else if (status == 2) {
            vHolder.iv_status.setVisibility(View.INVISIBLE);
            vHolder.iv_error.setVisibility(View.VISIBLE);
            vHolder.param.setBackgroundResource(R.drawable.edit_text_error_background);
        } else {
            vHolder.iv_status.setVisibility(View.INVISIBLE);
            vHolder.iv_error.setVisibility(View.INVISIBLE);
            if (vHolder.param != null) {
                vHolder.param.setBackgroundResource(R.drawable.edit_text_background);
            }
        }
    }


    @Override
    public int getItemCount(){
        if(listParams != null)
            return listParams.size();
        else
            return 0;
    }

    public void update (List<UserConfigurableParametersResponseMainItem1> list){
        this.listParams.clear();
        this.listParams = list;
        setDataChanged();
    }

    public void setDataChanged(){
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                notifyDataSetChanged();
            }
        });
    }
}

我要创建它的数据是这样的数组

    {

    "ParamCd":"xxxxxxx",
    "ParamVl":"xxxxxxx",
    "DfltVl":" ",
    "ParamVldtnRgExp":".*",
    "ParamWbCntrlCd":"EMAIL",   
    "ParamOrdr":54,
    "UsrCnfgrblPrmtrsRspnItms":[
        {
            "LangCd":"pt-PT",
            "LablTxt":"xxxxxxx",
            "HlprTxt":"xxxxxxxxxxxxxx",
            "ErrTxt":"xxxxxxxxxxxxxxxxxxxxx"
        },
        {
            "LangCd":"en-UK",
            "LablTxt":"xxxxxxx",
            "HlprTxt":"xxxxxxxxxxxxxx",
            "ErrTxt":"xxxxxxxxxxxxxxxxxxxxx"
        }
    ]
}

字段“ParamWbCntrlCd”定义出现在 RecyclerView 行中的 View 类型

最佳答案

在 Android 中,适配器用于创建表示特定数据集的 View 。您可以将适配器视为数据显示 之间的一种桥梁。

因此,ViewHolder 是您的数据在给定位置的表示。由于 View 通常比纯数据占用更多内存,RecyclerView滚动时重复使用它们。这就是为什么您无法检索与滚出屏幕的 View 关联的数据的原因:那些 ViewHolder 已被回收,并且它们的 View 之前的状态已经丢失。

为了解决这个问题,我建议您执行以下步骤:

  1. 创建 SparseArray<UserConfigurableParametersResponseMainItem1>作为您的适配器的一个字段。这SparseArray将包含已被用户修改的模型对象的副本。
  2. 为其内容可以修改的 ViewHolders 中的每个 View 配置监听器。

对于每个有趣的变化,请调用 holder.getAdapterPosition()检索此 ViewHolder 在数据集中的当前位置。使用那个 position检查 SparseArray 中的这个位置是否存在对象.如果不存在,请在 listParams 的相同位置创建原始数据的副本。 .然后设置此对象的属性以反射(reflect)用户所做的更改。

想法是在适配器中维护用户所做修改的“缓存”。这样,用户所做的修改就不会再丢失。

  1. onBindViewHolder , 从给定的 position 中检索数据在稀疏数组中。如果是null , 则没有发出任何修改,您可以绑定(bind)来自 listParams 的数据.否则,用户已经进行了修改,您必须绑定(bind)那些而不是原来的。

  2. 创建方法 UserConfigurableParametersResponseMainItem1 getModificationsForPosition(int position) .此方法直接映射到 SparseArray 中包含的数据.这样,调用Fragment如果返回值不是 null,则直接知道会发生修改.

  3. 单击“保存”按钮时,使用 for 循环调用 getModificationsForPosition(i)对于适配器的每个有效位置,检索所有修改过的对象,然后将它们保存在任何你想要的地方(SQLite 或远程服务器)。

  4. 根据保存的更改刷新适配器的数据集。例如,您可以替换 listParams 中的数据与稀疏数组中的那些。单击“保存”按钮时,不要忘记清除稀疏数组。

该结构具有以下优点:

  • 即使在 ViewHolder 被回收后,对 View 的修改也会保留。
  • 可以为每个位置单独丢弃修改。
  • 只保存修改过的数据,效率更高。

希望对您有所帮助!

关于android - 滚动出屏幕后如何从回收 View 中的 View 中获取值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47979837/

相关文章:

java - 如何停止返回登录页面的导航

java - Android Listview 无法正常工作?

java - Nosql模型结构

android - recyclerView.addOnScrollListener - "retrofit pagination with MVVM"正在加载相同的响应/列表

android-recyclerview - 如何使用来自 RecyclerView 元素的点击的导航组件?

android - 带有自定义 ArrayAdapter(和 getView 回收)的 ListViewText 中用于 EditText 的 TextWatcher

java - 如何在我的应用程序中使用-assumenosideeffects class android.util.Log

android - Recycler view with volley 图像请求(取消请求)

java - 无法获取从 BroadcastReceiver 发送的 Intent

java - Android:ListView 中每行的动态复选框数