You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Play应用获READ_CONTACTS权限后,联系人无法加载至自定义ListView的解决方法

Fixing Contact Loading into Custom ListView After READ_CONTACTS Permission

Hey there! Let's work through this issue together. You've already added the manifest permission, so we need to focus on two key areas: properly handling the permission request result and correctly querying/binding contact data to your ListView. Here's a step-by-step breakdown:

1. Ensure You're Handling Dynamic Permission Requests Correctly (Android 6.0+)

Even with the manifest permission, Android requires dynamic permission requests for dangerous permissions like READ_CONTACTS on API 23+. Make sure you're requesting the permission on first launch, and triggering the contact load only after the user grants it.

Modern Approach (Using ActivityResultContracts)

The old onRequestPermissionsResult method is deprecated, so use the Activity Result API instead:

// In your Activity/Fragment
private val requestContactPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
    if (isGranted) {
        // Permission granted—load contacts now
        loadContacts()
    } else {
        // Handle denial (show explanation, redirect to settings, etc.)
        Toast.makeText(this, "Permission needed to load contacts", Toast.LENGTH_SHORT).show()
    }
}

// Call this on first launch (e.g., in onCreate)
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
        // Request permission
        requestContactPermission.launch(Manifest.permission.READ_CONTACTS)
    } else {
        // Permission already granted—load contacts immediately
        loadContacts()
    }
}

2. Correctly Query Contact Data After Permission Grant

Once permission is granted, use ContentResolver to query the contacts database. Always do this off the main thread to avoid ANRs (Application Not Responding) errors.

Example: Load Contacts with Coroutines (Kotlin)

private fun loadContacts() {
    lifecycleScope.launch(Dispatchers.IO) {
        val contactList = mutableListOf<Contact>()
        val projection = arrayOf(
            ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
            ContactsContract.CommonDataKinds.Phone.NUMBER
        )

        contentResolver.query(
            ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
            projection,
            null,
            null,
            "${ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME} ASC"
        )?.use { cursor ->
            val nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
            val numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)

            while (cursor.moveToNext()) {
                val name = cursor.getString(nameIndex)
                val number = cursor.getString(numberIndex)
                contactList.add(Contact(name, number))
            }
        }

        // Switch back to main thread to update ListView
        withContext(Dispatchers.Main) {
            val listView = findViewById<ListView>(R.id.custom_list_view)
            listView.adapter = ContactAdapter(this@MainActivity, contactList)
        }
    }
}

// Data class to hold contact info
data class Contact(val name: String, val number: String)

Java Equivalent (Using AsyncTask)

If you're using Java, use an AsyncTask or ExecutorService to run the query in the background:

private void loadContacts() {
    new AsyncTask<Void, Void, List<Contact>>() {
        @Override
        protected List<Contact> doInBackground(Void... voids) {
            List<Contact> contactList = new ArrayList<>();
            String[] projection = {
                ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
                ContactsContract.CommonDataKinds.Phone.NUMBER
            };

            Cursor cursor = getContentResolver().query(
                ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                projection,
                null,
                null,
                ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC"
            );

            if (cursor != null) {
                int nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
                int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);

                while (cursor.moveToNext()) {
                    String name = cursor.getString(nameIndex);
                    String number = cursor.getString(numberIndex);
                    contactList.add(new Contact(name, number));
                }
                cursor.close();
            }
            return contactList;
        }

        @Override
        protected void onPostExecute(List<Contact> contacts) {
            ListView listView = findViewById(R.id.custom_list_view);
            listView.setAdapter(new ContactAdapter(MainActivity.this, contacts));
        }
    }.execute();
}

// Contact model class
public class Contact {
    private String name;
    private String number;

    public Contact(String name, String number) {
        this.name = name;
        this.number = number;
    }

    // Getters for name and number
    public String getName() { return name; }
    public String getNumber() { return number; }
}

3. Create a Custom Adapter for Your ListView

You need a custom adapter to bind the contact data to your ListView's item layout.

Example ContactAdapter (Kotlin)

class ContactAdapter(context: Context, private val contacts: List<Contact>) : BaseAdapter() {
    private val inflater = LayoutInflater.from(context)

    override fun getCount(): Int = contacts.size

    override fun getItem(position: Int): Contact = contacts[position]

    override fun getItemId(position: Int): Long = position.toLong()

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val view = convertView ?: inflater.inflate(R.layout.list_item_contact, parent, false)
        val contact = getItem(position)

        view.findViewById<TextView>(R.id.tv_contact_name).text = contact.name
        view.findViewById<TextView>(R.id.tv_contact_number).text = contact.number

        return view
    }
}

Corresponding List Item Layout (list_item_contact.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_contact_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:textStyle="bold"/>

    <TextView
        android:id="@+id/tv_contact_number"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        android:textColor="@android:color/darker_gray"/>

</LinearLayout>

4. Common Pitfalls to Check

  • Did you trigger loadContacts() only after permission is granted? If you're trying to load contacts before the user accepts, the query will fail silently.
  • Is your ListView ID correct in the Activity layout? Double-check that you're referencing the right ListView when setting the adapter.
  • Are you querying the correct URI? Use ContactsContract.CommonDataKinds.Phone.CONTENT_URI to get phone contacts (not the base ContactsContract.Contacts.CONTENT_URI, which doesn't include phone numbers by default).
  • Did you handle empty cursors? If there are no contacts, your ListView will show empty—add an empty state view if needed.

内容的提问来源于stack exchange,提问作者Hatay Berkay Işıkoğlu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:33:47