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());
}
}
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.
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.
onCreateViewHolder is called when you need a new View. by Pavlos-Petros Tournaris | Medium. This is not the way a recycling works.
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.
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.
There are a few things that need your attention:
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).
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();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With