同时从ListView中删除多个项目,空指针异常

时间:2016-07-08 10:58:13

标签: java android listview nullpointerexception

我试图实现一种同时从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)

如果你能以任何方式帮助我,那将是非常有用的,我已经被困在这几个小时并且无法找到我的错误。我到处检查,不知道为什么会出现空指针异常!

编辑:对于将问题标记为重复的用户,我知道如何解决空指针异常,这里的问题是找到导致它的原因。

3 个答案:

答案 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;
            }
        }