18.SQLite应用案例-课程表
一、程序界面
1.课程表首页
一周有7天,一天有10节课。
课程表首页的布局activity_main.xml框架设计大致如此:
- 最外层使用线性布局设置屏幕水平方向android:orientation=“horizontal”。
- 在内层使用八个GridLayout将屏幕分为八块。其中第一块是节课,后面的为周一至日,7天。
- 因为是水平排列的,所以权重和宽度相关联。
- 把每个GridLayout的宽度设置为0dp。除第一个GridLayout权值为0.5其余设为1。目的是让第一列占比低一些。
- 每一个GridLayout都设置为竖直方向android:orientation=“vertical”。目的是每一节课都是竖着往下排列的。
- 因为是竖直排列的,所以权重和高度有关。把每个GridLayout的高度设置为0dp。除第一个GridLayout权值为0.25其余设为1。目的是让第一行占比低一些。
activity_main.xml代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" tools:context=".MainActivity"> <GridLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="0.5" android:background="#FFFAFA" android:orientation="vertical" android:rowCount="11"> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="0.25" android:gravity="center" android:text="" /> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="1" android:gravity="center" android:text="1" /> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="1" android:gravity="center" android:text="2" /> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="1" android:gravity="center" android:text="3" /> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="1" android:gravity="center" android:text="4" /> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="1" android:gravity="center" android:text="5" /> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="1" android:gravity="center" android:text="6" /> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="1" android:gravity="center" android:text="7" /> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="1" android:gravity="center" android:text="8" /> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="1" android:gravity="center" android:text="9" /> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="1" android:gravity="center" android:text="10" /> </GridLayout> <GridLayout android:id="@+id/d1" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#FFFAFA" android:orientation="vertical" android:rowCount="11"> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="0.25" android:autoSizeMaxTextSize="30sp" android:autoSizeMinTextSize="5sp" android:autoSizeStepGranularity="1sp" android:gravity="center" android:text="周一" android:textSize="20sp" /> </GridLayout> <GridLayout android:id="@+id/d2" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#FFFAFA" android:orientation="vertical" android:rowCount="11"> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="0.25" android:gravity="center" android:text="周二" android:textSize="20dp" /> </GridLayout> <GridLayout android:id="@+id/d3" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#FFFAFA" android:orientation="vertical" android:rowCount="11"> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="0.25" android:gravity="center" android:text="周三" android:textSize="20dp" /> </GridLayout> <GridLayout android:id="@+id/d4" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#FFFAFA" android:orientation="vertical" android:rowCount="11"> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="0.25" android:gravity="center" android:text="周四" android:textSize="20dp" /> </GridLayout> <GridLayout android:id="@+id/d5" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#FFFAFA" android:orientation="vertical" android:rowCount="11"> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="0.25" android:gravity="center" android:text="周五" android:textSize="20dp" /> </GridLayout> <GridLayout android:id="@+id/d6" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#FFFAFA" android:orientation="vertical" android:rowCount="11"> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="0.25" android:gravity="center" android:text="周六" android:textSize="20dp" /> </GridLayout> <GridLayout android:id="@+id/d7" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#FFFAFA" android:orientation="vertical" android:rowCount="11"> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_rowWeight="0.25" android:gravity="center" android:text="周日" android:textSize="20dp" /> </GridLayout> </LinearLayout>
发现xml并没有像图一有课程框。可以直接在activity_main.xml中依次添加,但是考虑到有7×10个EditText需要弄,还需要为每个EditText设置id(这个id之后非常关键),而且在xml写死的话对后期拓展(课程数量增加、减少、重构)很不便。所以我使用Java在MainActivity中动态添加课程框。
MainActivity.java代码如下:
import androidx.appcompat.app.AppCompatActivity; import android.graphics.Color; import android.os.Bundle; import android.text.TextUtils; import android.view.Gravity; import android.widget.GridLayout; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private int[] weekId = {R.id.d1, R.id.d2, R.id.d3, R.id.d4, R.id.d5, R.id.d6, R.id.d7}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initWeeks(); } public void initWeeks() { //创建一个GridLayout对象 GridLayout gridLayout; //定义每个框的id,之后会动态改变id值 int id = 1; //渲染每一列(周) for (int i = 0; i < 7; i++) { //注入GridLayout对应的列,根据星期几调用LayoutColumn方法 gridLayout = findViewById(weekId[i]); //渲染每一行(节课) for (int j = 1; j < 10; j += 2) { //声明一个新的TextView TextView textView1 = new TextView(this); //给TextView设置style样式 textView1.setId(id++); textView1.setText(""); textView1.setMaxLines(5); textView1.setEllipsize(TextUtils.TruncateAt.END); textView1.setBackgroundColor(Color.parseColor("#F0FFFF")); textView1.setGravity(Gravity.CENTER); //GridLayout.LayoutParams设置在此gridLayout列中TextView布局 GridLayout.LayoutParams params1 = new GridLayout.LayoutParams(); params1.setMargins(5, 10, 5, 10); params1.width = GridLayout.LayoutParams.MATCH_PARENT; params1.height = 0; //设置在gridLayout中的方位,参数1:在第几行。参数2:占几行。参数3:权值 //这个权值是根据xml中第一个gridLayout节课权值设定的。 params1.rowSpec = GridLayout.spec(j, 2, 1); //把TextView和布局样式添加到此gridLayout中 gridLayout.addView(textView1, params1); } } } }
运行程序:
2.添加课程对话框
课程信息有:星期几,节次,课程名,任课教师
activity_add.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" style="@style/myDialogStyle" android:layout_width="300dp" android:layout_height="450dp" android:background="#88ffffff" android:orientation="vertical" tools:context=".AddActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="添加课程" android:textColor="@color/black" android:textSize="40dp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="时间:" android:textColor="@color/cardview_dark_background" android:textSize="30dp" /> <Spinner android:id="@+id/spinnerDay" android:layout_width="match_parent" android:layout_height="wrap_content" android:entries="@array/day"></Spinner> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="节课:" android:textColor="@color/cardview_dark_background" android:textSize="30dp" /> <Spinner android:id="@+id/spinnerTime" android:layout_width="match_parent" android:layout_height="wrap_content" android:entries="@array/time"></Spinner> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="科目:" android:textColor="@color/cardview_dark_background" android:textSize="30dp" /> <EditText android:id="@+id/edtSubject" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="任课教师:" android:textColor="@color/cardview_dark_background" android:textSize="30dp" /> <EditText android:id="@+id/edtTeacher" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="bottom"> <Button android:id="@+id/btnCancel" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="取消" /> <Button android:id="@+id/btnSave" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="保存" /> </LinearLayout> </LinearLayout>
AddActivity.java:
import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.Window; public class AddActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 继承AppCompatActivity的活动,要去掉标题栏,使用supportRequestWindowFeature()方法 supportRequestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_add); } }
在AndroidManifest.xml修改AddActivity的主题为对话框样式:
<activity android:name=".AddActivity" android:exported="true" android:theme="@style/Theme.AppCompat.Dialog"> <meta-data android:name="android.app.lib_name" android:value="" /> </activity>
3.菜单
如何启动添加课程的对话框呢?
我们使用右上角菜单+按钮
在res文件夹下创建menu文件夹,创建menu.xml:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_menu" android:icon="@android:drawable/ic_input_add" android:title="添加课程" app:showAsAction="ifRoom"></item> </menu>
在MainActivity类中重写onCreateOptionsMenu方法:
@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); MenuItem menuItem = menu.findItem(R.id.action_menu); menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem menuItem) { Intent intent = new Intent(); intent.setClass(MainActivity.this, AddActivity.class); startActivity(intent); return true; } }); return super.onCreateOptionsMenu(menu); }
4.样式文件
style.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="dayStyle"> <item name="android:layout_width">48dp</item> <item name="android:layout_height">30dp</item> <item name="android:layout_gravity">center</item> <item name="android:gravity">center</item> <item name="android:background">#e9e9e9</item> <item name="android:textSize">20dp</item> </style> <style id="c" name="numb"> <item name="android:layout_width">20dp</item> <item name="android:layout_height">70dp</item> <item name="android:gravity">center</item> <item name="android:layout_gravity">center</item> <item name="android:textSize">15dp</item> <item name="android:background">#e9e9e9</item> </style> <dimen name="text_width">40dp</dimen> <dimen name="text_height">130dp</dimen> <dimen name="text_size">10dp</dimen> <style name="Class"> <item name="android:layout_margin">5dp</item> <item name="android:layout_width">42dp</item> <item name="android:layout_height">130dp</item> <item name="android:layout_gravity">center</item> <item name="android:gravity">center</item> <item name="android:layout_rowSpan">2</item> <item name="android:textSize">12dp</item> </style> <style name="myDialogStyle" parent="Theme.AppCompat"> <!--边框--> <item name="android:windowFrame">@null</item> <!--是否浮现在activity之上--> <item name="android:windowIsFloating">true</item> <!--背景色,此处的背景色请一定要设置为透明度背景色--> <item name="android:windowBackground">@android:color/transparent</item> <!--window Is Translucent 窗口是半透明的--> <item name="android:windowIsTranslucent">false</item> <!--window No Title窗口无标题--> <item name="android:windowNoTitle">true</item> <!--弹出动画--> <item name="android:windowAnimationStyle">@null</item> </style> </resources>
运行程序:
二、添加课程数据
填写完课程信息后,把信息保存在SQLite中,重新加载MainActivity并渲染数据到课程表。
思路:
需要考虑如何映射数据关系,即当需要添加课程,该如何定位到我选择的位置?
这时候上面我们动态设置的id的作用就显示出来了。上述id是从1开始往下依次增加到35(7天×5节课)。可以通过星期几和第几节课算出对应该课程id。
1.使用SQLite数据库保存课程信息
在MainActivity类同级目录创建DBHelper类(建classes_db表):
package com.sdbi.classschedule; import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import androidx.annotation.Nullable; public class DBHelper extends SQLiteOpenHelper { public final static String TABLE_NAME = "tabClass"; public DBHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { // 建表 db.execSQL("create table " + TABLE_NAME + " (c_id Integer not null primary key," + " c_name varchar(50) not null," + " c_time varchar(50) not null," + " c_day varchar(50) not null," + " c_teacher varchar(50) not null)"); // 创建ContentValue设置参数 ContentValues contentValues = new ContentValues(); //课程名字 contentValues.put("c_name", "0"); //第几节课 contentValues.put("c_time", "0"); //星期几 contentValues.put("c_day", "0"); //任课教师 contentValues.put("c_teacher", "0"); //插入id 1-35 条课程数据,对应一周7*5课时,以便添加课程 for (int i = 1; i < 36; i++) { contentValues.put("c_id", i); db.insert(TABLE_NAME, null, contentValues); } } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
2.创建课程类
对应表中的课程信息,创建课程类Classes.java
public class Classes { private int c_id; private String c_name; private String c_time; private String c_day; private String c_teacher; public int getC_id() { return c_id; } public void setC_id(int c_id) { this.c_id = c_id; } public String getC_name() { return c_name; } public void setC_name(String c_name) { this.c_name = c_name; } public String getC_time() { return c_time; } public void setC_time(String c_time) { this.c_time = c_time; } public String getC_day() { return c_day; } public void setC_day(String c_day) { this.c_day = c_day; } public String getC_teacher() { return c_teacher; } public void setC_teacher(String c_teacher) { this.c_teacher = c_teacher; } @Override public String toString() { return "Classes{" + "c_id=" + c_id + ", c_name='" + c_name + '\'' + ", c_time='" + c_time + '\'' + ", c_day='" + c_day + '\'' + ", c_teacher='" + c_teacher + '\'' + '}'; } }
3.定义工具类Utils
定义一个工具类,用于将星期几转化成对应的整型数字
package com.sdbi.classschedule; public class Utils { public static int getDay(String day) { int j = 0; switch (day) { case "星期一": { j = 1; break; } case "星期二": { j = 2; break; } case "星期三": { j = 3; break; } case "星期四": { j = 4; break; } case "星期五": { j = 5; break; } case "星期六": { j = 6; break; } case "星期日": { j = 7; break; } } return j; } }
4.修改MainActivity
定义查询课程数据的方法query()和显示课程数据的方法display()。
并在onCreate()方法中调用显示课程数据的方法。
import androidx.appcompat.app.AppCompatActivity; import android.annotation.SuppressLint; import android.content.Intent; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.graphics.Color; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.widget.GridLayout; import android.widget.TextView; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; public static final String DB_NAME = "classes_db.db"; private int[] weekId = {R.id.d1, R.id.d2, R.id.d3, R.id.d4, R.id.d5, R.id.d6, R.id.d7}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initWeeks(); // 初始化课程表控件 // 获得数据库管理类对象 DBHelper dbHelper = new DBHelper(MainActivity.this, DB_NAME, null, 1); display(dbHelper); // 调用显示课程数据的方法 } public void initWeeks() { //创建一个GridLayout对象 GridLayout gridLayout; //定义每个框的id,之后会动态改变id值 int id = 1; //渲染每一列(周) for (int i = 0; i < 7; i++) { // 注入GridLayout对应的列,根据星期几调用LayoutColumn方法 gridLayout = findViewById(weekId[i]); // 渲染每一行(节课) for (int j = 1; j < 10; j += 2) { // 声明一个新的TextView TextView textView = new TextView(this); // 给TextView设置style样式 textView.setId(id++); textView.setText(""); textView.setMaxLines(5); textView.setEllipsize(TextUtils.TruncateAt.END); // TextView内容过长时,结尾加省略号 textView.setBackgroundColor(Color.parseColor("#F0FFFF")); textView.setGravity(Gravity.CENTER); // GridLayout.LayoutParams设置在此gridLayout列中TextView布局 GridLayout.LayoutParams params = new GridLayout.LayoutParams(); params.setMargins(5, 10, 5, 10); params.width = GridLayout.LayoutParams.MATCH_PARENT; params.height = 0; // 设置在gridLayout中的方位,参数1:在第几行。参数2:占几行。参数3:权值 // 这个权值是根据xml中第一个gridLayout节课权值设定的。 params.rowSpec = GridLayout.spec(j, 2, 1); // 把TextView和布局样式添加到此gridLayout中 gridLayout.addView(textView, params); } } Log.d(TAG, "initWeeks: id = " + id); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); MenuItem menuItem = menu.findItem(R.id.action_menu); menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem menuItem) { Intent intent = new Intent(); intent.setClass(MainActivity.this, AddActivity.class); startActivity(intent); return true; } }); return super.onCreateOptionsMenu(menu); } // 显示课程表数据 private void display(DBHelper dbHelper) { // 从数据库获取课程数据保存到列表中 List<Classes> classes = query(dbHelper); for (Classes aClass : classes) { // 第几节课 int i = Integer.parseInt(aClass.getC_time().charAt(0) + ""); // 星期几 int j = Utils.getDay(aClass.getC_day()); // 获取此课程对应TextView的id TextView tvClass = findViewById((j - 1) * 5 + ((i - 1) / 2 + 1)); // 判断如果课程星期==当前星期,如此课程信息和当前都是星期二就把背景颜色更换。 Date date = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEEE"); // 显示星期几 if (aClass.getC_day().equals(simpleDateFormat.format(date).toString())) { tvClass.setBackgroundColor(Color.rgb(28, 217, 204)); } // 课程表信息显示出来 tvClass.setText(aClass.getC_name() + "\n" + aClass.getC_teacher()); } } @SuppressLint("Range") public List<Classes> query(DBHelper dbHelper) { List<Classes> classesList = new ArrayList<>(); // 通过DBHelper类获取一个读写的SQLiteDatabase对象 SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor = db.query(DBHelper.TABLE_NAME, null, null, null, null, null, null); // 将游标移到开头 cursor.moveToFirst(); while (!cursor.isAfterLast()) { // 游标只要不是在最后一行之后,就一直循环 if (!cursor.getString(cursor.getColumnIndex("c_day")).equals("0")) { Classes aClass = new Classes(); aClass.setC_id(Integer.parseInt(cursor.getString(cursor.getColumnIndex("c_id")))); aClass.setC_name(cursor.getString(cursor.getColumnIndex("c_name"))); aClass.setC_time(cursor.getString(cursor.getColumnIndex("c_time"))); aClass.setC_day(cursor.getString(cursor.getColumnIndex("c_day"))); aClass.setC_teacher(cursor.getString(cursor.getColumnIndex("c_teacher"))); classesList.add(aClass); } // 将游标移到下一行 cursor.moveToNext(); } db.close(); return classesList; } }
5.修改AddActivity
增加更新数据的方法update(),给”保存“和”取消“按钮设置监听器。
package com.sdbi.classschedule; import androidx.appcompat.app.AppCompatActivity; import android.content.ContentValues; import android.content.Intent; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.view.View; import android.view.Window; import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; public class AddActivity extends AppCompatActivity { private Spinner spinnerDay, spinnerTime; private EditText edtSubject, edtTeacher; private Button btnCancel, btnSave; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 继承AppCompatActivity的活动,要去掉标题栏,使用supportRequestWindowFeature()方法 supportRequestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_add); // 当点击dialog之外完成此activity setFinishOnTouchOutside(true); //关闭按钮操作 btnCancel = (Button) findViewById(R.id.btnCancel); btnCancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { AddActivity.this.finish(); } }); //保存按钮操作 btnSave = findViewById(R.id.btnSave); btnSave.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(); spinnerDay = findViewById(R.id.spinnerDay); String day = spinnerDay.getSelectedItem().toString(); if (day.equals("--请选择--")) { btnSave.setError(""); return; } spinnerTime = findViewById(R.id.spinnerTime); String time = spinnerTime.getSelectedItem().toString(); if (time.equals("--请选择--")) { btnSave.setError(""); return; } edtSubject = findViewById(R.id.edtSubject); String text = edtSubject.getText().toString(); if ("".equals(text)) { btnSave.setError(""); return; } edtTeacher = findViewById(R.id.edtTeacher); String teacher = edtTeacher.getText().toString(); if ("".equals(teacher)) { btnSave.setError(""); return; } // 创建一个数据库对象 DBHelper dbHelper = new DBHelper(AddActivity.this, MainActivity.DB_NAME, null, 1); // 把数据存在contentValues中 ContentValues contentValues = new ContentValues(); // getId()方法目的是通过星期几和第几节课算出对应该课程id contentValues.put("c_id", getId(day, time)); contentValues.put("c_name", text); contentValues.put("c_time", time); contentValues.put("c_day", day); contentValues.put("c_teacher", teacher); // 更新数据库记录 update(dbHelper, contentValues); // 清空栈内所有activity intent.setFlags(intent.FLAG_ACTIVITY_CLEAR_TASK); // 启动MainActivity intent.setClass(AddActivity.this, MainActivity.class); startActivity(intent); } }); } public String getId(String day, String time) { //星期几转换成int类型 int day1 = Utils.getDay(day); //如1-2节课只取1 int time1 = Integer.parseInt(time.substring(0, 1)); return String.valueOf((day1 - 1) * 5 + ((time1 - 1) / 2 + 1)); } public void update(DBHelper dbHelper, ContentValues contentValues) { String[] a = {contentValues.get("c_id").toString()}; // 通过DBHelper类获取一个读写的SQLiteDatabase对象 SQLiteDatabase db = dbHelper.getWritableDatabase(); // 修改数据 db.update(DBHelper.TABLE_NAME, contentValues, "c_id = ?", a); // 释放连接 db.close(); } }
至此,课程数据的添加和课程表的显示功能已经完成。
三、查看课程详细数据
1、创建课程详情布局文件
activity_detail.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" style="@style/myDialogStyle" android:layout_width="300dp" android:layout_height="350dp" android:background="#fff" android:orientation="vertical" tools:context=".DetailActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="课程详情" android:textColor="@color/black" android:textSize="30dp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="时间:" android:textColor="@color/cardview_dark_background" android:textSize="20dp" /> <TextView android:id="@+id/tvDay" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="" android:textColor="@color/cardview_dark_background" android:textSize="20dp" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="节课:" android:textColor="@color/cardview_dark_background" android:textSize="20dp" /> <TextView android:id="@+id/tvTime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="" android:textColor="@color/cardview_dark_background" android:textSize="20dp" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="课程:" android:textColor="@color/cardview_dark_background" android:textSize="20dp" /> <TextView android:id="@+id/tvName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="" android:textColor="@color/cardview_dark_background" android:textSize="20dp" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="任课教师:" android:textColor="@color/cardview_dark_background" android:textSize="20dp" /> <TextView android:id="@+id/tvTeacher" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:autoSizeMaxTextSize="30sp" android:autoSizeMinTextSize="5sp" android:autoSizeStepGranularity="1sp" android:text="" android:textColor="@color/cardview_dark_background" android:textSize="20sp" /> </LinearLayout> </LinearLayout>
2、创建Activity
DetailActivity.java
import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.view.Window; import android.widget.TextView; public class DetailActivity extends AppCompatActivity { private TextView day, time, name, teacher; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); supportRequestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_detail); Intent intent = getIntent(); name = findViewById(R.id.tvName); name.setText(intent.getStringExtra("name")); day = findViewById(R.id.tvDay); day.setText(intent.getStringExtra("day")); time = findViewById(R.id.tvTime); time.setText(intent.getStringExtra("time")); teacher = findViewById(R.id.tvTeacher); teacher.setText(intent.getStringExtra("teacher")); } }
3、修改清单文件
将DetailActivity配置为对话框式活动
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.ClassSchedule" tools:targetApi="31"> <activity android:name=".DetailActivity" android:exported="true" android:theme="@style/Theme.AppCompat.Dialog"> <meta-data android:name="android.app.lib_name" android:value="" /> </activity> <activity android:name=".AddActivity" android:exported="true" android:theme="@style/Theme.AppCompat.Dialog"> <meta-data android:name="android.app.lib_name" android:value="" /> </activity> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <meta-data android:name="android.app.lib_name" android:value="" /> </activity> </application> </manifest>
4、修改MainActivity
添加单击事件打开课程详细页面
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import android.annotation.SuppressLint; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.graphics.Color; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.widget.GridLayout; import android.widget.TextView; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "MainActivity"; public static final String DB_NAME = "classes_db.db"; private DBHelper dbHelper; private List<Classes> classes = new ArrayList<Classes>(); private int[] weekId = {R.id.d1, R.id.d2, R.id.d3, R.id.d4, R.id.d5, R.id.d6, R.id.d7}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initWeeks(); // 初始化课程表控件 // 获得数据库管理类对象 dbHelper = new DBHelper(MainActivity.this, DB_NAME, null, 1); display(dbHelper); // 调用显示课程数据的方法 } public void initWeeks() { //创建一个GridLayout对象 GridLayout gridLayout; //定义每个框的id,之后会动态改变id值 int id = 1; //渲染每一列(周) for (int i = 0; i < 7; i++) { // 注入GridLayout对应的列,根据星期几调用LayoutColumn方法 gridLayout = findViewById(weekId[i]); // 渲染每一行(节课) for (int j = 1; j < 10; j += 2) { // 声明一个新的TextView TextView textView = new TextView(this); // 给TextView设置style样式 textView.setId(id++); textView.setText(""); textView.setMaxLines(5); textView.setEllipsize(TextUtils.TruncateAt.END); // TextView内容过长时,结尾加省略号 textView.setBackgroundColor(Color.parseColor("#F0FFFF")); textView.setGravity(Gravity.CENTER); // GridLayout.LayoutParams设置在此gridLayout列中TextView布局 GridLayout.LayoutParams params = new GridLayout.LayoutParams(); params.setMargins(5, 10, 5, 10); params.width = GridLayout.LayoutParams.MATCH_PARENT; params.height = 0; // 设置在gridLayout中的方位,参数1:在第几行。参数2:占几行。参数3:权值 // 这个权值是根据xml中第一个gridLayout节课权值设定的。 params.rowSpec = GridLayout.spec(j, 2, 1); // 把TextView和布局样式添加到此gridLayout中 gridLayout.addView(textView, params); } } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); MenuItem menuItem = menu.findItem(R.id.action_menu); menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem menuItem) { Intent intent = new Intent(); intent.setClass(MainActivity.this, AddActivity.class); startActivity(intent); return true; } }); return super.onCreateOptionsMenu(menu); } // 显示课程表数据 private void display(DBHelper dbHelper) { // 从数据库获取课程数据保存到列表中 classes = query(dbHelper); Log.d(TAG, "display: classes.size() = " + classes.size()); for (Classes aClass : classes) { Log.d(TAG, "display: id = " + aClass.getC_id() + ", time = " + aClass.getC_time() + ", day = " + aClass.getC_day()); // 第几节课,奇数节次,“1-2节”中的1,“5-6节”中的5 int i = Integer.parseInt(aClass.getC_time().charAt(0) + ""); // 星期几 int j = Utils.getDay(aClass.getC_day()); // 获取此课程对应TextView的id TextView tvClass = findViewById((j - 1) * 5 + ((i - 1) / 2 + 1)); // 判断如果课程星期==当前星期,如此课程信息和当前都是星期二就把背景颜色更换。 Date date = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEEE"); // 显示星期几 if (aClass.getC_day().equals(simpleDateFormat.format(date).toString())) { tvClass.setBackgroundColor(Color.rgb(28, 217, 204)); } // 课程表信息显示出来 tvClass.setText(aClass.getC_name() + "\n" + aClass.getC_teacher()); // 单击此课程框 tvClass.setOnClickListener(MainActivity.this); } } @SuppressLint("Range") public List<Classes> query(DBHelper dbHelper) { List<Classes> classesList = new ArrayList<>(); // 通过DBHelper类获取一个读写的SQLiteDatabase对象 SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor = db.query(DBHelper.TABLE_NAME, null, null, null, null, null, null); // 将游标移到开头 cursor.moveToFirst(); while (!cursor.isAfterLast()) { // 游标只要不是在最后一行之后,就一直循环 if (!cursor.getString(cursor.getColumnIndex("c_day")).equals("0")) { Classes aClass = new Classes(); aClass.setC_id(Integer.parseInt(cursor.getString(cursor.getColumnIndex("c_id")))); aClass.setC_name(cursor.getString(cursor.getColumnIndex("c_name"))); aClass.setC_time(cursor.getString(cursor.getColumnIndex("c_time"))); aClass.setC_day(cursor.getString(cursor.getColumnIndex("c_day"))); aClass.setC_teacher(cursor.getString(cursor.getColumnIndex("c_teacher"))); classesList.add(aClass); } // 将游标移到下一行 cursor.moveToNext(); } db.close(); return classesList; } @SuppressLint("Range") @Override public void onClick(View v) { TextView textView = (TextView) v; SQLiteDatabase db = dbHelper.getWritableDatabase(); Cursor cursor = db.query(DBHelper.TABLE_NAME, null, "c_id = ?", new String[]{String.valueOf(textView.getId())}, null, null, null); // 将游标移到开头 cursor.moveToFirst(); if (!cursor.isAfterLast()) { Classes Class = new Classes(); Intent intent = new Intent(); intent.putExtra("day", cursor.getString(cursor.getColumnIndex("c_day"))); intent.putExtra("time", cursor.getString(cursor.getColumnIndex("c_time"))); intent.putExtra("name", cursor.getString(cursor.getColumnIndex("c_name"))); intent.putExtra("teacher", cursor.getString(cursor.getColumnIndex("c_teacher"))); intent.setClass(MainActivity.this, DetailActivity.class); startActivity(intent); } } }
运行程序,单击课程
四、删除课程
因为课程数据是在数据库初始化时固定了35条空白记录,所以,删除课程时,不能删除该条记录,而是应该将该记录设置为空。
因而删除课程,实际上是使用update()方法将该课程的每个字段设置为“0”.
另外,我们可以通过长按课程框弹出对话框来确认是否删除该课程。
修改MainActivity.java
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import android.annotation.SuppressLint; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.graphics.Color; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.widget.GridLayout; import android.widget.TextView; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; public class MainActivity extends AppCompatActivity implements View.OnClickListener, View.OnLongClickListener { private static final String TAG = "MainActivity"; public static final String DB_NAME = "classes_db.db"; private DBHelper dbHelper; private List<Classes> classes = new ArrayList<Classes>(); private int[] weekId = {R.id.d1, R.id.d2, R.id.d3, R.id.d4, R.id.d5, R.id.d6, R.id.d7}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initWeeks(); // 初始化课程表控件 // 获得数据库管理类对象 dbHelper = new DBHelper(MainActivity.this, DB_NAME, null, 1); display(dbHelper); // 调用显示课程数据的方法 } public void initWeeks() { //创建一个GridLayout对象 GridLayout gridLayout; //定义每个框的id,之后会动态改变id值 int id = 1; //渲染每一列(周) for (int i = 0; i < 7; i++) { // 注入GridLayout对应的列,根据星期几调用LayoutColumn方法 gridLayout = findViewById(weekId[i]); // 渲染每一行(节课) for (int j = 1; j < 10; j += 2) { // 声明一个新的TextView TextView textView = new TextView(this); // 给TextView设置style样式 textView.setId(id++); textView.setText(""); textView.setMaxLines(5); textView.setEllipsize(TextUtils.TruncateAt.END); // TextView内容过长时,结尾加省略号 textView.setBackgroundColor(Color.parseColor("#F0FFFF")); textView.setGravity(Gravity.CENTER); // GridLayout.LayoutParams设置在此gridLayout列中TextView布局 GridLayout.LayoutParams params = new GridLayout.LayoutParams(); params.setMargins(5, 10, 5, 10); params.width = GridLayout.LayoutParams.MATCH_PARENT; params.height = 0; // 设置在gridLayout中的方位,参数1:在第几行。参数2:占几行。参数3:权值 // 这个权值是根据xml中第一个gridLayout节课权值设定的。 params.rowSpec = GridLayout.spec(j, 2, 1); // 把TextView和布局样式添加到此gridLayout中 gridLayout.addView(textView, params); } } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); MenuItem menuItem = menu.findItem(R.id.action_menu); menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem menuItem) { Intent intent = new Intent(); intent.setClass(MainActivity.this, AddActivity.class); startActivity(intent); return true; } }); return super.onCreateOptionsMenu(menu); } // 显示课程表数据 private void display(DBHelper dbHelper) { // 从数据库获取课程数据保存到列表中 classes = query(dbHelper); Log.d(TAG, "display: classes.size() = " + classes.size()); for (Classes aClass : classes) { Log.d(TAG, "display: id = " + aClass.getC_id() + ", time = " + aClass.getC_time() + ", day = " + aClass.getC_day()); // 第几节课,奇数节次,“1-2节”中的1,“5-6节”中的5 int i = Integer.parseInt(aClass.getC_time().charAt(0) + ""); // 星期几 int j = Utils.getDay(aClass.getC_day()); // 获取此课程对应TextView的id TextView tvClass = findViewById((j - 1) * 5 + ((i - 1) / 2 + 1)); // 判断如果课程星期==当前星期,如此课程信息和当前都是星期二就把背景颜色更换。 Date date = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEEE"); // 显示星期几 if (aClass.getC_day().equals(simpleDateFormat.format(date).toString())) { tvClass.setBackgroundColor(Color.rgb(28, 217, 204)); } // 课程表信息显示出来 tvClass.setText(aClass.getC_name() + "\n" + aClass.getC_teacher()); // 单击此课程框 tvClass.setOnClickListener(MainActivity.this); // 长按此课程框 tvClass.setOnLongClickListener(MainActivity.this); } } @SuppressLint("Range") public List<Classes> query(DBHelper dbHelper) { List<Classes> classesList = new ArrayList<>(); // 通过DBHelper类获取一个读写的SQLiteDatabase对象 SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor = db.query(DBHelper.TABLE_NAME, null, null, null, null, null, null); // 将游标移到开头 cursor.moveToFirst(); while (!cursor.isAfterLast()) { // 游标只要不是在最后一行之后,就一直循环 if (!cursor.getString(cursor.getColumnIndex("c_day")).equals("0")) { Classes aClass = new Classes(); aClass.setC_id(Integer.parseInt(cursor.getString(cursor.getColumnIndex("c_id")))); aClass.setC_name(cursor.getString(cursor.getColumnIndex("c_name"))); aClass.setC_time(cursor.getString(cursor.getColumnIndex("c_time"))); aClass.setC_day(cursor.getString(cursor.getColumnIndex("c_day"))); aClass.setC_teacher(cursor.getString(cursor.getColumnIndex("c_teacher"))); classesList.add(aClass); } // 将游标移到下一行 cursor.moveToNext(); } db.close(); return classesList; } @SuppressLint("Range") @Override public void onClick(View v) { TextView textView = (TextView) v; SQLiteDatabase db = dbHelper.getWritableDatabase(); Cursor cursor = db.query(DBHelper.TABLE_NAME, null, "c_id = ?", new String[]{String.valueOf(textView.getId())}, null, null, null); // 将游标移到开头 cursor.moveToFirst(); if (!cursor.isAfterLast()) { Classes Class = new Classes(); Intent intent = new Intent(); intent.putExtra("day", cursor.getString(cursor.getColumnIndex("c_day"))); intent.putExtra("time", cursor.getString(cursor.getColumnIndex("c_time"))); intent.putExtra("name", cursor.getString(cursor.getColumnIndex("c_name"))); intent.putExtra("teacher", cursor.getString(cursor.getColumnIndex("c_teacher"))); intent.setClass(MainActivity.this, DetailActivity.class); startActivity(intent); } } @SuppressLint("ResourceType") @Override public boolean onLongClick(View v) { TextView textView = (TextView) v; Classes aClass = null; int count = 0; for (Classes bClass : classes) { if (bClass.getC_id() == v.getId()) { aClass = bClass; break; } } Log.d(TAG, "onLongClick: id = " + v.getId() + ":" + aClass.getC_name() + ":" + aClass.getC_day() + ":" + aClass.getC_time()); AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("删除"); builder.setMessage("确定要删除\n" + aClass.getC_day() + aClass.getC_time() + aClass.getC_name() + "?"); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { SQLiteDatabase db = dbHelper.getWritableDatabase(); db.execSQL("UPDATE " + DBHelper.TABLE_NAME + " SET c_name = '0', c_teacher = '0', c_day = '0', c_time = '0' WHERE c_id = ?", new String[]{String.valueOf(v.getId())}); // 启动MainActivity Intent intent = new Intent(); intent.addFlags(intent.FLAG_ACTIVITY_CLEAR_TASK); intent.addFlags(intent.FLAG_ACTIVITY_NEW_TASK); intent.setClass(MainActivity.this, MainActivity.class); startActivity(intent); } }); builder.show(); return true; } }
运行程序