You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

返回ListeRecette时清空ListView并解决数组越界异常

问题描述

从ActionActivity启动ListeRecette Activity后,点击btnRechercher按钮会通过API GET请求填充ListView。但按下btnRetour关闭ListeRecette,再次从ActionActivity启动它时,ListView仍显示旧元素,点击这些元素会抛出IndexOutOfBoundsException异常,具体堆栈信息如下:

FATAL EXCEPTION: main
Process: com.example.chefchatter, PID: 8171
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.get(ArrayList.java:437)
at android.widget.ArrayAdapter.getItem(ArrayAdapter.java:394)
at com.example.chefchatter.activites.ListeRecette$1.onItemClick(ListeRecette.java:96)
at android.widget.AdapterView.performItemClick(AdapterView.java:330)
at android.widget.AbsListView.performItemClick(AbsListView.java:1257)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:3270)
at android.widget.AbsListView$3.run(AbsListView.java:4236)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7872)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

需求:确保再次启动ListeRecette时,ListView为空且无任何可点击元素。


相关代码

ActionActivity 代码

package com.example.chefchatter.activites;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.example.chefchatter.R;
import com.example.chefchatter.activites.ListeRecette;
import com.example.chefchatter.modele.Compte;
import com.example.chefchatter.presentateur.PresentateurCompte;

public class ActionActivity extends AppCompatActivity implements View.OnClickListener{
    private TextView tvBonjour;
    private ImageButton imgBtnHome;
    private Button btnDeconnexion;
    private  Button btnParcourirRecettes;
    private Button btnModifierCompte;

    private Button btnFavoris;
    private Compte compte;
    private PresentateurCompte presentateurCompte;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_action);

        tvBonjour = findViewById(R.id.tvBonjour);
        btnDeconnexion = findViewById(R.id.btnDeconnexion);
        btnParcourirRecettes = findViewById(R.id.btnParcourirRecettes);
        btnModifierCompte = findViewById(R.id.btnModifierCompte);
        btnFavoris = findViewById(R.id.btnParcourirFavoris);

        btnDeconnexion.setOnClickListener(this);
        btnParcourirRecettes.setOnClickListener(this);
        btnModifierCompte.setOnClickListener(this);
        btnFavoris.setOnClickListener(this);

        presentateurCompte = new PresentateurCompte(this);

    }
    protected void onResume() {
        super.onResume();
        compte = presentateurCompte.getCompte();
        if (compte != null) {
            String nomUtilisateur = compte.getNomUtilisateur();
            String messaqeBienvenue = getResources().getString(R.string.bonjour, nomUtilisateur);
            tvBonjour.setText(messaqeBienvenue);
        }
    }


    @Override
    public void onClick(View v) {
        if(v.getId() == R.id.btnDeconnexion){
            finish();
        }
        else if(v.getId() == R.id.btnParcourirRecettes){
            Intent intent = new Intent(this, ListeRecette.class);
            startActivity(intent);
        }
        else if(v.getId() == R.id.btnModifierCompte){

            Intent intent = new Intent(this, ModifierCompteActivity.class);
            startActivity(intent);
        }
        else if(v.getId() == R.id.btnParcourirFavoris){
            Intent intent = new Intent(this, ListeFavorisActivity.class);
            startActivity(intent);
        }
    }
}

ListeRecette 代码

package com.example.chefchatter.activites;

import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import com.example.chefchatter.dao.RecettesCallback;
import com.example.chefchatter.modele.Filtre;
import com.example.chefchatter.R;
import com.example.chefchatter.modele.Recette;
import com.example.chefchatter.presentateur.AdapteurRecette;
import com.example.chefchatter.presentateur.IngredientsAdapter;
import com.example.chefchatter.presentateur.PresentateurRecette;

import java.util.List;

public class ListeRecette extends AppCompatActivity implements View.OnClickListener{

    private Button btnRetour;
    private Button btnRechercher;

    private ListView listeRecette;

    private Spinner origine;
    private Spinner regime;
    private Spinner type;
    PresentateurRecette presentateurRecette;
    List<Recette> recettes;
    private AdapteurRecette adaptateur;
    private EditText txtIngredient;

