Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Android

Umberto D'Ovidio
Umberto D'Ovidio
4,563 Points

notifyDataSetHasChanged() throws an error when used inside TextWatcher.onTextChanged() inside a RecyclerView

I'm building an app that allows user to have a real time base-conversion of a number. Users input their number in an edit text, and they choose the base using plus and minus button. The problem I encountered so far is providing the real time conversion. All the editText inside the recycler view set their text to a BigInteger that can be converted depending on their base.

My idea was to update the BigInteger as the user is inputting a new number. Therefore every time users input a digit I should be able to update the BigInteger, notify the recycler view that the data as changed and then the edit text views should update automatically. Here's the source code for my adapter

package com.example.umbertomac.binaryconverter.adapters;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.text.method.DigitsKeyListener;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;

import com.example.umbertomac.binaryconverter.NumberWithBase;
import com.example.umbertomac.binaryconverter.R;
import java.util.Vector;


/**
 * Created by UmbertoMac on 24/06/16.
 */
public class ConvertAdapter extends RecyclerView.Adapter<ConvertAdapter.ConvertViewHolder> {

    public static final int MIN_BASE = 2;
    public static final int MAX_BASE = 32;
    private Context mContext;
    private Vector<Integer> mRoot;
    private NumberWithBase mNumber;



    public ConvertAdapter(Context context, Vector<Integer> root, String number){
        mContext = context;
        mRoot = root;
        mNumber = new NumberWithBase(number);
    }

    @Override
    public ConvertAdapter.ConvertViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.single_item_layout, parent, false);
        ConvertViewHolder holder = new ConvertViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(ConvertAdapter.ConvertViewHolder holder, int position) {
        holder.bindConverter(mRoot.get(position));
    }

    @Override
    public int getItemCount() {
        return mRoot.size();
    }

    public class ConvertViewHolder extends RecyclerView.ViewHolder {

        public TextView mBaseTextView;
        public EditText mEditText;
        public Button mMinusButton;
        public Button mPlusButton;
        public ImageButton mRemoveButton;


        public ConvertViewHolder(final View itemView) {
            super(itemView);
            mBaseTextView = (TextView) itemView.findViewById(R.id.baseLabel);
            mEditText = (EditText) itemView.findViewById(R.id.numberEditText);
            mMinusButton = (Button) itemView.findViewById(R.id.minusButton);
            mPlusButton = (Button) itemView.findViewById(R.id.plusButton);
            mRemoveButton = (ImageButton)itemView.findViewById(R.id.removeButton);

            mMinusButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (mRoot.get(getPosition()) > MIN_BASE) {
                        mRoot.set(getPosition(), (mRoot.get(getPosition()) - 1));
                        notifyDataSetChanged();
                    }
                }
            });

            mPlusButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (mRoot.get(getPosition()) < MAX_BASE){
                        mRoot.set(getPosition(), (mRoot.get( getPosition() ) +1));
                        notifyDataSetChanged();
                    }
                }
            });

            mRemoveButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    mRoot.remove(getPosition());
                    notifyDataSetChanged();
                }
            });

            mEditText.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                }

                @Override
                public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                }

                @Override
                public void afterTextChanged(Editable editable) {
                    if(editable.toString().length() > 0) {
                        // change Big Integer
                        mNumber.setDecimalNumber(editable.toString(), mRoot.get( getPosition() ) );
                        // notify change
                        notifyDataSetChanged();
                    }
                }
            });


            // TODO: convert numbers at the same time

        }

        public void bindConverter(final int root){
            mBaseTextView.setText(String.format("%02d", root));

            // String containing all the allowed digits depending on base
            String digits = mNumber.getScaleFromBase(root);

            if (root < 11) {
                // filter input
                mEditText.setInputType(InputType.TYPE_CLASS_NUMBER |
                        InputType.TYPE_TEXT_FLAG_MULTI_LINE);
                mEditText.setKeyListener(DigitsKeyListener.getInstance(digits));
                mEditText.setSingleLine(false);
                mEditText.setMaxLines(2);

            } else {
                mEditText.setInputType(InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS |
                        InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
                mEditText.setSingleLine(false);
                mEditText.setMaxLines(2);
                mEditText.setImeOptions(EditorInfo.IME_FLAG_NO_ENTER_ACTION);
                // TODO: filter input for base higher than 10
            }

            // set editText to BigInteger displaying it in the correct base
            // i.e. if the BigInteger is "8" it will be displayed as 8 if the base is 10
            // and as 1000 if the base is 2

            mEditText.setText(mNumber.getDecimalNumber(mRoot.get( getPosition() )));
        }
    }

}

