Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error setting text to EditText inside RecyclerView's onBindViewHolder method

I'm trying to set a text to an EditText inside my custom adapter and I'm getting this stack:

java.lang.NullPointerException: Attempt to read from field 'android.support.v7.widget.ViewInfoStore android.support.v7.widget.RecyclerView.mViewInfoStore' on a null object reference
    at android.support.v7.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:8194)
    at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:8180)
    at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:8168)
    at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1573)
    at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1519)
    at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:614)
    at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3812)
    at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:3225)
    at android.view.View.measure(View.java:17547)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1436)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:722)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:613)
    at android.view.View.measure(View.java:17547)
    at android.widget.ScrollView.measureChildWithMargins(ScrollView.java:1260)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
    at android.widget.ScrollView.onMeasure(ScrollView.java:337)
    at android.view.View.measure(View.java:17547)
    at android.support.constraint.ConstraintLayout.onMeasure(ConstraintLayout.java:1676)
    at android.view.View.measure(View.java:17547)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
    at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:141)
    at android.view.View.measure(View.java:17547)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
    at android.support.v7.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:400)
    at android.view.View.measure(View.java:17547)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
    at android.view.View.measure(View.java:17547)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1436)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:722)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:613)
    at android.view.View.measure(View.java:17547)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2615)
    at android.view.View.measure(View.java:17547)
    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2015)
    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1173)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1379)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1061)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5885)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
    at android.view.Choreographer.doCallbacks(Choreographer.java:580)
    at android.view.Choreographer.doFrame(Choreographer.java:550)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:135)
    at android.app.ActivityThread.main(ActivityThread.java:5254)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)


at com.andro
02-21 14:14:29.702 1707-1707/com.android.inputmethod.latin E/RichInputConnection: Unable to connect to the editor to retrieve text.
02-21 14:14:31.705 1707-1707/com.android.inputmethod.latin E/RichInputConnection: Unable to connect to the editor to retrieve text.
02-21 14:14:33.706 1707-1707/com.android.inputmethod.latin E/RichInputConnection: Unable to connect to the editor to retrieve text.
02-21 14:14:35.709 1707-1707/com.android.inputmethod.latin E/RichInputConnection: Unable to connect to the editor to retrieve text.

I'm not sure what is wrong, I am setting text to a TextView exactly the same way and it works fine. I've looked over forums and articles and I've seen people with similar issues but It seems they are either not binding the view with I's ViewHolder property correctly or trying to access the

Here It is my custom Adapter class:

public class ProductListAdapter extends RecyclerView.Adapter<ProductListViewHolder>{
    private List<Product> productList;
    private ProductItemManager productItemManager;

    public ProductListAdapter(List<Product> productList, ProductItemManager productItemManager) {
        this.productList = productList;
        this.productItemManager = productItemManager;
    }

    @NonNull
    @Override
    public ProductListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View productListView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.adapter_product_list, parent, false);
        return new ProductListViewHolder(productListView, productItemManager);
    }

    @Override
    public void onBindViewHolder(@NonNull ProductListViewHolder productListViewHolder, final int position) {
        Product product = productList.get(position);

        Double finalPrice = product.getSellingPrice() * product.getQuantity();

        productListViewHolder.productDescription.setText(product.getDescription());
        productListViewHolder.productSellingPrice.setText("un. " + Utils.doubleToReal(product.getSellingPrice()));
        productListViewHolder.productFinalPrice.setText(Utils.doubleToReal(finalPrice));
        productListViewHolder.productQuantity.setText(Double.toString(product.getQuantity()));
    }

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

And here It is my ViewHolder

public class ProductListViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    public TextView productDescription, productSellingPrice, productFinalPrice;
    public ImageView removeProductIcon;
    public EditText productQuantity;
    private ProductItemManager productItemManager;

    public ProductListViewHolder(View itemView, ProductItemManager productItemManager) {
        super(itemView);

        productDescription = itemView.findViewById(R.id.productDescription);
        productSellingPrice = itemView.findViewById(R.id.productSellingPrice);
        productFinalPrice = itemView.findViewById(R.id.productFinalPrice);
        removeProductIcon = itemView.findViewById(R.id.removeProductIcon);
        productQuantity = itemView.findViewById(R.id.productQuantity);

        this.productItemManager = productItemManager;

        removeProductIcon.setOnClickListener(this);

        this.productChangeListener();
    }

    private void productChangeListener() {
        productQuantity.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) {
                Double quantity = Double.parseDouble(productQuantity.getText().toString());
                productItemManager.setProductQuantity(getAdapterPosition(), quantity);
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
    }

    @Override
    public void onClick(View v) {
        productItemManager.removeProduct(getAdapterPosition());
    }
}
like image 875
Guilherme Ramalho Avatar asked Feb 21 '19 13:02