    private Filtre filtre = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_liste_recette);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        origine = findViewById(R.id.sChoixOrigine);
        regime = findViewById(R.id.sChoixRegime);
        type = findViewById(R.id.sChoixType);

        ArrayAdapter<CharSequence> adapterOrigine = ArrayAdapter.createFromResource(this, R.array.spinner_choice_origine, android.R.layout.simple_spinner_item);
        adapterOrigine.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        origine.setAdapter(adapterOrigine);

        ArrayAdapter<CharSequence> adapterRegime = ArrayAdapter.createFromResource(this, R.array.spinner_choice_regime, android.R.layout.simple_spinner_item);
        adapterRegime.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        regime.setAdapter(adapterRegime);

        ArrayAdapter<CharSequence> adapterType = ArrayAdapter.createFromResource(this, R.array.spinner_choice_type, android.R.layout.simple_spinner_item);
        adapterType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        type.setAdapter(adapterType);

        listeRecette = findViewById(R.id.lvFiltre);

        btnRechercher = findViewById(R.id.btnRecherche);
        btnRechercher.setOnClickListener(this);

        btnRetour = findViewById(R.id.btnRet);
        btnRetour.setOnClickListener(this);

        presentateurRecette = new PresentateurRecette(this);

        adaptateur = new AdapteurRecette(this,
                R.layout.layout_recettes,presentateurRecette);
        listeRecette.setAdapter(adaptateur);

        txtIngredient = findViewById(R.id.etListeIngredients);

        listeRecette.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    Intent iRecetteDescription = new Intent(getApplicationContext(), DescriptionRecette.class);
                    Recette recetteSelectionnee = (Recette) parent.getAdapter().getItem(position);
                    String src = recetteSelectionnee.getSrc();
                    String nom = recetteSelectionnee.getNom();
                    //ajouter mots clefs
                    Integer idRecette = recetteSelectionnee.getId();
                    String tempsCuisson = recetteSelectionnee.getCuisson();
                    String tempsPreparation = recetteSelectionnee.getPreparation();
                    String portions = recetteSelectionnee.getPortion();
                    String description = recetteSelectionnee.getDescription();
                    String origine = recetteSelectionnee.getOrigine();
                    String regime = recetteSelectionnee.getRegime();
                    String type = recetteSelectionnee.getType();
                    String etape = recetteSelectionnee.getEtape();
                    iRecetteDescription.putExtra("ID", idRecette);
                    iRecetteDescription.putExtra("SRC", src);
                    iRecetteDescription.putExtra("NOM", nom);
                    iRecetteDescription.putExtra("TEMPS_CUISSON", tempsCuisson);
                    iRecetteDescription.putExtra("TEMPS_PREPARATION", tempsPreparation);
                    iRecetteDescription.putExtra("PORTIONS", portions);
                    iRecetteDescription.putExtra("DESCRIPTION", description);
                    iRecetteDescription.putExtra("ORIGINE", origine);
                    iRecetteDescription.putExtra("REGIME", regime);
                    iRecetteDescription.putExtra("TYPE", type);
                    iRecetteDescription.putExtra("ETAPES", etape);
                    startActivity(iRecetteDescription);

            }
        });
    }

    @Override
    public void onClick(View v) {
        if(v == btnRetour){
            finish();
        }
        else if(v == btnRechercher){

            String[] ingredientsListe = txtIngredient.getText().toString().split(",");
            String[] origineValues = getResources().getStringArray(R.array.spinner_choice_origine_values);
            String[] regimeValues = getResources().getStringArray(R.array.spinner_choice_regime_values);
            String[] typeValues = getResources().getStringArray(R.array.spinner_choice_type_values);

            String origine = this.origine.getSelectedItemPosition() == 0 ? "" : origineValues[this.origine.getSelectedItemPosition()];
            String regime = this.regime.getSelectedItemPosition() == 0 ? "" : regimeValues[this.regime.getSelectedItemPosition()];
            String type = this.type.getSelectedItemPosition() == 0 ? "" : typeValues[this.type.getSelectedItemPosition()];

            filtre = new Filtre(origine, regime, type, ingredientsListe);
            presentateurRecette.obtenirRecettes(filtre ,new RecettesCallback() {
                @Override
                public void onRecettesReceived(List<Recette> recettes) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ListeRecette.this.recettes = presentateurRecette.getRecettes();
                            adaptateur.clear();
                            adaptateur.addAll(recettes);
                            raffraichirListe();
                        }
                    });

                }

            });

    }
 }

    public void raffraichirListe() {
        this.adaptateur.notifyDataSetChanged();
        Toast.makeText(this,adaptateur.getCount()+" recettes retrouvées",Toast.LENGTH_SHORT).show();
    }


    @Override
    protected void onResume() {
        super.onResume();
        // Initialize the adapter if null
        if (adaptateur == null) {
            adaptateur = new AdapteurRecette(this, R.layout.layout_recettes, presentateurRecette);
            listeRecette.setAdapter(adaptateur);
        } else {
            adaptateur.clear();
            adaptateur.notifyDataSetChanged();
        }
    }

