• Что бы вступить в ряды "Принятый кодер" Вам нужно:
    Написать 10 полезных сообщений или тем и Получить 10 симпатий.
    Для того кто не хочет терять время,может пожертвовать средства для поддержки сервеса, и вступить в ряды VIP на месяц, дополнительная информация в лс.

  • Пользаватели которые будут спамить, уходят в бан без предупреждения. Спам сообщения определяется администрацией и модератором.

  • Гость, Что бы Вы хотели увидеть на нашем Форуме? Изложить свои идеи и пожелания по улучшению форума Вы можете поделиться с нами здесь. ----> Перейдите сюда
  • Все пользователи не прошедшие проверку электронной почты будут заблокированы. Все вопросы с разблокировкой обращайтесь по адресу электронной почте : info@guardianelinks.com . Не пришло сообщение о проверке или о сбросе также сообщите нам.

HarmonyOS NEXT Development Case: Ruler Application

Lomanu4 Оффлайн

Lomanu4

Команда форума
Администратор
Регистрация
1 Мар 2015
Сообщения
1,481
Баллы
155

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.



The following code demonstrates how to create an interactive ruler application using HarmonyOS NEXT. This implementation features dynamic scale generation, gesture-based interaction, and real-time UI adaptation.

Implementation Code


import { window } from '@kit.ArkUI'; // Import window management APIs
import { deviceInfo } from '@kit.BasicServicesKit'; // Import device information APIs

// Ruler scale line definition
class RulerLine {
index: number; // Index of the scale line
height: number; // Height of the scale line

constructor(index: number, height: number) {
this.index = index; // Initialize index
this.height = height; // Initialize height
}

// Display scale numbering
showNumber(): string {
return this.index % 10 === 0 ? `${Math.floor(this.index / 10)}` : ''; // Show number every 10 units
}
}

// Custom text styling extension
@Extend(Text)
function fancy() {
.fontColor("#019dfe") // Set font color
.fontSize(20); // Set font size
}