Guilherme Ramalho


People also ask

What is the use of onBindViewHolder in android?

bindViewHolder. This method internally calls onBindViewHolder to update the ViewHolder contents with the item at the given position and also sets up some private fields to be used by RecyclerView.

How data binding helps you when working with EditText inside RecyclerView?

When the EditText is changes by the user input, the attribute in model that binds to this EditText also changes. We'll use this type of binding to helps us solve this problem with less code and callbacks.

When onCreateViewHolder is Called?

onCreateViewHolder is called when you need a new View. by Pavlos-Petros Tournaris | Medium. This is not the way a recycling works.

What is the use of ViewHolder in RecyclerView android?

A ViewHolder describes an item view and metadata about its place within the RecyclerView. Adapter implementations should subclass ViewHolder and add fields for caching potentially expensive findViewById results.


2 Answers

You should call listener set on TextWatch, once you view bound. In onBindViewHolder call your registration.

......
@Override
public void onBindViewHolder(@NonNull ProductListViewHolder productListViewHolder, final int position) {
......
productListViewHolder.productChangeListener();
....

And I also suggest to change function with registration listeners. Without creation hundred of items, during RecyclerView creation.

like image 151
GensaGames Avatar answered Nov 08 '22 08:11

GensaGames


There are a few things that need your attention:

  1. Having EditText in RecyclerViews (and lists in general) in generally not a good idea, because of their UX. You either have to stop the user from scrolling the list, or do some action based on the scroll, to make things smoother (since they can be in progress of inputting something).

  2. There's a very important issue in the code and that is a reference to the "TextWatcher" of the edit text that is being recycled. You need to remove the text watcher while a view is being recycled. What happens If you don't remove the TextWatcher? Since it is not like OnClickListener (It is not a set function and is an add function), Everytime that your cells are being recreated, A new text watcher get's added to the EditText with it's references to the old ViewHolder.

To fix this, you need to add a method to your ViewHolder to unbind any stuff that it holds:

public class ProductListViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        public TextView productDescription, productSellingPrice, productFinalPrice;
        public ImageView removeProductIcon;
        public EditText productQuantity;
        private ProductItemManager productItemManager;
        private TextWatcher textWatcher;

        public ProductListViewHolder(View itemView, ProductItemManager productItemManager) {
            super(itemView);

            productDescription = itemView.findViewById(R.id.productDescription);
            productSellingPrice = itemView.findViewById(R.id.productSellingPrice);
            productFinalPrice = itemView.findViewById(R.id.productFinalPrice);
            removeProductIcon = itemView.findViewById(R.id.removeProductIcon);
            productQuantity = itemView.findViewById(R.id.productQuantity);

            this.productItemManager = productItemManager;

            removeProductIcon.setOnClickListener(this);

            this.productChangeListener();
        }

        private void productChangeListener() {
            textWatcher = 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) {
                    Double quantity = Double.parseDouble(productQuantity.getText().toString());
                    productItemManager.setProductQuantity(getAdapterPosition(), quantity);
                }

                @Override
                public void afterTextChanged(Editable s) {

                }
            };
            productQuantity.addTextChangedListener(textWatcher);
        }

        public void unbind(){
            productQuantity.removeTextChangedListener(textWatcher);
        }
        @Override
        public void onClick(View v) {
            productItemManager.removeProduct(getAdapterPosition());
        }
    }

Then, you can use RecyclerView's onViewRecycled method to know when your view is being recycled and remove the TextWatcher:

@Override
    public void onViewRecycled(ProductListViewHolder productListViewHolder) {
        super.onViewRecycled(holder);
        productListViewHolder.unbind();
    }
like image 40
Adib Faramarzi Avatar answered Nov 08 '22 08:11

Adib Faramarzi