Android GridView集成ThingSpeak图表遇空指针异常及布局问题
解决GridView中ThingSpeak图表空指针和布局错乱问题
看起来你遇到的核心问题有两个:NullPointerException和GridView布局错乱,我来帮你逐一排查并给出修复方案:
一、空指针的根源分析
你的错误日志指向TempHumidity.java第60行(chartView.setZoomEnabled(false);),原因很直接:
- 你的Activity加载的布局是
main.xml,里面只有一个GridView,根本没有R.id.chart这个控件; chart控件实际上在你用来作为Adapter item的activity_main.xml里,直接在Activity中调用findViewById找不到它,所以chartView是null,调用它的方法自然抛出空指针。
二、布局错乱的原因
你的GridViewAdapter实现存在明显问题:
- 每次
getView都inflate包含三个图表的activity_main.xml,相当于每个GridView单元格里都塞了三个图表,完全不符合2x2网格的需求; - 你传入了
charts数组,但在getView里根本没用到,既没有根据position加载对应的图表,也没有区分第四个单元格的WebView。
三、完整修复方案
1. 重构布局文件
给GridView的每个item单独创建布局:
- 创建
grid_item_chart.xml(用于前三个图表单元格):
<?xml version="1.0" encoding="utf-8"?> <lecho.lib.hellocharts.view.LineChartView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/item_chart" android:layout_width="match_parent" android:layout_height="200dp" android:scaleType="fitXY" />
- 创建
grid_item_webview.xml(用于第四个直播单元格):
<?xml version="1.0" encoding="utf-8"?> <WebView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/item_webview" android:layout_width="match_parent" android:layout_height="200dp" />
2. 重写GridViewAdapter
让Adapter支持两种类型的item(图表和WebView),并在getView中正确初始化每个item:
import android.content.Context; import android.graphics.Color; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; import android.widget.BaseAdapter; import android.widget.Toast; import com.macroyau.thingspeakandroid.ThingSpeakChannel; import com.macroyau.thingspeakandroid.ThingSpeakLineChart; import com.macroyau.thingspeakandroid.model.ChannelFeed; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import lecho.lib.hellocharts.model.LineChartData; import lecho.lib.hellocharts.model.Viewport; import lecho.lib.hellocharts.view.LineChartView; public class GridViewAdapter extends BaseAdapter { private Context context; private List<Integer> itemTypes; private List<ChartConfig> chartConfigs; // 封装每个图表的配置信息 public static class ChartConfig { public long channelId; public int fieldId; public String lineColor; public ChartConfig(long channelId, int fieldId, String lineColor) { this.channelId = channelId; this.fieldId = fieldId; this.lineColor = lineColor; } } public GridViewAdapter(Context context) { this.context = context; itemTypes = new ArrayList<>(); chartConfigs = new ArrayList<>(); // 添加三个图表的配置 chartConfigs.add(new ChartConfig(377467, 1, "#D32F2F")); chartConfigs.add(new ChartConfig(357670, 1, "#D32F2F")); chartConfigs.add(new ChartConfig(377509, 1, "#D32F2F")); itemTypes.add(0); itemTypes.add(0); itemTypes.add(0); // 添加第四个WebView类型的item itemTypes.add(1); } @Override public int getCount() { return itemTypes.size(); } @Override public int getItemViewType(int position) { return itemTypes.get(position); } @Override public int getViewTypeCount() { return 2; // 两种item类型:图表、WebView } @Override public Object getItem(int position) { return getItemViewType(position) == 0 ? chartConfigs.get(position) : null; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { int viewType = getItemViewType(position); if (viewType == 0) { // 处理图表item ChartViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(context).inflate(R.layout.grid_item_chart, parent, false); holder = new ChartViewHolder(); holder.chartView = convertView.findViewById(R.id.item_chart); convertView.setTag(holder); } else { holder = (ChartViewHolder) convertView.getTag(); } // 初始化图表控件 LineChartView chartView = holder.chartView; chartView.setZoomEnabled(false); chartView.setValueSelectionEnabled(true); // 获取当前图表的配置 ChartConfig config = chartConfigs.get(position); Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.MINUTE, -1); // 配置并加载ThingSpeak图表数据 ThingSpeakLineChart tsChart = new ThingSpeakLineChart(config.channelId, config.fieldId); tsChart.setNumberOfEntries(200); tsChart.setValueAxisLabelInterval(10); tsChart.setDateAxisLabelInterval(1); tsChart.useSpline(true); tsChart.setLineColor(Color.parseColor(config.lineColor)); tsChart.setAxisColor(Color.parseColor("#455a64")); tsChart.setChartStartDate(calendar.getTime()); final LineChartView finalChartView = chartView; tsChart.setListener(new ThingSpeakLineChart.ChartDataUpdateListener() { @Override public void onChartDataUpdated(long channelId, int fieldId, String title, LineChartData lineChartData, Viewport maxViewport, Viewport initialViewport) { finalChartView.setLineChartData(lineChartData); finalChartView.setMaximumViewport(maxViewport); finalChartView.setCurrentViewport(initialViewport); } }); tsChart.loadChartData(); // 加载Channel Feed信息(可选,根据需求调整) ThingSpeakChannel tsChannel = new ThingSpeakChannel(config.channelId); tsChannel.setChannelFeedUpdateListener(new ThingSpeakChannel.ChannelFeedUpdateListener() { @Override public void onChannelFeedUpdated(long channelId, String channelName, ChannelFeed channelFeed) { ((TempHumidity)context).getSupportActionBar().setTitle(channelName); ((TempHumidity)context).getSupportActionBar().setSubtitle("Channel " + channelId); Date lastUpdate = channelFeed.getChannel().getUpdatedAt(); Toast.makeText(context, lastUpdate.toString(), Toast.LENGTH_LONG).show(); } }); tsChannel.loadChannelFeed(); } else { // 处理WebView item WebViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(context).inflate(R.layout.grid_item_webview, parent, false); holder = new WebViewHolder(); holder.webView = convertView.findViewById(R.id.item_webview); convertView.setTag(holder); // 初始化WebView,后续替换为你的直播URL holder.webView.getSettings().setJavaScriptEnabled(true); // holder.webView.loadUrl("你的摄像头直播地址"); } else { holder = (WebViewHolder) convertView.getTag(); } } return convertView; } // 图表ViewHolder,优化复用 private static class ChartViewHolder { LineChartView chartView; } // WebViewViewHolder,优化复用 private static class WebViewHolder { WebView webView; } }
3. 简化TempHumidity Activity
把原来在Activity中初始化图表的逻辑全部移到Adapter后,Activity只需要初始化GridView即可:
package com.macroyau.thingspeakandroid.demo; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.GridView; public class TempHumidity extends AppCompatActivity { GridView simpleGrid; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); simpleGrid = findViewById(R.id.simpleGridView); GridViewAdapter customAdapter = new GridViewAdapter(this); simpleGrid.setAdapter(customAdapter); } }
四、额外注意事项
- 记得在AndroidManifest.xml中添加网络权限:
<uses-permission android:name="android.permission.INTERNET" />; - 可以给GridView添加
android:stretchMode="columnWidth"、android:verticalSpacing="1dp"和android:horizontalSpacing="1dp"来优化网格布局的显示效果; - WebView加载直播时,需根据直播源的要求调整WebView的设置(比如开启JavaScript、处理HTTPS证书等)。
内容的提问来源于stack exchange,提问作者Tia
相关产品推荐
相关产品推荐

