Android RecyclerView加载列表时TextView.setText空指针异常排查
问题背景
正在开发加密货币价格提醒应用,核心功能为:当用户选定的加密货币价格进入预设预算区间时,向用户发送买入提醒。应用主Activity需要展示通过API拉取的全量加密货币列表,采用RecyclerView实现该列表功能,目前已完成Adapter适配器、CryptoCurrency数据模型类、列表项布局文件的编写,传入测试数据验证列表展示效果时程序崩溃。
异常信息
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
相关实现代码
MainActivity.java
package com.example.pricepointcrypto; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.os.Bundle; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { RecyclerView recyclerView; CryptoCurrencyAdapter cryptoCurrencyAdapter; ArrayList<CryptoCurrency> cryptoCurrencies = new ArrayList<>(); private String[] currencyNames = {"Bitcoin (BTC)", "Ethereal (ETH)"}; private String[] currencyRates = {"20509.88 USD" , "1174.3 USD"}; private String[] mktCap = {"3440.0%", "3235.5765%"}; private String[] totalCoins = {"3345444", "3215434"}; private String[] tradedVolume = {"4534B", "98978B"}; private int[] currencyLogos = {R.drawable.cryptocurrencylogo, R.drawable.ic_launcher_background}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); for(int i=0;i<currencyNames.length;i++) { CryptoCurrency cryptoCurrency = new CryptoCurrency(); cryptoCurrency.setCurrencyName(currencyNames[i]); cryptoCurrency.setCurrentRate(currencyRates[i]); cryptoCurrency.setMarketCap(mktCap[i]); cryptoCurrency.setTotalCoins(totalCoins[i]); cryptoCurrency.setTradedVolume(tradedVolume[i]); cryptoCurrency.setCurrencyLogo(currencyLogos[i]); cryptoCurrencies.add(cryptoCurrency); } cryptoCurrencyAdapter = new CryptoCurrencyAdapter(cryptoCurrencies); recyclerView = (RecyclerView)findViewById(R.id.crypto_currencies); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(cryptoCurrencyAdapter); } }
CryptoCurrency.java
package com.example.pricepointcrypto; public class CryptoCurrency { private String currencyName; private String currentRate; private String marketCap; private String totalCoins; private String tradedVolume; private int currencyLogo; public int getCurrencyLogo() { return currencyLogo; } public void setCurrencyLogo(int currencyLogo) { this.currencyLogo = currencyLogo; } public String getCurrencyName() { return currencyName; } public void setCurrencyName(String currencyName) { this.currencyName = currencyName; } public String getCurrentRate() { return currentRate; } public void setCurrentRate(String currentRate) { this.currentRate = currentRate; } public String getMarketCap() { return marketCap; } public void setMarketCap(String marketCap) { this.marketCap = marketCap; } public String getTotalCoins() { return totalCoins; } public void setTotalCoins(String totalCoins) { this.totalCoins = totalCoins; } public String getTradedVolume() { return tradedVolume; } public void setTradedVolume(String tradedVolume) { this.tradedVolume = tradedVolume; } }
CryptoCurrencyAdapter.java
package com.example.pricepointcrypto; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.cardview.widget.CardView; import androidx.recyclerview.widget.RecyclerView; import java.util.List; public class CryptoCurrencyAdapter extends RecyclerView.Adapter<CryptoCurrencyAdapter.ViewHolder> { List<CryptoCurrency> cryptoCurrencyList; Context context; public CryptoCurrencyAdapter(List<CryptoCurrency> cryptoCurrencyList) { this.cryptoCurrencyList = cryptoCurrencyList; } @NonNull @Override public CryptoCurrencyAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.crypto_card_layout, parent,false); ViewHolder viewHolder = new ViewHolder(view); context = parent.getContext(); return viewHolder; } @Override public void onBindViewHolder(@NonNull CryptoCurrencyAdapter.ViewHolder holder, int position) { CryptoCurrency cryptoCurrency = cryptoCurrencyList.get(position); holder.currencyName.setText(cryptoCurrency.getCurrencyName()); holder.currentRate.setText(cryptoCurrency.getCurrentRate()); holder.marketCap.setText(cryptoCurrency.getMarketCap()); holder.totalCoins.setText(cryptoCurrency.getTotalCoins()); holder.tradedVolume.setText(cryptoCurrency.getTradedVolume()); holder.currencyImg.setImageResource(cryptoCurrency.getCurrencyLogo()); holder.currencyCard.setOnClickListener(view -> Toast.makeText(context,"The position is:"+position,Toast.LENGTH_SHORT).show()); } @Override public int getItemCount() { return cryptoCurrencyList.size(); } public class ViewHolder extends RecyclerView.ViewHolder { ImageView currencyImg; TextView currencyName; TextView currentRate; TextView marketCap; TextView totalCoins; TextView tradedVolume; CardView currencyCard; public ViewHolder(@NonNull View itemView) { super(itemView); currencyImg = itemView.findViewById(R.id.currency_logo); currencyName = itemView.findViewById(R.id.currency_name); currentRate = itemView.findViewById(R.id.current_rate); marketCap = itemView.findViewById(R.id.market_cap); totalCoins = itemView.findViewById(R.id.coin_total); tradedVolume = itemView.findViewById(R.id.traded_volume); currencyCard = itemView.findViewById(R.id.currency_card); } } }
异常触发原因
空指针的直接触发点是onBindViewHolder执行时,持有的某个TextView/ImageView实例为null,根本原因是资源ID匹配失败:ViewHolder初始化时调用findViewById传入的R.id.xxx,在加载的crypto_card_layout布局中找不到对应控件,方法直接返回null,后续调用null对象的setText/setImageResource方法就会崩溃。
常见的触发场景:
- 列表项布局中控件的
android:id拼写错误,比如把currency_name多写字母、大小写写错 - 布局中漏写了对应控件的ID属性
onCreateViewHolder加载布局时传错了资源ID,误加载了其他不含对应控件的布局
注:当前MainActivity的测试数据组装、RecyclerView配置,以及CryptoCurrency数据模型的代码均无逻辑问题,不需要修改。
修复方案
- 打开
res/layout/crypto_card_layout.xml列表项布局文件,逐行核对所有控件的ID,和ViewHolder中绑定的ID一一对应:- 加密货币logo ImageView 对应ID:
currency_logo - 货币名称TextView 对应ID:
currency_name - 当前价格TextView 对应ID:
current_rate - 市值TextView 对应ID:
market_cap - 总发行量TextView 对应ID:
coin_total - 交易量TextView 对应ID:
traded_volume - 外层卡片CardView 对应ID:
currency_card
- 加密货币logo ImageView 对应ID:
- 给缺失ID的控件补上对应
@+id/xxx属性,把写错的ID改成和代码中完全一致的命名。 - 核对
onCreateViewHolder中inflate的布局资源是R.layout.crypto_card_layout,不要误引用其他布局。 - 修改完成后在Android Studio顶部菜单栏执行
Build -> Clean Project,清除资源缓存后重新运行即可。
可参考的正确列表项布局示例:
<?xml version="1.0" encoding="utf-8"?> <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/currency_card" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp" app:cardCornerRadius="8dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="12dp"> <ImageView android:id="@+id/currency_logo" android:layout_width="48dp" android:layout_height="48dp" android:contentDescription="currency logo"/> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginStart="12dp" android:orientation="vertical"> <TextView android:id="@+id/currency_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" android:textStyle="bold"/> <TextView android:id="@+id/current_rate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:textSize="14sp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp"> <TextView android:id="@+id/market_cap" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textSize="12sp"/> <TextView android:id="@+id/coin_total" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textSize="12sp"/> <TextView android:id="@+id/traded_volume" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textSize="12sp"/> </LinearLayout> </LinearLayout> </LinearLayout> </androidx.cardview.widget.CardView>
内容的提问来源于stack exchange,提问作者Sarimm Chaudhry
相关产品推荐
相关产品推荐

