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的思路及代码

自定义 ListView Android

使用自定义适配器自定义 Android ListView

Android 自定义 ListView/Adapter

带有 Intent 和自定义 Listview 的 Android

Android - ListView 中的 EditTexts 绑定到自定义 ArrayAdapter - 跟踪更改