NullPointerException when scrolling fast

2019-08-30 10:20发布

问题:

I'm trying to include category fields into my coustom ListView. It works fine until I start scrolling fast. When scrolling very slow this error doesn't occur. It's really confusing me. Could anyone please help? My Code:

package com.mamlambo.tutorial.tutlist;

import java.util.ArrayList;
import java.util.TreeSet;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.ImageView;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

public class TutListActivity extends Activity {

private DatabaseManager mHelper;
private SQLiteDatabase mDatenbank;
private ArrayList<String> devicesList;
private TreeSet<Object> mSeparatorsSet;
ListView listView;
EfficientAdapter objectAdapter;

private class EfficientAdapter extends BaseAdapter {
    private LayoutInflater mInflater;
    private Bitmap mIcon1;
    private Bitmap mIcon2;

    private static final int TYPE_ITEM = 0;
    private static final int TYPE_SEPARATOR = 1;
    private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1;

    public EfficientAdapter(Context context) {
        // Cache the LayoutInflate to avoid asking for a new one each time.
        mInflater = LayoutInflater.from(context);

        // Icons bound to the rows.
        mIcon1 = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon);
        mIcon2 = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon);
    }

    /**
     * The number of items in the list is determined by the number of speeches
     * in our array.
     *
     * @see android.widget.ListAdapter#getCount()
     */
    public int getCount() {
        return devicesList.size();
    }

    public void addItem(final String item) {
        devicesList.add(item);
        notifyDataSetChanged();
    }

    /**
     * Since the data comes from an array, just returning the index is
     * sufficent to get at the data. If we were using a more complex data
     * structure, we would return whatever object represents one row in the
     * list.
     *
     * @see android.widget.ListAdapter#getItem(int)
     */
    public Object getItem(int position) {
        return position;
    }
    /**
     * Use the array index as a unique id.
     *
     * @see android.widget.ListAdapter#getItemId(int)
     */
    public long getItemId(int position) {
        return position;
    }

    @Override
    public void notifyDataSetChanged() // Create this function in your adapter class
    {
        super.notifyDataSetChanged();
    }

    public void addSeparatorItem(final String item) {
        devicesList.add(item);
        // save separator position
        mSeparatorsSet.add(devicesList.size() - 1);
        notifyDataSetChanged();
    }

    @Override
    public int getItemViewType(int position) {
        return mSeparatorsSet.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        // A ViewHolder keeps references to children views to avoid unneccessary calls
        // to findViewById() on each row.
        ViewHolder holder;
        // When convertView is not null, we can reuse it directly, there is no need
        // to reinflate it. We only inflate a new View when the convertView supplied
        // by ListView is null.
        int type = getItemViewType(position);

        if (convertView == null) {

            holder = new ViewHolder();

            switch (type) {
                case TYPE_ITEM:

                    convertView = mInflater.inflate(R.layout.list_item, null);

                    holder.text = (TextView) convertView.findViewById(R.id.Title_List_Item);
                    holder.icon = (ImageView) convertView.findViewById(R.id.Delete_List_Item);

                    convertView.findViewById(R.id.Title_List_Item).setTag(devicesList.get(position));
                    convertView.findViewById(R.id.Delete_List_Item).setTag(devicesList.get(position));

                    holder.text.setText(devicesList.get(position));
                    holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);

                    holder.text.setOnClickListener(mOnTitleClickListener);
                    holder.icon.setOnClickListener(mOnIconClickListener);

                    break;

                case TYPE_SEPARATOR:

                    convertView = mInflater.inflate(R.layout.seperator_item, null);

                    holder.text = (TextView)convertView.findViewById(R.id.Text_Seperator_Item);

                    holder.text.setText(devicesList.get(position));

                    break;
            }

            convertView.setTag(holder);

        } else {
            // Get the ViewHolder back to get fast access to the TextView
            // and the ImageView.
            holder = (ViewHolder) convertView.getTag();
        }
        return convertView;
    }


    private OnClickListener mOnTitleClickListener = new OnClickListener() {
        public void onClick(View v) {
                if(v.getTag().toString() == "ADD DEVICE"){
                    Intent addDevice = new Intent(getApplicationContext(), addDevice.class);
                    startActivity(addDevice);
                }else{
                    Intent showRemote = new Intent(TutListActivity.this, Remote.class);
                    showRemote.putExtra("selectedDevice", v.getTag().toString());
                    startActivity(showRemote);
                }
        }
    };

    private OnClickListener mOnIconClickListener = new OnClickListener() {
        public void onClick(View v) {
            String NameOfDevice[] = new String[2];
            NameOfDevice = v.getTag().toString().split(",");
            mDatenbank.execSQL("DELETE FROM addedDevices WHERE name = '"+NameOfDevice[0]+"'");

            ladeDevices();
            objectAdapter.notifyDataSetChanged();
        }
    };

    class ViewHolder {
        TextView text;
        ImageView icon;
    }
}


