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.

Если проект не запускается, удалите файлы project.gradle, .idea, build, gradle, build.gradle, и создайте новый проект в соответствии со своей версией, затем скопируйте соответствующие файлы нового проекта в корневую директорию.

Инструкция по использованию

Добавление компонентов

Компонент включает базовые элементы: строку дат, боковую панель и представление расписания. После добавления следующего кода будут включены все три базовых элемента. Обратите внимание, что следует добавить фоновое изображение; если его нет, можно использовать белый фон.

Добавление компонента в XML:


	<com.zhuangfei.timetable.view.WeekView
        ohos:id="$+id:id_weekview"
        ohos:width="match_parent"
        ohos:height="match_content"/>

    <com.zhuangfei.timetable.TimetableView
        ohos:id="$+id:id_timetableView"
        ohos:width="match_parent"
        ohos:height="match_parent"
        ohos:background_element="$color:app_white"/>
Настройка источника данных

Существует два способа настройки источника данных, рассмотрим каждый из них:

Метод 1: использование специального формата List. Schedule — это предоставленный контролем объект курса. Вы можете заключить свои данные в этот формат и выполнить следующее конфигурирование:


	mTimetableView.data(scheduleList)
                .curWeek(1)
                .showView();

Метод 2: метод 1 часто не удовлетворяет требованиям многих сценариев, поэтому обычно требуется определить свой собственный объект курса. Вы можете следовать нижеуказанным шагам для его использования:

  • Создайте собственный объект и реализуйте интерфейс 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;
	}
}

``````markdown
## Удаление предмета


```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();
    }
}

Добавление предмета

/**
 * Добавление предмета
 * Внутренним использованием коллекций для управления данными предметов, можно просто использовать методы работы с коллекциями
 * В конце обновляем представление (глобальное обновление)
 */
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();
    }
}

Отображение и скрытие недели

/**
 * Скрытие недели
 * Изменение отображения контента требует полного обновления (низкая производительность)
 * Совет: установите это свойство при инициализации
 *
 * После вызова updateView(), данные будут перестроены, и текущий курс будет установлен на текущую неделю
 */
protected void hideNonThisWeek() {
    mTimetableView.isShowNotCurWeek(false).updateView();
}

/**
 * Отображение недели
 * Изменение отображения контента требует полного обновления (низкая производительность)
 */
protected void showNonThisWeek() {
    mTimetableView.isShowNotCurWeek(true).updateView();
}

Настройка максимального количества элементов

/**
 * Установка максимального количества элементов в боковой панели, влияет только на отрисовку боковой панели, а не на содержание предметов
 * @param num
 */
protected void setMaxItem(int num) {
    mTimetableView.maxSlideItem(num).updateSlideView();
}

Отображение и скрытие времени

/**
 * Отображение времени
 * Устанавливаем слушатель сборки боковой панели, TimeSlideAdapter — это реализация боковой панели со временем
 */
protected void showTime() {
    String[] times = new String[]{"8:00", "9:00", "10:10", "11:00", "15:00", "16:00", "17:00", "18:00", "19:30", "20:30", "21:30", "22:30"};
    OnSlideBuildAdapter listener = (OnSlideBuildAdapter) mTimetableView.onSlideBuildListener();
    listener.setTimes(times)
            .setTimeTextColor(Color.BLACK.getValue());
    mTimetableView.updateSlideView();
}



/**
 * Скрытие времени
 * Устанавливаем слушатель сборки боковой панели как null, что приведёт к использованию по умолчанию метода сборки, то есть время не будет отображаться
 * Изменяем только свойства боковой панели, поэтому достаточно обновить только боковую панель (высокая производительность), нет необходимости обновлять всё (низкая производительность)
 */
protected void hideTime() {
    mTimetableView.callback((ISchedule.OnSlideBuildListener) null);
    mTimetableView.updateSlideView();
}
```

## Отображение и скрытие WeekView

