返回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; } }
解决方案
问题根源:
AdapteurRecette重写的getCount()依赖presentateur.getNbRecettes(),但调用adaptateur.clear()时未同步清空 Presentateur 内的数据列表,导致getCount()返回旧数量,ListView 仍渲染旧条目视图。- 点击条目时,父类
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()));
相关产品推荐
相关产品推荐

