4
votes

Erreur lors de la définition du texte sur EditText dans la méthode onBindViewHolder de RecyclerView

J'essaie de définir un texte sur un EditText dans mon adaptateur personnalisé et j'obtiens cette pile:

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

Je ne sais pas quoi est faux, je mets le texte sur un TextView exactement de la même manière et cela fonctionne très bien. J'ai regardé des forums et des articles et j'ai vu des gens avec des problèmes similaires, mais il semble qu'ils ne lient pas correctement la vue avec la propriété ViewHolder de I ou essaient d'accéder à la

Ici c'est ma classe d'adaptateur personnalisée:

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

Et ici c'est mon ViewHolder

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.


7 commentaires

Copie possible de Qu'est-ce qu'une NullPointerException, et comment puis-je le réparer?


post plein stacktrace non seulement erreur


J'ai modifié la question en ajoutant la pile complète.


Donc, je testais juste l'application et j'ai découvert que le problème ne semble se produire que lorsque j'ai un écouteur TextWatcher dans le constructeur de ProductListViewHolder. J'apprécierais toute réflexion à ce sujet.


Ce serait utile si vous publiez le code de la classe ProductItemManager car c'est de là que ce bogue semble provenir.


@GuilhermeRamalho pouvez-vous s'il vous plaît vérifier ma réponse et voir si cela aide? Merci :)


@GuilhermeRamalho De la trace de la pile, il semble que vous imbriquez 2 recycler View, le trou à l'intérieur d'un ScrollView ... pourriez-vous s'il vous plaît poster le code source de R.layout.adapter_product_list? le deuxième recyclerView semble manquer un adaptateur et un layoutManager ...


5 Réponses :


4
votes

Vous devez appeler l'auditeur défini sur TextWatch, une fois que vous l'avez vu lié. Dans onBindViewHolder appelez votre enregistrement.

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

Et je suggère également de changer de fonction avec les écouteurs d'enregistrement. Sans création de centaines d'articles, lors de la création de RecyclerView .


1 commentaires

Selon un article que j'ai lu, déclarer les auditeurs en dehors du détenteur de la vue est une mauvaise pratique. Est-ce vrai? L'auteur a recommandé d'implémenter l'écouteur sur le visualiseur, puis de créer ma propre interface qui sera ensuite étendue par mon activité et là, la méthode de mon interface devrait être remplacée. C'est ce que j'ai fait sur le ViewHolder pour l'événement de clic et cela a bien fonctionné.



1
votes

Le TextWatcher va être appelé à chaque fois que le texte qu'il regarde change. Cela signifie non seulement que les modifications utilisateur appelleront l'observateur (bien), mais chaque fois qu'un détenteur de vue est rebondi, l'observateur sera également appelé (pas si bon). Cela signifie également que productItemManager.setProductQuantity () sera appelé inutilement. (Je dis "inutilement" mais, peut-être, c'est quelque chose que vous avez l'intention de se produire.)

Une façon d'éviter l'observateur de texte lorsqu'un détenteur de vue est lié est de définir un View.OnFocusChangeListener () code> sur productQuantity qui s'appliquera et supprimera l'observateur de texte lorsque le focus est gagné ou perdu pour le champ. En d'autres termes, l'observateur de texte ne sera actif que pendant la modification de EditText .

productQuantity.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (hasFocus) {
            productQuantity.addTextChangedListener(textWatcher);
        } else {
            productQuantity.removeTextChangedListener(textWatcher);
        }
    }
});

Cela peut ne pas résoudre le problème que vous rencontrez , mais cela éliminera le travail inutile qui peut être un facteur contributif.

Je pense que publier setProductQuantity () aidera quelqu'un à aller à la racine du problème.


0 commentaires

0
votes

Je voudrais être d'accord avec l'implémentation proposée par @Cheticamp. D'après mon expérience, l'écouteur de changement de texte peut se déclencher dans différentes situations où il n'était pas censé être appelé. Les EditText sont souvent impliqués dans la correction automatique des mots et la fourniture de suggestions et peuvent être appelés lors de l'initialisation. Par conséquent, je voudrais vous recommander de mettre ce qui suit dans votre déclaration EditText dans le fichier de mise en page.

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) {
            String editTextContent = productQuantity.getText().toString();
            if(editTextContent != null && editTextContent.trim().length() > 0) {
                Double quantity = Double.parseDouble(productQuantity.getText().toString());
                productItemManager.setProductQuantity(getAdapterPosition(), quantity);
            }
        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    });
}

Et au lieu d'utiliser setOnFocusChangeListener , vous pourriez envisager d'avoir une vérification null dans votre observateur de texte comme suit.

android:inputType="textNoSuggestions"

J'espère que cela vous aidera!


0 commentaires

3
votes

Il y a quelques éléments qui nécessitent votre attention:

  1. Avoir EditText dans RecyclerViews (et les listes en général) n'est généralement pas une bonne idée, à cause de leur UX. Vous devez soit empêcher l'utilisateur de faire défiler la liste, soit effectuer une action basée sur le défilement, pour rendre les choses plus fluides (car ils peuvent être en train de saisir quelque chose).

  2. Il y a un problème très important dans le code et c'est une référence au "TextWatcher" du texte d'édition qui est recyclé. Vous devez supprimer l'observateur de texte lorsqu'une vue est en cours de recyclage. Que se passe-t-il si vous ne supprimez pas TextWatcher? Comme ce n'est pas comme OnClickListener (ce n'est pas une fonction set et c'est une fonction add ), chaque fois que vos cellules sont recréées, un le nouvel observateur de texte est ajouté à EditText avec ses références à l'ancien ViewHolder.

Pour résoudre ce problème, vous devez ajouter une méthode à votre ViewHolder pour dissocier tout ce qu'il contient:

@Override
    public void onViewRecycled(ProductListViewHolder productListViewHolder) {
        super.onViewRecycled(holder);
        productListViewHolder.unbind();
    }

Ensuite, vous pouvez utiliser le méthode onViewRecycled pour savoir quand votre vue est recyclée et supprimer TextWatcher:

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


0 commentaires

0
votes
 //update this line in your code, you will get your answer :) 

productListViewHolder.productQuantity.setText(Double.toString(productList.get(position).getQuantity()));

3 commentaires

Pouvez-vous expliquer cela davantage? Qu'est-ce qui n'allait pas avec la ligne d'origine, qu'avez-vous fait pour qu'elle fonctionne? Gardez à l'esprit que le PO doit pouvoir apprendre de votre amélioration et non simplement la copier


Récemment, j'avais fait face à ce même problème, et je l'ai résolu en accédant directement à l'empoisonnement de la liste principale.


Veuillez ajouter toutes les explications à la réponse, pas à la section des commentaires. De plus, expliquez plus en détail ce que vous avez changé et pourquoi - qu'est-ce qui n'allait pas avec le code d'origine?