```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 не имеет этой возможности, поэтому требуются оба компонента для её использования.

#### По умолчанию контроль выбора недели

1. **Добавление контрола**
   В файле макета добавьте TimetableView, затем добавьте WeekView над ним.

```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. **Инициализация**
   Пример инициализации:

```java
WeekView weekView;

mWeekView.source(mySubjects)
        .curWeek(1)
        .callback(new IWeekView.OnWeekItemClickedListener() {
            @Override
            public void onWeekClicked(int week) {
                int cur = mTimetableView.curWeek();
                mTimetableView.onDateBuildListener()
                        .onUpdateDate(cur, week);
                mTimetableView.changeWeekOnly(week);
            }
        })
        .callback(new IWeekView.OnWeekLeftClickedListener() {
            @Override
            public void onWeekLeftClicked() {
                onWeekLeftLayoutClicked();
            }
        })
        .isShow(false)
        .showView();
```

#### Пользовательская настройка WeekView

Вы можете выбрать один из следующих способов для создания пользовательского контрола выбора недели:

- **Любое кастомное решение**, так как контрол выбора недели и таблицы расписания не связаны друг с другом, вы можете создать любое решение.
- **Соответствие стандартному контролу выбора недели**, реализовать интерфейс WeekViewEnable.
- **Расширение стандартного контрола выбора недели**, наследование от WeekView и переопределение методов.

### Стилизация строки дат

Этот раздел демонстрирует, как настроить свойства строки дат и выполнить пользовательскую настройку строки дат.

#### Скрытие и отображение строки дат

```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` должны быть переопределены и новая высота должна быть установлена.

Вы также можете реализовать интерфейс `OnDateBuildListener` для более гибкого подхода, хотя это может усложнить процесс.

```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;
                }

                @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;
```
**Пример отложенного отображения дат**

Задача: установить начальную дату начала учебного года, и до этой даты всегда отображаться дата первой недели учебного года.

Мы используем пользовательскую реализацию компонента отображения дат для достижения этого требования:- **Шаг 1:** Создание пользовательского компонента отображения дат

Необходимо установить пороговое значение и список начальных дат. Если текущее время меньше порогового значения, используется список начальных дат, в противном случае вычисляются текущие даты недели. Это довольно просто.

```java
import com.zhuangfei.timetable.listener.OnDateBuildAapter;
import com.zhuangfei.timetable.model.ScheduleSupport;
import ohos.agp.components.DirectionalLayout;
import java.text.SimpleDateFormat;
import java.util.List;

/**
 * Пользовательский компонент отображения дат
 * Created by Liu ZhuangFei on 2018/8/24.
 */
public class OnDateDelayAdapter extends OnDateBuildAapter {

    /**
     * Пороговое значение, после которого начинается обновление дат
     */
    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;
        }

        if (whenBeginSchool() <= 0) {
            weekDates = ScheduleSupport.getDateStringFromWeek(curWeek, targetWeek);
        }
        int month = Integer.parseInt(weekDates.get(0));
        textViews[0].setText(month + "\nмесяц");
        for (int i = 1; i < 8; i++) {
            if (textViews[i] != null) {
                textViews[i].setText(weekDates.get(i) + " день");
            }
        }
    }

    /**
     * Вычисление дней до начала учебного года
     *
     * @return Возможные значения: -1 — нет даты начала учебного года, невозможно вычислить;
     *         0 — уже начался учебный год; >0 — количество дней
     */
    public long whenBeginSchool() {
        if (!"".equals(startTimeStr)) {
            int calWeek = ScheduleSupport.timeTransfrom(startTimeStr);
            if (calWeek > 0) {
                return 0;
            } else {
                long seconds = (startTime - System.currentTimeMillis()) / 1000;
                long day = seconds / (24 * 3600);
                return day;
            }
        }
        return -1;
    }
}
```

- **Шаг 2:** Использование

