allprojects {
repositories {
maven {
url 'https://s01.oss.sonatype.org/content/repositories/releases/'
}
}
}
dependencies {
implementation('com.gitee.chinasoft_ohos:timetable-view:1.0.0')
......
}
```Проект можно запустить напрямую на SDK 6, DevEco Studio 2.2 Beta1.
Если проект не запускается, удалите файлы .gradle, .idea, build, gradle, build.gradle, создайте новый проект в соответствии с вашей версией и скопируйте соответствующие файлы нового проекта в корневую директорию.
#### Инструкция по использованию
##### Добавление компонентов
Компонент включает базовые элементы: строку дат, боковую панель, таблицу расписания. После добавления следующего кода в файле разметки будут включены эти три базовых элемента. Не забудьте добавить фон, если нет изображения фона, можно добавить белый фон.
Добавление компонентов в XML:
```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 часто не удовлетворяет требованиям в различных сценариях, поэтому часто требуется определить собственный класс сущности для курса. Вы можете следовать следующим шагам для его использования:- Создайте собственный класс сущности и реализуйте интерфейс ScheduleEnable
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) {
e.printStackTrace();
}
SimpleSlice.this.getUITaskDispatcher().asyncDispatch(() -> {
mWeekView.source(mySubjects).showView();
mTimetableView.source(mySubjects).showView();
});
}
}).start();
}
}```markdown
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();
}
})
.callback(new IWeekView.OnWeekRightClickedListener() {
@Override
public void onWeekRightClicked() {
onWeekRightLayoutClicked();
}
});
}
/**
* Устанавливает скрытие, по умолчанию отображается
*/
.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);
}
});```markdown
onItemUpdate(layout, textView, countTextView, schedule, gd); if (schedule.getName().equals("【Реклама】")) { layout.removeAllComponents(); } }
/**
/**
/**
public void showWeekView() {
mWeekView.isShow(true);
titleTextView.setTextColor(Color.RED);
}
}
```
##### Основные возможности
**Настройка свойств**
Вот весь код, разделенный на три шага:
1. Получение компонентов.
2. Настройка свойств WeekView.
3. Настройка свойств TimetableView.
Компоненты можно получить следующим образом:
```java
TimetableView mTimetableView;
WeekView mWeekView;
``````java
/**
* Инициализация компонента TimetableView
*/
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() {
}
}); public void onFlaglayoutClick(int day, int start) {
mTimetableView.hideFlaglayout();
ToastViewDialog.toast(BaseFuncSlice.this, "Флаг-панель нажата: неделя " + (day + 1) + ", " + start + " пара");
}
})
.showView();
}**Удаление предмета**```java
/**
* Удаление предмета
* Внутренне использует коллекцию для хранения данных о предметах, операции над коллекцией выполняются методами работы с ней
* В конце обновляется представление (полное обновление)
*/
protected void deleteSubject() {
int size = mTimetableView.dataSource().size();
int pos = (int) (new SecureRandom().nextDouble() * size);
if (size > 0) {
mTimetableView.dataSource().remove(pos);
mTimetableView.updateView();
}
}
```
**Добавление предмета**
```java
/**
* Добавление предмета
* Внутренне использует коллекцию для хранения данных о предметах, операции над коллекцией выполняются методами работы с ней
* В конце обновляется представление (полное обновление)
*/
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();
}
}
```
**Скрытие предметов вне текущей недели**
```java
/**
* Скрытие предметов вне текущей недели
* Изменение отображения контента, поэтому требуется полное обновление (низкая производительность)
* Рекомендация: установите это свойство при инициализации
* <p>
* После вызова updateView() курс будет перестроен, и предметы вернутся к текущей неделе
*/
protected void hideNonThisWeek() {
mTimetableView.isShowNotCurWeek(false).updateView();
}
``` /**
* Отображение предметов вне текущей недели
* Изменение отображения контента, поэтому требуется полное обновление (низкая производительность)
* Рекомендация: установите это свойство при инициализации
*/
protected void showNonThisWeek() {
mTimetableView.isShowNotCurWeek(true).updateView();
}**Установка максимального количества уроков**
```java
/**
* Установка максимального количества уроков в боковой панели, влияет только на отрисовку боковой панели, не влияет на данные о предметах
* @param num
*/
protected void setMaxItem(int num) {
mTimetableView.maxSlideItem(num).updateSlideView();
}
```
**Отображение и скрытие времени уроков**
```java
/**
* Отображение времени
* Установка слушателя построения боковой панели, 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();
}
```
markdown
## Скрытие времени
Скрываем время, устанавливая слушатель стороны в `null`. Это приведет к использованию по умолчанию метода построения, и время не будет отображаться. Изменяется только свойство стороны, поэтому обновляется только сторона (высокая производительность), без необходимости обновления всего (низкая производительность).
```java
protected void hideTime() {
mTimetableView.callback((ISchedule.OnSlideBuildListener) null);
mTimetableView.updateSlideView();
}
```
### Отображение и скрытие WeekView
```java
/**
* Отображение WeekView
*/
protected void showWeekView() {
mWeekView.isShow(true);
}
/**
* Скрытие WeekView
*/
protected void hideWeekView() {
mWeekView.isShow(false);
}
```## Установка ширины месяца
```java
/**
* Установка ширины месяца
*/
private void setMonthWidth() {
mTimetableView.monthWidthDp(100).updateView();
}
/**
* Сброс ширины месяца, по умолчанию 40dp
*/
private void resetMonthWidth() {
mTimetableView.monthWidthDp(80).updateView();
}
```
## Отображение и скрытие выходных дней
```java
/**
* Скрытие выходных дней
*/
private void hideWeekends() {
mTimetableView.isShowWeekends(false).updateView();
}
/**
* Отображение выходных дней
*/
private void showWeekends() {
mTimetableView.isShowWeekends(true).updateView();
}
```
### Контроль выбора недели
Контроль выбора недели WeekView является встроенным контролем для выбора недели. Вы можете использовать его для быстрого доступа к функции выбора недели. Сам по себе TimetableView не имеет функции выбора недели, поэтому требуется их совместное использование. Полный код представлен в BaseFuncSlice.
### По умолчанию контроль выбора недели
1. Добавление контрола
Добавьте TimetableView в файле разметки, а затем добавьте WeekView над TimetableView.
```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"/>
```
2. Инициализация
Приведен полный код для инициализации:
1. Получение контрола
2. Установка свойств WeekView
Получите контрол следующим образом:
```java
WeekView weekView;
``````java
// Установка свойств выбора недели
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(WeekView);
}
})
.callback(new IWeekView.OnWeekLeftClickedListener() {
@Override
public void onWeekLeftClicked() {
onWeekLeftLayoutClicked();
}
})
.isShow(false) // Скрытие по умолчанию, показ по умолчанию
.showView();
```
**Пользовательская панель выбора недели**
Вы можете выбрать один из следующих методов для реализации пользовательской панели выбора недели:
- Пользовательская настройка, так как панель выбора недели и расписание не связаны, вы можете реализовать её по своему усмотрению.
- Соблюдение определённых мной стандартов панели выбора недели, реализация интерфейса WeekViewEnable.
- Расширение стандартной реализации панели выбора недели, наследование от WeekView и переопределение методов.##### Стили панели дат
В этом разделе демонстрируется, как устанавливать свойства панели дат и шаги для её пользовательской настройки. Общие шаги, такие как добавление компонента, получение компонента, установка источника данных и отображение представления, не рассматриваются. Рассматриваются только ключевые части.
Полный код см. в DateSlice.**Показ и скрытие панели дат**
```java
/**
* Скрытие панели дат
*/
protected void hideDateView() {
mTimetableView.hideDateView();
}
/**
* Показ панели дат
*/
protected void showDateView() {
mTimetableView.showDateView();
}
```
**Восстановление стандартной панели дат**
```java
/**
* Восстановление стандартной панели дат
*/
protected void cancelCustomDateView() {
mTimetableView.callback((ISchedule.OnDateBuildListener) null)
.updateDateView();
}
```
**Пользовательская дата**
Шаг 1: Пользовательский макет
Необходимо определить два макета. Первый xml определяет стиль месяца:
```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
<?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>
```
Шаг 2: Пользовательский класс реализации
Внимание: Из-за необходимости изменения стандартной высоты, методы `onBuildDayLayout` и `onBuildMonthLayout` должны быть переопределены и установлены новой высотой, иначе изменения не будут применены.```java
/**
* Пользовательская дата
* Этот код немного длинный, но легко понять. Присмотритесь к нему, и вы получите много полезной информации, хи-хи.
*/
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;
}
```
``````markdown
## Пример отложенного отображения даты
### Требование
Установить дату начала учебного года, и до этой даты отображать даты первой недели учебного года.
### Реализация
Для реализации этого требования используется пользовательский компонент даты.
- **Шаг 1: Настройка пользовательского компонента даты**
Необходимо установить пороговое значение и по умолчанию использовать набор дат. Если текущее время меньше порогового значения, используется набор дат по умолчанию, в противном случае вычисляются даты текущей недели. Это довольно просто.
``````java
@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;
}
```
### Обновление компонента даты
```java
.updateDateView();
```
``````markdown
import com. zhuangfei. timetable. listener. OnDateBuildAdapter;
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 OnDateBuildAdapter {
/**
* Пороговое значение, после которого начинается обновление дат
* В противном случае будут отображаться даты из 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, первый элемент - номер месяца, остальные - даты недели (без китайских символов)
*/
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;
}
```
```java
public long когдаНачаласьШкола() {
if (!"".equals(startTimeStr)) {
int calWeek = ScheduleSupport.времяПреобразование(startTimeStr);
if (calWeek > 0) {
return 0;
} else {
long seconds = (startTime - System.currentTimeMillis()) / 1000;
long день = seconds / (24 * 3600);
return день;
}
}
return -1;
}
```
```markdown
```java
public long когдаНачаласьШкола() {
if (!"".equals(startTimeStr)) {
int calWeek = ScheduleSupport.времяПреобразование(startTimeStr);
if (calWeek > 0) {
return 0;
} else {
long seconds = (startTime - System.currentTimeMillis()) / 1000;
long день = seconds / (24 * 3600);
return день;
}
}
return -1;
}
```
```Примечание: В данном контексте, для сохранения стиля и понимания кода, некоторые имена функций и переменных переведены дословно, сохраняя смысл оригинала.
Второй шаг: использование
Большая часть работы уже выполнена, осталось понять, как использовать это. Пример использования приведен ниже:
Вот метод, который получает пример OnDateDelayAdapter и инициализирует его:
```java
/**
* Конфигурация OnDateDelayAdapter
*/
public OnDateDelayAdapter getDateDelayAdapter() {
OnDateDelayAdapter onDateDelayAdapter = new OnDateDelayAdapter();
/**
* Вычисление времени начала учебного года в формате timestamp
*/
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:
```java
``` 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();Эти две строки кода могут использоваться для вычисления даты начала учебного года:
```java
OnDateDelayAdapter adapter = (OnDateDelayAdapter) mTimetableView.onDateBuildListener();
long when = adapter.whenBeginSchool();
```
Чтобы гарантировать правильность дат, вам также потребуется корректировать даты в методе `onStart`.```java
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`.
**Подготовка**
Добавление компонентов
```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"/>
```
Получение компонентов
```java
private void initViews() {
mTimetableView = (TimetableView) findComponentById(ResourceTable.Id_id_timetableView);
List<MySubject> mySubjects = SubjectRepertory.loadDefaultSubjects();
mTimetableView.source(mySubjects)
.curWeek(1)
.showView();
}
```
**Отображение и скрытие временных меток**
```java /**
* Отображение времени
* Установка слушателя построения боковой панели, 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();
}
```**Изменение фона боковой панели**
```java
/**
* Изменение фона боковой панели, по умолчанию используется OnSlideBuildAdapter,
* поэтому можно привести к этому типу
*
* @param color
*/
protected void modifySlideBgColor(RgbColor color) {
OnSlideBuildAdapter listener = (OnSlideBuildAdapter) mTimetableView.onSlideBuildListener();
listener.setBackground(color);
mTimetableView.updateSlideView();
}
```
**Изменение цвета текста элементов**
```java
/**
* Изменение цвета текста элементов боковой панели
*
* @param color
*/
protected void modifyItemTextColor(int color) {
OnSlideBuildAdapter listener = (OnSlideBuildAdapter) mTimetableView.onSlideBuildListener();
listener.setTextColor(color);
mTimetableView.updateSlideView();
}
```
**Изменение цвета текста времени**
```java /**
* Изменение цвета текста времени боковой панели
*
* @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();
}
```**Сброс настроек боковой панели**
```java
/**
* Отмена пользовательских настроек боковой панели и возврат к значениям по умолчанию
* Необходимо только сбросить слушатель
*/
protected void cancelCustomSlideView() {
mTimetableView.callback((ISchedule.OnSlideBuildListener) null)
.updateSlideView();
}
```
**Пользовательская настройка боковой панели**
Шаг 1: Создание макета
Создайте XML-файл, содержимое которого можно полностью настроить
```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>
```
Шаг 2: Установка слушателя
Для упрощения можно наследоваться от OnSlideBuildAdapter и переопределить метод getView(), или же напрямую реализовать интерфейс ISchedule.OnSlideBuildListener
```java
``` /**
* Пользовательская настройка боковой панели
* Использует пользовательский файл разметки для реализации эффекта выравнивания текста по верхнему краю (по умолчанию текст выравнивается по центру)
*/
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
**Скрытие и отображение курсов, не относящихся к текущей неделе**
```java
/**
* Скрытие курсов, не относящихся к текущей неделе
* Изменение отображения содержимого, поэтому необходимо обновить все (низкая производительность)
* Рекомендация: установите это свойство при инициализации
*/
protected void hideNonThisWeek() {
mTimetableView.isShowNotCurWeek(false).updateView();
}
/**
* Отображение курсов, не относящихся к текущей неделе
* Изменение отображения содержимого, поэтому необходимо обновить все (низкая производительность)
* Рекомендация: установите это свойство при инициализации
*/
protected void showNonThisWeek() {
mTimetableView.isShowNotCurWeek(true).updateView();
}
```
**Установка отступов и радиуса**
```java
/**
* Установка отступов и радиуса
* Этот метод может одновременно устанавливать радиус для всех четырех углов, установка радиуса для отдельного угла см. ниже
*/
protected void setMarginAndCorner() {
mTimetableView.cornerAll(0)
.marTop(0)
.marLeft(0)
.updateView();
}
```
**Установка радиуса для каждого угла**
```java /**
* Установка радиуса для каждого угла (четыре угла управляются отдельно)
*
* @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();
}
```**Изменение отображаемого текста**
```java
/**
* Изменение отображаемого текста
*/
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();
}
```
**Установка цвета фона для неактуальных занятий**
```java
/**
* Установка цвета фона для занятий, не относящихся к текущей неделе
*
* @param color
*/
public void setNonThisWeekBgcolor(RgbColor color) {
mTimetableView.colorPool().setUselessColor(color);
mTimetableView.updateView();
}
```
**Стили для перекрывающихся занятий**
```java
``` /**
* Изменение стиля перекрытия курсов. В этом интерфейсе вы можете настроить множество различных эффектов.
*/
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();
}
```**Добавление рекламы**Реклама обычно представляет собой изображение и ссылку, поэтому общая идея такова: сначала вставляем запись в базу данных, имя записи может быть произвольным, но оно должно отличаться от имен обычных курсов, чтобы можно было определить, что это реклама. Затем устанавливаем недели, дни и продолжительность рекламы. Далее отслеживаем создание элементов курса, и если обнаруживается реклама, удаляем стандартное представление курса, добавляем изображение рекламы и устанавливаем событие нажатия.
```java```markdown
/**
* Установка свойств
*/
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);
}
}
});
``````markdown
PixelMap pixelMap = loadImage(url);
``````markdown
```java
import com.zhuangfei.timetable.listener.OnItemBuildAdapter;
import com.zhuangfei.timetable.model.Schedule;
/**
* Английский курс
*/
public class OnEnglishCourseBuildAdapter extends OnItemBuildAdapter {
@Override
public String[] getStringArray() {
return new String[]{null, "Math", "Physics", "Chemistry", "Biology", "History", "Geography"};
}
@Override
public void onUpdateItem(int curWeek, int targetWeek) {
if (textViews == null || textViews.length < 8) {
return;
}
String[] courseArray = {"Math", "Physics", "Chemistry", "Biology", "History", "Geography"};
courseDates = Schedule.getDateStringFromWeek(curWeek, targetWeek);
int course = Integer.parseInt(courseDates.get(0));
textViews[0].setText(courseArray[course - 1]);
for (int i = 1; i < 8; i++) {
if (textViews[i] != null) {
textViews[i].setText(courseDates.get(i));
}
}
}
}
```
```/**
* Текстовое представление для английских предметов
*/
public class OnEnglishItemBuildAdapter extends OnItemBuildAdapter {
@Override
public String getItemText(Schedule schedule, boolean isThisWeek) {
if (schedule == null || schedule.getName().equals("")) {
return "Неизвестно";
}
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;
}
}
```
```### Установка прослушки```java
/**
* Переключение на английский язык
*/
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.
```java
mTimetableView.colorPool();
```
**Установка определенного цвета**
Если требуется, чтобы все курсы использовали цвета только из определенного набора, необходимо очистить цветовой бак и добавить конкретные цвета. При распределении цветов они будут циклически распределяться из цветового бака.
```java
/**
* Установка определенного цвета. По умолчанию в цветовом баке есть некоторые цвета,
* поэтому сначала необходимо очистить цветовой бак.
*
* @param colors
*/
public void setColor(RgbColor... colors) {
mTimetableView.colorPool().clear().add(colors);
mTimetableView.updateView();
}
```
**Сброс цветового бака**После сброса цветового бака он возвращается в исходное состояние.
```java
/**
* Сброс цветового бака
*/
public void resetColor() {
mTimetableView.colorPool().reset();
mTimetableView.updateView();
}
```
**Добавление цвета**
Цвета могут быть добавлены в цветовой бак. При распределении цветов для курсов они будут извлекаться из цветового бака в порядке их добавления, поэтому не гарантируется, что добавленные цвета будут использованы.
```java
/**
* Добавление цвета
*
* @param colors
*/
public void addColor(RgbColor... colors) {
mTimetableView.colorPool().add(colors);
mTimetableView.updateView();
}
```
**Установка цвета фона для курсов вне текущей недели**
Цвета могут быть добавлены в цветовой бак. При распределении цветов для курсов они будут извлекаться из цветового бака в порядке их добавления, поэтому не гарантируется, что добавленные цвета будут использованы.
```java
/**
* Установка цвета фона для курсов вне текущей недели
*
* @param color
*/
public void setNonThisWeekBgcolor(int color) {
mTimetableView.colorPool().setUselessColor(color);
mTimetableView.updateView();
}
```
**Установка цвета для курса**Метод `setIgnoreUserlessColor` используется для настройки цветовой карты, чтобы игнорировать цвета курсов, которые не относятся к текущей неделе. Если значение установлено на `true`, это означает, что если цвет для курса найден в цветовой карте, он будет применён, независимо от того, относится ли курс к текущей или прошедшей неделе. Если значение установлено на `false`, это означает, что если цвет для курса найден в цветовой карте и курс относится к текущей неделе, цвет будет применён. Если курс не относится к текущей неделе, будет использован цвет для прошедших недель.```java
/**
* Устанавливает цвет для курса, для неустановленных курсов цвет будет автоматически назначен
*/
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```java
``` ```java
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
<?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>
```
**Установка прослушивателя**
```java
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 для отслеживания событий клика по флаговому макету, не беспокоясь о других деталях.
В следующем коде отслеживаются три события:
- Клик по элементу расписания
- Долгий клик по элементу расписания
- Клик по пустой ячейке (по умолчанию) и клик по флаговому макету
```java
``` 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, "Long click: day " + day + ", start " + start);
}
})
.build();
}**Показ и скрытие**
```java
/**
* Показать флаговый интерфейс
*/
mTimetableView.showFlaglayout();
/**
* Скрыть флаговый интерфейс
*/
mTimetableView.hideFlaglayout();
```
##### Дополнительные данные
При установке источника данных можно использовать пользовательские типы данных, но необходимо реализовать интерфейс ScheduleEnable. Почему? Потому что внутреннее представление данных в TimetableView имеет формат List<Schedule>, а интерфейс служит для преобразования данных. При нажатии на элемент происходит обратный вызов, который возвращает список List<Schedule>. В этот момент пользовательские типы данных недоступны. Как получить идентификатор курса в этом случае?Полный код см. в ExtrasSlice
**Добавление дополнительных данных**
В классе Schedule отсутствует этот атрибут, но он присутствует в пользовательском типе данных. Поэтому в класс Schedule добавлено поле extras, которое представляет собой Map. Таким образом, проблема решается следующим образом:
```java
```java
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;
}```java
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;
}
}
```
```**Получение дополнительных данных**
```java
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();
}
``````xml
<?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"/>
</DependentLayout>
```
Дополнительные данные получены следующим образом:
```java
String id = bean.getExtras().get(MySubject.EXTRAS_ID);
```
##### Инструментальные классы
Компонент предоставляет инструментальный класс, который позволяет удобно работать с данными о расписании без использования интерфейса. В этом разделе показано, как использовать инструментальный класс для визуализации цвета расписания.
Полный код см. в NonViewSlice
**Список и адаптер**
В разметке Slice добавьте ListContainer
```xml
<ListContainer
ohos:id="$+id:id_listview"
ohos:width="match_parent"
ohos:height="match_parent"
ohos:orientation="vertical"></ListContainer>
```
item_nonview.xml — это разметка для каждого элемента в ListView, и её содержимое следующее:
```xml
<?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"/>
</DependentLayout>
``````xml
<DirectionalLayout
ohos:height="1px"
ohos:width="match_parent"
ohos:alignment="bottom"
ohos:background_element="$color:colorCg"/>
</DependentLayout>
```
```После подготовки файлов с макетом, требуется адаптер.``````xml
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;
/**
* Создано Liu ZhuangFei 18/06/2018. 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;
}
}
Как оно находит цвет курса? Посмотрите на следующий код:```xml
Расписание расписание = (Расписание) getItem(i);
РасписаниеЦветнойПул цветнойПул = new РасписаниеЦветнойПул(context);
держатель.nameTextView.setText(расписание.getНазвание());
RgbColor цветAuto = цветнойПул.getЦветAuto(расписание.getЦветСлучайный());
ShapeElement shapeElement = new ShapeElement();
shapeElement.setRgbColor(цветAuto);
держатель.colorTextView.setBackground(shapeElement);
```
Продолжайте читать, основная идея заключается в этой строке: данные адаптера уже распределены по цветам (как это происходит, см. ниже). Распределение цветов означает присвоение номера, а затем использование этого номера для получения цвета из цветового пула. Метод `getColorAuto()` не вызывает ошибку выхода за границы массива, так как внутри используется операция по модулю для циклического получения значений из цветового пула.
```java
RgbColor цветAuto = цветнойПул.getЦветAuto(расписание.getЦветСлучайный());
```
Установка адаптера
```java
расписания = new ArrayList<>();
listView = (ListContainer) findComponentById(ResourceTable.Id_id_listview);
адаптер = new NonViewAdapter(this, расписания);
listView.setItemProvider(адаптер);
```
Получение данных
```java
public List<Расписание> getData() {
List<Расписание> список = РасписаниеПоддержка.transform(ПредметРепозиторий.loadDefaultПредметы());
список = РасписаниеПоддержка.getЦветОтражение(список);
return список;
}
```
**Отображение всех курсов**
```java
/**
* Получение всех курсов
*/
protected void все() {
расписания.clear();
расписания.addAll(getData());
адаптер.notifyDataChanged();
}
```
**Курсы, которые проводятся в первую неделю**
```java
``` /**
* Получить первую неделю с занятиями и отобразить её
*/
protected void естьВремя() {
List<Расписание> результат = new ArrayList<>();
List<Расписание>[] массив = РасписаниеПоддержка.splitПредметыWithDay(getData());
for (int i = 0; i < массив.length; i++) {
List<Расписание> временныйСписок = массив[i];
for (Расписание расписание : временныйСписок) {
if (РасписаниеПоддержка.isThisWeek(расписание, 1)) {
результат.add(расписание);
}
}
}
расписания.clear();
расписания.addAll(результат);
адаптер.notifyDataChanged();
}
```**Курсы с занятиями в понедельник**
```java
```
/**
* Отображает курсы, у которых есть занятия в понедельник первой недели
*/
protected void haveTimeWithMonday() {
List<Schedule> tmpList = ScheduleSupport.getHaveSubjectsWithDay(
getData(), 1, 0);
schedules.clear();
schedules.addAll(tmpList);
adapter.notifyDataChanged();
}
```#### Тестовые данные
Проверка кода CodeCheck прошла без ошибок
Проверка кода CloudTest прошла без ошибок
Проверка на вирусы прошла успешно
Функции демонстрационного режима текущей версии практически не отличаются от оригинального компонента
#### Итерации версий
- 1.0.0
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )