Android 自定义 ListView onClickListener returns 空

Android Custom ListView onClickListener returns null

我的自定义 ListView 发生了一些非常奇怪的事情。加载列表后,如果我非常快速地单击 ListItems,它会崩溃,但如果我给它一秒钟,那么一切正常。

我的列表包含部分标题(见屏幕截图),它改变了模型列表中 UI 列表的位置。因此,当我填充 ListView 时,我在 listAdapter class 中进行了映射,我相信这会导致代码崩溃。我在映射完成之前单击了视图,我得到了给定键的 "Value" 为 null。


public class ListAdapter extends BaseAdapter implements SectionIndexer {
private Context mContext;
private static final String TAG = "ListAdapter";
private LayoutInflater mInflater;
private ArrayList mData = new ArrayList();

// MapIndex to map the scroll alphabet to section
private HashMap<String, Integer> mapIndex;

// To return the right index otherwise the click will return wrong objects becasue the count the section headers
private ConcurrentHashMap<Integer, Integer> viewPositions;

// contains all the sections alphabets
private String[] sections;

/** Int code to represent regular list items **/
public static final int TYPE_ITEM = 0;

/** Int val to represent header list item **/
private static final int TYPE_SEPARATOR = 1;

/** There are two different items **/
private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1;

private static TextView name;
private static TextView industry;
private static UserModel user;
private ImageView logo;
private static int mCount = 0;
public ListAdapter(Context context, ArrayList<UserModel> users) {
    Collections.sort(users, UserModel.Comparators.NAME);
    viewPositions = new ConcurrentHashMap<Integer, Integer>();
    mContext = context;
    mInflater = ((Activity)mContext).getLayoutInflater();
    mapIndex = new LinkedHashMap<String, Integer>();

    for (int i = 0; i < users.size(); i++){

       String ch =  users.get(i).getFirstName().substring(0,1);
       ch.toUpperCase();
       /** If HashMap doesn't have the Alphabet put the new one **/
       if (!mapIndex.containsKey(ch)){
        mapIndex.put(ch, i);
        addSeparatorItem(ch);
        mCount ++;
        }
        addItem(users.get(i));
        viewPositions.put(mCount,i);
        mCount ++;
    }

    Set<String> sectionLetters = mapIndex.keySet();
    sections = new String[sectionLetters.size()];
    sectionLetters.toArray(sections);

}

public int getRealPosition(int key){
    Log.d(TAG, "getRealPosition: Key = " + key + "Value = " + viewPositions.get(key));
    return viewPositions.get(key);
}


public void addItem(final UserModel item) {
    mData.add(item);
    notifyDataSetChanged();
}

public void addSeparatorItem(final String item) {
    mData.add(item);
    notifyDataSetChanged();
}

@Override
public int getItemViewType(int position) {
    if (mData.get(position) instanceof UserModel){
        return TYPE_ITEM;
    }
    return TYPE_SEPARATOR;
}

@Override
public int getViewTypeCount() {
    return TYPE_MAX_COUNT;
}

@Override
public int getCount() {
    return mData.size();
}

@Override
public Object getItem(int position) {
    return mData.get(position);
}


@Override
public long getItemId(int position) {
    return position;
}


@Override
public View getView(int position, View convertView, ViewGroup parent) {
    int type = getItemViewType(position);
        switch (type) {
            case TYPE_ITEM:
                user = ((UserModel)mData.get(position));
                convertView = mInflater.inflate(R.layout.fragment_contact_list, null);
                name = (TextView) convertView.findViewById(R.id.list_contactName);
                name.setText(user.getFirstName() + " " + user.getLastName());
                industry = (TextView) convertView.findViewById(R.id.list_industry);
                industry.setText(user.getIndustry());
                logo = (ImageView) convertView.findViewById(R.id.list_logo); 
                break;
            case TYPE_SEPARATOR:
                TextView seperator = (TextView) mInflater.inflate(R.layout.list_header, null);
                seperator.setText(mData.get(position).toString());
                seperator.setClickable(false);
                seperator.setBackgroundColor(mContext.getResources().getColor(R.color.blue));
                convertView = seperator;
                break;
        }
        return convertView;

}

/********************* SectionIndexer Methods ***********************/
@Override
public Object[] getSections() {
    return sections;
}

@Override
public int getPositionForSection(int sectionIndex) {
    return mapIndex.get(sections[sectionIndex]);
}

@Override
public int getSectionForPosition(int position) {
    return 0;
}
}