Большая часть работы выполнена на первом шаге, осталось только использовать его. Пример использования приведён ниже:

Следующий метод получает экземпляр `OnDateDelayAdapter` и инициализирует его.

```java
/**
 * Конфигурация OnDateDelayAdapter
 */
public OnDateDelayAdapter getDateDelayAdapter() {
    OnDateDelayAdapter onDateDelayAdapter = new OnDateDelayAdapter();

    /**
     * Вычисление времени начала учебного года
     */
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    long startTime = 0;
    try {
        startTime = sdf.parse("2022-09-03 00:00").getTime();
    } catch (ParseException e) {
        e.printStackTrace();
    }

    /**
     * Вычисление дат первой недели учебного года, здесь мы имитируем эти данные
     */
    List<String> dateList = Arrays.asList("9", "03", "04", "05", "06", "07", "08", "09");

    onDateDelayAdapter.setStartTime(startTime);
    onDateDelayAdapter.setDateList(dateList);
    return onDateDelayAdapter;
}
```

Затем экземпляр `OnDateDelayAdapter` можно установить на контрол `TimetableView`.

```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 + " дней");
}
```

### Настройка боковой панели

На левой стороне представления расписания есть колонка боковой панели. В этом разделе показано, как настроить свойства боковой панели и создать пользовательскую боковую панель.

#### Подготовка

Добавление компонентов

```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(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 hideNonCurrentWeekCourses() {
    mTimetableView.isShowNotCurrentWeek(false).updateView();
}

/**
 * Выводит недействующие за текущий период курсы
 * Изменение отображаемого контента требует полной перезагрузки (низкая производительность)
 * Рекомендация: установите это свойство при инициализации
 */
protected void showNonCurrentWeekCourses() {
    mTimetableView.isShowNotCurrentWeek(true).updateView();
}
```

**Настройка отступов и закругленных углов**

```java
/**
 * Настройка отступов и закругленных углов
 * Этот метод позволяет одновременно настраивать все четыре угла, чтобы настроить один угол, используйте следующий метод
 */
protected void setMarginAndCorner() {
    mTimetableView.cornerAll(0)
                   .marginTop(0)
                   .marginLeft(0)
                   .updateView();
}
```

**Настройка одного угла**

```java
/**
 * Настройка углов (четыре угла контролируются отдельно)
 *
 * @param topLeft
 * @param topRight
 * @param bottomRight
 * @param bottomLeft
 */
public void setCorner(final int topLeft, final int topRight, final int bottomRight, final int bottomLeft) {
    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[]{topLeft, topLeft, topRight, topRight, bottomRight, bottomRight, bottomLeft, bottomLeft});
        }
    });
    mTimetableView.updateView();
}
```

**Изменение отображаемого текста**

```java
/**
 * Изменение отображаемого текста
 */
public void buildItemText() {
    mTimetableView.callback(new OnItemBuildAdapter() {
        @Override
        public String getItemText(Schedule schedule, boolean isCurrentWeek) {
            if (isCurrentWeek) {
                return "[текущая неделя]" + schedule.getName();
            } else {
                return "[не текущая неделя]" + schedule.getName();
            }
        }
    })
                   .updateView();
}
```

**Установка фона для не текущих курсов**

```java
/**
 * Установка фона для не текущих курсов
 *
 * @param color
 */
public void setNonCurrentWeekBackgroundColor(RgbColor color) {
    mTimetableView.colorPool().setUselessColor(color);
    mTimetableView.updateView();
}
```

**Стилизация перекрывающихся курсов**