But apparently I am not allowed to call notifySetDataHasChanged inside the TextWatcher.onTextChanged() as the compiler throws me this error:

8 11:47:55.210 27534-27534/com.example.umbertomac.binaryconverter E/AndroidRuntime: FATAL EXCEPTION: main
                                                                                        Process: com.example.umbertomac.binaryconverter, PID: 27534
                                                                                        java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
                                                                                            at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:2349)
                                                                                            at android.support.v7.widget.RecyclerView$RecyclerViewDataObserver.onChanged(RecyclerView.java:4551)
                                                                                            at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyChanged(RecyclerView.java:10366)
                                                                                            at android.support.v7.widget.RecyclerView$Adapter.notifyDataSetChanged(RecyclerView.java:6044)
                                                                                            at com.example.umbertomac.binaryconverter.adapters.ConvertAdapter$ConvertViewHolder$4.afterTextChanged(ConvertAdapter.java:120)
                                                                                            at android.widget.TextView.sendAfterTextChanged(TextView.java:8004)
                                                                                            at android.widget.TextView.setText(TextView.java:4356)
                                                                                            at android.widget.TextView.setText(TextView.java:4204)
                                                                                            at android.widget.EditText.setText(EditText.java:84)
                                                                                            at android.widget.TextView.setText(TextView.java:4179)
                                                                                            at com.example.umbertomac.binaryconverter.adapters.ConvertAdapter$ConvertViewHolder.bindConverter(ConvertAdapter.java:157)
                                                                                            at com.example.umbertomac.binaryconverter.adapters.ConvertAdapter.onBindViewHolder(ConvertAdapter.java:52)
                                                                                            at com.example.umbertomac.binaryconverter.adapters.ConvertAdapter.onBindViewHolder(ConvertAdapter.java:26)
                                                                                            at android.support.v7.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:5768)
                                                                                            at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:5801)
                                                                                            at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5037)
                                                                                            at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4913)
                                                                                            at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2029)
                                                                                            at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1414)
                                                                                            at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1377)
                                                                                            at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:578)
                                                                                            at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3260)
                                                                                            at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:2788)
                                                                                            at android.view.View.measure(View.java:18796)
                                                                                            at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
                                                                                            at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
                                                                                            at android.view.View.measure(View.java:18796)
                                                                                            at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                                                            at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
                                                                                            at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:135)
                                                                                            at android.view.View.measure(View.java:18796)
                                                                                            at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                                                            at android.support.v7.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:391)
                                                                                            at android.view.View.measure(View.java:18796)
                                                                                            at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                                                            at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
                                                                                            at android.view.View.measure(View.java:18796)
                                                                                            at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                                                            at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1458)
                                                                                            at android.widget.LinearLayout.measureVertical(LinearLayout.java:746)
                                                                                            at android.widget.LinearLayout.onMeasure(LinearLayout.java:629)
                                                                                            at android.view.View.measure(View.java:18796)
                                                                                            at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                                                            at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
                                                                                            at com.android.internal.policy.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2643)
                                                                                            at android.view.View.measure(View.java:18796)
                                                                                            at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2108)
                                                                                            at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1224)
                                                                                            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1460)
                                                                                            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1115)
                                                                                            at an