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;
}
我的自定义 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;
}