```java
``````md
## Добавление рекламы

Реклама обычно представляет собой изображение и ссылку, поэтому общая идея такова: сначала добавьте запись в набор данных, имя которой может быть любым, но должно отличаться от имени обычного курса, чтобы можно было определить, что это реклама. Затем установите неделю начала, номер занятия и продолжительность для этой записи. Наблюдайте за процессом сборки курсов, если обнаружено, что это реклама, удалите стандартный макет курса и замените его изображением рекламы, а затем добавьте событие нажатия.

```java
mySubjects = SubjectRepertory.loadDefaultSubjects();
MySubject adSubject = new MySubject();
adSubject.setName("【реклама】");
adSubject.setStart(1);
adSubject.setStep(2);
adSubject.setDay(7);
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= 20; i++) {
	list.add(i);
}
adSubject.setWeekList(list);
adSubject.setUrl(AD_URL);
mySubjects.add(adSubject);
SimpleSlice.this.getUITaskDispatcher().asyncDispatch(() -> {
	mWeekView.source(mySubjects).showView();
	mTimetableView.source(mySubjects).showView();
});

mTimetableView.currentWeek(1)
	.currentTerm("третьий семестр третьего года обучения")
	.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 currentWeek) {
	titleTextView.setText("неделя " + currentWeek);
	}
})
	.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.EXTRA_AD_URL);

	PixelMap pixelMap = loadImage(url);

	getUITaskDispatcher().syncDispatch(() -> {
	imageView.setPixelMap(pixelMap);
	pixelMap.release();
	});

	imageView.setClickedListener(new Component.ClickedListener() {
	@Override
	public void onClick(Component component) {
	ToastViewDialog.toast(SimpleSlice.this, "Перейти на страницу рекламы");
	}
	});
	}

});
```

### Переключение между русским и английским языками

#### Шаг 1: Англоязычная дата

```java
import com.zhuangfei.timetable.listener.OnDateBuildAdapter;
import com.zhuangfei.timetable.model.ScheduleSupport;

/**
 * Английская дата строки
 */
public class OnEnglishDateBuildAdapter extends OnDateBuildAdapter {
@Override
public String[] getStringArray() {
	return new String[]{null, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
}

@Override
public void onUpdateDate(int curWeek, int targetWeek) {
	if (textViews == null || textViews.length < 8) {
		return;
	}

	String[] monthArray = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
	weekDates = ScheduleSupport.getDateStringFromWeek(curWeek, targetWeek);
	int month = Integer.parseInt(weekDates.get(0));
	textViews[0].setText(monthArray[month - 1]);
	for (int i = 1; i < 8; i++) {
		if (textViews[i] != null) {
			textViews[i].setText(weekDates.get(i));
		}
	}
}
}
```

#### Шаг 2: Английский курс

```java
import com.zhuangfei.timetable.listener.OnItemBuildAdapter;
import com.zhuangfei.timetable.model.Schedule;

/**
 * Английский текст курса
 */
public class OnEnglishItemBuildAdapter extends OnItemBuildAdapter {
@Override
public String getItemText(Schedule schedule, boolean isThisWeek) {
	if (schedule == null || schedule.getName().equals("")) {
		return "Unknown";
	}
	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;
}
}
```

#### Шаг 3: Установка слушателя

```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` представляет собой коллекцию контроллеров, которая управляет цветами курсов и отвечает за хранение и получение этих цветов.

#### Получение цветового пула

Код ниже возвращает экземпляр объекта `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("Digital Image Processing", Color.RED.getValue());
        colorMap.put("Algorithm Analysis and Design", Color.BLUE.getValue());
        mTimetableView.colorPool().setIgnoreUserlessColor(false).setColorMap(colorMap);
        mTimetableView.updateView();
    }
```

##### Замена прокрутки

Зачем этот функционал? Представьте следующие ситуации: эффект отскока при свайпе, эффект обновления при свайпе, отслеживание положения прокрутки, решение конфликта прокрутки при вложенной прокрутке. Эти требования могут быть решены путём создания собственного View.
Замена прокрутки означает использование пользовательского ScrollView вместо обычного ScrollView, что позволяет иметь лучшую гибкость.
В этом разделе мы продемонстрируем, как создать интерфейс расписания с эффектом отскока при свайпе. Знание создания пользовательских View находится вне области этого руководства, если вас интересует эта тема, вы можете найти информацию в интернете.
Полный код доступен в ElasticSlice

**Создание пользовательского View**

Вы должны подготовить пользовательское View, которое зависит от ваших требований. Вы можете использовать эластичный ScrollView, представленный ниже:

```java
``````markdown
## Эластичный скроллинг

