【问题标题】:OnClick in one RecyclerView item affects other items一个 RecyclerView 项目中的 OnClick 会影响其他项目
【发布时间】:2019-02-16 13:57:13
【问题描述】:

编辑#1:通过调试,我发现错误“消失”了。基本上我设置了一个断点,然后慢慢地检查每个multiChoiceItem 和其他RecyclerView 子项的高度没有改变。这是否意味着这是一个绘图/计时相关的问题?

编辑#2:另外,一个新的发现,如果我改变Child: 6的高度,它会改变Child: 3和Child: 0

对于这个冗长的问题,我深表歉意。我已经检查了有关同一问题的其他答案,但没有一个适用。我试过自己解决这个问题,但不能,所以我希望得到一些帮助。如果有什么我可以做的让这篇文章更容易阅读,请告诉我,我会马上做的!

按照我的代码编写方式,这在技术上应该是不可能发生的,但它就是这样。


问题:我在RecyclerView 项目中有一个TextView 的onClickListener()。 onClickListener() 在RecyclerAdapter 的容器类中调用multiChoiceItem AlertDialog,然后在完成后调用notifyDataSet(),最后使用addOnLayoutChangeListener() 测量新RecyclerView 绘制后的高度。

通知数据集结束然后导致RecyclerView 项目中的TextView 更改为显示每个选中 项目的文本。然后在addOnLayoutChangeListener() 中测量此高度并发送到ViewModel,ViewModel 测量三个片段的相同位置项目的高度并将项目高度设置为最大高度,以便它们看起来都具有相同的高度。

令人困惑的部分:此问题仅发生在三个片段之一,并且其他受影响的项目高度与其他两个片段不匹配。这告诉我这是本地化到一个片段(有自己的类)


守则: 代码很长,所以我将其简化为我认为重要的部分

ViewHolder

class TextViewViewHolder extends RecyclerView.ViewHolder {

    TextView vhTVTextView;
    TextView vhTVMainTextView;
    CardView vhTVCardView;
    TextViewClickedListener vhTextViewClickedListener;

    // Gets current position from 'onBindViewHolder'
    int vhPosition = 0;

    public TextViewViewHolder(View itemView, TextViewClickedListener textViewClickedListener) {
        super(itemView);

        this.vhTextViewClickedListener = textViewClickedListener;

        this.vhTVCardView = itemView.findViewById(R.id.thoughtCard);
        this.vhTVTextView = itemView.findViewById(R.id.thoughtNumber);
        this.vhTVMainTextView = itemView.findViewById(R.id.textEntry);

        /*
            When the main TextView is clicked, it calls a function in the container
            'FragTextView' which pops up an AlertDialog. It was chosen to do it in the
            container instead of here because the Adapter is so adapt the lists data to the view
            and the container is what dictates what the lists data actually is.
         */
        vhTVMainTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(vhTextViewClickedListener != null) {
                    vhTextViewClickedListener.onTextViewClicked(vhPosition);
                }
            }
        });
    }
}

onBindViewHolder

@Override
public int getItemViewType(int position) {
    /*
        If mThoughtEntries is not null, then that means we can find the ViewType we are working
        with inside of it. Otherwise, we are mDistortions and we must be working on TYPE_TEXTVIEW
     */
    if(mThoughtEntries != null) return mThoughtEntries.get(position).getViewType();
    else return Constants.TYPE_TEXTVIEW;
}

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

    int adapterPosition = holder.getAdapterPosition();
    switch (holder.getItemViewType()) {
        case Constants.TYPE_EDITTEXT:
            EditTextViewHolder editTextViewHolder = (EditTextViewHolder)holder;
            // update MyCustomEditTextListener every time we bind a new item
            // so that it knows what item in mDataset to update
            editTextViewHolder.mMyCustomEditTextListener.setTWPosition(holder.getAdapterPosition());

            //Displaying list item to its correct position
            editTextViewHolder.vhETTextView.setText(String.valueOf(adapterPosition + 1));
            editTextViewHolder.vhETEditText.setText(mThoughtEntries.get(adapterPosition).getThought());
            break;

        case Constants.TYPE_TEXTVIEW:
            TextViewViewHolder textViewViewHolder = (TextViewViewHolder)holder;

            // Send current position to viewHolder so when the text listener is called, it knows
            // exactly which position of the Distortions list to change
            textViewViewHolder.vhPosition = adapterPosition;

            //Displaying list item to its correct position
            textViewViewHolder.vhTVTextView.setText(String.valueOf(adapterPosition + 1));
            textViewViewHolder.vhTVMainTextView.setText(distortionsToString(mDistortions.get(adapterPosition)));

            break;
    }
}

父级中的警报对话框

@Override
public void onTextViewClicked(int position) {
    //pass the 'context' here
    AlertDialog.Builder alertDialog = new AlertDialog.Builder(getContext());
    final int recyclerPosition = position;

    /*
        Turning the distortions into a list of strings and an array of what should, or should
        not, be checked.
     */
    final String[] distortionStrings = distortionNameToStringArray(mDistortions.get(position));
    final boolean[] checkedDistortions = distortionCheckToBooleanArray(mDistortions.get(position));

    alertDialog.setMultiChoiceItems(distortionStrings, checkedDistortions,
            new DialogInterface.OnMultiChoiceClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                    if (isChecked) {
                        // If the user checked the item, add it to the selected items
                        mDistortions.get(recyclerPosition).get(which).setChecked(true);
                    } else {
                        // Else, if the item is already in the array, remove it
                        mDistortions.get(recyclerPosition).get(which).setChecked(false);
                    }
                    /*
                        Because the RecyclerView takes a while to draw, if we call the below function
                        as we normally we would, it would appear to have no effect because it would
                        be automatically overwritten when the RecyclerView is drawn. So we call this
                        onLayout change listener to wait til the view is drawn and then we call
                        the function
                     */
                    mRecyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
                        @Override
                        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                            mRecyclerView.removeOnLayoutChangeListener(this);
                            // Send new height to the ViewModel
                            if(mLayoutManager.findViewByPosition(recyclerPosition) != null) {
                                // Get view of item measuring
                                View recyclerChild = mLayoutManager.findViewByPosition(recyclerPosition);
                                // Get LinearLayout from view
                                LinearLayout linearLayout = recyclerChild.findViewById(R.id.horizontalLayout);
                                // This is called to find out how big a view should be. The constraints are to check
                                // measurement when it is set to 'wrap_content'.
                                linearLayout.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                                // Get height of the specified view
                                int height = linearLayout.getMeasuredHeight();
                                // Send to child abstracted class which then calls function from 'SharedEntryFragments'
                                setViewModelHeight(height, recyclerPosition);
                            }
                        }
                    });
                    mAdapter.notifyDataSetChanged();
                }
            });

    alertDialog.setPositiveButton("Okay", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            // DO SOMETHING HERE
            dialog.cancel();
        }
    });

    AlertDialog dialog = alertDialog.create();
    dialog.show();
}

使所有片段项高度相等的函数

我知道这部分代码不会影响它,因为if(positionalHeight.get(i) != 0) {} 会跳过更改高度的视图所以从技术上讲...它们永远不应该改变!

    /*
        This is the listener that will set all the RecyclerViews childrens heights. It
        listens to getTallestLiveHeight() inside of 'SharedEntryFragments.java' and when
        a change occurs, this is called
     */
    if(getActivity() != null) {
        // The container holds the ViewModel so this must make sure getActivity() is not null
        mViewModel = ViewModelProviders.of(getActivity()).get(SharedEntryFragments.class);
        /*
            Creates the observer which updates the UI. The observer takes the
            PositionalHeight class as an input. This class keeps track of which index
            of the RecyclerView to change and what height it will be changed to.
         */
        final Observer<List<Integer>> maxHeight = new Observer<List<Integer>>() {
            @Override
            public void onChanged(@Nullable final List<Integer> positionalHeight) {
                if (positionalHeight != null) {
                    // Get the index that we are going to change and its height
                    //int position = positionalHeight.getPosition();
                    //int height = positionalHeight.getHeight();

                    /*
                        We're going to run through each child of mRecyclerView and change
                        its height accordingly
                     */
                    int listSize = positionalHeight.size();
                    for(int i = 0; i < listSize; i++) {
                        // If height reads zero then skip because it will make our view disappear
                        if(positionalHeight.get(i) != 0) {
                            // This is the child item that we will be changing
                            View recyclerChild = mLayoutManager.findViewByPosition(i);

                            // Ensure that the child exists before continuing
                            if (recyclerChild != null) {
                                // We will be changing the CardView's height
                                // TODO might have to add a check to detect which viewholder
                                CardView cardView = recyclerChild.findViewById(R.id.thoughtCard);
                                // Get the LayoutParams first to ensure everything stays the same
                                ViewGroup.LayoutParams lparams = cardView.getLayoutParams();
                                // Get and set height
                                lparams.height = positionalHeight.get(i);
                                cardView.setLayoutParams(lparams);
                            }
                        }
                    }
                }
            }
        };
        mViewModel.getTallestLiveHeight().observe(this, maxHeight);
    }
}

【问题讨论】:

    标签: java android android-fragments android-recyclerview


    【解决方案1】:

    我希望我可以为其他人提供更好的答案,但这是我发现的:

    由于某种原因,当我在 AlertDialog 函数中调用 mAdapter.notifyDataSetChanged(); 时,RecyclerView 中的每三个项目都更改为相等的高度。我决定将其更改为mAdapter.notifyItemChanged(recyclerPosition); 以节省内存,巧合的是,该错误已经消失了。

    如果有人可以解释原因,我会将其设置为已接受的答案,但到目前为止,这已满足问题,因此我将其保留为答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多