四、增加横向逻辑

横向布局与测量:XML实践与代码解析

上一章我们已经增加了指定方向的attr.接下来,接下来我们来看一下,怎么横向测量和布局。

先晒一下我们的xml

  <com.jhzl.customview.MineLineLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:background="@color/black"
        android:layout_gravity="center">

        <EditText
            android:background="@android:color/holo_red_dark"
            android:text="hahah"
            android:layout_width="200dp"
            android:layout_height="@dimen/dp100"/>


        <EditText
            android:background="@android:color/holo_green_dark"
            android:text="hahah"
            android:layout_marginTop="20dp"
            android:layout_marginLeft="20dp"
            android:layout_width="200dp"
            android:layout_height="100dp"/>

        <EditText
            android:background="@android:color/holo_blue_dark"
            android:text="hahah"
            android:layout_width="@dimen/dp250"
            android:layout_height="100dp"/>
    </com.jhzl.customview.MineLineLayout>

横向布局的测量

 private void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
        int totalHeight = 0;
        int totalWidth = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            //计算最大高度
            totalHeight = Math.max(totalHeight,child.getMeasuredHeight());
            MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
            //计算总宽度
            totalWidth = totalWidth + ((MarginLayoutParams) layoutParams).leftMargin+child.getMeasuredWidth();
        }
        Log.d(TAG,"totalWidth = "+totalWidth+"  totalHeight = "+totalHeight);
        setMeasuredDimension(totalWidth, totalHeight);
    }

测量完了之后,就是我们熟悉的layout环节了:

 private void horizontalLayout() {
        int curLeft = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            VerticalLayoutParams layoutParams = (VerticalLayoutParams) child.getLayoutParams();
            curLeft = curLeft + layoutParams.leftMargin;
            child.layout(curLeft, 0, curLeft+child.getMeasuredWidth(),  child.getMeasuredHeight());
            curLeft = curLeft + child.getMeasuredWidth();
        }
    }

接下来就是我们熟悉的试验结果观察环节了:
在这里插入图片描述

<think>我们正在解决ArkTS中数组字面量类型推断错误(arkts-no-noninferrable-arr-literals)并实现横向tabs榜单功能。错误原因:数组字面量中的元素类型不能被推断,需要显式指定类型。解决方案:1.定义一个接口(或类)来描述数组元素的类型。2.在声明数组时显式指定类型(如:Array<接口名>或接口名[])。3.确保每个元素都符合该接口。同时,我们还要实现横向tabs榜单功能,使用Tabs组件,并设置barPosition为Start(顶部)且可滚动(scrollable=true)来实现横向标签栏。下面我们分步骤进行:步骤1:定义榜单数据类型步骤2:创建榜单数据(显式指定类型)步骤3:实现横向Tabs组件,每个Tab对应一个榜单步骤4:在TabContent中展示榜单列表(使用ForEach,注意解决类型推断问题)注意:榜单数据可能是一个数组,每个榜单有多个条目(如歌曲、视频等)。参考引用[4]:ArkTS不支持使用对象字面量声明类型,应使用接口或类。根据上述分析,我们开始代码实现。</think> ### 解决 ArkTS 数组字面量类型错误与实现横向 Tabs 榜单 #### 错误原因分析 1. **数组字面量类型不可推断**:ArkTS 严格禁止未指定类型的数组字面量 (`arkts-no-noninferrable-arr-literals`)[^1] 2. **对象字面量作为类型声明**:尝试使用 `{...}` 声明类型违反 ArkTS 规范 (`arkts-no-obj-literals-as-types`)[^2] 3. **动态类型映射限制**:ArkTS 不支持 TS 的映射类型语法[^3] --- ### 解决方案:实现横向 Tabs 榜单功能 #### 1. 定义数据结构类型 (解决类型错误) ```typescript // 榜单类型 interface LeaderboardTab { id: number; name: string; active: boolean; } // 榜单内容类型 interface LeaderboardItem { id: string; rank: number; title: string; score: number; } ``` #### 2. 创建强类型数据源 ```typescript @State tabs: LeaderboardTab[] = [ { id: 1, name: "热门榜", active: true }, { id: 2, name: "新歌榜", active: false }, { id: 3, name: "飙升榜", active: false } ]; @State hotList: LeaderboardItem[] = [ { id: "s001", rank: 1, title: "星辰大海", score: 95 }, { id: "s002", rank: 2, title: "追光者", score: 92 }, { id: "s003", rank: 3, title: "起风了", score: 89 } ]; ``` #### 3. 实现横向 Tabs 组件 ```typescript @Component struct HorizontalTabs { @State tabs: LeaderboardTab[] = []; build() { Scroll(.horizontal) { Row() { ForEach(this.tabs, (tab: LeaderboardTab) => { Button(tab.name) .stateEffect(tab.active) .backgroundColor(tab.active ? "#6200EA" : "#F5F5F5") .onClick(() => this.activateTab(tab.id)) }, (tab) => tab.id.toString()) } .padding(10) } .scrollable(ScrollDirection.Horizontal) } private activateTab(id: number) { this.tabs = this.tabs.map(tab => ({ ...tab, active: tab.id === id })); } } ``` #### 4. 完整榜单组件 ```typescript @Entry @Component struct LeaderboardPage { @State currentTab: string = "hot"; build() { Column() { // 横向导航标签 HorizontalTabs({ tabs: this.tabs }) // 榜单内容 TabContent(this.currentTab) { // 热门榜 if(this.currentTab === "hot") { ForEach(this.hotList, (item: LeaderboardItem) => { LeaderboardItem(item) }, (item) => item.id) } // 更多榜单可在此扩展... } } } } ``` ### 关键修复点 | 问题类型 | 错误代码示例 | 正确修复方式 | |---------|-------------|------------| | 未定义数组类型 | `let list = [{id:1}, ...]` | `@State list: ItemType[] = [...]` | | 对象字面量类型 | `type Tab = {id:number}` | `interface Tab {id:number}` | | 键值生成缺失 | `ForEach(list, item => {...})` | 添加 `(item) => item.id.toString()` | ### 性能优化建议 1. **懒加载长列表**: ```typescript LazyForEach(this.dataSource, (item: ItemType) => { // 渲染逻辑 }, (item) => item.id.toString()) ``` 2. **避免动态类型转换**: ```typescript // 错误: 使用动态属性 let value = data[fieldName]; // 正确: 使用类型守卫 if("title" in data) { Text(data.title) } ``` 3. **分页加载数据**: ```typescript private loadMore = () => { this.currentPage++; this.list = [...this.list, ...fetchPageData(this.currentPage)]; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值