优化 TitleBar 和 Index 页面
This commit is contained in:
parent
c0854269a9
commit
99aaff6876
@ -1,24 +1,79 @@
|
||||
import { TitleBar } from '@devwiki/common_ui/src/main/ets/component/TitleBar';
|
||||
import { ComposeTitleBar } from '@ohos.arkui.advanced.ComposeTitleBar';
|
||||
import { EditableTitleBar } from '@ohos.arkui.advanced.EditableTitleBar';
|
||||
import { SelectTitleBar } from '@ohos.arkui.advanced.SelectTitleBar';
|
||||
import { WebView, ComponentConst, CommonRes, TitleBar, WebViewController } from '@devwiki/common_ui';
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct Index {
|
||||
@State message: string = 'Hello World';
|
||||
|
||||
onLeftClicked(event: ClickEvent) {
|
||||
this.message = "TitleBar LeftClicked";
|
||||
@State viewModel: IndexViewModel = new IndexViewModel();
|
||||
private webViewController?: IndexWebViewController;
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.webViewController = new IndexWebViewController(this.viewModel);
|
||||
}
|
||||
|
||||
onTitleBarLeftClick(event: ClickEvent) {
|
||||
this.getUIContext().getRouter().back();
|
||||
}
|
||||
|
||||
onTitleBarRightClick(event: ClickEvent) {
|
||||
this.webViewController?.refresh();
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
TitleBar({onLeftClicked: this.onLeftClicked}).height(48);
|
||||
Divider();
|
||||
RelativeContainer() {
|
||||
TitleBar({
|
||||
title: this.viewModel.pageTitle,
|
||||
onLeftClicked: this.onTitleBarLeftClick,
|
||||
rightIcon: CommonRes.getIconRefresh(),
|
||||
// 必须这么写 onTitleBarRightClick内部的代码才执行,
|
||||
// 直接 onRightClicked: this.onTitleBarRightClick 这么写, 代码不执行
|
||||
onRightClicked: (event: ClickEvent) => { this.onTitleBarRightClick(event)},
|
||||
})
|
||||
.width('100%')
|
||||
.alignRules({
|
||||
top: { anchor: ComponentConst.ContainerId, align: VerticalAlign.Top },
|
||||
left: { anchor: ComponentConst.ContainerId, align: HorizontalAlign.Start }
|
||||
}).id("title_bar");
|
||||
|
||||
Text(this.message);
|
||||
}
|
||||
.height('100%')
|
||||
Divider().alignRules({
|
||||
top: { anchor: "title_bar", align: VerticalAlign.Bottom },
|
||||
left: { anchor: ComponentConst.ContainerId, align: HorizontalAlign.Start }
|
||||
}).width('100%').id("divider")
|
||||
|
||||
WebView({ webUrl: this.viewModel?.webUrl, controller: this.webViewController }).width('100%')
|
||||
.alignRules({
|
||||
top: { anchor: "divider", align: VerticalAlign.Bottom },
|
||||
left: { anchor: ComponentConst.ContainerId, align: HorizontalAlign.Start },
|
||||
bottom: { anchor: ComponentConst.ContainerId, align: VerticalAlign.Bottom }
|
||||
}).id("host_web")
|
||||
}.width('100%').height('100%')
|
||||
}.width('100%').height('100%')
|
||||
}
|
||||
}
|
||||
|
||||
class IndexWebViewController extends WebViewController {
|
||||
private viewModel: IndexViewModel;
|
||||
|
||||
constructor(viewModel: IndexViewModel) {
|
||||
super()
|
||||
this.viewModel = viewModel;
|
||||
}
|
||||
|
||||
onPageEnd(title: string): void {
|
||||
this.viewModel.pageTitle = title;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class IndexViewModel {
|
||||
webUrl: string = "https://devwiki.net";
|
||||
pageTitle: ResourceStr = "ViewModel Title";
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
onTitleChanged(title: string) {
|
||||
this.pageTitle = title;
|
||||
}
|
||||
}
|
@ -33,6 +33,11 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"requestPermissions": [
|
||||
{
|
||||
"name" : "ohos.permission.INTERNET"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1 +1,7 @@
|
||||
export { TitleBar } from './src/main/ets/component/TitleBar'
|
||||
export { ComponentConst } from './src/main/ets/component/ComponentConst'
|
||||
export { WebViewEventKey } from './src/main/ets/event/EventKey'
|
||||
export { Emitter } from './src/main/ets/event/Emitter'
|
||||
export { WebView, WebViewController } from './src/main/ets/component/web/WebView'
|
||||
|
||||
export { CommonRes } from './src/main/ets/utils/CommonRes'
|
||||
|
@ -1,81 +1,121 @@
|
||||
import { ComponentConst } from './ComponentConst'
|
||||
|
||||
export enum TitleBarMenuType{
|
||||
/**
|
||||
* 不显示
|
||||
*/
|
||||
None = 0,
|
||||
/**
|
||||
* 显示Icon
|
||||
*/
|
||||
Icon = 1,
|
||||
/**
|
||||
* 显示文本
|
||||
*/
|
||||
Text = 2
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Component
|
||||
export struct TitleBar {
|
||||
|
||||
@State barHeight: number = 48;
|
||||
@State menuPadding: number = 8;
|
||||
@Prop title: ResourceStr;
|
||||
|
||||
@State leftText: ResourceStr = "Back";
|
||||
@State leftTextVisible: Visibility = Visibility.Hidden;
|
||||
@State leftIconVisible: Visibility = Visibility.Visible;
|
||||
@Prop barHeight: number = 48;
|
||||
@Prop menuPadding: number = 8;
|
||||
|
||||
@State rightText: ResourceStr = "Menu";
|
||||
@State rightTextVisible: Visibility = Visibility.Hidden;
|
||||
@State rightIconVisible: Visibility = Visibility.Hidden;
|
||||
@Prop leftText: ResourceStr = "";
|
||||
@Prop leftIcon: Resource = $r("app.media.ic_chevron_left");
|
||||
@Prop leftMenuType: TitleBarMenuType = TitleBarMenuType.Icon;
|
||||
|
||||
@State title: ResourceStr = "Title";
|
||||
@State titleTextAlign: TextAlign = TextAlign.Start;
|
||||
@State titleVisible: Visibility = Visibility.Visible;
|
||||
@Prop rightText: ResourceStr = "";
|
||||
@Prop rightIcon: Resource = $r("app.media.ic_close");
|
||||
@Prop rightMenuType: TitleBarMenuType = TitleBarMenuType.Icon;
|
||||
|
||||
@Prop titleTextAlign: TextAlign = TextAlign.Start;
|
||||
@Prop titleVisible: Visibility = Visibility.Visible;
|
||||
|
||||
onLeftClicked?: Function;
|
||||
onRightClicked?: Function;
|
||||
|
||||
@Styles setWidthAndHeight() {
|
||||
.height(this.barHeight).width(this.barHeight)
|
||||
}
|
||||
|
||||
build() {
|
||||
Row() {
|
||||
RelativeContainer() {
|
||||
Button() {
|
||||
Stack() {
|
||||
Text(this.leftText).textAlign(TextAlign.Center)
|
||||
.width(this.barHeight).height(this.barHeight).textAlign(TextAlign.Center)
|
||||
.visibility(this.leftTextVisible)
|
||||
Image($r("app.media.ic_chevron_left"))
|
||||
.width(this.barHeight).height(this.barHeight).padding(this.menuPadding)
|
||||
.visibility(this.leftIconVisible)
|
||||
if (this.leftMenuType != TitleBarMenuType.None) {
|
||||
Button() {
|
||||
if (this.leftMenuType == TitleBarMenuType.Icon) {
|
||||
Image(this.leftIcon)
|
||||
.setWidthAndHeight()
|
||||
.padding(this.menuPadding)
|
||||
} else {
|
||||
Text(this.leftText)
|
||||
.textAlign(TextAlign.Center)
|
||||
.setWidthAndHeight()
|
||||
}
|
||||
}
|
||||
}.backgroundColor(0xFFFFFF).type(ButtonType.Normal).stateEffect(true)
|
||||
.onClick((event) => {
|
||||
this.onLeftClicked?.(event);
|
||||
}).alignRules({
|
||||
top: {anchor: ComponentConst.ContainerId, align: VerticalAlign.Top},
|
||||
left: {anchor: ComponentConst.ContainerId, align: HorizontalAlign.Start},
|
||||
bottom: {anchor: ComponentConst.ContainerId, align: VerticalAlign.Bottom }
|
||||
})
|
||||
.id("left_menu")
|
||||
|
||||
Text(this.title).visibility(this.titleVisible).textAlign(this.titleTextAlign).margin({left: this.menuPadding})
|
||||
.setWidthAndHeight()
|
||||
.backgroundColor(0xFFFFFF)
|
||||
.type(ButtonType.Normal)
|
||||
.stateEffect(true)
|
||||
.onClick((event) => {
|
||||
this.onLeftClicked?.(event);
|
||||
})
|
||||
.alignRules({
|
||||
top: { anchor: ComponentConst.ContainerId, align: VerticalAlign.Top},
|
||||
left: { anchor: "left_menu", align: HorizontalAlign.End},
|
||||
right: { anchor: "right_menu", align: HorizontalAlign.Start},
|
||||
bottom: {anchor: ComponentConst.ContainerId, align: VerticalAlign.Bottom }
|
||||
}).id("center_row")
|
||||
top: { anchor: ComponentConst.ContainerId, align: VerticalAlign.Top },
|
||||
left: { anchor: ComponentConst.ContainerId, align: HorizontalAlign.Start },
|
||||
}).id("left_menu")
|
||||
}
|
||||
|
||||
Stack(){
|
||||
if (this.titleVisible == Visibility.Visible) {
|
||||
Text(this.title)
|
||||
.textAlign(this.titleTextAlign)
|
||||
.margin({
|
||||
left: this.leftMenuType != TitleBarMenuType.None ? this.barHeight : 0,
|
||||
right: this.leftMenuType != TitleBarMenuType.None ? this.barHeight : 0
|
||||
})
|
||||
.padding({left: this.leftMenuType != TitleBarMenuType.None ? 0 : this.menuPadding})
|
||||
.alignRules({
|
||||
top: { anchor: ComponentConst.ContainerId, align: VerticalAlign.Top },
|
||||
left: {
|
||||
anchor: this.leftMenuType != TitleBarMenuType.None ? "left_menu" : ComponentConst.ContainerId,
|
||||
align: HorizontalAlign.Start
|
||||
},
|
||||
right: {
|
||||
anchor: this.leftMenuType != TitleBarMenuType.None ? "right_menu" : ComponentConst.ContainerId,
|
||||
align: HorizontalAlign.End
|
||||
},
|
||||
bottom: { anchor: ComponentConst.ContainerId, align: VerticalAlign.Bottom }
|
||||
})
|
||||
.id("title")
|
||||
}
|
||||
|
||||
if (this.rightMenuType != TitleBarMenuType.None) {
|
||||
Button() {
|
||||
Image($r("app.media.ic_close"))
|
||||
.width(this.barHeight).height(this.barHeight)
|
||||
.padding(this.menuPadding)
|
||||
}.backgroundColor(0xFFFFFF).type(ButtonType.Normal).stateEffect(true).visibility(this.rightIconVisible)
|
||||
if (this.rightMenuType == TitleBarMenuType.Icon) {
|
||||
Image(this.rightIcon)
|
||||
.setWidthAndHeight().padding(this.menuPadding)
|
||||
} else {
|
||||
Text(this.rightText)
|
||||
.textAlign(TextAlign.Center)
|
||||
.setWidthAndHeight()
|
||||
}
|
||||
}
|
||||
.setWidthAndHeight()
|
||||
.backgroundColor(0xFFFFFF)
|
||||
.type(ButtonType.Normal)
|
||||
.stateEffect(true)
|
||||
.onClick((event) => {
|
||||
this.onRightClicked?.(event);
|
||||
});
|
||||
|
||||
Button() {
|
||||
Text(this.rightText).textAlign(TextAlign.Center)
|
||||
.width(this.barHeight).height(this.barHeight).textAlign(TextAlign.Center)
|
||||
}.backgroundColor(0xFFFFFF).type(ButtonType.Normal).stateEffect(true).visibility(this.rightTextVisible)
|
||||
.onClick((event) => {
|
||||
this.onRightClicked?.(event);
|
||||
});
|
||||
}.alignRules({
|
||||
top: {anchor: ComponentConst.ContainerId, align: VerticalAlign.Top},
|
||||
right: {anchor: ComponentConst.ContainerId, align: HorizontalAlign.End},
|
||||
bottom: {anchor: ComponentConst.ContainerId, align: VerticalAlign.Bottom }
|
||||
})
|
||||
.id("right_menu")
|
||||
})
|
||||
.alignRules({
|
||||
top: { anchor: ComponentConst.ContainerId, align: VerticalAlign.Top },
|
||||
right: { anchor: ComponentConst.ContainerId, align: HorizontalAlign.End },
|
||||
}).id("right_menu")
|
||||
}
|
||||
}
|
||||
.width('100%').height('100%')
|
||||
}.height(this.barHeight)
|
||||
|
15
common_ui/src/main/ets/component/web/WebScript.ets
Normal file
15
common_ui/src/main/ets/component/web/WebScript.ets
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
/**
|
||||
* H5 调用 native的函数
|
||||
*/
|
||||
export interface WebScriptCallback {
|
||||
getCount(): number;
|
||||
}
|
||||
|
||||
/**
|
||||
* native 调用 H5的函数
|
||||
*/
|
||||
export interface WebScriptFunction {
|
||||
|
||||
alert(message: string): void;
|
||||
}
|
68
common_ui/src/main/ets/component/web/WebView.ets
Normal file
68
common_ui/src/main/ets/component/web/WebView.ets
Normal file
@ -0,0 +1,68 @@
|
||||
import web_webview from '@ohos.web.webview';
|
||||
import { WebScriptCallback, WebScriptFunction } from './WebScript';
|
||||
|
||||
@Component
|
||||
export struct WebView {
|
||||
|
||||
private webviewController: web_webview.WebviewController = new web_webview.WebviewController();
|
||||
controller: WebViewController = new WebViewController();
|
||||
@Prop webUrl: string = "";
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controller.setWebviewController(this.webviewController);
|
||||
}
|
||||
|
||||
build() {
|
||||
Web({ src: this.webUrl, controller: this.webviewController })
|
||||
.javaScriptAccess(true)// 允许使用 js
|
||||
.javaScriptProxy({
|
||||
object: null,
|
||||
name: "",
|
||||
methodList: [],
|
||||
controller: this.webviewController
|
||||
})
|
||||
.onPageEnd(() => { this.controller.onPageEnd(this.webviewController.getTitle()) })
|
||||
.width('100%')
|
||||
.height('100%');
|
||||
}
|
||||
}
|
||||
|
||||
interface WebViewCallback {
|
||||
onPageEnd(title: string): void;
|
||||
}
|
||||
|
||||
|
||||
export class WebViewController implements WebScriptCallback, WebScriptFunction, WebViewCallback {
|
||||
|
||||
private webviewController?: web_webview.WebviewController
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
setWebviewController(webviewController: web_webview.WebviewController) {
|
||||
this.webviewController = webviewController;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.webviewController?.refresh();
|
||||
}
|
||||
|
||||
getCurrentUrl(): string {
|
||||
return this.webviewController?.getUrl() ?? "";
|
||||
}
|
||||
|
||||
getCount(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
alert(message: string): void {
|
||||
this.webviewController?.runJavaScript(`window.alert(${message})`);
|
||||
}
|
||||
|
||||
loadUrl(url: string) {
|
||||
this.webviewController?.loadUrl(url);
|
||||
}
|
||||
|
||||
onPageEnd(title: string) {
|
||||
}
|
||||
}
|
110
common_ui/src/main/ets/event/Emitter.ets
Normal file
110
common_ui/src/main/ets/event/Emitter.ets
Normal file
@ -0,0 +1,110 @@
|
||||
|
||||
export interface EmitterItem {
|
||||
key: string
|
||||
listener: Function
|
||||
}
|
||||
|
||||
type XYEmitterAllType = string | object | boolean | number | Object | Number | BigInt;
|
||||
|
||||
export class Emitter {
|
||||
private static instance: Emitter;
|
||||
private static events: Map<string, EmitterItem[]> = new Map
|
||||
private static viscosityData: Map<string, XYEmitterAllType[]> = new Map
|
||||
|
||||
public static getInstance(): Emitter {
|
||||
if (!Emitter.instance) {
|
||||
Emitter.instance = new Emitter();
|
||||
}
|
||||
|
||||
return Emitter.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加监听
|
||||
* @param key
|
||||
* @param listener
|
||||
* @returns
|
||||
*/
|
||||
public on(key: string, listener: Function): EmitterItem {
|
||||
let item: EmitterItem = {key: key, listener: listener};
|
||||
if (typeof listener !== "function") {
|
||||
throw new TypeError('the listener not a function')
|
||||
}
|
||||
let items: EmitterItem[] | undefined = Emitter.events.get(key);
|
||||
if (!items) {
|
||||
items = []
|
||||
}
|
||||
items.push(item)
|
||||
Emitter.events.set(key, items)
|
||||
if (Emitter.viscosityData.has(key)) {
|
||||
let value = Emitter.viscosityData.get(key)
|
||||
if (value == undefined) {
|
||||
listener();
|
||||
} else {
|
||||
listener(...value);
|
||||
}
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消监听
|
||||
* @param listener
|
||||
*/
|
||||
public off(listener: EmitterItem): void {
|
||||
let items = Emitter.events.get(listener.key)
|
||||
if (!items) {
|
||||
return;
|
||||
}
|
||||
items = items.filter((it)=> {
|
||||
return it !== listener;
|
||||
})
|
||||
Emitter.events.set(listener.key, items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布普通消息
|
||||
* @param key
|
||||
* @param args
|
||||
*/
|
||||
public emmit<T>(key: string, ...args: T[]) {
|
||||
let items = Emitter.events.get(key)
|
||||
if (!items || items.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
items.forEach((item: EmitterItem, index: number) => {
|
||||
if (typeof item.listener === 'function') {
|
||||
item.listener(...args)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布粘性消息
|
||||
* @param key
|
||||
* @param args
|
||||
*/
|
||||
public emmitViscosity<T>(key: string, ...args: T[]) {
|
||||
this.emmit(key, ...args);
|
||||
Emitter.viscosityData.set(key, args as XYEmitterAllType[]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除粘性消息
|
||||
* @param key
|
||||
*/
|
||||
public deleteViscosity<T>(key: string) {
|
||||
if (Emitter.viscosityData.has(key)) {
|
||||
Emitter.viscosityData.delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除所有监听
|
||||
*/
|
||||
public removeAllListener() {
|
||||
Emitter.events = new Map;
|
||||
}
|
||||
|
||||
}
|
5
common_ui/src/main/ets/event/EventKey.ets
Normal file
5
common_ui/src/main/ets/event/EventKey.ets
Normal file
@ -0,0 +1,5 @@
|
||||
export class WebViewEventKey {
|
||||
public static readonly TitleEvent: string = "web_view_event_title";
|
||||
public static readonly DarkModeChangedEvent: string = "web_view_event_dark_mode_changed";
|
||||
public static readonly UrlChangedEvent: string = "web_view_event_url_change_changed";
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export function add(a:number, b:number) {
|
||||
return a + b;
|
||||
}
|
17
common_ui/src/main/ets/utils/CommonRes.ets
Normal file
17
common_ui/src/main/ets/utils/CommonRes.ets
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
export class CommonRes {
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
public static getIconRefresh(): Resource {
|
||||
return $r("app.media.ic_refresh");
|
||||
}
|
||||
|
||||
public static getIconBack(): Resource {
|
||||
return $r("app.media.ic_chevron_left");
|
||||
}
|
||||
|
||||
public static getIconClose(): Resource {
|
||||
return $r("app.media.ic_close");
|
||||
}
|
||||
}
|
@ -9,6 +9,11 @@
|
||||
"2in1"
|
||||
],
|
||||
"deliveryWithInstall": true,
|
||||
"pages": "$profile:main_pages"
|
||||
"pages": "$profile:main_pages",
|
||||
"requestPermissions": [
|
||||
{
|
||||
"name" : "ohos.permission.INTERNET"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
1
common_ui/src/main/resources/base/media/ic_refresh.svg
Normal file
1
common_ui/src/main/resources/base/media/ic_refresh.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>refresh</title><path d="M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z" /></svg>
|
After Width: | Height: | Size: 311 B |
Loading…
Reference in New Issue
Block a user