Android新手求助:如何用静态方法获取短信并在RecyclerView中展示
Hey there! Let's fix up your code step by step so you can successfully display SMS messages in a RecyclerView with CardViews. I'll break this down into easy-to-follow parts since you're new to Android development.
First, let's spot the key issues in your current code:
- The
SMSclass incorrectly inherits fromAppCompatActivity— it should be a simple data model class to hold SMS data, not an Activity. - The
getSMS()static method tries to usegetContentResolver()without a Context, which won't work because this method is tied to a Context (like an Activity). getSMS()returns anArrayList<String>, but yourSmsAdapterexpects aList<SMS>— this type mismatch will cause errors.- You haven't implemented dynamic permission requests for reading SMS, which is mandatory on Android 6.0 (API level 23) and above.
Step 1: Fix the SMS Data Model Class
Let's rewrite the SMS class to be a proper data holder, and update the getSMS() method to accept a Context and return a list of SMS objects.
public class SMS { private String phoneNo; private String smsBody; // Constructor to initialize SMS data public SMS(String phoneNo, String smsBody) { this.phoneNo = phoneNo; this.smsBody = smsBody; } // Getters and setters public String getPhoneNo() { return phoneNo; } public void setPhoneNo(String phoneNo) { this.phoneNo = phoneNo; } public String getSmsBody() { return smsBody; } public void setSmsBody(String smsBody) { this.smsBody = smsBody; } // Static method to fetch SMS from device, requires Context public static List<SMS> getSMS(Context context) { List<SMS> smsList = new ArrayList<>(); // URI for inbox SMS content provider Uri uriSMSURI = Uri.parse("content://sms/inbox"); // Query only the columns we need to be efficient Cursor cur = context.getContentResolver().query( uriSMSURI, new String[]{"address", "body"}, null, null, null ); if (cur != null) { // Iterate through the cursor to collect SMS data while (cur.moveToNext()) { String address = cur.getString(cur.getColumnIndex("address")); String body = cur.getString(cur.getColumnIndex("body")); smsList.add(new SMS(address, body)); } cur.close(); // Always close cursors to avoid memory leaks } return smsList; } }
Quick Explanation:
- Removed
AppCompatActivityinheritance — this class only needs to store data, not act as a screen. - Added a Context parameter to
getSMS()so we can accessgetContentResolver()(which lets us read system SMS data). - We now create an
SMSobject for each message instead of concatenating strings, which matches what your adapter expects.
Step 2: Update MainActivity to Handle Permissions & Load SMS
We need to add code to request SMS read permission, and only load messages once permission is granted. We'll also fix the RecyclerView setup to use the correct data type.
import android.Manifest; import android.content.pm.PackageManager; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.view.MenuItem; import android.widget.Toast; import com.s4starb4boy.smstopdf.adapter.SmsAdapter; import com.s4starb4boy.smstopdf.model.SMS; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private static final int READ_SMS_PERMISSIONS_REQUEST = 100; private Toolbar toolbar; private RecyclerView recyclerView; private SmsAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setUpToolbar(); setUpRecyclerView(); checkReadSmsPermission(); // Check permission first } private void setUpRecyclerView() { recyclerView = findViewById(R.id.recyclerView); // Initialize adapter with empty list first adapter = new SmsAdapter(this, new ArrayList<>()); recyclerView.setAdapter(adapter); // Set vertical layout for the list LinearLayoutManager layoutManager = new LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager); recyclerView.setItemAnimator(new DefaultItemAnimator()); } private void checkReadSmsPermission() { // Check if permission is already granted if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED) { // Request permission if not granted ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.READ_SMS}, READ_SMS_PERMISSIONS_REQUEST ); } else { // Permission already granted, load SMS data loadSmsData(); } } // Handle permission request result @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == READ_SMS_PERMISSIONS_REQUEST) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { loadSmsData(); } else { Toast.makeText(this, "Need SMS read permission to display messages", Toast.LENGTH_LONG).show(); } } } private void loadSmsData() { // Fetch SMS using our fixed static method List<SMS> smsList = SMS.getSMS(this); // Update the adapter with real data adapter = new SmsAdapter(this, smsList); recyclerView.setAdapter(adapter); } private void setUpToolbar() { toolbar = findViewById(R.id.toolbar); toolbar.setTitle("SMS Viewer"); toolbar.inflateMenu(R.menu.menu_main); toolbar.setOnMenuItemClickListener(item -> { String msg = ""; switch (item.getItemId()) { case R.id.settings: msg = "Settings"; break; case R.id.edit: msg = "Edit"; break; case R.id.search: msg = "Search"; break; case R.id.exit: msg = "Exit"; break; case R.id.delete: msg = "Delete"; break; } Toast.makeText(MainActivity.this, msg + " Clicked", Toast.LENGTH_SHORT).show(); return true; }); } }
Quick Explanation:
- Added
checkReadSmsPermission()to request SMS access at runtime (required for modern Android versions). loadSmsData()fetches the SMS list and updates the RecyclerView adapter with the real data.- We initialize the adapter with an empty list first, then replace it once we have permission and data.
Step 3: Update the SMS Item Layout (sms_list.xml)
Make sure your item layout uses a CardView to wrap the SMS content. Create or update res/layout/sms_list.xml:
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" card_view:cardCornerRadius="4dp" card_view:cardElevation="4dp" card_view:cardUseCompatPadding="true"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <TextView android:id="@+id/tvPhoneNo" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="16sp" android:textStyle="bold" /> <TextView android:id="@+id/tvMessageBody" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:textSize="14sp" /> </LinearLayout> </android.support.v7.widget.CardView>
Quick Explanation:
- CardView gives each list item a material design look with rounded corners and shadow.
- The LinearLayout stacks the phone number and message body vertically with padding for spacing.
Step 4: Add Permission to AndroidManifest.xml
Don't forget to declare the SMS read permission in your manifest (inside <manifest> but outside <application>):
<uses-permission android:name="android.permission.READ_SMS" />
Step 5: Verify the SmsAdapter Class
Your existing adapter is mostly correct — here's the confirmed version (no major changes, just ensuring it matches our updated data model):
package com.s4starb4boy.smstopdf.adapter; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.s4starb4boy.smstopdf.R; import com.s4starb4boy.smstopdf.model.SMS; import java.util.List; public class SmsAdapter extends RecyclerView.Adapter<SmsAdapter.SmsViewHolder> { private static final String TAG = SmsAdapter.class.getSimpleName(); private List<SMS> smsList; private LayoutInflater mInflater; public SmsAdapter(Context context, List<SMS> smsList) { this.smsList = smsList; this.mInflater = LayoutInflater.from(context); } @Override public SmsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { Log.i(TAG, "onCreateViewHolder"); View view = mInflater.inflate(R.layout.sms_list, parent, false); return new SmsViewHolder(view); } @Override public void onBindViewHolder(SmsViewHolder holder, int position) { Log.i(TAG, "onBindViewHolder: " + position); SMS currentObj = smsList.get(position); holder.setData(currentObj, position); } @Override public int getItemCount() { return smsList.size(); } class SmsViewHolder extends RecyclerView.ViewHolder{ TextView tvPhoneNo; TextView tvsmsBody; int position; SMS current; public SmsViewHolder(View itemView) { super(itemView); tvPhoneNo = itemView.findViewById(R.id.tvPhoneNo); tvsmsBody = itemView.findViewById(R.id.tvMessageBody); } public void setData(SMS current, int position) { this.tvPhoneNo.setText(current.getPhoneNo()); this.tvsmsBody.setText(current.getSmsBody()); this.current = current; this.position = position; } } }
For Beginners: Key Concepts to Know
- Runtime Permissions: Android requires apps to ask users for access to sensitive data (like SMS) when the app is running, not just during installation.
- Content Providers: These are system-level databases that hold data like SMS, contacts, and photos. We use
getContentResolver()to query them. - RecyclerView: A efficient widget for displaying large lists. The adapter connects your data to the UI items (CardViews in this case).
If any part doesn't make sense — like how permissions work or what a content provider is — just ask, and I'll break it down further!
内容的提问来源于stack exchange,提问作者user810265

