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

MPAndroidChart LineChart X轴无法显示多个日期的问题排查

问题分析与解决方案

问题描述

现有GraphActivity类实现了Firebase Realtime Database数据上传与LineChart展示功能,数据上传成功,但图表仅显示最后一条日期,X轴无法展示所有已上传的日期。

代码中的核心问题

1. X轴格式化逻辑错误

当前X轴的ValueFormatter只处理value=0和value=1的情况,其余值返回空字符串,导致只有前两个X轴标签会显示,后续日期全部被隐藏。同时startDate固定为初始化时的newDate,无法匹配每条数据的实际日期。

2. Firebase数据读取逻辑混乱

  • 代码中先切换到Date节点上传数据,再切换到Price节点上传并监听,但监听Price节点时,错误地从价格节点的snapshot中读取日期(Long longVal = snapshot.getValue(Long.class); newDate = longVal.toString();),导致日期数据被价格数值覆盖,完全无法关联正确的日期与价格。
  • 没有将日期和价格作为一组数据存储,无法正确匹配每条价格对应的日期。

3. 数据集合未清空,重复添加

每次onDataChange触发时,entries列表没有被清空,会重复添加历史数据,导致图表数据混乱。

4. 重复设置图表数据

在ValueEventListener外部又重复创建了LineDataSet和LineData并设置给图表,这会覆盖异步获取的正确数据,干扰图表展示。

修复步骤

第一步:修正Firebase数据结构与上传逻辑

将日期和价格作为一组数据存储,定义数据模型类:

// 数据模型类,用于关联日期与价格
class PriceRecord {
    public String date;
    public int price;

    // Firebase需要空构造函数
    public PriceRecord() {}

    public PriceRecord(String date, int price) {
        this.date = date;
        this.price = price;
    }
}

修改上传逻辑,将日期和价格作为完整数据上传:

// 替换原有的Date、Price节点上传代码
FirebaseDatabase db = FirebaseDatabase.getInstance();
DatabaseReference reference = db.getReference("PriceRecords");
// 上传一组完整的日期+价格数据
reference.push().setValue(new PriceRecord(newDate, price));

第二步:修正X轴格式化逻辑

重新实现X轴的ValueFormatter,直接根据Entry的X值(日期时间戳)转换为日期字符串,同时配置X轴避免标签重叠:

XAxis xAxis = chart.getXAxis();
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setLabelRotationAngle(45f); // 旋转标签避免重叠
xAxis.setGranularity(1f);
xAxis.setAvoidFirstLastClipping(true);
xAxis.setValueFormatter(new ValueFormatter() {
    private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());

    @Override
    public String getFormattedValue(float value) {
        // 将X值(时间戳)转换为日期字符串
        return dateFormat.format(new Date((long) value));
    }
});

第三步:修正数据读取与图表更新逻辑

在onDataChange中清空entries列表,正确读取每组数据的日期和价格,再更新图表:

List<Entry> entries = new ArrayList<>();
// 监听PriceRecords节点数据变化
reference.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
        entries.clear(); // 每次获取数据前清空列表,避免重复添加
        for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
            PriceRecord record = snapshot.getValue(PriceRecord.class);
            if (record != null) {
                long dateMillis = getMillisFromString(record.date);
                entries.add(new Entry(dateMillis, record.price));
            }
        }

        // 创建数据集并更新图表
        LineDataSet dataSet = new LineDataSet(entries, productName);
        dataSet.setCircleRadius(6f);
        dataSet.setValueTextSize(10f);
        dataSet.setValueFormatter(new ValueFormatter() {
            @Override
            public String getFormattedValue(float value) {
                return value + " ₺";
            }
        });

        ArrayList<ILineDataSet> dataSets = new ArrayList<>();
        dataSets.add(dataSet);
        LineData lineData = new LineData(dataSets);

        chart.setData(lineData);
        chart.invalidate(); // 刷新图表
    }

    @Override
    public void onCancelled(@NonNull DatabaseError error) {
        Log.e("Firebase", "Veri çekme hatası: " + error.getMessage());
    }
});

第四步:移除重复的图表设置代码

删除onCreate中ValueEventListener外部的重复代码(包括重复创建LineDataSet、LineData并设置给图表的逻辑)。

完整修复后的核心代码片段

public class GraphActivity extends AppCompatActivity {

    private ActivityGraphBinding binding;
    private LineChart chart;
    private DatabaseReference reference;
    private List<Entry> entries = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityGraphBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        chart = findViewById(R.id.lineChart);

        // 图表基础配置
        chart.setTouchEnabled(true);
        chart.setDragEnabled(true);
        chart.setScaleEnabled(true);
        chart.setPinchZoom(true);
        chart.getDescription().setEnabled(false);

        // X轴配置
        XAxis xAxis = chart.getXAxis();
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
        xAxis.setLabelRotationAngle(45f);
        xAxis.setGranularity(1f);
        xAxis.setAvoidFirstLastClipping(true);
        xAxis.setValueFormatter(new ValueFormatter() {
            private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());

            @Override
            public String getFormattedValue(float value) {
                return dateFormat.format(new Date((long) value));
            }
        });

        // Y轴配置
        YAxis yAxis = chart.getAxisLeft();
        yAxis.setDrawZeroLine(true);
        yAxis.setDrawTopYLabelEntry(true);
        yAxis.setGranularity(1);
        yAxis.setTextSize(12f);
        yAxis.setValueFormatter(new ValueFormatter() {
            @Override
            public String getFormattedValue(float value) {
                return value + " ₺";
            }
        });
        chart.getAxisRight().setEnabled(false);

        // Firebase初始化与数据上传
        FirebaseDatabase db = FirebaseDatabase.getInstance();
        reference = db.getReference("PriceRecords");
        reference.push().setValue(new PriceRecord(newDate, price));

        // 监听数据变化并更新图表
        reference.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                entries.clear();
                for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                    PriceRecord record = snapshot.getValue(PriceRecord.class);
                    if (record != null) {
                        long dateMillis = getMillisFromString(record.date);
                        entries.add(new Entry(dateMillis, record.price));
                    }
                }

                LineDataSet dataSet = new LineDataSet(entries, productName);
                dataSet.setCircleRadius(6f);
                dataSet.setValueTextSize(10f);
                dataSet.setValueFormatter(new ValueFormatter() {
                    @Override
                    public String getFormattedValue(float value) {
                        return value + " ₺";
                    }
                });

                ArrayList<ILineDataSet> dataSets = new ArrayList<>();
                dataSets.add(dataSet);
                LineData lineData = new LineData(dataSets);

                chart.setData(lineData);
                chart.invalidate();
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                Log.e("Firebase", "Veri çekme hatası: " + error.getMessage());
            }
        });
    }

    private long getMillisFromString(String dateString) {
        SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
        try {
            Date date = format.parse(dateString);
            return date.getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return 0;
    }
}

// 数据模型类
class PriceRecord {
    public String date;
    public int price;

    public PriceRecord() {}

    public PriceRecord(String date, int price) {
        this.date = date;
        this.price = price;
    }
}

内容的提问来源于stack exchange,提问作者Berat Akdoğan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 13:14:57