// Main ruler component
@Entry
@Component
struct RulerComponent {
@State maxRulerHeight: number = 0; // Maximum ruler height
@State @Watch('onCellWidthChanged') cellWidthInPixels: number = 17.28; // Pixels per millimeter
@State textWidth: number = 80; // Text label width
@State rulerLines: RulerLine[] = []; // Array of scale lines
@State leftOffsetX: number = -300; // Left offset position
@State currentPositionX: number = -300; // Current X position
@State @Watch('onContainerHeightChanged') containerHeight: number = 53; // Container height
@State originalContainerHeight: number = 53; // Original container height
@State @Watch('onCellWidthChanged') containerWidth: number = 0; // Container width

// Handle cell width changes
onCellWidthChanged() {
this.maxRulerHeight = vp2px(this.containerWidth) / this.cellWidthInPixels / 10;
}

// Handle container height changes
onContainerHeightChanged() {
this.containerHeight = Math.max(this.containerHeight, 53); // Ensure minimum height
}

// Lifecycle: Component initialization
aboutToAppear(): void {
// Set landscape orientation
window.getLastWindow(getContext()).then((windowClass) => {
windowClass.setPreferredOrientation(window.Orientation.LANDSCAPE);
});

// Initialize scale lines
for (let i = 0; i <= 15 * 10; i++) {
const lineHeight: number = (i % 10 === 0) ? 90 : (i % 5 === 0) ? 60 : 45;
this.rulerLines.push(new RulerLine(i, lineHeight));
}
}

// UI construction
build() {
Column() {
Stack() {
// Scale lines rendering
Stack() {
ForEach(this.rulerLines, (line: RulerLine, index: number) => {
Line()
.width(1)
.height(`${line.height}px`)
.backgroundColor(Color.White)
.margin({ left: `${this.cellWidthInPixels * index}px` });
Text(line.showNumber())
.fontColor(Color.White)
.fontSize(18)
.width(`${this.textWidth}px`)
.height(`${this.textWidth}px`)
.textAlign(TextAlign.Center)
.margin({
left: `${this.cellWidthInPixels * index - this.textWidth / 2}px`,
top: `${line.height}px`
});
});
}.width('100%').height('100%').align(Alignment.TopStart);

// Control panel
Column({ space: 15 }) {
Text(`Current Device: ${deviceInfo.marketName}`).fancy();
Counter() {
Text(`Measurement: ${this.maxRulerHeight.toFixed(2)} cm`).fancy();
}
.foregroundColor(Color.White)
.width(300)
.onInc(() => {
this.containerHeight = px2vp(vp2px(this.containerHeight) + this.cellWidthInPixels / 10;
})
.onDec(() => {
this.containerHeight = px2vp(vp2px(this.containerHeight) - this.cellWidthInPixels / 10;
});

Counter() {
Text(`mm Scale: ${this.cellWidthInPixels.toFixed(2)}px`).fancy();
}
.foregroundColor(Color.White)
.width(300)
.onInc(() => {
this.cellWidthInPixels += 0.01;
})
.onDec(() => {
this.cellWidthInPixels = Math.max(0.01, this.cellWidthInPixels - 0.01);
});
}

// Interactive measurement area
RelativeContainer() {
Rect()
.fill("#80019dfe")
.borderColor("#019dfe")
.borderWidth({ left: 1, right: 1 })
.clip(true)
.width("100%")
.height("100%")
.onAreaChange((oldArea: Area, newArea: Area) => {
this.containerWidth = newArea.width as number;
});

// Left draggable handle
Stack() {
Circle({ height: 30, width: 30 })
.fill("#019dfe")
.stroke(Color.Transparent)
.strokeWidth(3);
Circle({ height: 40, width: 40 })
.fill(Color.Transparent)
.stroke("#019dfe")
.strokeWidth(3);
}
.hitTestBehavior(HitTestMode.Block)
.padding(20)
.alignRules({
center: { anchor: "__container__", align: VerticalAlign.Center },
middle: { anchor: "__container__", align: HorizontalAlign.Start }
})
.gesture(PanGesture({
fingers: 1,
direction: PanDirection.Horizontal,
distance: 1
}).onActionUpdate((event: GestureEvent) => {
this.leftOffsetX = this.currentPositionX + event.offsetX / 2;
this.containerHeight = this.originalContainerHeight - event.offsetX;
}).onActionEnd(() => {
this.currentPositionX = this.leftOffsetX;
this.originalContainerHeight = this.containerHeight;
}));

// Right draggable handle
Stack() {
Circle({ height: 30, width: 30 })
.fill("#019dfe")
.stroke(Color.Transparent)
.strokeWidth(3);
Circle({ height: 40, width: 40 })
.fill(Color.Transparent)
.stroke("#019dfe")
.strokeWidth(3);
}
.hitTestBehavior(HitTestMode.Block)
.padding(20)
.alignRules({
center: { anchor: "__container__", align: VerticalAlign.Center },
middle: { anchor: "__container__", align: HorizontalAlign.End }
})
.gesture(PanGesture({
fingers: 1,
direction: PanDirection.Horizontal,
distance: 1
}).onActionUpdate((event: GestureEvent) => {
this.leftOffsetX = this.currentPositionX + event.offsetX / 2;
this.containerHeight = this.originalContainerHeight + event.offsetX;
}).onActionEnd(() => {
this.currentPositionX = this.leftOffsetX;
this.originalContainerHeight = this.containerHeight;
}));
}
.width(this.containerHeight)
.height("100%")
.translate({ x: this.leftOffsetX })
.gesture(PanGesture({
fingers: 1,
direction: PanDirection.Horizontal,
distance: 1
}).onActionUpdate((event: GestureEvent) => {
if (event) {
this.leftOffsetX = this.currentPositionX + event.offsetX;
}
}).onActionEnd(() => {
this.currentPositionX = this.leftOffsetX;
}));
}
}.height('100%').width('100%')
.padding({ left: 30, right: 10 })
.backgroundColor("#181b22");
}
}
Key Features


  1. Dynamic Scale Generation

    Creates ruler scales programmatically with varying heights for different scale marks (main, medium, and small divisions).


  2. Adaptive Layout

    Utilizes HarmonyOS's responsive layout system to maintain proper scaling across different screen sizes.


  3. Interactive Measurement

    Implements dual draggable handles with smooth animation for precise measurement operations.


  4. Real-time Unit Conversion

    Provides pixel-to-centimeter conversion with adjustable scale density.


  5. Gesture Support

    Implements multi-directional pan gestures for both ruler adjustment and measurement operations.
Usage

  1. Drag the blue handles to perform measurements
  2. Use the "+"/"-" buttons to adjust scale density
  3. The ruler automatically adapts to device orientation
  4. Displays real-time measurement results in centimeters

This implementation demonstrates core HarmonyOS NEXT capabilities including declarative UI, reactive state management, and advanced gesture handling.


Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

 
Вверх Снизу