style
的构造函数如View(Context, AttributeSet, defStyleAttr: Int, defStyleRes: Int)
,需要使用new View(new ContextThemeWrapper(this, styleRes))
方式padding, textColor
等有效果,并不是所有值都有效// ROW_WEIGHT 与 COLUMN_WEIGHT 为float型,如1.0f
ridLayout.Spec rowSpec = GridLayout.spec(ROW_INDEX, ROW_SPAN, ROW_WEIGHT);
GridLayout.Spec columnSpec = GridLayout.spec(COLUMN_INDEX, COLUMN_SPAN, COLUMN_WIEGHT);
GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(rowSpec, columnSpec);
android:layout_gravity="fill"
可以使单元格元素铺满宽高layout_weight
时最好一并指定layout_width=0
界面构建: 使用GridLayout作为每一节课的布局,使用AppBarLayout实现向上滚动隐藏最上方周列表的效果。因为要实现添加课程的功能,因此采用动态添加每一节课的子控件方式,需要使用LayoutParameters设置控件的属性。
布局文件: 顶部使用AppBar显示周信息,下面使用GridLayout显示课表,每一节课使用CardView+TextView
显示,空的小节需要使用一个空View设置,通过控制columnWeight
使控件均分剩余空间。
NestedScrollView
与AppBarLayout
实现上滑隐藏顶部效果
app:layout_behavior="@string/appbar_scrolling_view_behavior">
rowSpan
设置多个小节的课app:layout_scrollFlags="scroll|enterAlways"
类ClassInfo为数据对象,其它的学期、第一周开始日期为静态变量。启动时获取数据,然后遍历所有课程,创建CardView或空View添加到GridLayout中。
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/main_float_btn_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
app:backgroundTint="@color/colorAccent"
android:clickable="true"
android:focusable="true"
android:src="@drawable/sqsetting"/>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<GridLayout
android:id="@+id/main_grid_layout_all_class"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnCount="6"
android:orientation="vertical"
android:useDefaultMargins="true"
android:background="@color/whiteSmoke"
android:rowCount="13">
<TextView
style="@style/TextViewTimeTable"
android:text="@string/timeClass1" />
<TextView
style="@style/TextViewTimeTable"
android:text="@string/timeClass2" />
<TextView
style="@style/TextViewTimeTable"
android:text="@string/timeClass3" />
<TextView
style="@style/TextViewTimeTable"
android:text="@string/timeClass4" />
<TextView
style="@style/TextViewTimeTable"
android:text="@string/timeClass5" />
<TextView
style="@style/TextViewTimeTable"
android:text="@string/timeClass6" />
<TextView
style="@style/TextViewTimeTable"
android:text="@string/timeClass7" />
<TextView
style="@style/TextViewTimeTable"
android:text="@string/timeClass8" />
<TextView
style="@style/TextViewTimeTable"
android:text="@string/timeClass9" />
<TextView
style="@style/TextViewTimeTable"
android:text="@string/timeClass10" />
<TextView
style="@style/TextViewTimeTable"
android:text="@string/timeClass11" />
<TextView
style="@style/TextViewTimeTable"
android:text="@string/timeClass12" />
<TextView
style="@style/TextViewTimeTable"
android:text="@string/timeClass13" />
GridLayout>
androidx.core.widget.NestedScrollView>
<com.google.android.material.appbar.AppBarLayout
app:elevation="0dp"
android:background="@color/colorAccent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
app:layout_scrollFlags="scroll|enterAlways"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<LinearLayout
android:layout_width="45dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/main_text_view_week_index"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="第一周"
android:textStyle="bold"
android:textColor="@color/whiteSmoke"
android:textSize="14sp" />
<TextView
android:id="@+id/main_text_view_semester"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="研一上"
android:textColor="@color/whiteSmoke"
android:textSize="13sp" />
LinearLayout>
<LinearLayout
android:id="@+id/main_linear_weekdays"
android:orientation="horizontal"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
style="@style/TextViewWeekDay"
android:text="@string/monday" />
<TextView
style="@style/TextViewWeekDay"
android:text="@string/tuesday" />
<TextView
style="@style/TextViewWeekDay"
android:text="@string/wednesday" />
<TextView
style="@style/TextViewWeekDay"
android:text="@string/thursday" />
<TextView
style="@style/TextViewWeekDay"
android:text="@string/friday" />
LinearLayout>
LinearLayout>
com.google.android.material.appbar.AppBarLayout>
androidx.coordinatorlayout.widget.CoordinatorLayout>
package com.example.lessonschedule;
// import .......
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static ArrayList<ClassInfo> allClasses;
private static int semesterIndex, grade;
private static String teachWeekStartDate;
private GridLayout allClassGrid;
private TextView semesterTextView, weekIndexTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
allClassGrid = findViewById(R.id.main_grid_layout_all_class);
semesterTextView = findViewById(R.id.main_text_view_semester);
weekIndexTextView = findViewById(R.id.main_text_view_week_index);
findViewById(R.id.main_float_btn_setting).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, SettingActivity.class));
}
});
if (allClasses == null) getStoredData();
addAllClass();
setSemesterWeek();
}
private void setSemesterWeek() {
final String[] num2ChineseNum = new String[]{null, "一", "二", "三"};
if (grade > 0 && grade <= 3 && semesterIndex >= 0 && semesterIndex <= 1)
semesterTextView.setText(String.format("研%s%s", num2ChineseNum[grade], (semesterIndex == 0 ? "上" : "下")));
int dayOfWeek = GregorianCalendar.getInstance().get(Calendar.DAY_OF_WEEK);
// 周日是第一天
if (dayOfWeek >= 2 && dayOfWeek <= 6) {
LinearLayout linearLayout = findViewById(R.id.main_linear_weekdays);
linearLayout.getChildAt(dayOfWeek - 2).setBackgroundColor(getResources().getColor(R.color.colorAccentDark));
}
try {
Date now = new Date();
Date start = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).parse(teachWeekStartDate);
if (start != null) {
Log.e(TAG, "setSemesterWeek: " + start + ", now: " + now + " dif: " + (now.getTime() - start.getTime()));
long weekIndex = (now.getTime() - start.getTime()) / (7L * 24 * 60 * 60 * 1000) + 1;
weekIndexTextView.setText(String.format(Locale.CHINA, "第%d周", weekIndex));
}
} catch (Exception e) {
Log.e(TAG, "getStoredData: incorrect date format");
}
}
private void addAllClass() {
int k = 0;
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 13; ) {
if (k < allClasses.size() && allClasses.get(k).getWeekdayIndex() == i && allClasses.get(k).getClassStartIndex() == j) {
ClassInfo tmpClassInfo = allClasses.get(k);
TextView textView = new TextView(new ContextThemeWrapper(this, R.style.TextViewClassInfo));
textView.setText(tmpClassInfo.toClassInfoString());
textView.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
CardView cardView = new CardView(new ContextThemeWrapper(this, R.style.CardViewClass));
cardView.addView(textView);
cardView.setCardBackgroundColor(tmpClassInfo.getBgColor());
GridLayout.Spec rowSpec = GridLayout.spec(j - 1, tmpClassInfo.getClassPeriod());
GridLayout.Spec columnSpec = GridLayout.spec(i, 1.0f);
GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(rowSpec, columnSpec);
layoutParams.setGravity(Gravity.FILL);
layoutParams.height = 0;
layoutParams.width = 0;
allClassGrid.addView(cardView, layoutParams);
j += tmpClassInfo.getClassPeriod();
k++;
} else {
View child = new TextView(this);
GridLayout.Spec rowSpec = GridLayout.spec(j - 1, 1);
GridLayout.Spec columnSpec = GridLayout.spec(i, 1, 1.0f);
GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(rowSpec, columnSpec);
layoutParams.width = 0;
layoutParams.height = GridLayout.LayoutParams.WRAP_CONTENT;
layoutParams.setGravity(Gravity.FILL);
child.setBackgroundColor(getResources().getColor(R.color.lightGray, null));
allClassGrid.addView(child, layoutParams);
j += 1;
}
}
}
}
private void getStoredData() {
allClasses = new ArrayList<>();
allClasses.add(new ClassInfo("计算数论", "3A112", 1, 15, 1, 6, 2));
allClasses.add(new ClassInfo("信号与信息处理", "3C101", 2, 16, 1, 11, 3));
allClasses.add(new ClassInfo("高级计算机网络", "3B202", 1, 16, 2, 3, 3));
allClasses.add(new ClassInfo("高级计算机网络", "3C101", 8, 12, 4, 1, 2));
allClasses.add(new ClassInfo("计算数论", "3A112", 1, 14, 4, 6, 2));
semesterIndex = 1;
grade = 1;
teachWeekStartDate = "2020-02-17";
}
}
package com.example.lessonschedule;
// import ...
public class ClassInfo {
static enum ColorSupported {
Red, Pink, Purple, Deep_Purple, Indigo, Blue, Light_Blue, Cyan, Teal, Green, Light_Green, Lime, Amber, Orange
}
static final Map<ColorSupported, Integer> colorEnum2Color = new HashMap<ColorSupported, Integer>() {{
put(ColorSupported.Red, Color.rgb(244, 67, 54));
put(ColorSupported.Pink, Color.rgb(233, 30, 99));
put(ColorSupported.Purple, Color.rgb(156, 39, 176));
put(ColorSupported.Deep_Purple, Color.rgb(103, 58, 183));
put(ColorSupported.Indigo, Color.rgb(63, 81, 181));
put(ColorSupported.Blue, Color.rgb(33, 150, 243));
put(ColorSupported.Light_Blue, Color.rgb(3, 169, 244));
put(ColorSupported.Cyan, Color.rgb(0, 188, 212));
put(ColorSupported.Teal, Color.rgb(0, 150, 136));
put(ColorSupported.Green, Color.rgb(76, 175, 80));
put(ColorSupported.Light_Green, Color.rgb(139, 195, 74));
put(ColorSupported.Lime, Color.rgb(205, 220, 57));
put(ColorSupported.Amber, Color.rgb(255, 193, 7));
put(ColorSupported.Orange, Color.rgb(255, 152, 0));
}};
private String name;
private String location;
private int weekdayIndex;
private int startWeek, endWeek;
private int classStartIndex, classPeriod;
private Integer bgColor;
ClassInfo(String name, String location, int startWeek, int endWeek, int weekdayIndex, int classStartIndex, int classPeriod) {
this.name = name;
this.location = location;
this.startWeek = startWeek;
this.endWeek = endWeek;
this.classStartIndex = classStartIndex;
this.classPeriod = classPeriod;
this.weekdayIndex = weekdayIndex;
this.bgColor = colorEnum2Color.get(ColorSupported.values()[(int) (Math.random() * ColorSupported.values().length)]);
}
ClassInfo(String name, String location, int startWeek, int endWeek, int weekdayIndex, int classStartIndex, int classPeriod, ColorSupported bgColor) {
this(name, location, startWeek, endWeek, weekdayIndex, classStartIndex, classPeriod);
this.bgColor = colorEnum2Color.get(bgColor);
}
String toClassInfoString() {
return name + "\n" + location + "\n" + startWeek + "~" + endWeek + "周";
}
// getter and setter
}