### Описание
Эластичный скроллинг  это компонент, который позволяет прокручивать содержимое с эффектом отскока при нажатии.

#### Класс `ElasticScrollView`
```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;

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();
    }

    public boolean isNeedAnimation() {
        return !normal.isEmpty();
    }

    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;
    }
}
```

### Лайаут файл
Создайте лайаут файл с любым именем и вставьте следующий код:

```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();

    mTimetableView.source(mySubjects)
            .callback(new ISchedule.OnScrollViewBuildListener() {
                @Override
                public Component getScrollView(LayoutScatter mInflate) {
                    return mInflate.parse(ResourceTable.Layout_custom_myscrollview, null, true);
                }
            })
            .showView();
}
```

### Флаговый лайаут
Флаговый лайаут  это специальный тип лайаута, который появляется при клике на пустую клетку таблицы.#### Настройка флагового лайаута
```java
mTimetableView.source(mySubjects)
        .curWeek(1)
        .maxSlideItem(10)
        .callback(new ISchedule.OnItemClickListener() {
            @Override
            public void onItemClick(Component v, List<Schedule> scheduleList) {
                display(scheduleList);
            }
        })
        .callback(new ISchedule.OnItemLongClickListener() {
            @Override
            public void onLongClick(Component v, int day, int start) {
                ToastViewDialog.toast(FlaglayoutSlice.this, "Долгое нажатие: Неделя " + day + ", " + start + "-й урок");
            }
        })
        .callback(new ISchedule.OnFlaglayoutClickListener() {
            @Override
            public void onFlaglayoutClick(int day, int start) {
                mTimetableView.hideFlaglayout();
                ToastViewDialog.toast(FlaglayoutSlice.this, "Нажата кнопка: Неделя " + (day + 1) + ", " + start + "-й урок");
            }
        })
        .showView();
```



#### Изменение цвета фона флагового лайаута
```java
private void modifyFlagBgcolor(RgbColor color) {
    mTimetableView.flagBgcolor(color).updateFlaglayout();
}

private void resetFlagBgcolor() {
    mTimetableView.resetFlagBgcolor().updateFlaglayout();
}
```

#### Отключение и активация флагового лайаута
```java
private void cancelFlagBgcolor() {
    mTimetableView.isShowFlaglayout(false).updateFlaglayout();
}

private void resetFlaglayout() {
    mTimetableView.isShowFlaglayout(true).updateFlaglayout();
}
```

#### Показ и скрытие флагового лайаута
```java
mTimetableView.showFlaglayout();

mTimetableView.hideFlaglayout();
```

### Дополнительные данные
```java
// Дополнительные данные могут быть использованы для настройки поведения компонента
```
```При установке данных источника можно использовать пользовательские типы данных, но они должны реализовать интерфейс `ScheduleEnable`. Почему это важно? Внутренне в компоненте `TimetableView` данные хранятся в виде списка объектов типа `List<Schedule>`.

Интерфейс `ScheduleEnable` служит для преобразования пользовательских типов данных в формат, который может быть использован внутри `TimetableView`. Когда происходит событие нажатия, вызывается обратный вызов, который получает список объектов типа `List<Schedule>`, а не пользовательские типы данных.

Для того чтобы получить ID курса после события нажатия, можно воспользоваться полем `extras` в объекте `Schedule`.

### Сохранение дополнительных данных

Если в пользовательском типе данных есть дополнительные поля, такие как ID курса, эти поля могут быть сохранены в объекте `Schedule` через поле `extras`. Например:

```java
public class MySubject implements ScheduleEnable {

