1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/chinasoft_ohos-TimetableView

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

Таблица Расписания

Описание проекта

  • Название проекта: TimetableView Супер Расписание
  • Серия: адаптация и перенос третьих сторон для openharmony
  • Функциональность: это открытое, полное и эффективное расписание для openharmony
  • Статус переноса проекта: основные функции завершены
  • Различия при вызове: отсутствуют
  • Версия разработки: SDK 6, DevEco Studio 2.2 Beta1
  • Базовая версия: Release v2.0.7-beta

Поддерживаемые функции

  • Поддержка установки свойств через XML
  • Самостоятельная настройка бизнес-логики
  • Управление цветами для уроков
  • Возможность замены ScrollView
  • Настройка фона и прозрачности
  • Добавление дополнительной информации в источники данных
  • Кликабельные пустые ячейки, долгое нажатие на уроки
  • Настройка ширины месяцев
  • Скрытие выходных
  • Самостоятельная настройка логики расписания
  • Локальная конфигурация: изоляция, загрузка, экспорт в текстовый формат
  • Указание цветов для уроков

Демонстрация эффекта

Демонстрационное изображение

Инструкция по установке

  1. В файле build.gradle в корневой директории проекта добавьте:
allprojects {
   repositories {
       maven {
           url 'https://s01.oss.sonatype.org/content/repositories/releases/'
       }
   }
}
  1. В файле build.gradle модуля entry добавьте:
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(); } }

/**

  • Обработчик клика по левой части выбора недели
  • Изменение текущей недели в диалоговом окне */ 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 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);
	    }
	}

```

##### Основные возможности

**Настройка свойств**

Вот весь код, разделенный на три шага:

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 )

Вы можете оставить комментарий после Вход в систему

Введение

Открытый, совершенный и эффективный компонент для календаря на основе openharmony. Развернуть Свернуть
Java
MIT
Отмена

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/chinasoft_ohos-TimetableView.git
git@api.gitlife.ru:oschina-mirror/chinasoft_ohos-TimetableView.git
oschina-mirror
chinasoft_ohos-TimetableView
chinasoft_ohos-TimetableView
master