OnClick в одном элементе RecyclerView влияет на другие элементы - PullRequest
0 голосов
/ 16 февраля 2019

Правка №1: Благодаря отладке я обнаружил, что ошибка «исчезает».По сути, я устанавливаю точку останова и медленно выполняю шаги проверки каждого multiChoiceItem, а высота других дочерних элементов RecyclerView не изменяется.Означает ли это, что это проблема, связанная с рисованием / временем?

Правка № 2: Кроме того, новая находка, если я изменю высоту Child: 6, меняется на Child: 3 иChild: 0

Прошу прощения за длинный вопрос.Я проверил другие ответы на ту же проблему, и ни один из них не подходит.Я пытался решить это сам и просто не мог, поэтому я хотел бы помочь.Если я могу что-то сделать, чтобы это было легче читать, пожалуйста, дайте мне знать, и я пойму правильно!

С тем, как написан мой код, это технически должно быть невозможно, но покавот оно.


Проблема: У меня есть onClickListener() для TextView внутри элемента RecyclerView.onClickListener() вызывает multiChoiceItem AlertDialog в классе контейнера RecyclerAdapter, который после завершения вызывает notifyDataSet(), после завершения с addOnLayoutChangeListener() в конце, который измеряет высоту после рисования нового RecyclerView.

Уведомление о завершении набора данных приводит к изменению TextView в элементе RecyclerView для отображения текста каждого элемента Checked .Затем эта высота измеряется в addOnLayoutChangeListener() и отправляется в 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;
    }
}

AlertDialog в родительском элементе

@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);
    }
}

1 Ответ

0 голосов
/ 18 февраля 2019

Хотелось бы дать лучший ответ для других людей, но я обнаружил вот что:

По какой-то причине, когда я вызываю mAdapter.notifyDataSetChanged(); в функции AlertDialog, каждый третий элемент в RecyclerView изменялсяравной высоте.Я решил изменить его на mAdapter.notifyItemChanged(recyclerPosition);, чтобы сэкономить память, и, по совпадению, ошибка исчезла.

Если кто-то может объяснить, почему, я установлю это как принятый ответ, но на данный момент это удовлетворяетвопрос, так что я буду держать его в качестве ответа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...