    public static final String EXTRAS_ID = "extras_id";

    // ... остальные поля ...

    @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()); // Сохраняем ID курса в extras
        return schedule;
    }

    // ... остальные методы ...
}
```

### Чтение дополнительных данных

Чтобы прочитать дополнительные данные, такие как ID курса, можно использовать следующий код:

```java
private void initViews() {
    TimetableView timetableView = (TimetableView) findViewById(R.id.id_timetableView);

    timetableView.source(mySubjects)
            .curWeek(1)
            .maxSlideItem(10)
            .callback(new OnItemClickListener() {
                @Override
                public void onItemClick(Component v, List<Schedule> scheduleList) {
                    display(scheduleList);
                }
            })
            .showView();
}

protected void display(List<Schedule> beans) {
    StringBuilder sb = new StringBuilder();
    for (Schedule bean : beans) {
        sb.append("[").append(bean.getName()).append("] ").append("ID: ")
                .append(bean.getExtras().get(MySubject.EXTRAS_ID)).append("\n");
    }
    Toast.makeText(this, sb.toString(), Toast.LENGTH_SHORT).show();
}
```

### Инструментальный класс

Класс `ScheduleColorPool` используется для получения цвета курса на основе уникального идентификатора. Это позволяет визуализировать цвета курсов в списке.

Пример использования этого класса:

```java
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);
```

Это позволяет корректно отображать цвета курсов в списке, используя заранее подготовленные цвета из пула.

### Установка адаптера

Адаптер для ListView может быть установлен следующим образом:

```java
NonViewAdapter adapter = new NonViewAdapter(context, mySchedules);
listView.setItemProvider(adapter);
```

Таким образом, все необходимые данные и цвета будут правильно отображены в списке.

```java

public List<Расписание> получитьДанные() {
    List<Расписание> список = РасписаниеПоддержка.преобразовать(ПредметРепертоРий.загрузитьСтандартныеПредметы());
    список = РасписаниеПоддержка.получитьЦветовоеОтображение(список);
    return список;
}

```

**Отображение всех курсов**

```java

/**
 * Получение всех курсов
 */
protected void все() {
    расписание.clear();
    расписание.addAll(получитьДанные());
    адаптер.уведомитьОИзмененииДанных();
}
```

**Курсы с занятиями в первую неделю**

```java

/**
 * Получение курсов с занятиями в первую неделю и отображение их
 */
protected void естьЗанятия() {
    List<Расписание> результат = new ArrayList<>();
    List<Расписание>[] массив = РасписаниеПоддержка.разделитьПредметыПоДням(получитьДанные());
    for (int i = 0; i < массив.length; i++) {
        List<Расписание> временныйСписок = массив[i];
        for (Расписание расписание : временныйСписок) {
            if (РасписаниеПоддержка.являетсяЭтойНеделей(расписание, 1)) {
                результат.add(расписание);
            }
        }
    }
    расписание.clear();
    расписание.addAll(результат);
    адаптер.уведомитьОИзмененииДанных();
}
```

**Курсы с занятиями в понедельник первой недели**

```java

/**
 * Отображение курсов с занятиями в понедельник первой недели
 */
protected void естьЗанятияСПонедельника() {
    List<Расписание> временныйСписок = РасписаниеПоддержка.получитьПредметыСЗанятиямиПоДню(
            получитьДанные(), 1, 0);
    расписание.clear();
    расписание.addAll(временныйСписок);
    адаптер.уведомитьОИзмененииДанных();
}
```

#### Тестовая информация

CodeCheck проверка кода прошла успешно

CloudTest проверка кода прошла успешно

Прохождение проверки безопасности от вирусов

Текущая версия демона имеет функционал, аналогичный оригинальному компоненту

#### Версионирование

- 1.0.0

Комментарии ( 0 )

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

Введение

Открытый, совершенный и эффективный компонент для календаря на основе openharmony. Развернуть Свернуть
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