public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.mainlayout);

    mHelper = new DatabaseManager(this);
    mDatenbank = mHelper.getReadableDatabase();

    listView = (ListView) findViewById(R.id.list);
    objectAdapter = new EfficientAdapter(this);

    ladeDevices();
}

/*

@Override
protected void onPause() {
    super.onPause();
    mDatenbank.close();
    dataloaded = false;
}

@Override
protected void onResume() {
    super.onResume();
    mDatenbank = mHelper.getReadableDatabase();
    ladeDevices();
}

*/

private void ladeDevices() {
        devicesList = new ArrayList<String>();
        mSeparatorsSet = new TreeSet<Object>();

        Cursor devicesCursor = mDatenbank.query("addedDevices", new String[] {"name", "hersteller"}, null, null, null, null, null);
        startManagingCursor(devicesCursor);
        devicesCursor.moveToFirst();

        for (int i=0;i<devicesCursor.getCount();i++){ 
            String addingElement = new String(devicesCursor.getString(0)+", "+devicesCursor.getString(1));
            objectAdapter.addItem(addingElement);
            devicesCursor.moveToNext();
            if (i % 4 == 0) {
                objectAdapter.addSeparatorItem("separator " + i);
            }
        } 
        objectAdapter.addItem("ADD DEVICE");
        listView.setAdapter(objectAdapter);
    }

}

Thanks!

Edit: LogCat Output:

11-19 15:01:44.898: E/AndroidRuntime(957): FATAL EXCEPTION: main
11-19 15:01:44.898: E/AndroidRuntime(957): java.lang.NullPointerException
11-19 15:01:44.898: E/AndroidRuntime(957):  at com.mamlambo.tutorial.tutlist.TutListActivity$EfficientAdapter.getView(TutListActivity.java:150)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.widget.AbsListView.obtainView(AbsListView.java:2012)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.widget.ListView.makeAndAddView(ListView.java:1772)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.widget.ListView.fillUp(ListView.java:705)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.widget.ListView.correctTooHigh(ListView.java:1395)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.widget.ListView.fillGap(ListView.java:637)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.widget.AbsListView.trackMotionScroll(AbsListView.java:4546)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.widget.AbsListView.scrollIfNeeded(AbsListView.java:2852)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.widget.AbsListView.onTouchEvent(AbsListView.java:3106)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.View.dispatchTouchEvent(View.java:5486)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1953)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1714)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1959)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1728)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1959)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1728)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1959)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1728)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1959)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1728)
11-19 15:01:44.898: E/AndroidRuntime(957):  at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1892)
11-19 15:01:44.898: E/AndroidRuntime(957):  at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1371)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.app.Activity.dispatchTouchEvent(Activity.java:2364)
11-19 15:01:44.898: E/AndroidRuntime(957):  at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1840)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.View.dispatchPointerEvent(View.java:5662)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:2863)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2442)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.os.Handler.dispatchMessage(Handler.java:99)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.os.Looper.loop(Looper.java:137)
11-19 15:01:44.898: E/AndroidRuntime(957):  at android.app.ActivityThread.main(ActivityThread.java:4340)
11-19 15:01:44.898: E/AndroidRuntime(957):  at java.lang.reflect.Method.invokeNative(Native Method)
11-19 15:01:44.898: E/AndroidRuntime(957):  at java.lang.reflect.Method.invoke(Method.java:511)
11-19 15:01:44.898: E/AndroidRuntime(957):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
11-19 15:01:44.898: E/AndroidRuntime(957):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
11-19 15:01:44.898: E/AndroidRuntime(957):  at dalvik.system.NativeStart.main(Native Method)

edit: I added a new Version. I put the listView.setAdapter(objectAdapter); inside the method that loads the list entries from the Database and puts them in the ArrayList. That way the Adapter is set new every time something changes (entries are beeing deleted). Doing this allows me to put the setTag() and onClickListener() calls into the if (convertView == null) {...} so that those are only called when something changes and not everytime getView() is called. I believe that this must have been the major mistake, although I'm not sure if my solution is a very good one.

回答1:

Declare

View view = null;

as instance variable in adapter class Your else part should look like this

else {
        view = convertView;
}
holder = (ViewHolder) convertView.getTag();