public class ContactListActivity extends AppCompatActivity {

private ArrayList<UserModel> mUserList;
private ListAdapter adapter;
private IndexableListView mListView;
public static final String CONTACT_FRAGMENT = "ContactFragment";

@Override
public void onCreate(Bundle savedInstances) {
    super.onCreate(savedInstances);
    setContentView(R.layout.list_layout);
    //setHasOptionsMenu(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    mUserList = UserList.get(this).getUserList();
    Collections.sort(mUserList, UserModel.Comparators.NAME);

    mListView = (IndexableListView) findViewById(R.id.listview);
    adapter = new ListAdapter(this, mUserList);
    mListView.setAdapter(adapter);
    mListView.setFastScrollEnabled(true);
    mListView.setDivider(null);

    mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (adapter.getItemViewType(position) == adapter.TYPE_ITEM) {
                    Intent intent = new Intent(view.getContext(), ContactActivity.class);
                    intent.putExtra(HomeFragment.LAUNCH_FRAGMENT, CONTACT_FRAGMENT);
                    intent.putExtra("userId", adapter.getRealPosition(position));
                    startActivity(intent);
                }
        }
    });
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.sort_menu, menu);
    return true;
}



@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == R.id.sortName){
        Log.d("Sort ", "Sort by Name Clicked");
    }

    return super.onOptionsItemSelected(item);
}
}

12-10 09:47:39.951 17995-17995/com.example.macintosh.klickcard D/ListAdapter: getRealPosition: Key = 6Value = null
12-10 09:47:39.971 17995-17995/com.example.macintosh.klickcard D/AndroidRuntime: Shutting down VM
12-10 09:47:39.981 17995-17995/com.example.macintosh.klickcard E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.macintosh.klickcard, PID: 17995
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Integer.intValue()' on a null object reference
at com.example.macintosh.klickcard.Helpers.ListAdapter.getRealPosition(ListAdapter.java:83)
at com.example.macintosh.klickcard.ContactListActivity.onItemClick(ContactListActivity.java:52)
at android.widget.AdapterView.performItemClick(AdapterView.java:339)
at android.widget.AbsListView.performItemClick(AbsListView.java:1544)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:3721)
at android.widget.AbsListView.run(AbsListView.java:5660)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6837)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)

您每次都在 ListAdapter getView 方法中创建新的 View 元素,这可能是崩溃的原因。您可以使用之前创建的视图元素。

public View getView(int position, View convertView, ViewGroup parent) {

  // Create view element only if convertView is null
  if (convertView == null) {

    LayoutInflater inflater = context.getLayoutInflater();
    convertView= inflater.inflate(YOUR_XML_RESOURCE_ID, parent, false);

  }

  // Populate convertView with necessary details

  return convertView;
}

我花了一段时间才弄明白这一点。我发现的几件事是不检查转换视图是否为空,因为它会扰乱列表的组织。其次是不使用 hashmap,因为适配器 class 在随机时间被调用并且您映射到错误的位置因此空指针异常。诀窍就是这个方法。没什么大不了的,只是一个简单的索引偏移。

public int getRealPosition(int key){

    user = (UserModel) getItem(key);
    String ch =  user.getFirstName().substring(0,1);

    for (int i = 0; i < sections.length; i++) {
        if (ch.equalsIgnoreCase(sections[i])) {
            return key - (i + 1);
        }
    }
    return -1;
}