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
相关产品推荐
相关产品推荐

