我试图实现一种同时从ListView中删除多个项目的方法。我找到了一个似乎运行良好的在线教程,并在我的应用程序中实现了它。
教程中的代码允许我们在按下其中一个时间几秒后从ListView中选择多个项目,并使用删除按钮进行小菜单弹出。我们可以选择多个项目,然后按删除将其从列表中删除。
当我测试它时,一切似乎工作正常,除了一件事,当我选择一个或多个要删除的项目后按下删除按钮时,我一直得到一个空指针异常。
以下是我关注的教程:Android Delete Multiple Selected Items in ListView Tutorial
这是我的活动代码:
public class Favoris extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{
/**fields*/
private ListView produitFavorisListView;
private List<StackProduits> listProduits = new ArrayList<>();
private ProduitsAdapter adapterFavoris;
//SharedPreferences mPrefs;
/**
* Method used to initialize the current activity, by inflating the activity's UI and interacting with
* implemented widgets in the UI. Used to get and set the toolbar, (a floating action button), the
* drawer which is used as the side menu, a navigation view.
* @param savedInstanceState
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_favoris);
/**get ListView reference*/
produitFavorisListView = (ListView) findViewById(R.id.favoritesList);
/**getting back data from shared preferences*/
final SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
final Gson gson = new Gson();
//getting back favorites
Set<String> myJson = mPrefs.getStringSet("listJson2", new HashSet<String>());
//adapterFavoris = new ProduitsAdapter(getApplicationContext(), 0, listProduits);
if (myJson.isEmpty() && listProduits.isEmpty()) {
produitFavorisListView.setAdapter(null);
//Log.i("INFO", "No items"); todo - log info: no items
}
else if (myJson.isEmpty() && listProduits != null) {
adapterFavoris.notifyDataSetChanged();
adapterFavoris = new ProduitsAdapter(getApplicationContext(), -1, listProduits);
produitFavorisListView.setAdapter(adapterFavoris);
}
else{
//for each where we get back values from sting set, then convert to product
for (String id : myJson) {
StackProduits savedProduct = gson.fromJson(id, StackProduits.class);
//savedProduct.setIsAddedAsFav("1");
listProduits.add(savedProduct);
}
adapterFavoris = new ProduitsAdapter(getApplicationContext(), -1, listProduits);
produitFavorisListView.setAdapter(adapterFavoris);
}
//Set the click listener to launch the browser when a row is clicked.
produitFavorisListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View v, int pos, long id) {
Intent intentProduitFavorisDetail = new Intent(Favoris.this, ProduitDetail.class);
StackProduits ProduitFavoris = ProduitsXmlPullParser.getStackProduitFromFile(Favoris.this).get(pos);
intentProduitFavorisDetail.putExtra("produit", ProduitFavoris);
startActivity(intentProduitFavorisDetail);
}
});
/**handle multiple item selection for deletion*/
produitFavorisListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
produitFavorisListView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener(){
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.favorite_menu, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false; //done
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()){
case R.id.delete_fav:
// Calls getSelectedIds method from ListViewAdapter Class
SparseBooleanArray selected = adapterFavoris.getSelectedIds();
// Captures all selected ids with a loop
for (int i = (selected.size() - 1); i >= 0; i--) {
if (selected.valueAt(i)) {
StackProduits selecteditem = adapterFavoris.getItem(selected.keyAt(i));
// Remove selected items following the ids
adapterFavoris.remove(selecteditem);
adapterFavoris.notifyDataSetChanged();
/*if(listProduits.isEmpty()){
if()
}*/
}
}
//save after modifications
String getProduct;
Set<String> stringListProductSave = new HashSet<>();
Gson gsonSave = new Gson();
for(int i=0; i<listProduits.size();i++){
getProduct = gsonSave.toJson(listProduits.get(i));
stringListProductSave.add(getProduct);
}
SharedPreferences.Editor prefsEditorSave = mPrefs.edit();
prefsEditorSave.putStringSet("listJson2", stringListProductSave);
prefsEditorSave.apply();
adapterFavoris.notifyDataSetChanged();
// Close CAB
mode.finish();
return true;
default:
return false;
}
}
@Override
public void onDestroyActionMode(ActionMode mode) {
adapterFavoris.removeSelection();
}
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
// Capture total checked items
final int checkedCount = produitFavorisListView.getCheckedItemCount();
// Set the CAB title according to total checked items
mode.setTitle(checkedCount + " Selected");
// Calls toggleSelection method from ListViewAdapter Class
adapterFavoris.toggleSelection(position);
}
});
}
}
这是我的ListView适配器代码:
public class ProduitsAdapter extends ArrayAdapter<StackProduits> {
/**
* fields
*/
ImageLoader imageLoader;
DisplayImageOptions options;
List<StackProduits> productList;
SparseBooleanArray mSelectedItemsIds;
/**
* Constructor.
*
* @param ctx
* @param textViewResourceId
* @param sites
*/
public ProduitsAdapter(Context ctx, int textViewResourceId, List<StackProduits> sites) {
super(ctx, textViewResourceId, sites);
//Setup the ImageLoader, we'll use this to display our images
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(ctx).build();
imageLoader = ImageLoader.getInstance();
imageLoader.init(config);
mSelectedItemsIds = new SparseBooleanArray();
//Setup options for ImageLoader so it will handle caching for us.
options = new DisplayImageOptions.Builder()
.cacheInMemory()
.cacheOnDisc()
.build();
}
/**
* This method is responsible for creating row views out of a StackProduits object that can be put
* into our ListView.
* <p/>
* (non-Javadoc)
*
* @see android.widget.ArrayAdapter#getView(int, android.view.View, android.view.ViewGroup)
*/
@Override
public View getView(int pos, View convertView, ViewGroup parent) {
RelativeLayout row = (RelativeLayout) convertView;
//Log.i("StackSites", "getView pos = " + pos);
if (null == row) { //No recycled View, we have to inflate one.
LayoutInflater inflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = (RelativeLayout) inflater.inflate(R.layout.item_row, null);
}
//Get our View References from item_row.xml
final ImageView iconImg = (ImageView) row.findViewById(R.id.iconImg);
TextView txtDesignation = (TextView) row.findViewById(R.id.nameTxt);
TextView txtAbout = (TextView) row.findViewById(R.id.aboutTxt);
TextView txtPrice = (TextView) row.findViewById(R.id.priceTxt);
TextView txtTotalArea = (TextView) row.findViewById(R.id.areaTxt);
final ProgressBar indicator = (ProgressBar) row.findViewById(R.id.progress);
//Initially we want the progress indicator visible, and the image invisible
indicator.setVisibility(View.VISIBLE); //show progress indicator
iconImg.setVisibility(View.INVISIBLE); //make image invisible
//Setup a listener we can use to switch from the loading indicator to the Image once it's ready
//changed ImageLoadingListener with SimpleImageLoadingListener
SimpleImageLoadingListener listener = new SimpleImageLoadingListener() {
@Override
public void onLoadingStarted(String arg0, View arg1) {
// TODO Auto-generated method stub
}
@Override
public void onLoadingCancelled(String arg0, View arg1) {
// TODO Auto-generated method stub
}
@Override
public void onLoadingComplete(String arg0, View arg1, Bitmap arg2) {
indicator.setVisibility(View.INVISIBLE);
iconImg.setVisibility(View.VISIBLE);
}
@Override
public void onLoadingFailed(String arg0, View arg1, FailReason arg2) {
// TODO Auto-generated method stub
}
};
//Load the image and use our options so caching is handled.
imageLoader.displayImage(getItem(pos).getImgUrl(), iconImg, options, listener);
//Set the relevant text in our TextViews (ListView)
txtDesignation.setText(getItem(pos).getDesignation());
txtAbout.setText(getItem(pos).getAbout());
txtPrice.setText(getItem(pos).getPrice());
txtTotalArea.setText(getItem(pos).getArea());
//return view that represents the full row
return row;
}
@Override
public void remove(StackProduits object) {
productList.remove(object);
notifyDataSetChanged();
}
public void removeSelection() {
mSelectedItemsIds = new SparseBooleanArray();
notifyDataSetChanged();
}
public void toggleSelection(int position) {
selectView(position, !mSelectedItemsIds.get(position));
}
public void selectView(int position, boolean value) {
if (value)
mSelectedItemsIds.put(position, value);
else
mSelectedItemsIds.delete(position);
notifyDataSetChanged();
}
public SparseBooleanArray getSelectedIds() {
return mSelectedItemsIds;
}
}
这是我的logcat:
07-08 11:51:28.106 23049-23049/com.example.adam_jaamour.cfimmobilier W/System: ClassLoader referenced unknown path: /data/app/com.example.adam_jaamour.cfimmobilier-1/lib/x86
07-08 11:51:28.260 23049-23049/com.example.adam_jaamour.cfimmobilier W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
07-08 11:51:28.437 23049-23087/com.example.adam_jaamour.cfimmobilier D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
[ 07-08 11:51:28.443 23049:23049 D/ ]
HostConnection::get() New Host Connection established 0xac33dfb0, tid 23049
[ 07-08 11:51:28.484 23049:23087 D/ ]
HostConnection::get() New Host Connection established 0xac33e100, tid 23087
07-08 11:51:28.487 23049-23087/com.example.adam_jaamour.cfimmobilier I/OpenGLRenderer: Initialized EGL, version 1.4
07-08 11:51:35.513 23049-23049/com.example.adam_jaamour.cfimmobilier E/libEGL: call to OpenGL ES API with no current context (logged once per thread)
07-08 11:51:35.664 23049-23059/com.example.adam_jaamour.cfimmobilier I/art: Background sticky concurrent mark sweep GC freed 14665(1431KB) AllocSpace objects, 16(572KB) LOS objects, 16% free, 8MB/10MB, paused 9.804ms total 28.247ms
07-08 11:51:35.735 23049-23087/com.example.adam_jaamour.cfimmobilier E/Surface: getSlotFromBufferLocked: unknown buffer: 0xa9e5ee50
07-08 11:51:38.871 23049-23049/com.example.adam_jaamour.cfimmobilier W/ViewRootImpl: Cancelling event due to no window focus: MotionEvent { action=ACTION_CANCEL, actionButton=0, id[0]=0, x[0]=316.2744, y[0]=1459.6875, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=13426969, downTime=13423514, deviceId=0, source=0x1002 }
07-08 11:51:38.872 23049-23049/com.example.adam_jaamour.cfimmobilier W/ViewRootImpl: Cancelling event due to no window focus: MotionEvent { action=ACTION_CANCEL, actionButton=0, id[0]=0, x[0]=316.2744, y[0]=1459.6875, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=13426969, downTime=13423514, deviceId=0, source=0x1002 }
07-08 11:51:38.872 23049-23049/com.example.adam_jaamour.cfimmobilier W/ViewRootImpl: Cancelling event due to no window focus: MotionEvent { action=ACTION_CANCEL, actionButton=0, id[0]=0, x[0]=316.2744, y[0]=1459.6875, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=13426969, downTime=13423514, deviceId=0, source=0x1002 }
07-08 11:51:38.872 23049-23049/com.example.adam_jaamour.cfimmobilier W/ViewRootImpl: Cancelling event due to no window focus: MotionEvent { action=ACTION_CANCEL, actionButton=0, id[0]=0, x[0]=316.2744, y[0]=1459.6875, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=13426969, downTime=13423514, deviceId=0, source=0x1002 }
07-08 11:51:42.399 23049-23049/com.example.adam_jaamour.cfimmobilier D/AndroidRuntime: Shutting down VM
--------- beginning of crash
07-08 11:51:42.401 23049-23049/com.example.adam_jaamour.cfimmobilier E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.adam_jaamour.cfimmobilier, PID: 23049
java.lang.NullPointerException: Attempt to invoke interface method 'boolean java.util.List.remove(java.lang.Object)' on a null object reference
at com.example.adam_jaamour.cfimmobilier.Produits.ProduitsAdapter.remove(ProduitsAdapter.java:144)
at com.example.adam_jaamour.cfimmobilier.Favoris$2.onActionItemClicked(Favoris.java:138)
at android.widget.AbsListView$MultiChoiceModeWrapper.onActionItemClicked(AbsListView.java:6242)
at com.android.internal.policy.PhoneWindow$DecorView$ActionModeCallback2Wrapper.onActionItemClicked(PhoneWindow.java:3540)
at android.support.v7.view.SupportActionModeWrapper$CallbackWrapper.onActionItemClicked(SupportActionModeWrapper.java:168)
at android.support.v7.app.AppCompatDelegateImplV7$ActionModeCallbackWrapperV7.onActionItemClicked(AppCompatDelegateImplV7.java:1750)
at android.support.v7.app.AppCompatDelegateImplV7$ActionModeCallbackWrapperV7.onActionItemClicked(AppCompatDelegateImplV7.java:1750)
at android.support.v7.view.StandaloneActionMode.onMenuItemSelected(StandaloneActionMode.java:136)
at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:811)
at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:152)
at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:958)
at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:948)
at android.support.v7.widget.ActionMenuView.invokeItem(ActionMenuView.java:618)
at android.support.v7.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:139)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
如果你能以任何方式帮助我,那将是非常有用的,我已经被困在这几个小时并且无法找到我的错误。我到处检查,不知道为什么会出现空指针异常!
编辑:对于将问题标记为重复的用户,我知道如何解决空指针异常,这里的问题是找到导致它的原因。
答案 0 :(得分:1)
在显示的代码中,您没有初始化productList,因此您获得了nullPointer。
应该在构造函数中:
public ProduitsAdapter(Context ctx, int textViewResourceId, List<StackProduits> sites) {
super(ctx, textViewResourceId, sites);
this.productList = sites;//////////here
//Setup the ImageLoader, we'll use this to display our images
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(ctx).build();
imageLoader = ImageLoader.getInstance();
imageLoader.init(config);
mSelectedItemsIds = new SparseBooleanArray();
//Setup options for ImageLoader so it will handle caching for us.
options = new DisplayImageOptions.Builder()
.cacheInMemory()
.cacheOnDisc()
.build();
}
答案 1 :(得分:1)
您得到NullPointerException,因为在ProduitsAdapter类中,您尚未初始化productList变量。您可以在构造函数中初始化它,如下所示:
public ProduitsAdapter(Context ctx, int textViewResourceId, List<StackProduits> sites) {
super(ctx, textViewResourceId, sites);
productList = sites;
//Setup the ImageLoader, we'll use this to display our images
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(ctx).build();
imageLoader = ImageLoader.getInstance();
imageLoader.init(config);
mSelectedItemsIds = new SparseBooleanArray();
//Setup options for ImageLoader so it will handle caching for us.
options = new DisplayImageOptions.Builder()
.cacheInMemory()
.cacheOnDisc()
.build();
}
或者您可以完全删除变量,因为您已经将列表存储在父类的变量中。但是你必须将你的“删除”方法更新为:
@Override
public void remove(StackProduits object) {
super.remove(object);
notifyDataSetChanged();
}
我更喜欢第二种解决方案,因为您不会复制StackProduits列表。
答案 2 :(得分:1)
您必须修改 onActionItemClicked()
查看您要删除项目的注释区域,然后在每次不必执行此操作时更新notifyDataSetChanged()
。 notifyDataSetChanged()
只有一次。
评论下面的评论。
而且我认为你必须改变你的for循环逻辑。
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()){
case R.id.delete_fav:
// Calls getSelectedIds method from ListViewAdapter Class
SparseBooleanArray selected = adapterFavoris.getSelectedIds();
// Captures all selected ids with a loop
for (int i = (selected.size() - 1); i >= 0; i--) {
if (selected.valueAt(i)) {
StackProduits selecteditem = adapterFavoris.getItem(selected.keyAt(i));
// Remove selected items following the ids
adapterFavoris.remove(selecteditem);
// Look here you remove all item item from list
// and then update notifyDataSetChanged() only once.
// Comment the below as i commented.
//adapterFavoris.notifyDataSetChanged();
/*if(listProduits.isEmpty()){
if()
}*/
}
}
//save after modifications
String getProduct;
Set<String> stringListProductSave = new HashSet<>();
Gson gsonSave = new Gson();
for(int i=0; i<listProduits.size();i++){
getProduct = gsonSave.toJson(listProduits.get(i));
stringListProductSave.add(getProduct);
}
SharedPreferences.Editor prefsEditorSave = mPrefs.edit();
prefsEditorSave.putStringSet("listJson2", stringListProductSave);
prefsEditorSave.apply();
adapterFavoris.notifyDataSetChanged();
// Close CAB
mode.finish();
return true;
default:
return false;
}
}