//    @Override
//    protected void onResume() {
//        super.onResume();
//        int r = checkSelfPermission("android.permission.INTERNET");
//        if (r == PackageManager.PERMISSION_GRANTED) {
//            //    Toast.makeText(this, "Accès Internet permis!", Toast.LENGTH_LONG).show();
//            this.presentateurRecette.obtenirRecettes(filtre, new RecettesCallback() {
//                @Override
//                public void onRecettesReceived(List<Recette> recettes) {
//                    runOnUiThread(new Runnable() {
//                        @Override
//                        public void run() {
//                            ListeRecette.this.recettes = presentateurRecette.getRecettes();
//                            adaptateur.clear();
//                            adaptateur.addAll(recettes);
//                            raffraichirListe();
//                        }
//                    });
//
//                }
//            }
//            );
//        } else {
//            Toast.makeText(this, "Accès Internet non permis!", Toast.LENGTH_LONG).show();
//        }
//    }
}

AdapteurRecette 代码

package com.example.chefchatter.presentateur;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.bumptech.glide.Glide;
import com.example.chefchatter.R;
import com.example.chefchatter.modele.Recette;

public class AdapteurRecette extends ArrayAdapter {
    private Context contexte;
    private int viewResourceId;
    private Resources resources;
    private PresentateurRecette presentateur;
    public AdapteurRecette(@NonNull Context context, int resource, PresentateurRecette presentateur) {
        super(context, resource);
        this.contexte = context;
        this.viewResourceId = resource;
        this.resources = contexte.getResources();
        this.presentateur = presentateur;
    }



    @Override
    public int getCount() {
        return this.presentateur.getNbRecettes();
    }
    @SuppressLint("NewApi")
    @NonNull
    @Override
    public View getView(int position, @Nullable View view, @NonNull ViewGroup parent) {

        if (view == null) {
            LayoutInflater layoutInflater = (LayoutInflater) contexte.getSystemService(Context.
                    LAYOUT_INFLATER_SERVICE);
            view = layoutInflater.inflate(this.viewResourceId, parent, false);
        }
        final Recette recette = this.presentateur.getRecette(position);
        if (recette != null) {
            final TextView tvNomRecette = (TextView) view.findViewById(R.id.lrTvNomRecette);
            final TextView tvPrenomChef = (TextView) view.findViewById(R.id.lrTvPrenomChef);
            final ImageView imageRecette = (ImageView) view.findViewById(R.id.lrIvPhoto);

            tvNomRecette.setText(String.format("Nom de la recette: %s", recette.getNom()));
            tvPrenomChef.setText(String.format("Chef: %s", recette.getPrenom()));
            Glide.with(this.contexte)
                    .load(recette.getSrc())
                    .into(imageRecette);
        }
        return view;
    }

}

解决方案

问题根源:

  1. AdapteurRecette 重写的 getCount() 依赖 presentateur.getNbRecettes(),但调用 adaptateur.clear() 时未同步清空 Presentateur 内的数据列表,导致 getCount() 返回旧数量,ListView 仍渲染旧条目视图。
  2. 点击条目时,父类 ArrayAdapter 的 getItem() 方法调用内部已清空的列表,触发索引越界异常。

修复步骤:

1. 重构 AdapteurRecette,让适配器自主管理数据

public class AdapteurRecette extends ArrayAdapter<Recette> {
    private Context contexte;
    private int viewResourceId;
    private List<Recette> recettes;

    public AdapteurRecette(@NonNull Context context, int resource, List<Recette> recettes) {
        super(context, resource, recettes);
        this.contexte = context;
        this.viewResourceId = resource;
        this.recettes = recettes;
    }

    @Override
    public int getCount() {
        return recettes != null ? recettes.size() : 0;
    }

    @SuppressLint("NewApi")
    @NonNull
    @Override
    public View getView(int position, @Nullable View view, @NonNull ViewGroup parent) {
        if (view == null) {
            LayoutInflater layoutInflater = (LayoutInflater) contexte.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = layoutInflater.inflate(this.viewResourceId, parent, false);
        }
        final Recette recette = recettes.get(position);
        if (recette != null) {
            final TextView tvNomRecette = view.findViewById(R.id.lrTvNomRecette);
            final TextView tvPrenomChef = view.findViewById(R.id.lrTvPrenomChef);
            final ImageView imageRecette = view.findViewById(R.id.lrIvPhoto);

            tvNomRecette.setText(String.format("Nom de la recette: %s", recette.getNom()));
            tvPrenomChef.setText(String.format("Chef: %s", recette.getPrenom()));
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 05:23:13