【问题标题】:How to display date from SQLite db as X-axis in GraphView of another activity, from showing only 1-1-1970?如何在另一个活动的 GraphView 中将 SQLite db 中的日期显示为 X 轴,仅显示 1-1-1970?
【发布时间】:2019-06-06 00:19:57
【问题描述】:

我构建了一个应用程序,它在 MainActivity.java 中进行一些计算并将结果存储在 SQLite 数据库中。当用户按下按钮时,会打开带有相应 GraphView 的 GraphActivity.java。也许是因为我对 SQLite 数据库还没有太多经验,但我无法让 Graph 的 X 轴显示每个结果的存储日期。

那么,鉴于 Insert 方法和 Graph 发生在不同的活动中,我怎样才能让存储的日期在 GraphActivity 中显示为 X 轴?

我遵循了 GraphView 日期和 SQLite 的 YouTube 教程,但它在同一活动中具有存储和图形。 (https://www.youtube.com/watch?v=lSgK6-cKjmA&list=PLFh8wpMiEi88ojfNpavGpMB0dtP4mvEqa&index=16)

我尝试使用 Intent EXTRAS 发送日期变量,但是当按下 Save 按钮时它会强制您进入 GraphActivity。 然后我尝试在 GraphActivity 中获取时间,它可以工作,但对于每个结果,X 轴上的日期显示为连续的“1-1-1970”。 我使用格式来显示带有时间戳的当前日期,但它仍然没有显示每个结果的存储日期,只是所有的当前日期。

MainActivity.java:结果和日期的插入方法

public void insertData() {
    try {
        (...)

        // Gets the time & date the results are stored

        String timeStamp = new SimpleDateFormat("d M yy hh:mm").format(Calendar.getInstance().getTime());

        // Create a ContentValues object where column names are the keys,
        // and container attributes from the MainActivity are the values.

        ContentValues values = new ContentValues();
        values.put(DexameniEntry.COLUMN_CONTAINER_NAME, "TODO");
        values.put(DexameniEntry.COLUMN_a, X1aInput);
        values.put(DexameniEntry.COLUMN_DATE, timeStamp);
        values.put(DexameniEntry.COLUMN_b, X1bInput);
        values.put(DexameniEntry.COLUMN_c, X1cInput);
        values.put(DexameniEntry.COLUMN_d, dX1StringDouble);
        values.put(DexameniEntry.COLUMN_e, eX1StringDouble);
        values.put(DexameniEntry.COLUMN_percent, percentX1fromDoubletoString);

        (...)
        } catch (NumberFormatException e) {

        } catch (NullPointerException e) {
        }
}

GraphActivity.java : 设置 GraphView

public class GraphActivity extends AppCompatActivity { 
    (...)
    SimpleDateFormat sdf = new SimpleDateFormat("EEE d M yyyy h mm", Locale.US);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.graph_activity);

    GraphView bigGraphX1 = findViewById(R.id.bigGraph);
    mDbHelper = new ResultsDbHelper(this);
    sqLiteDatabase = mDbHelper.getWritableDatabase();

    bigGraphX1.getGridLabelRenderer().setLabelFormatter(new DefaultLabelFormatter() {

        @Override
        public String formatLabel(double value, boolean isValueX) {
            if (isValueX) {
                return
                        sdf.format(new Date((long)value));
            }else {
                return super.formatLabel(value, isValueX);
            }
        }
    });

    (...)

编辑 DbHelper.class(名为 ResultsDbHelper)

public class ResultsDbHelper extends SQLiteOpenHelper {

   public static final String LOG_TAG = ResultsDbHelper.class.getSimpleName();

//    Name of the database in a String type constant (to be referenced later)
private static final String DATABASE_NAME = "containerResults.db";
//    Database version in a Integer type constant for updating the database
private static final int DATABASE_VERSION = 1;

//    Constructor: Constructs a new instance of ResultsDbHelper.
//     * @param is the context of the app

public ResultsDbHelper(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

//    Modify the onCreate method to have the table created on startup of the app
@Override
public void onCreate(SQLiteDatabase db) {

// Define the schema of the table: which columns will be created by the onCreate method.
//        The whole schema creation is put in a String variable (CREATE_RESULTS_TABLE) for easy reference

    String CREATE_RESULTS_TABLE = "CREATE TABLE " + DexameniEntry.TABLE_NAME + " ("
            + DexameniEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
            + DexameniEntry.COLUMN_CONTAINER_NAME + " TEXT NOT NULL, "
            + DexameniEntry.COLUMN_DATE + " TEXT NOT NULL, "
            + DexameniEntry.COLUMN_a + " REAL NOT NULL, "
            + DexameniEntry.COLUMN_b + " REAL NOT NULL, "
            + DexameniEntry.COLUMN_c + " REAL NOT NULL, "
            + DexameniEntry.COLUMN_d + " REAL NOT NULL, "
            + DexameniEntry.COLUMN_e + " REAL NOT NULL, "
            + DexameniEntry.COLUMN_percent + " REAL NOT NULL);";

//        Execute the SQL Statement (Create the table)
    db.execSQL(CREATE_RESULTS_TABLE);
}

/**
 * This is called when the database needs to be upgraded.
 */
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    final String LOG_TAG = ("Upgrading the database from version " + oldVersion + " to " + newVersion);
    // on upgrade drop older tables
    db.execSQL("DROP TABLE IF EXISTS " + DexameniEntry.TABLE_NAME);

    //        Recreate tables
    onCreate(db);
    }
}

我希望 X 轴显示每个结果的存储日期,以便所有值在 GraphView 中显示为 LineSeries。但我似乎无法从数据库中获取日期到图表,所以日期总是相同的,并且 LineSeries 不连贯。

编辑:在发布问题之前,我还开始寻找一种方法,在 FormatLabel 方法中将 DATE 列的数据库引用放入 GraphActivity。例如

@Override
    public String formatLabel(double value, boolean isValueX) {
        if (isValueX) {
            return
             sdf.format(new Date(**put here the DATE column variable so that the X-axis is populated the column's data**);
        }else {
            return super.formatLabel(value, isValueX);
        }

我也不能这样做,因为我不知道如何编写这段代码......

编辑Picture of the issue

我不知道还有什么办法可以解决这个问题,你能帮帮我吗?

编辑 2GraphActivity after changes(似乎图表仍未由 db 数据填充。在 db?idk 中保存数据可能存在问题..)

【问题讨论】:

  • 您能否编辑您的问题以包含 ResultsDBHelper 类。
  • 您在new Date((long)value) 中的值似乎太小了。顺便说一句,考虑不使用DateSimpleDateFormat,这些类设计不佳且早已过时。更好的选择是将ThreeTenABP 添加到您的Android 项目中,以便您可以使用java.time, the modern Java date and time API
  • X 轴上的日期为连续的“1-1-1970”,即开始日期,即 unix 纪元中的值 0。所以所有结果都为 0。使用 SQL 使用一种公认的日期格式在处理日期时有很多好处(它没有 DATE 类型,通常使用 TEXT 的列关联(尽管您可以存储在 unix 时间戳中)),例如YYYY-MM-DD HH:MM:SS 见Date And Time Functions
  • @MikeT 我包含了 ResultsDbHelper 类。我也为日期列使用了 TEXT 类型...
  • @OleV.V 你说的值太小是什么意思?我的意思是它们是日期而不是数字,比如 10,太小了。 (对不起,如果我不明白)。那么添加 ThreeTenABP 会解决问题还是一般建议?

标签: java sqlite date android-sqlite android-graphview


【解决方案1】:

您试图在 MainActivity 中做太多事情,并且您可能对日期有问题,因为从您的 cmets 显示他们/它是 0。

基本上,您可能希望传递生成图表的标准(例如图表的日期范围),然后通过 Databasehelper (ResultsHelper) 中编码的方法从数据库中提取数据。

而不是在添加数据时获取当前时间并将其以 D M Y 格式存储在数据库中。您可以通过以可排序格式存储日期并以对 SQLite 友好的格式操作/格式化日期(主要是 YYYY-MM-DD HH:MM:SS)来简单地处理(例如,根据日期范围提取数据)(尽管您可以只需存储时间戳)。

因此,我建议进行一些相对简单的更改。

  1. 将日期列定义更改为:-

    + DexameniEntry.COLUMN_DATE + " TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, " //<<<<<<<<<< LET SQLITE GET THE DATE TIME

    • 如果您根本不提供值,这允许 SQLite 存储当前日期和时间,这是通过注释掉/删除该行来完成的:- //values.put(DexameniEntry.COLUMN_DATE, timeStamp); //<<<<<<<<<< Use default
  2. 将 insertData 方法移动到 ResultHeper 中(然后可以在其他地方使用)

  3. 在 ResultHelper 中添加一个方法,将数据提取到 Cursor 中,包括动态生成列。

    • 动态列是动态生成的附加列,告诉 SQLite 执行操作。
    • 例如假设您要添加 2 列 a 和 b,您可以说 SELECT *, a + b AS my_addition FROM the_table;,结果将包括所有列和 my_addition 列。
  4. 将用于从 MainActivity 中提取图形数据的基本/最低选择标准传递给 GraphActivity,然后在 graphActivity 中提取实际数据。

注意更改为使用日期标签填充图表。

工作示例

以下代码基于您的代码和以上几点,是一个工作示例(它不会填充图表,但会获取数据)。

DexameniEntry.java

public class DexameniEntry {

    public static final String[] WEEKDAYS_LONG = new String[]{"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
    public static final String[] WEEKDAYS_SHORT = new String[]{"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};

    public static final String _ID = BaseColumns._ID;
    public static final String TABLE_NAME = "container_results";
    public static final String COLUMN_CONTAINER_NAME = "container_name";
    public static final String COLUMN_DATE = "container_date";
    public static final String COLUMN_a = "container_value_a";
    public static final String COLUMN_b = "container_value_b";
    public static final String COLUMN_c = "container_value_c";
    public static final String COLUMN_d = "container_value_d";
    public static final String COLUMN_e = "container_value_e";
    public static final String COLUMN_percent = "container_percent";


    public static final String EXTRACT_COLUMN_DATELABEL = "date_label";
    public static final String EXTRACT_COLUMN_GRAPHDATELABEL = "graph_date_label";

    public static final String INTENTKEY_GRAPHVALUE = "ik_graph_value";
}
  • 这主要由猜测工作(列名和表名)组成。但是请注意以下两个常量(可以使用 WEEKDAYS_?? 常量)
    • EXTRACT_COLUMN_DATELABEL(动态生成的列名)和
    • EXTRACT_COLUMN_GRAPHDATELABEL(动态生成的unix时间戳)
    • INTENTKEY_GRAPHVALUE(用于传递意图值)

结果助手

public class ResultsDbHelper extends SQLiteOpenHelper {

    public static final String LOG_TAG = ResultsDbHelper.class.getSimpleName();

    // Date format that suits SQLite i.e. YYYY-MM-DD HH:MM:SS
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); // Not used but could be useful

    //    Name of the database in a String type constant (to be referenced later)
    private static final String DATABASE_NAME = "containerResults.db";
    //    Database version in a Integer type constant for updating the database
    private static final int DATABASE_VERSION = 1;

    SQLiteDatabase mDB;

//    Constructor: Constructs a new instance of ResultsDbHelper.
//     * @param is the context of the app

    public ResultsDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        mDB = this.getWritableDatabase(); // Sets mDB (also forces open of DB)
    }

    //    Modify the onCreate method to have the table created on startup of the app
    @Override
    public void onCreate(SQLiteDatabase db) {

// Define the schema of the table: which columns will be created by the onCreate method.
//        The whole schema creation is put in a String variable (CREATE_RESULTS_TABLE) for easy reference

        String CREATE_RESULTS_TABLE = "CREATE TABLE " + DexameniEntry.TABLE_NAME + " ("
                + DexameniEntry._ID + " INTEGER PRIMARY KEY, " //<<<<<<<<<< AUTOINCREMENT NOT REQD AS IS INEFFICIENT
                + DexameniEntry.COLUMN_CONTAINER_NAME + " TEXT NOT NULL, "
                + DexameniEntry.COLUMN_DATE + " TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, " //<<<<<<<<<< LET SQLITE GET THE DATE TIME
                + DexameniEntry.COLUMN_a + " REAL NOT NULL, "
                + DexameniEntry.COLUMN_b + " REAL NOT NULL, "
                + DexameniEntry.COLUMN_c + " REAL NOT NULL, "
                + DexameniEntry.COLUMN_d + " REAL NOT NULL, "
                + DexameniEntry.COLUMN_e + " REAL NOT NULL, "
                + DexameniEntry.COLUMN_percent + " REAL NOT NULL);";

//        Execute the SQL Statement (Create the table)
        db.execSQL(CREATE_RESULTS_TABLE);
    }

    /**
     * This is called when the database needs to be upgraded.
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        final String LOG_TAG = ("Upgrading the database from version " + oldVersion + " to " + newVersion);
        // on upgrade drop older tables
        db.execSQL("DROP TABLE IF EXISTS " + DexameniEntry.TABLE_NAME);

        //        Recreate tables
        onCreate(db);
    }

    public void insertData(String name,
                           Double X1aInput,
                           Double X1bInput,
                           Double X1cInput,
                           Double X1dInput,
                           Double X1eInput,
                           Double percent) {

        String timeStamp = "";
        try {
            // Gets the time & date the results are stored
            //timeStamp = new SimpleDateFormat("d M yy hh:mm").format(Calendar.getInstance().getTime());
            timeStamp = sdf.format(Calendar.getInstance().getTime());

        } catch (NumberFormatException e) {

        } catch (NullPointerException e) {
        }

        // Create a ContentValues object where column names are the keys,
        // and container attributes from the MainActivity are the values.

        ContentValues values = new ContentValues();
        values.put(DexameniEntry.COLUMN_CONTAINER_NAME, name);
        values.put(DexameniEntry.COLUMN_a, X1aInput);
        //values.put(DexameniEntry.COLUMN_DATE, timeStamp); //<<<<<<<<<< Use default
        values.put(DexameniEntry.COLUMN_b, X1bInput);
        values.put(DexameniEntry.COLUMN_c, X1cInput);
        values.put(DexameniEntry.COLUMN_d, X1dInput);
        values.put(DexameniEntry.COLUMN_e, X1eInput);
        values.put(DexameniEntry.COLUMN_percent, percent);
        mDB.insert(DexameniEntry.TABLE_NAME,null,values);
    }

    public Cursor getAllResults() {
        String formatted_date = "strftime('%d %m %Y'," +
                DexameniEntry.COLUMN_DATE + ") AS " + DexameniEntry.EXTRACT_COLUMN_DATELABEL;
        String unixtype_date = "julianday(" + DexameniEntry.COLUMN_DATE + ") * 86400 * 1000 AS " +DexameniEntry.EXTRACT_COLUMN_GRAPHDATELABEL;
        String[] columns = new String[]{"*",formatted_date,unixtype_date};
        return mDB.query(DexameniEntry.TABLE_NAME,columns,null,null,null,null,DexameniEntry.COLUMN_DATE + " ASC");
    }
}
  • 日期列已更改为具有特殊的 DEFAULT 值 CURRENT_TIMESTAMP
  • insertData 方法已从 MainActivity 移到此处,现在需要将参数传递给它(请参阅它在 MainActivity 中的使用)
  • 已添加 getAllResults(请参阅 GrapActivty 了解使用 getAllreults 的位置)
    • 请注意,生成的光标包含表中的所有列加上两个额外(动态生成)列,这些列的日期为 DD MM YYYY 格式(日期列的日期为 YYYY- DD-MM HH:MM:SS 格式(稍后输出示例))和另一个作为 unix 时间戳。 (julianday(the_column) * 86400 * 1000 AS the_column_name)

MainActivity.java

MainActivity 会加载一些数据(仅在第一次运行时)并有一个按钮,单击该按钮将启动 GraphActivity。

public class MainActivity extends AppCompatActivity {

    Button mShowGraph;
    Context mContext;
    ResultsDbHelper mDBHlpr;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        mShowGraph = this.findViewById(R.id.show_graph);
        setupShowGraphButton();

        //Instantiate the DatabaseHelper
        mDBHlpr = new ResultsDbHelper(this);

        // Add some data if none exists
        if (DatabaseUtils.queryNumEntries(mDBHlpr.getWritableDatabase(),DexameniEntry.TABLE_NAME) < 1) {
            mDBHlpr.insertData("C1", 10.12, 11.12, 12.12, 13.12, 14.12, 25D);
            mDBHlpr.insertData("C2", 20.12, 21.12, 22.12, 23.12, 24.12, 35D);
            mDBHlpr.insertData("C3", 31.12, 32.12, 33.12, 34.12, 35.12, 25D);
        }
    }

    // Setup the Buttons on click listener to start the GraphActivity
    private void setupShowGraphButton() {
        mShowGraph.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i  = new Intent(mContext,GraphActivity.class);
                i.putExtra(DexameniEntry.INTENTKEY_GRAPHVALUE,100);
                startActivity(i);
            }
        });
    }
}

图形活动

虽然这会显示图表,但它不会加载任何数据,而是从数据库中提取日期并写入日志。

public class GraphActivity extends AppCompatActivity {

    Context mContext;
    Button mDone;
    GraphView mBigGraphX1;
    ResultsDbHelper mDBHlpr;
    Cursor mResults;
    LineGraphSeries<DataPoint> mLGS;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_graph);
        mContext = this;
        mDone = this.findViewById(R.id.done);
        setupDoneButton();
        mDBHlpr = new ResultsDbHelper(this);
        mResults = mDBHlpr.getAllResults();
        mBigGraphX1 = this.findViewById(R.id.graphview);
        demoDatesFromDB();
        doTheGraphLineStuff();
    }

    private void setupDoneButton() {
        mDone.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }

    @Override
    protected void onDestroy() {
        mResults.close(); //<<<<<<<<<< Should always close cursor when done
        super.onDestroy();
    }

    private void demoDatesFromDB() {
        StringBuilder sb = new StringBuilder("Data From Table ").append(DexameniEntry.TABLE_NAME).append(" as per :-");
        while (mResults.moveToNext()) {
            String date_asperdb = mResults.getString(mResults.getColumnIndex(DexameniEntry.COLUMN_DATE));
            String date_formatted = mResults.getString(mResults.getColumnIndex(DexameniEntry.EXTRACT_COLUMN_DATELABEL));
            long unix_date = mResults.getLong(mResults.getColumnIndex(DexameniEntry.EXTRACT_COLUMN_GRAPHDATELABEL));
            sb.append("\n\tResults for row ");
            sb.append(String.valueOf(mResults.getPosition() + 1));
            sb.append(" Date as per DB is ").append(date_asperdb);
            sb.append(" Formatted Date is ").append(date_formatted);
            sb.append(" Unix Date is ").append(unix_date);
        }
        Log.d("DEMODATEINFO",sb.toString());
    }

    private void doTheGraphLineStuff() {

        long one_day = 1000 * 60 * 60 * 24;
        mResults = mDBHlpr.getAllResults();
        int rowcount = mResults.getCount();


        long[] extracted_dates = new long[rowcount];

        DataPoint[] dataPoints = new DataPoint[rowcount];
        while (mResults.moveToNext()) {

            /**
             * NOTE  as all 3 rows are added within milliseconds this adds on a day
             */
            extracted_dates[mResults.getPosition()] = mResults.getLong(
                    mResults.getColumnIndex(
                            DexameniEntry.EXTRACT_COLUMN_GRAPHDATELABEL
                    ))
                    + (one_day * ((long)mResults.getPosition() + 1L) //<<<< Frig the data show it is one day more than the previous

            );
            dataPoints[mResults.getPosition()] = new DataPoint(
                    extracted_dates[mResults.getPosition()],
                    mResults.getDouble(mResults.getColumnIndex(DexameniEntry.COLUMN_a))
            );

        }
        mLGS = new LineGraphSeries<>(dataPoints);
        mBigGraphX1.addSeries(mLGS);
        mBigGraphX1.getGridLabelRenderer().setLabelFormatter(new DateAsXAxisLabelFormatter(mContext));
        mBigGraphX1.getGridLabelRenderer().setNumHorizontalLabels(rowcount);
        mBigGraphX1.getViewport().setMinX(extracted_dates[0]);
        mBigGraphX1.getViewport().setMaxX(extracted_dates[rowcount - 1]);
        mBigGraphX1.getViewport().setXAxisBoundsManual(true);
        mBigGraphX1.getGridLabelRenderer().setHumanRounding(false);

    }

结果

以下输出到日志:-

2019-01-12 09:01:07.194 3073-3073/so54140390.so54140390graph D/DEMODATEINFO: Data From Table container_results as per :-
        Results for row 1 Date as per DB is 2019-01-11 21:02:06 Formatted Date is 11 01 2019
        Results for row 2 Date as per DB is 2019-01-11 21:02:06 Formatted Date is 11 01 2019
        Results for row 3 Date as per DB is 2019-01-11 21:02:06 Formatted Date is 11 01 2019

因此,只需更改或添加适合的格式即可。 - 这方面的圣经是SQL As Understood By SQLite- Date And Time Functions

上图:-

【讨论】:

  • 亲爱的@MikeT 感谢您以如此清晰的方式写作。我非常感谢!我在 getAllResults、doTheGraphLineStuff、onD​​estroy 和 demoDatesFromDB 方法中遇到了重复的 NullPointerException,同时遵循我用 try-catch 解决的代码。可悲的是,该图没有显示任何内容(请参阅编辑)。我不知道该怎么办,我花了过去两个月试图解决这个问题,我累了,我放弃了。从这里获得帮助是我最后的希望。也许我对我想让我的应用程序做的事情过于雄心勃勃,尽管如果我能做到这一切会很棒。再次感谢您宝贵的时间
  • 我的建议是,使用我提供的代码创建一个新项目,然后逐步修改和测试它,使其与您的项目相匹配。附言我已经修复了 ResultHelper 代码的缩进。
  • @BlackBlouse 请注意以下几行 SQLiteDatabase mDB;mDB = this.getWritableDatabase(); // Sets mDB (also forces open of DB)。这些设置 mDB 以便可以在 getAllResults 方法中使用。将 try/catch 包裹在数据库内容周围通常会比没有包裹更令人头疼,也就是说,您很可能只是将错误的结果推迟到更令人困惑的点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-27
  • 1970-01-01
  • 2014-02-14
  • 2021-11-09
  • 2016-07-30
相关资源
最近更新 更多