1.在项目根目录下的build.gradle文件中,
allprojects {
repositories {
maven {
url 'https://s01.oss.sonatype.org/content/repositories/releases/'
}
}
}
2.在entry模块的build.gradle文件中,
dependencies {
implementation('com.gitee.chinasoft_ohos:timetable-view:1.0.0')
......
}
在sdk6,DevEco Studio 2.2 Beta1下项目可直接运行
如无法运行,删除项目.gradle,.idea,build,gradle,build.gradle文件,
并依据自己的版本创建新项目,将新项目的对应文件复制到根目录下
该控件包含的基础组件有日期栏、侧边栏、课表视图,在布局文件中加入如下代码后会包含这三个基础组件,注意要添加背景色,没有背景图片可以添加白色背景。
XML中添加控件:
<com.zhuangfei.timetable.view.WeekView
ohos:id="$+id:id_weekview"
ohos:width="match_parent"
ohos:height="match_content"/>
<com.zhuangfei.timetable.TimetableView
ohos:id="$+id:id_timetableView"
ohos:width="match_parent"
ohos:height="match_parent"
ohos:background_element="$color:app_white"/>
数据源的设置方式有两种,以下分别来介绍:
方法1:使用指定的格式List,Schedule是控件提供的课程实体类,你可以将自己的数据封装为指定格式,然后进行如下配置即可
mTimetableView.data(scheduleList)
.curWeek(1)
.showView();
方法2:方法1在很多场景下都满足不了需求,往往需要定义自己的课程实体类,你可以跟随以下几个步骤来使用它
public class MySubject implements ScheduleEnable {
@Override
public Schedule getSchedule() {
Schedule schedule = new Schedule();
schedule.setDay(getDay());
schedule.setName(getName());
schedule.setRoom(getRoom());
schedule.setStart(getStart());
schedule.setStep(getStep());
schedule.setTeacher(getTeacher());
schedule.setWeekList(getWeekList());
schedule.setColorRandom(2);
schedule.putExtras(EXTRAS_ID,getId());
schedule.putExtras(EXTRAS_AD_URL,getUrl());
return schedule;
}
}
/**
* SimpleSlice
*
* @since 2021-03-29
*/
public class SimpleSlice extends AbilitySlice implements Component.ClickedListener {
/**
* 控件
*/
TimetableView mTimetableView;
WeekView mWeekView;
DirectionalLayout layout;
Text titleTextView;
List<MySubject> mySubjects;
/**
* 记录切换的周次,不一定是当前周
*/
int target = -1;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_base_func);
initViews();
}
private void initViews() {
titleTextView = (Text) findComponentById(ResourceTable.Id_id_title);
layout = (DirectionalLayout) findComponentById(ResourceTable.Id_id_layout);
layout.setClickedListener(this);
initTimetableView();
requestData();
}
@Override
public void onActive() {
super.onActive();
mTimetableView.onDateBuildListener()
.onHighLight();
}
EventHandler runner = new EventHandler(EventRunner.create()) {
@Override
protected void processEvent(InnerEvent event) {
super.processEvent(event);
mySubjects = SubjectRepertory.loadDefaultSubjects();
MySubject adSubject = new MySubject();
adSubject.setName("【广告】");
adSubject.setStart(1);
adSubject.setStep(2);
adSubject.setDay(7);
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= 20; i++) {
list.add(i);
}
adSubject.setWeekList(list);
mySubjects.add(adSubject);
SimpleSlice.this.getUITaskDispatcher().asyncDispatch(() -> {
mWeekView.source(mySubjects).showView();
mTimetableView.source(mySubjects).showView();
});
}
};
/**
* 2秒后刷新界面,模拟网络请求
*/
private void requestData() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
String errorString = e.toString();
}
runner.sendEvent(InnerEvent.get());
}
}).start();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
@Override
public void onClick(Component component) {
switch (component.getId()) {
case ResourceTable.Id_id_layout:
/**
* 如果周次选择已经显示了,那么将它隐藏,更新课程、日期
*/
if (mWeekView.isShowing()) {
hideWeekView();
} else {
showWeekView();
}
break;
default:break;
}
}
/**
* 初始化课程控件
*/
private void initTimetableView() {
mWeekView = (WeekView) findComponentById(ResourceTable.Id_id_weekview);
mTimetableView = (TimetableView) findComponentById(ResourceTable.Id_id_timetableView);
/**
* 设置周次选择属性
*/
mWeekView.curWeek(1)
.callback(new IWeekView.OnWeekItemClickedListener() {
@Override
public void onWeekClicked(int week) {
int cur = mTimetableView.curWeek();
/**
* 更新切换后的日期,从当前周cur->切换的周week
*/
mTimetableView.onDateBuildListener()
.onUpdateDate(cur, week);
mTimetableView.changeWeekOnly(week);
}
})
.callback(new IWeekView.OnWeekLeftClickedListener() {
@Override
public void onWeekLeftClicked() {
onWeekLeftLayoutClicked();
}
})
/**
* 设置隐藏,默认显示
*/
.isShow(false)
.showView();
mTimetableView.curWeek(1)
.curTerm("大三下学期")
.callback(new ISchedule.OnItemClickListener() {
@Override
public void onItemClick(Component v, List<Schedule> scheduleList) {
display(scheduleList);
}
})
.callback(new ISchedule.OnItemLongClickListener() {
@Override
public void onLongClick(Component v, int day, int start) {
ToastViewDialog.toast(SimpleSlice.this, "长按:周" + day + ",第" + start + "节");
}
})
.callback(new ISchedule.OnWeekChangedListener() {
@Override
public void onWeekChanged(int curWeek) {
titleTextView.setText("第" + curWeek + "周");
}
})
.callback(new OnItemBuildAdapter() {
@Override
public void onItemUpdate(StackLayout layout, Text textView, Text countTextView, Schedule schedule, ShapeElement gd) {
super.onItemUpdate(layout, textView, countTextView, schedule, gd);
if (schedule.getName().equals("【广告】")) {
layout.removeAllComponents();
}
}
})
.showView();
}
/**
* 周次选择布局的左侧被点击时回调<br/>
* 对话框修改当前周次
*/
protected void onWeekLeftLayoutClicked() {
final String items[] = new String[20];
int itemCount = mWeekView.itemCount();
for (int i = 0; i < itemCount; i++) {
items[i] = "第" + (i + 1) + "周";
}
target = -1;
}
/**
* 显示内容
*
* @param beans
*/
protected void display(List<Schedule> beans) {
String str = "";
for (Schedule bean : beans) {
str += bean.getName() + "," + bean.getWeekList().toString() + "," + bean.getStart() + "," + bean.getStep() + "\n";
}
ToastViewDialog.toast(this, str);
}
/**
* 隐藏周次选择,此时需要将课表的日期恢复到本周并将课表切换到当前周
*/
public void hideWeekView() {
mWeekView.isShow(false);
titleTextView.setTextColor(Color.BLUE);
int cur = mTimetableView.curWeek();
mTimetableView.onDateBuildListener()
.onUpdateDate(cur, cur);
mTimetableView.changeWeekOnly(cur);
}
public void showWeekView() {
mWeekView.isShow(true);
titleTextView.setTextColor(Color.RED);
}
}
配置属性
我直接把这部分整个代码放出来了,分以下三步:
TimetableView mTimetableView;
WeekView mWeekView;
/**
* 初始化课程控件
*/
private void initTimetableView() {
/**
* 获取控件
*/
mWeekView = (WeekView) findComponentById(ResourceTable.Id_id_weekview);
mTimetableView = (TimetableView) findComponentById(ResourceTable.Id_id_timetableView);
/**
* 设置周次选择属性
*/
mWeekView.source(mySubjects)
.curWeek(1)
.callback(new IWeekView.OnWeekItemClickedListener() {
@Override
public void onWeekClicked(int week) {
int cur = mTimetableView.curWeek();
/**
* 更新切换后的日期,从当前周cur->切换的周week
*/
mTimetableView.onDateBuildListener()
.onUpdateDate(cur, week);
mTimetableView.changeWeekOnly(week);
}
})
.callback(new IWeekView.OnWeekLeftClickedListener() {
@Override
public void onWeekLeftClicked() {
onWeekLeftLayoutClicked();
}
})
.isShow(false)
.showView();
mTimetableView.source(mySubjects)
.curWeek(1)
.curTerm("大三下学期")
.maxSlideItem(10)
.monthWidthDp(50)
.callback(new ISchedule.OnItemClickListener() {
@Override
public void onItemClick(Component v, List<Schedule> scheduleList) {
display(scheduleList);
}
})
.callback(new ISchedule.OnItemLongClickListener() {
@Override
public void onLongClick(Component v, int day, int start) {
ToastViewDialog.toast(BaseFuncSlice.this, "长按:周" + day + ",第" + start + "节");
}
})
.callback(new ISchedule.OnWeekChangedListener() {
@Override
public void onWeekChanged(int curWeek) {
titleTextView.setText("第" + curWeek + "周");
}
})
/**
* 旗标布局点击监听
*/
.callback(new ISchedule.OnFlaglayoutClickListener() {
@Override
public void onFlaglayoutClick(int day, int start) {
mTimetableView.hideFlaglayout();
ToastViewDialog.toast(BaseFuncSlice.this, "点击了旗标:周" + (day + 1) + ",第" + start + "节");
}
})
.showView();
}
删除课程
/**
* 删除课程
* 内部使用集合维护课程数据,操作集合的方法来操作它即可
* 最后更新一下视图(全局更新)
*/
protected void deleteSubject() {
int size = mTimetableView.dataSource().size();
int pos = (int) (new SecureRandom().nextDouble() * size);
if (size > 0) {
mTimetableView.dataSource().remove(pos);
mTimetableView.updateView();
}
}
添加课程
/**
* 添加课程
* 内部使用集合维护课程数据,操作集合的方法来操作它即可
* 最后更新一下视图(全局更新)
*/
protected void addSubject() {
List<Schedule> dataSource = mTimetableView.dataSource();
int size = dataSource.size();
if (size > 0) {
Schedule schedule = dataSource.get(0);
dataSource.add(schedule);
mTimetableView.updateView();
}
}
非本周课程显示与隐藏
/**
* 隐藏非本周课程
* 修改了内容的显示,所以必须更新全部(性能不高)
* 建议:在初始化时设置该属性
* <p>
* updateView()被调用后,会重新构建课程,课程会回到当前周
*/
protected void hideNonThisWeek() {
mTimetableView.isShowNotCurWeek(false).updateView();
}
/**
* 显示非本周课程
* 修改了内容的显示,所以必须更新全部(性能不高)
* 建议:在初始化时设置该属性
*/
protected void showNonThisWeek() {
mTimetableView.isShowNotCurWeek(true).updateView();
}
最大节次设置
/**
* 设置侧边栏最大节次,只影响侧边栏的绘制,对课程内容无影响
* @param num
*/
protected void setMaxItem(int num) {
mTimetableView.maxSlideItem(num).updateSlideView();
}
节次时间显示与隐藏
/**
* 显示时间
* 设置侧边栏构建监听,TimeSlideAdapter是控件实现的可显示时间的侧边栏
*/
protected void showTime() {
String[] times = new String[]{"8:00", "9:00", "10:10", "11:00", "15:00", "16:00", "17:00", "18:00", "19:30", "20:30", "21:30", "22:30"};
OnSlideBuildAdapter listener = (OnSlideBuildAdapter) mTimetableView.onSlideBuildListener();
listener.setTimes(times)
.setTimeTextColor(Color.BLACK.getValue());
mTimetableView.updateSlideView();
}
/**
* 隐藏时间
* 将侧边栏监听置Null后,会默认使用默认的构建方法,即不显示时间
* 只修改了侧边栏的属性,所以只更新侧边栏即可(性能高),没有必要更新全部(性能低)
*/
protected void hideTime() {
mTimetableView.callback((ISchedule.OnSlideBuildListener) null);
mTimetableView.updateSlideView();
}
WeekView显示与隐藏
/**
* 显示WeekView
*/
protected void showWeekView() {
mWeekView.isShow(true);
}
/**
* 隐藏WeekView
*/
protected void hideWeekView() {
mWeekView.isShow(false);
}
月份宽度设置
/**
* 设置月份宽度
*/
private void setMonthWidth() {
mTimetableView.monthWidthDp(100).updateView();
}
/**
* 设置月份宽度,默认40dp
*/
private void resetMonthWidth() {
mTimetableView.monthWidthDp(80).updateView();
}
周末显示与隐藏
/**
* 隐藏周末
*/
private void hideWeekends() {
mTimetableView.isShowWeekends(false).updateView();
}
/**
* 显示周末
*/
private void showWeekends() {
mTimetableView.isShowWeekends(true).updateView();
}
周次选择栏WeekView是控件实现的一个默认的周次选择控件,你可以使用它快速的拥有周次选择功能,TimetableView`本身是没有周次选择功能的,所以需要两者配合使用,完整代码参见 BaseFuncSlice
默认的周次选择栏
1.添加控件
在布局文件中放一个TimetableView,然后在TimetableView的上边放一个WeekView
<com.zhuangfei.timetable.view.WeekView
ohos:id="$+id:id_weekview"
ohos:width="match_parent"
ohos:height="match_content"/>
<com.zhuangfei.timetable.TimetableView
ohos:id="$+id:id_timetableView"
ohos:width="match_parent"
ohos:height="match_parent"
ohos:background_element="$color:app_white"/>
2.初始化 我直接把这部分整个代码放出来了,分以下2步:
使用如下方式获取到控件
WeekView weekView;
周次选择控件应该与课程表控件结合使用,TimetableView的初始化和使用已省略,请参考其他章节
//设置周次选择属性
mWeekView.source(mySubjects)
.curWeek(1)
.callback(new IWeekView.OnWeekItemClickedListener() {
@Override
public void onWeekClicked(int week) {
int cur = mTimetableView.curWeek();
//更新切换后的日期,从当前周cur->切换的周week
mTimetableView.onDateBuildListener()
.onUpdateDate(cur, week);
//课表切换周次
mTimetableView.changeWeekOnly(week);
}
})
.callback(new IWeekView.OnWeekLeftClickedListener() {
@Override
public void onWeekLeftClicked() {
onWeekLeftLayoutClicked();
}
})
.isShow(false)//设置隐藏,默认显示
.showView();
自定义周次选择栏
你可以选择以下方法来实现周次选择栏的自定义:
本节主要演示如何对日期栏的属性设置以及自定义日期栏的步骤,通用步骤比如:添加控件、获取控件,设置数据源以及显示视图什么的都不再重复了,只讲解核心部分 完整代码参见 DateSlice
日期栏显示与隐藏
/**
* 隐藏日期栏
*/
protected void hideDateView() {
mTimetableView.hideDateView();
}
/**
* 显示日期栏
*/
protected void showDateView() {
mTimetableView.showDateView();
}
恢复默认日期栏
/**
* 恢复默认日期栏
*/
protected void cancelCustomDateView() {
mTimetableView.callback((ISchedule.OnDateBuildListener) null)
.updateDateView();
}
自定义日期栏
Step1:自定义布局
需要定义两个布局,第一个xml定义的月份的样式
<?xml version="1.0" encoding="utf-8"?>
<Text
ohos:id="$+id:id_week_month"
ohos:width="match_content"
ohos:height="match_content"
ohos:text_alignment="vertical_center|horizontal_center"
ohos:text_size="12fp"
xmlns:ohos="http://schemas.huawei.com/res/ohos"/>
第二个xml定义的是星期一至星期日的每项的样式
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:id="$+id:id_week_layout"
ohos:height="40vp"
ohos:width="match_content"
ohos:alignment="horizontal_center|vertical_center"
ohos:orientation="vertical"
>
<Text
ohos:id="$+id:id_week_day"
ohos:height="match_content"
ohos:width="match_content"
ohos:text_size="12vp"
ohos:text_font="$string:textfront_bold"/>
</DirectionalLayout>
Step2:自定义实现类
注意:由于要修改默认的height,所以onBuildDayLayout onBuildMonthLayout都必须重写并设置为新的height,否则无效。
你也可以直接实现OnDateBuildListener接口,更灵活但是操作复杂。
/**
* 自定义日期栏
* 该段代码有点长,但是很好懂,仔细看看会有收获的,嘻嘻
*/
protected void customDateView() {
mTimetableView.callback(
new OnDateBuildAapter() {
@Override
public Component onBuildDayLayout(LayoutScatter mInflate, int pos, int width, int height) {
int newHeight = 50;
Component view = mInflate.parse(ResourceTable.Layout_item_custom_dateview, null, true);
Text dayTextView = (Text) view.findComponentById(ResourceTable.Id_id_week_day);
dayTextView.setText(dateArray[pos]);
layouts[pos] = (DirectionalLayout) view.findComponentById(ResourceTable.Id_id_week_layout);
DirectionalLayout.LayoutConfig lp = new DirectionalLayout.LayoutConfig(width, newHeight);
layouts[pos].setLayoutConfig(lp);
return view;
}
@Override
public Component onBuildMonthLayout(LayoutScatter mInflate, int width, int height) {
int newHeight = 30;
Component first = mInflate.parse(ResourceTable.Layout_item_custom_dateview_first, null, true);
/**
* 月份设置
*/
textViews[0] = (Text) first.findComponentById(ResourceTable.Id_id_week_month);
layouts[0] = null;
DirectionalLayout.LayoutConfig lp = new DirectionalLayout.LayoutConfig(width, newHeight);
int month = Integer.parseInt(weekDates.get(0));
first.setLayoutConfig(lp);
textViews[0].setText(month + "\n月");
return first;
}
})
.updateDateView();
}
日期延迟显示案例
需求:设定一个开学时间,在开学时间到来之前,一直显示开学时的第一周的日期
我们使用自定义日期栏来实现这个需求
需要设置一个阈值以及一个默认的日期集合,如果当前时间戳小于阈值时,使用默认的日期集合,否则计算当前周的日期,所以这个就很简单了
import com.zhuangfei.timetable.listener.OnDateBuildAapter;
import com.zhuangfei.timetable.model.ScheduleSupport;
import ohos.agp.components.DirectionalLayout;
import java.text.SimpleDateFormat;
import java.util.List;
/**
* 自定义日期栏
* Created by Liu ZhuangFei on 2018/8/24.
*/
public class OnDateDelayAdapter extends OnDateBuildAapter {
/**
* 阈值,即超过这个时间戳后开始更新日期
* 否则将一直显示initDates中的日期
*/
protected long startTime;
protected String startTimeStr;
protected SimpleDateFormat sdf;
/**
* 日期集合,8个元素,当前时间小于等于阈值时使用
*/
List<String> initDates = null;
public OnDateDelayAdapter() {
sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
}
/**
* 设置日期集合
*
* @param dates 元素个数必须大于等于8,第一个为月份数值,第2-8为周一至周日的日期数值(不带中文)
*/
public void setDateList(List<String> dates) {
if (dates.size() >= 8) {
this.initDates = dates;
}
}
public void setStartTime(long startTime) {
this.startTime = startTime;
this.startTimeStr = sdf.format(startTime);
}
@Override
public void onInit(DirectionalLayout layout, float alpha) {
super.onInit(layout, alpha);
/**
* 增加的
*/
long curTime = System.currentTimeMillis();
if (curTime <= startTime) {
weekDates = initDates;
}
}
@Override
public void onUpdateDate(int curWeek,int targetWeek) {
if (textViews == null || textViews.length < 8) {
return;
}
if (whenBeginSchool() <= 0) {
weekDates = ScheduleSupport.getDateStringFromWeek(curWeek,targetWeek);
}
int month = Integer.parseInt(weekDates.get(0));
textViews[0].setText(month + "\n月");
for (int i = 1; i < 8; i++) {
if (textViews[i] != null) {
textViews[i].setText(weekDates.get(i) + "日");
}
}
}
/**
* 计算距离开学的天数
*
* @return 返回值2种类型,-1:没有开学时间,无法计算;0:已经开学;>0:天数
*/
public long whenBeginSchool() {
if (!"".equals(startTimeStr)) {
int calWeek = ScheduleSupport.timeTransfrom(startTimeStr);
if (calWeek > 0) {
return 0;
} else {
long seconds = (startTime - System.currentTimeMillis()) / 1000;
long day = seconds / (24 * 3600);
return day;
}
}
return -1;
}
}
大部分工作第一步已经做完了,剩下的事情就是如何去使用它,示例如下:
下面这个方法是获取一个OnDateDelayAdapter示例,并且对其初始化
/**
* 配置OnDateDelayAdapter
*/
public OnDateDelayAdapter getDateDelayAdapter() {
OnDateDelayAdapter onDateDelayAdapter = new OnDateDelayAdapter();
/**
* 计算开学时间戳
*/
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
long startTime = 0;
try {
startTime = sdf.parse("2022-09-03 00:00").getTime();
} catch (ParseException e) {
e.printStackTrace();
}
/**
* 计算开学时的一周日期,我这里模拟一下
*/
List<String> dateList = Arrays.asList("9", "03", "04", "05", "06", "07", "08", "09");
onDateDelayAdapter.setStartTime(startTime);
onDateDelayAdapter.setDateList(dateList);
return onDateDelayAdapter;
}
然后将OnDateDelayAdapter实例设置到TimetableView控件上
mTimetableView.source(mySubjects)
.curWeek(1)
.curTerm("大三下学期")
.maxSlideItem(10)
.callback(getDateDelayAdapter())//这行要放在下行的前边
.callback(new ISchedule.OnWeekChangedListener() {
@Override
public void onWeekChanged(int curWeek) {
OnDateDelayAdapter adapter= (OnDateDelayAdapter) mTimetableView.onDateBuildListener();
long when=adapter.whenBeginSchool();
if(when>0){
titleTextView.setText("距离开学还有"+when+"天");
}else{
titleTextView.setText("第" + curWeek + "周");
}
}
})
.showView();
其中这两句代码可以用来计算距离开学的日期
OnDateDelayAdapter adapter= (OnDateDelayAdapter) mTimetableView.onDateBuildListener();
long when=adapter.whenBeginSchool();
为了保证日期能够更正,你还需要在onStart中对日期更正
int cur = mTimetableView.curWeek();
mTimetableView.onDateBuildListener().onUpdateDate(cur, cur);
OnDateDelayAdapter adapter = (OnDateDelayAdapter) mTimetableView.onDateBuildListener();
long when = adapter.whenBeginSchool();
if (when > 0) {
titleTextView.setText("距离开学还有" + when + "天");
}
在课程视图的左侧有一列是侧边栏,本节演示如何对侧边栏的属性进行配置以及自定义侧边栏的步骤 完整代码参见 SlideSlice
准备
添加控件
<com.zhuangfei.timetable.view.WeekView
ohos:id="$+id:id_weekview"
ohos:width="match_parent"
ohos:height="match_content"/>
<com.zhuangfei.timetable.TimetableView
ohos:id="$+id:id_timetableView"
ohos:width="match_parent"
ohos:height="match_parent"
ohos:background_element="$color:app_white"/>
获取控件
private void initViews() {
mTimetableView = (TimetableView) findComponentById(ResourceTable.Id_id_timetableView);
List<MySubject> mySubjects = SubjectRepertory.loadDefaultSubjects();
mTimetableView.source(mySubjects)
.curWeek(1)
.showView();
}
节次时间显示与隐藏
/**
* 显示时间
* 设置侧边栏构建监听,TimeSlideAdapter是控件实现的可显示时间的侧边栏
* 只修改了侧边栏的属性,所以只更新侧边栏即可(性能高),没有必要更新全部(性能低)
*
* @see OnSlideBuildAdapter
*/
protected void showTime() {
String[] times = new String[]{"8:00", "9:00", "10:10", "11:00","15:00", "16:00", "17:00", "18:00","19:30", "20:30", "21:30", "22:30"};
OnSlideBuildAdapter slideAdapter = new OnSlideBuildAdapter();
slideAdapter.setTimes(times);
mTimetableView.callback(slideAdapter);
mTimetableView.updateSlideView();
}
/**
* 隐藏时间
* 将侧边栏监听置Null后,会默认使用默认的构建方法,即不显示时间
* 只修改了侧边栏的属性,所以只更新侧边栏即可(性能高),没有必要更新全部(性能低)
*/
protected void hideTime() {
mTimetableView.callback((ISchedule.OnSlideBuildListener) null)
.updateSlideView();
}
修改侧边栏背景
/**
* 修改侧边栏背景,默认的使用的是OnSlideBuildAdapter,
* 所以可以强转类型
*
* @param color
*/
protected void modifySlideBgColor(RgbColor color) {
OnSlideBuildAdapter listener = (OnSlideBuildAdapter) mTimetableView.onSlideBuildListener();
listener.setBackground(color);
mTimetableView.updateSlideView();
}
修改节次文本颜色
/**
* 修改侧边栏节次文本的颜色值
*
* @param color
*/
protected void modifyItemTextColor(int color) {
OnSlideBuildAdapter listener = (OnSlideBuildAdapter) mTimetableView.onSlideBuildListener();
listener.setTextColor(color);
mTimetableView.updateSlideView();
}
修改时间文本颜色
/**
* 修改侧边栏时间文本的颜色值
*
* @param color
*/
protected void modifyItemTimeColor(int color) {
String[] times = new String[]{"8:00", "9:00", "10:10", "11:00", "15:00", "16:00", "17:00", "18:00", "19:30", "20:30", "21:30", "22:30"};
OnSlideBuildAdapter listener = (OnSlideBuildAdapter) mTimetableView.onSlideBuildListener();
listener.setTimes(times)
.setTimeTextColor(color);
mTimetableView.updateSlideView();
}
侧边栏效果重置
/**
* 取消自定义的侧边栏,回到默认状态
* 只需要将监听器置空即可
*/
protected void cancelCustomSlideView() {
mTimetableView.callback((ISchedule.OnSlideBuildListener) null)
.updateSlideView();
}
自定义侧边栏
Step1:创建布局
创建一个XML文件,该文件的内容可完全自定义
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
ohos:width="match_parent"
ohos:height="match_content"
ohos:orientation="vertical"
xmlns:ohos="http://schemas.huawei.com/res/ohos">
<Text
ohos:text_color="$color:app_qing2"
ohos:width="match_parent"
ohos:height="match_content"
ohos:text_alignment="top|horizontal_center"
ohos:id="$+id:item_slide_textview"
ohos:text_size="14fp"/>
</DirectionalLayout>
Step2:设置监听
为了简单起见,可以继承自OnSlideBuildAdapter,重写getView()方法,当然也可以直接实现ISchedule.OnSlideBuildListener接口
/**
* 自定义侧边栏效果
* 使用自定义的布局文件实现的文字居顶部的效果(默认居中)
*/
protected void customSlideView() {
mTimetableView.callback(
new OnSlideBuildAdapter() {
@Override
public Component getView(int pos, LayoutScatter inflater, int itemHeight, int marTop) {
/**
* 获取View并返回,注意设置marTop值
*/
Component v = inflater.parse(ResourceTable.Layout_item_custom_slide, null, false);
Text tv = (Text) v.findComponentById(ResourceTable.Id_item_slide_textview);
DirectionalLayout.LayoutConfig lp = new DirectionalLayout.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT,
itemHeight);
lp.setMargins(0, marTop, 0, 0);
tv.setLayoutConfig(lp);
tv.setText((pos + 1) + "");
return v;
}
})
.updateSlideView();
}
本节将演示如何配置课程项的样式,完整代码参见 ItemStyleSlice
非本周课程显示与隐藏
/**
* 隐藏非本周课程
* 修改了内容的显示,所以必须更新全部(性能不高)
* 建议:在初始化时设置该属性
*/
protected void hideNonThisWeek() {
mTimetableView.isShowNotCurWeek(false).updateView();
}
/**
* 显示非本周课程
* 修改了内容的显示,所以必须更新全部(性能不高)
* 建议:在初始化时设置该属性
*/
protected void showNonThisWeek() {
mTimetableView.isShowNotCurWeek(true).updateView();
}
设置间距以及弧度
/**
* 设置间距以及弧度
* 该方法只能同时设置四个角的弧度,设置单个角的弧度可参考下文
*/
protected void setMarginAndCorner() {
mTimetableView.cornerAll(0)
.marTop(0)
.marLeft(0)
.updateView();
}
设置单个角弧度
/**
* 设置角度(四个角分别控制)
*
* @param leftTop
* @param rightTop
* @param rightBottom
* @param leftBottom
*/
public void setCorner(final int leftTop, final int rightTop, final int rightBottom, final int leftBottom) {
mTimetableView.callback(new OnItemBuildAdapter() {
@Override
public void onItemUpdate(StackLayout layout, Text textView, Text countTextView, Schedule schedule, ShapeElement gd) {
super.onItemUpdate(layout, textView, countTextView, schedule, gd);
/**
* 数组8个元素,四个方向依次为左上、右上、右下、左下,
* 每个方向在数组中占两个元素,值相同
*/
gd.setCornerRadiiArray(new float[]{leftTop, leftTop, rightTop, rightTop, rightBottom, rightBottom, leftBottom, leftBottom});
}
});
mTimetableView.updateView();
}
修改显示的文本
/**
* 修改显示的文本
*/
public void buildItemText() {
mTimetableView.callback(new OnItemBuildAdapter() {
@Override
public String getItemText(Schedule schedule, boolean isThisWeek) {
if (isThisWeek) {
return "[本周]" + schedule.getName();
} else {
return "[非本周]" + schedule.getName();
}
}
})
.updateView();
}
设置非本周课的背景
/**
* 设置非本周课的背景
*
* @param color
*/
public void setNonThisWeekBgcolor(RgbColor color) {
mTimetableView.colorPool().setUselessColor(color);
mTimetableView.updateView();
}
课程重叠的样式
/**
* 修改课程重叠的样式,在该接口中,你可以自定义出很多的效果
*/
protected void modifyOverlayStyle() {
mTimetableView.callback(new OnItemBuildAdapter() {
@Override
public void onItemUpdate(StackLayout layout, Text textView, Text countTextView, Schedule schedule, ShapeElement gd) {
super.onItemUpdate(layout, textView, countTextView, schedule, gd);
/**
* 可见说明重叠,取消角标,添加角度
*/
if (countTextView.getVisibility() == Component.VISIBLE) {
countTextView.setVisibility(Component.HIDE);
gd.setCornerRadiiArray(new float[]{0, 0, 20, 20, 0, 0, 0, 0});
}
}
});
mTimetableView.updateView();
}
添加广告
广告一般是一张图片和一个链接,所以整体思路是这样的,首先向数据集里插入一条数据,数据的名字可以任意,但是必须和普通课程有所区分,根据这个名字可以判断出它是广告,然后给该数据设置上课周次、开始节次、持续节次;然后监听课程项的构建,如果监测到该课程是广告,那么将默认的课程布局remove掉,然后添加一个广告图片,再给它设置一个点击事件
/**
* 构造数据集
*/
mySubjects = SubjectRepertory.loadDefaultSubjects();
MySubject adSubject = new MySubject();
adSubject.setName("【广告】");
adSubject.setStart(1);
adSubject.setStep(2);
adSubject.setDay(7);
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= 20; i++) {
list.add(i);
}
adSubject.setWeekList(list);
adSubject.setUrl(AD_URL);
mySubjects.add(adSubject);
SimpleSlice.this.getUITaskDispatcher().asyncDispatch(() -> {
mWeekView.source(mySubjects).showView();
mTimetableView.source(mySubjects).showView();
});
/**
* 设置属性
*/
mTimetableView.curWeek(1)
.curTerm("大三下学期")
.callback(new ISchedule.OnItemClickListener() {
@Override
public void onItemClick(Component v, List<Schedule> scheduleList) {
display(scheduleList);
}
})
.callback(new ISchedule.OnItemLongClickListener() {
@Override
public void onLongClick(Component v, int day, int start) {
ToastViewDialog.toast(SimpleSlice.this, "长按:周" + day + ",第" + start + "节");
}
})
.callback(new ISchedule.OnWeekChangedListener() {
@Override
public void onWeekChanged(int curWeek) {
titleTextView.setText("第" + curWeek + "周");
}
})
.callback(new OnItemBuildAdapter() {
@Override
public void onItemUpdate(StackLayout layout, Text textView, Text countTextView, Schedule schedule, ShapeElement gd) {
super.onItemUpdate(layout, textView, countTextView, schedule, gd);
if (schedule.getName().equals("【广告】")) {
layout.removeAllComponents();
Image imageView = new Image(SimpleSlice.this);
imageView.setHeight(DependentLayout.LayoutConfig.MATCH_PARENT);
imageView.setWidth(DependentLayout.LayoutConfig.MATCH_PARENT);
layout.addComponent(imageView);
String url = (String) schedule.getExtras().get(MySubject.EXTRAS_AD_URL);
PixelMap pixelMap = loadImage(url);
/**
* 普通解码
*/
getUITaskDispatcher().syncDispatch(() -> {
imageView.setPixelMap(pixelMap);
pixelMap.release();
});
imageView.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
ToastViewDialog.toast(SimpleSlice.this, "进入广告网页链接");
}
});
}
}
})
.showView();
中英文切换
step1:英文日期栏
import com.zhuangfei.timetable.listener.OnDateBuildAapter;
import com.zhuangfei.timetable.model.ScheduleSupport;
/**
* 英语日期栏
*/
public class OnEnglishDateBuildAdapter extends OnDateBuildAapter {
@Override
public String[] getStringArray() {
return new String[]{null, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
}
@Override
public void onUpdateDate(int curWeek, int targetWeek) {
if (textViews == null || textViews.length < 8) {
return;
}
String[] monthArray = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sept","Oct","Nov","Dec"};
weekDates = ScheduleSupport.getDateStringFromWeek(curWeek, targetWeek);
int month = Integer.parseInt(weekDates.get(0));
textViews[0].setText(monthArray[month - 1]);
for (int i = 1; i < 8; i++) {
if (textViews[i] != null) {
textViews[i].setText(weekDates.get(i));
}
}
}
}
step2:英文课程项
import com.zhuangfei.timetable.listener.OnItemBuildAdapter;
import com.zhuangfei.timetable.model.Schedule;
/**
* 英文的课程文本设置
*/
public class OnEnglishItemBuildAdapter extends OnItemBuildAdapter {
@Override
public String getItemText(Schedule schedule, boolean isThisWeek) {
if (schedule == null || schedule.getName().equals("")) {
return "Unknow";
}
if (schedule.getRoom() == null) {
if (!isThisWeek) {
return "[Non]" + schedule.getName();
}
return schedule.getName();
}
String res = schedule.getName() + "@" + schedule.getRoom();
if (!isThisWeek) {
res = "[Non]" + res;
}
return res;
}
}
step3:设置监听
/**
* 切换为英文
*/
public void changeEnglishLanguage() {
mTimetableView.callback(new OnEnglishDateBuildAdapter())
.callback(new OnEnglishItemBuildAdapter())
.updateView();
}
/**
* 切换为中文
*/
public void changeChineseLanguage() {
mTimetableView.callback((ISchedule.OnDateBuildListener) null)
.callback((ISchedule.OnItemBuildListener) null)
.updateView();
}
颜色池
颜色池ScheduleColorPool的内部实现是控件维护的一个集合,它管理着课程项的颜色,负责对颜色的存取 完整代码参见 ColorPoolSlice
获取颜色池
以下代码的返回结果就是一个ScheduleColorPool实例对象
mTimetableView.colorPool();
指定颜色
如果需要让所有课程只在某几种颜色中分配颜色,只需要清空颜色池并加入特定的颜色,在分配颜色时会循环分配颜色池中的颜色
/**
* 设置指定的颜色,默认情况下颜色池中有一些颜色
* 所以这里需要先清空一下颜色池
*
* @param colors
*/
public void setColor(RgbColor... colors) {
mTimetableView.colorPool().clear().add(colors);
mTimetableView.updateView();
}
重置颜色池
重置后,颜色池恢复到初始状态
/**
* 重置颜色池
*/
public void resetColor() {
mTimetableView.colorPool().reset();
mTimetableView.updateView();
}
追加颜色
向颜色池中追加颜色,在为课程项分配颜色时,会按照颜色池中的顺序依次取出颜色,所以并不保证追加的颜色一定会被用到
/**
* 追加颜色
*
* @param colors
*/
public void addColor(RgbColor... colors) {
mTimetableView.colorPool().add(colors);
mTimetableView.updateView();
}
设置非本周课程颜色
向颜色池中追加颜色,在为课程项分配颜色时,会按照颜色池中的顺序依次取出颜色,所以并不保证追加的颜色一定会被用到
/**
* 设置非本周课的背景
*
* @param color
*/
public void setNonThisWeekBgcolor(int color) {
mTimetableView.colorPool().setUselessColor(color);
mTimetableView.updateView();
}
指定课程的颜色
setIgnoreUserlessColor 用来设置颜色映射是否忽略非本周的课程颜色,如果设置为true,表示该课程在颜色映射中能够查找到,那么不管是本周还是非本周它都将显示映射中的颜色;如果设置为false,表示如果该课程在颜色映射中能够查找到,并且该课程是本周的,那么将该课程设置为映射中的颜色,如果非本周,则使用非本周颜色对其渲染
/**
* 指定课程的颜色,未指定的课程自动分配
*/
public void forColor() {
Map<String, Integer> colorMap = new HashMap<>();
colorMap.put("数字图像处理", Color.RED.getValue());
colorMap.put("算法分析与设计", Color.BLUE.getValue());
mTimetableView.colorPool().setIgnoreUserlessColor(false).setColorMap(colorMap);
mTimetableView.updateView();
}
为什么会有这个功能?想象这样几个场景:下拉反弹效果、下拉刷新效果、监听到滚动的位置、解决滚动布局嵌套导致的滑动冲突。这几个需求都可以使用自定义View的方式来解决。 替换滚动布局的意思是:你可以使用自定义的ScrollView来替换控件中的普通的ScrollView,可以想象它将有更好的扩展性。 本节将演示如何实现一个有下拉反弹效果的课表界面,自定义View的知识不在本节的范围之内,有兴趣可以百度。 完整代码参见 ElasticSlice
自定义View
你需要先准备好一个自定义View,根据你的需求而定,可以参考以下的弹性滚动布局ElasticScrollView
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ScrollView;
import ohos.agp.utils.Rect;
import ohos.app.Context;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;
/**
* ElasticScrollView
* <p>
* 弹性滚动布局,下拉时会反弹
*
* @since 2021-03-29
*/
public class ElasticScrollView extends ScrollView implements Component.TouchEventListener {
private Component inner;
private float yy;
private Rect normal = new Rect();
private boolean animationFinish = true;
public ElasticScrollView(Context context) {
super(context);
}
public ElasticScrollView(Context context, AttrSet attrs) {
super(context, attrs);
}
public void commOnTouchEvent(TouchEvent ev) {
if (animationFinish) {
int action = ev.getAction();
MmiPoint point = ev.getPointerPosition(ev.getIndex());
switch (action) {
case TouchEvent.PRIMARY_POINT_DOWN:
yy = point.getY();
break;
case TouchEvent.PRIMARY_POINT_UP:
yy = 0;
if (isNeedAnimation()) {
animation();
}
break;
case TouchEvent.POINT_MOVE:
final float preY = yy == 0 ? point.getY() : yy;
float nowY = point.getY();
int deltaY = (int) (preY - nowY);
yy = nowY;
/**
* 当滚动到最上或者最下时就不会再滚动,这时移动布局
*/
if (isNeedMove()) {
if (normal.isEmpty()) {
/**
* 保存正常的布局位置
*/
normal.set(inner.getLeft(), inner.getTop(), inner.getRight(), inner.getBottom());
}
/**
* 移动布局
*/
inner.setComponentPosition(inner.getLeft(), inner.getTop() - deltaY / 2, inner.getRight(), inner.getBottom() - deltaY / 2);
}
break;
default:
break;
}
}
}
/**
* 开启动画移动
*/
public void animation() {
AnimatorProperty ta = new AnimatorProperty(inner);
ta.moveFromX(0).moveToX(0).moveFromY(0).moveToY(normal.top - inner.getTop()).setDelay(200);
ta.setStateChangedListener(new Animator.StateChangedListener() {
@Override
public void onStart(Animator animator) {
}
@Override
public void onStop(Animator animator) {
}
@Override
public void onCancel(Animator animator) {
}
@Override
public void onEnd(Animator animator) {
inner.setComponentPosition(normal.left, normal.top, normal.right, normal.bottom);
normal.set(0, 0, 0, 0);
animationFinish = true;
}
@Override
public void onPause(Animator animator) {
}
@Override
public void onResume(Animator animator) {
}
});
ta.start();
}
/**
* 是否需要开启动画
*
* @return 是否需要开启动画
*/
public boolean isNeedAnimation() {
return !normal.isEmpty();
}
/**
* 是否需要移动布局
*
* @return 是否需要移动布局
*/
public boolean isNeedMove() {
int offset = inner.getEstimatedHeight() - getHeight();
int scrollY = getScrollValue(0);
if (scrollY == 0 || scrollY == offset) {
return true;
}
return false;
}
@Override
public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
if (inner != null) {
commOnTouchEvent(touchEvent);
}
return false;
}
}
布局文件
备一个布局文件,命名任意,将以下内容复制到布局文件中,然后将根控件换成自定义控件,注意ID不能改变
<?xml version="1.0" encoding="utf-8"?>
<com.chinasoft.ohos.views.ElasticScrollView
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:id="$+id:id_scrollview"
ohos:width="match_parent"
ohos:height="match_parent">
<include ohos:layout="$layout:view_content"
ohos:height="match_content"
ohos:width="match_parent"/>
</com.chinasoft.ohos.views.ElasticScrollView>
设置监听
private void initViews() {
mTimetableView = (TimetableView) findComponentById(ResourceTable.Id_id_timetableView);
List<MySubject> mySubjects = SubjectRepertory.loadDefaultSubjects();
/**
* 过程很简单,步骤如下:
* 1.创建一个xml文件,命名为custom_myscrollview.xml
* 2.拷贝一段代码至该文件中,具体内容可以参见custom_myscrollview.xml
* 3.将根布局控件修改为自定义的控件,其他内容无需修改
* 4.设置滚动布局构建监听并实现其方法,将自定义的xml转换为View返回即可
*
*/
mTimetableView.source(mySubjects)
.callback(new ISchedule.OnScrollViewBuildListener() {
@Override
public Component getScrollView(LayoutScatter mInflate) {
return mInflate.parse(ResourceTable.Layout_custom_myscrollview, null, true);
}
})
.showView();
}
什么是旗标布局?我自己随意起的名,不要当真,嘻嘻。 旗标布局是指当我们点击空白格子的时候出现一个布局,该布局占据一个格子的位置,它所在的位置就是我们所点击的格子的位置,该布局可以响应事件并回调处理 完整代码参见 FlaglayoutSlice
默认情况下,旗标布局是开启状态的,即不需要任何配置,在空白格子处点击会出现旗标布局,当然,它也可以关闭。
事件监听
OnSpaceItemClickListener是空白格子点击监听器,当用户点击空白格子时,会回调该接口中的方法并传入点击的格子的位置,然后需要将旗标布局移动到指定格子位置,OnSpaceItemClickAdapter是这个接口的默认实现,这部分一般不用开发者关心
现在的效果是点击空白格子后,在点击的格子位置会出现一个旗标布局,开发者只需要使用使用OnFlaglayoutClickListener来监听旗标布局的点击事件即可,无需关心其他
以下代码中监听了三个事件:
private void initViews() {
mTimetableView = (TimetableView) findComponentById(ResourceTable.Id_id_timetableView);
List<MySubject> mySubjects = SubjectRepertory.loadDefaultSubjects();
mTimetableView.source(mySubjects)
.curWeek(1)
.maxSlideItem(10)
.callback(new ISchedule.OnItemClickListener() {
@Override
public void onItemClick(Component v, List<Schedule> scheduleList) {
display(scheduleList);
}
})
.callback(new ISchedule.OnItemLongClickListener() {
@Override
public void onLongClick(Component v, int day, int start) {
ToastViewDialog.toast(FlaglayoutSlice.this, "长按:周" + day + ",第" + start + "节");
}
})
.callback(new ISchedule.OnFlaglayoutClickListener() {
@Override
public void onFlaglayoutClick(int day, int start) {
mTimetableView.hideFlaglayout();
ToastViewDialog.toast(FlaglayoutSlice.this, "点击了旗标:周" + (day + 1) + ",第" + start + "节");
}
})
.showView();
}
背景修改与重置
/**
* 修改旗标布局的背景色
*
* @param color
*/
private void modifyFlagBgcolor(RgbColor color) {
mTimetableView.flagBgcolor(color).updateFlaglayout();
}
/**
* 重置旗标布局的背景色
*/
private void resetFlagBgcolor() {
mTimetableView.resetFlagBgcolor().updateFlaglayout();
}
开启与关闭
/**
* 取消旗标布局
*/
private void cancelFlagBgcolor() {
mTimetableView.isShowFlaglayout(false).updateFlaglayout();
}
/**
* 显示旗标布局
*/
private void resetFlaglayout() {
mTimetableView.isShowFlaglayout(true).updateFlaglayout();
}
显示与隐藏
/**
* 显示旗标布局
*/
mTimetableView.showFlaglayout();
/**
* 隐藏旗标布局
*/
mTimetableView.hideFlaglayout();
设置数据源时可以使用自定义的数据类型,但是必须实现ScheduleEnable接口,为什么呢?因为在TimetableView内部保存的数据格式是List的,接口只是起到一个转换的作用,点击事件会回调,会获取到点击位置的一个List集合,此时我们已经拿不到自定义的数据类型了,那么此时我想拿到这门课程的ID怎么办? 完整代码参见 ExtrasSlice
存入额外数据
Schedule中没有这个字段,自定义类型中有,所以在Schedule新增了一个extras字段,它是一个Map。所以现在这个问题可以这样解决:
import com.zhuangfei.timetable.model.Schedule;
import com.zhuangfei.timetable.model.ScheduleEnable;
import java.util.List;
/**
* 自定义实体类需要实现ScheduleEnable接口并实现getSchedule()
*
* @see ScheduleEnable#getSchedule()
*/
public class MySubject implements ScheduleEnable {
public static final String EXTRAS_ID = "extras_id";
public static final String EXTRAS_AD_URL = "extras_ad_url";
private int id = 0;
/**
* 课程名
*/
private String name;
private String time;
/**
* 教室
*/
private String room;
/**
* 教师
*/
private String teacher;
/**
* 第几周至第几周上
*/
private List<Integer> weekList;
/**
* 开始上课的节次
*/
private int start;
/**
* 上课节数
*/
private int step;
/**
* 周几上
*/
private int day;
private String term;
/**
* 一个随机数,用于对应课程的颜色
*/
private int colorRandom = 0;
private String url;
public void setUrl(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
public MySubject() {
}
public void setTime(String time) {
this.time = time;
}
public String getTime() {
return time;
}
public void setTerm(String term) {
this.term = term;
}
public String getTerm() {
return term;
}
public MySubject(String term,String name, String room, String teacher, List<Integer> weekList
, int start, int step, int day, int colorRandom, String time) {
super();
this.term = term;
this.name = name;
this.room = room;
this.teacher = teacher;
this.weekList = weekList;
this.start = start;
this.step = step;
this.day = day;
this.colorRandom = colorRandom;
this.time = time;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRoom() {
return room;
}
public void setRoom(String room) {
this.room = room;
}
public String getTeacher() {
return teacher;
}
public void setTeacher(String teacher) {
this.teacher = teacher;
}
public void setWeekList(List<Integer> weekList) {
this.weekList = weekList;
}
public List<Integer> getWeekList() {
return weekList;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getStep() {
return step;
}
public void setStep(int step) {
this.step = step;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public int getColorRandom() {
return colorRandom;
}
public void setColorRandom(int colorRandom) {
this.colorRandom = colorRandom;
}
@Override
public Schedule getSchedule() {
Schedule schedule = new Schedule();
schedule.setDay(getDay());
schedule.setName(getName());
schedule.setRoom(getRoom());
schedule.setStart(getStart());
schedule.setStep(getStep());
schedule.setTeacher(getTeacher());
schedule.setWeekList(getWeekList());
schedule.setColorRandom(2);
schedule.putExtras(EXTRAS_ID,getId());
schedule.putExtras(EXTRAS_AD_URL,getUrl());
return schedule;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
}
读出额外数据
private void initViews() {
mTimetableView = (TimetableView) findComponentById(ResourceTable.Id_id_timetableView);
mTimetableView.source(mySubjects)
.curWeek(1)
.maxSlideItem(10)
.callback(new ISchedule.OnItemClickListener() {
@Override
public void onItemClick(Component v, List<Schedule> scheduleList) {
display(scheduleList);
}
})
.showView();
}
protected void display(List<Schedule> beans) {
String str = "";
for (Schedule bean : beans) {
str += "[" + bean.getName() + "]的id:" + bean.getExtras().get(MySubject.EXTRAS_ID) + "\n";
}
ToastViewDialog.toast(this, str);
}
它是通过这样的方式拿到的额外数据:
String id=bean.getExtras().get(MySubject.EXTRAS_ID);
控件提供了一个工具类,可以方便的以无界面的方式操作课程数据,本节演示如何使用工具类实现对课程颜色的可视化展示 完整代码参见 NonViewSlice
列表与适配器
在Slice的布局中放一个ListContainer
<ListContainer
ohos:id="$+id:id_listview"
ohos:width="match_parent"
ohos:height="match_parent"
ohos:orientation="vertical"></ListContainer>
item_nonview.xml是ListView中每一项的布局,它的内容如下:
<?xml version="1.0" encoding="utf-8"?>
<DependentLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_content"
ohos:width="match_parent"
ohos:orientation="horizontal">
<Text
ohos:id="$+id:id_nonview_name"
ohos:height="50vp"
ohos:width="match_content"
ohos:align_baseline="end"
ohos:align_parent_left="true"
ohos:left_margin="15vp"
ohos:max_text_lines="1"
ohos:right_margin="15vp"
ohos:text_alignment="vertical_center"
ohos:text_size="12fp"
ohos:truncation_mode="ellipsis_at_end"
ohos:vertical_center="true"/>
<Text
ohos:id="$+id:id_nonview_color"
ohos:height="30vp"
ohos:width="30vp"
ohos:align_parent_right="true"
ohos:right_margin="15vp"
ohos:vertical_center="true"/>
<DirectionalLayout
ohos:height="1px"
ohos:width="match_parent"
ohos:alignment="bottom"
ohos:background_element="$color:colorCg"/>
</DependentLayout>
布局文件准备好之后,需要一个适配器
import com.chinasoft.ohos.ResourceTable;
import com.zhuangfei.timetable.model.Schedule;
import com.zhuangfei.timetable.model.ScheduleColorPool;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.app.Context;
import java.util.List;
/**
* Created by Liu ZhuangFei on 2018/6/18. BaseAdapter
*/
public class NonViewAdapter extends BaseItemProvider {
List<Schedule> schedules;
Context context;
LayoutScatter inflater;
public NonViewAdapter(Context context, List<Schedule> schedules) {
this.context = context;
this.schedules = schedules;
inflater = LayoutScatter.getInstance(context);
}
@Override
public int getCount() {
return schedules.size();
}
@Override
public Object getItem(int i) {
return schedules.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public Component getComponent(int i, Component convertView, ComponentContainer viewGroup) {
Component mView = null;
ViewHolder holder;
if (null == convertView) {
holder = new ViewHolder();
convertView = inflater.parse(ResourceTable.Layout_item_nonview,null,false);
holder.nameTextView = (Text) convertView.findComponentById(ResourceTable.Id_id_nonview_name);
holder.colorTextView = (Text) convertView.findComponentById(ResourceTable.Id_id_nonview_color);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Schedule schedule = (Schedule) getItem(i);
ScheduleColorPool colorPool = new ScheduleColorPool(context);
holder.nameTextView.setText(schedule.getName());
RgbColor colorAuto = colorPool.getColorAuto(schedule.getColorRandom());
ShapeElement shapeElement = new ShapeElement();
shapeElement.setRgbColor(colorAuto);
holder.colorTextView.setBackground(shapeElement);
return convertView;
}
/**
* ViewHolder
*
* @since 2021-03-29
*/
class ViewHolder {
Text nameTextView;
Text colorTextView;
}
}
它是如何将课程的颜色找出来的?看下段代码:
Schedule schedule = (Schedule) getItem(i);
ScheduleColorPool colorPool = new ScheduleColorPool(context);
holder.nameTextView.setText(schedule.getName());
RgbColor colorAuto = colorPool.getColorAuto(schedule.getColorRandom());
ShapeElement shapeElement = new ShapeElement();
shapeElement.setRgbColor(colorAuto);
holder.colorTextView.setBackground(shapeElement);
继续看,核心就是这一行,适配器中的数据都是被分配过颜色了(怎么分配的见下文),所谓分配颜色就是给它一个编号,然后拿着编号到颜色池中取颜色,getColorAuto()不会产生数组越界问题,内部使用模运算来循环的在颜色池中取值
RgbColor colorAuto = colorPool.getColorAuto(schedule.getColorRandom());
设置适配器
schedules = new ArrayList<>();
listView = (ListContainer) findComponentById(ResourceTable.Id_id_listview);
adapter = new NonViewAdapter(this, schedules);
listView.setItemProvider(adapter);
获取数据
public List<Schedule> getData() {
List<Schedule> list = ScheduleSupport.transform(SubjectRepertory.loadDefaultSubjects());
list = ScheduleSupport.getColorReflect(list);
return list;
}
显示所有课程
/**
* 获取所有课程
*/
protected void all() {
schedules.clear();
schedules.addAll(getData());
adapter.notifyDataChanged();
}
第一周有课的课程
/**
* 获取第一周有课的课程并显示出来
*/
protected void haveTime() {
List<Schedule> result = new ArrayList<>();
List<Schedule>[] arr = ScheduleSupport.splitSubjectWithDay(getData());
for (int i = 0; i < arr.length; i++) {
List<Schedule> tmpList = arr[i];
for (Schedule schedule : tmpList) {
if (ScheduleSupport.isThisWeek(schedule, 1)) {
result.add(schedule);
}
}
}
schedules.clear();
schedules.addAll(result);
adapter.notifyDataChanged();
}
周一有课的课程
/**
* 显示第一周周一有课的课程
*/
protected void haveTimeWithMonday() {
List<Schedule> tmpList = ScheduleSupport.getHaveSubjectsWithDay(
getData(), 1, 0);
schedules.clear();
schedules.addAll(tmpList);
adapter.notifyDataChanged();
}
CodeCheck代码测试无异常
CloudTest代码测试无异常
病毒安全检测通过
当前版本demo功能与原组件基本无差异
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
1. 开源生态
2. 协作、人、软件
3. 评估模型