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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:43:36