# (Vue)MetroWidget

# 1.基本介绍

MetroWidget组件用于 JsView-Vue3 架构。 该组件提供自动排布和焦点管理功能

# 2.控件说明

props、可调用函数,以及item template的属性说明,以代码包(tgz)的MetroWidget.vue的头信息为准。
路径:
node_modules/@shijiu/jsview-vue/utils/JsViewEngineWidget/MetroWidget/MetroWidget.vue

# 3.技巧展示

插槽如何使用?

  • 插槽属性参见slotProp

提问: item的onFocus/onBlur和JsvFocusBlock的onFocus/onBlur有何区别?

  • 整个MetroWidget在焦点树上只有一个节点, 既MetroWidget内部只有一个<jsv-focus-block/>, props中的onFocus/onBlur对应这个JsvFocusBlock
  • item的onFocus/onBlur对应的是MetroWidget自身内部状态的改变

按键是怎么处理的?

  • 上下左右键已经由控件接管,不需要开发者处理。
  • 通过注册onClick回调来处理用户的OK键动作。
  • 其他按键组件不会处理, 按JsvFocusBlock统一的冒泡处理

焦点到达边缘后怎么离开此组件?

  • 当焦点移动到控件边缘时,会调用onEdge回调。在回调中通过参数传递的值来决定焦点转移的行为

# 4.示例搭建

# demo.vue

  • 前置操作
    import {
      MetroWidget, 
      HORIZONTAL, // 定义MetroWidget方向的Symbol
      EdgeDirection, // MetroWidget onEdge回调中的方向比对
      useFocusHub,
    } from "jsview";
    import { onMounted } from "vue";
    import Item from "./Item.vue" // 描画item的组件
    
    const focusHub = useFocusHub();
    
    // mounted后设置焦点
    onMounted(() => { focusHub.setFocus(name); })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  • template中添加MetroWidget组件
    <template>
      <metro-widget
        :name="name"
        :left="widgetLayout.left"
        :top="widgetLayout.top"
        :width="widgetLayout.width"
        :height="widgetLayout.height"
        :provideData="provideData"
        :direction="HORIZONTAL"
        :measures="measures"
        :onEdge="onEdge"
      >
      </metro-widget>
    </template>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  • 在script中定义MetroWidget所需的prop
    //组件的焦点ID
    const name = "widget"
    //组件的布局信息
    const widgetLayout = {
      left: 50,
      top: 50,
      width: 300,
      height: 200,
    }
    //提供数据的回调
    const provideData = () => {
      const data = [];
      for (let i = 0; i < 30; i++) {
        data.push({
          width: 90,
          height: 90,
          marginRight: 10,
          marginBottom: 10,
          color: "#00DD00",
          content: i,
        });
      }
      return data;
    }
    // 提供item布局信息的回调
    const measures = (data) => {
      return {
        width: data.width,
        height: data.height,
        marginRight: data.marginRight,
        marginBottom: data.marginBottom,
      };
    };
    //组件到达边缘时的回调
    const onEdge = (edgeInfo) => {
      if (edgeInfo.direction == EdgeDirection.top) {
        console.log("top edge");
      } else {
        console.log("other side edge")
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
  • 添加MetroWidget的slot, 并在slot中描画item
    <template>
      <metro-widget
        ...
      >
        <template #renderItem="{ data, onAction }">
          <item :data="data" :onAction="onAction" />
        </template>
      </metro-widget>
    </template>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 全部代码
    <script setup>
    import {
      MetroWidget,
      HORIZONTAL, // 定义MetroWidget方向的Symbol
      EdgeDirection, // MetroWidget onEdge回调中的方向比对
      useFocusHub,
    } from "jsview";
    import Item from "./Item.vue"
    import { onMounted } from "vue";
    
    const focusHub = useFocusHub();
    
    // mounted后设置焦点
    onMounted(() => {
      focusHub.setFocus(name);
    });
    
    //组件的焦点ID
    const name = "widget";
    //组件的布局信息
    const widgetLayout = {
      left: 50,
      top: 50,
      width: 300,
      height: 200,
    };
    //提供数据的回调
    const provideData = () => {
      const data = [];
      for (let i = 0; i < 30; i++) {
        data.push({
          width: 90,
          height: 90,
          marginRight: 10,
          marginBottom: 10,
          color: '#00DD00',
          content: i,
        });
      }
      return data;
    };
    // 提供item布局信息的回调
    const measures = (data) => {
      return {
        width: data.width,
        height: data.height,
        marginRight: data.marginRight,
        marginBottom: data.marginBottom,
      };
    };
    //组件到达边缘时的回调
    const onEdge = (edgeInfo) => {
      if (edgeInfo.direction == EdgeDirection.top) {
        console.log("top edge");
      } else {
        console.log("other side edge");
      }
    };
    </script>
    
    
      <template>
      <metro-widget
        :name="name"
        :left="widgetLayout.left"
        :top="widgetLayout.top"
        :width="widgetLayout.width"
        :height="widgetLayout.height"
        :provideData="provideData"
        :direction="HORIZONTAL"
        :measures="measures"
        :onEdge="onEdge"
      >
        <template #renderItem="{ data, onAction }">
          <item :data="data" :onAction="onAction" />
        </template>
      </metro-widget>
    </template>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78

# item.vue

  • 主要操作
    • 根据数据描画内容
    • 向onAction对象注册回调
    • 在回调中更改自身状态
  • 全部代码
    <script setup>
    import { ref, shallowRef, inject } from "vue";
    
    const props = defineProps({
      data: Object,
      onAction: Object,
    });
    
    const focused = ref(false);
    //在回调中更改自身状态
    const onFocus = () => {
      focused.value = true;
    };
    const onBlur = () => {
      focused.value = false;
    };
    const onClick = () => {
      console.log("item onclick ", props.data);
    };
    // 注册回调
    props.onAction.register("onFocus", onFocus);
    props.onAction.register("onBlur", onBlur);
    props.onAction.register("onClick", onClick);
    </script>
    
    <template>
      <div
        :style="{
          width: data.width,
          height: data.height,
          fontSize: 30,
          color: focused ? '#FF0000' : '#FFFFFF',
          backgroundColor: data.color,
        }"
      >
        {{ data.content }}
      </div>
    </template>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
Last Updated: 10/25/2024, 8:52:41 AM