Android 自定义 ListView onClickListener 返回 null
Posted
技术标签:
【中文标题】Android 自定义 ListView onClickListener 返回 null【英文标题】:Android Custom ListView onClickListener returns null 【发布时间】:2016-03-15 04:50:04 【问题描述】:我的自定义 ListView 发生了一些非常奇怪的事情。加载列表后,如果我非常快地单击 ListItems,它会崩溃,但如果我再给它一秒钟,那么一切正常。
我的列表包含部分标题(见屏幕截图),它改变了模型列表中 UI 列表的位置。因此,当我填充 ListView 时,我会在 listAdapter 类中进行映射,我相信这会导致代码崩溃。我在映射完成之前单击视图,我得到给定键的“值”为空。
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$1.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$3.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)
【问题讨论】:
发布您的堆栈跟踪 @Jas 堆栈跟踪已添加。 【参考方案1】:您每次都在 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;
【讨论】:
似乎并非如此。这就是我最初设置它的方式,但列表重复了错误部分下的元素。即将 A 中的列表项放在最后一部分下。它也不能修复崩溃。【参考方案2】:我花了一段时间才弄明白。我发现的几件事是不要检查转换视图是否为空,因为它会破坏列表的组织。其次是不使用哈希图,因为适配器类在随机时间被调用,并且您映射到错误的位置,因此出现空指针异常。诀窍就是这种方法。没有什么在意的只是一个简单的索引转换。
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;
【讨论】:
以上是关于Android 自定义 ListView onClickListener 返回 null的主要内容,如果未能解决你的问题,请参考以下文章
转Android自定义Adapter的ListView的思路及代码