简体中文
列表组件
在App中,基于recycle-view的list,才能实现长列表的资源自动回收,以保障列表加载很多项目时,屏幕外的资源被有效回收。list-view就是基于recycle-view的list组件。
每个list由1个父组件list-view及若干子组件list-item构成。
在list-item上使用v-for指令循环list-item,自动会回收屏幕外的列表项资源。
list-view和scroll-view都是滚动组件,list适用于长列表场景,其他场景适用于scroll-view。
注意: list-view仅识别list-item、sticky-header组件其他组件无法识别不能正常显示
名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
scroll-x | boolean | false | 允许横向滚动,不支持同时设置scroll-y属性为true,同时设置true时scroll-y生效 |
scroll-y | boolean | true | 允许纵向滚动,不支持同时设置scroll-x属性为true,同时设置true时scroll-y生效 |
rebound | boolean | true | 控制是否回弹效果 |
upper-threshold | number | 50 | 距顶部/左边多远时(单位px),触发 scrolltoupper 事件 |
lower-threshold | number | 50 | 距底部/右边多远时(单位px),触发 scrolltolower 事件 |
scroll-top | number | 0 | 设置竖向滚动条位置 |
scroll-left | number | 0 | 设置横向滚动条位置 |
show-scrollbar | boolean | true | 控制是否出现滚动条 |
scroll-into-view | string | - | 值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素 |
scroll-with-animation | boolean | false | 是否在设置滚动条位置时使用滚动动画,设置false没有滚动动画 |
refresher-enabled | boolean | false | 开启下拉刷新,暂时不支持scroll-x = true横向刷新 |
refresher-threshold | number | 45 | 设置下拉刷新阈值, 仅 refresher-default-style = 'none' 自定义样式下生效 |
refresher-max-drag-distance | number | - | 设置下拉最大拖拽距离(单位px),默认是下拉刷新控件高度的2.5倍 |
refresher-default-style | string | black | 设置下拉刷新默认样式,支持设置 black | white | none, none 表示不使用默认样式 refresher-default-style |
refresher-background | string | #FFF | 设置下拉刷新区域背景颜色 |
refresher-triggered | boolean | false | 设置当前下拉刷新状态,true 表示下拉刷新已经被触发,false 表示下拉刷新未被触发 |
custom-nested-scroll | boolean | - | 子元素是否开启嵌套滚动 将滚动事件与父元素协商处理 |
@refresherpulling | (event: RefresherEvent) => void | - | 下拉刷新控件被下拉 |
@refresherrefresh | (event: RefresherEvent) => void | - | 下拉刷新被触发 |
@refresherrestore | (event: RefresherEvent) => void | - | 下拉刷新被复位 |
@refresherabort | (event: RefresherEvent) => void | - | 下拉刷新被中止 |
@scrolltoupper | (event: ScrollToUpperEvent) => void | - | 滚动到顶部/左边,会触发 scrolltoupper 事件 |
@scrolltolower | (event: ScrollToLowerEvent) => void | - | 滚动到底部/右边,会触发 scrolltolower 事件 |
@scroll | (event: ScrollEvent) => void | - | 滚动时触发,event.detail = {scrollLeft, scrollTop, scrollHeight, scrollWidth, deltaX, deltaY} |
值名称 | 描述 |
---|---|
black | 深颜色雪花样式 |
white | 浅白色雪花样式 |
none | 不使用默认样式 |
安卓系统版本 | 安卓 uni-app | 安卓 uni-app-x | iOS 系统版本 | iOS uni-app | iOS uni-app-x | |
---|---|---|---|---|---|---|
black | 5.0 | x | 3.9+ | - | - | - |
white | 5.0 | x | 3.9+ | - | - | - |
none | 5.0 | x | 3.93 | - | - | - |
名称 | 类型 | 必填 | 默认值 | 描述 |
---|---|---|---|---|
detail | RefresherEventDetail | 是 | - | - |
type | string | 是 | - | 事件类型 |
target | Element | 是 | - | 触发事件的组件 |
currentTarget | Element | 是 | - | 当前组件 |
timeStamp | number | 是 | - | 事件发生时的时间戳 |
名称 | 类型 | 必备 | 默认值 | 描述 |
---|---|---|---|---|
dy | number | 是 | - | - |
名称 | 类型 | 必填 | 默认值 | 描述 |
---|---|---|---|---|
stopPropagation | () => void | 是 | - | 阻止当前事件的进一步传播 |
preventDefault | () => void | 是 | - | 阻止当前事件的默认行为 |
名称 | 类型 | 必填 | 默认值 | 描述 |
---|---|---|---|---|
detail | ScrollToUpperEventDetail | 是 | - | - |
type | string | 是 | - | 事件类型 |
target | Element | 是 | - | 触发事件的组件 |
currentTarget | Element | 是 | - | 当前组件 |
timeStamp | number | 是 | - | 事件发生时的时间戳 |
名称 | 类型 | 必备 | 默认值 | 描述 |
---|---|---|---|---|
direction | string | 是 | - | 滚动方向 top 或 left |
名称 | 类型 | 必填 | 默认值 | 描述 |
---|---|---|---|---|
stopPropagation | () => void | 是 | - | 阻止当前事件的进一步传播 |
preventDefault | () => void | 是 | - | 阻止当前事件的默认行为 |
名称 | 类型 | 必填 | 默认值 | 描述 |
---|---|---|---|---|
detail | ScrollToLowerEventDetail | 是 | - | - |
type | string | 是 | - | 事件类型 |
target | Element | 是 | - | 触发事件的组件 |
currentTarget | Element | 是 | - | 当前组件 |
timeStamp | number | 是 | - | 事件发生时的时间戳 |
名称 | 类型 | 必备 | 默认值 | 描述 |
---|---|---|---|---|
direction | string | 是 | - | 滚动方向 bottom 或 right |
名称 | 类型 | 必填 | 默认值 | 描述 |
---|---|---|---|---|
stopPropagation | () => void | 是 | - | 阻止当前事件的进一步传播 |
preventDefault | () => void | 是 | - | 阻止当前事件的默认行为 |
名称 | 类型 | 必填 | 默认值 | 描述 |
---|---|---|---|---|
detail | ScrollEventDetail | 是 | - | - |
type | string | 是 | - | 事件类型 |
target | Element | 是 | - | 触发事件的组件 |
currentTarget | Element | 是 | - | 当前组件 |
timeStamp | number | 是 | - | 事件发生时的时间戳 |
名称 | 类型 | 必备 | 默认值 | 描述 |
---|---|---|---|---|
scrollTop | number | 是 | - | 竖向滚动的距离 |
scrollLeft | number | 是 | - | 横向滚动的距离 |
scrollHeight | number | 是 | - | 滚动区域的高度 |
scrollWidth | number | 是 | - | 滚动区域的宽度 |
deltaY | number | 是 | - | 当次滚动事件竖向滚动量 |
deltaX | number | 是 | - | 当次滚动事件横向滚动量 |
名称 | 类型 | 必填 | 默认值 | 描述 |
---|---|---|---|---|
stopPropagation | () => void | 是 | - | 阻止当前事件的进一步传播 |
preventDefault | () => void | 是 | - | 阻止当前事件的默认行为 |
<script>
import { type ItemType } from '@/components/enum-data/enum-data.vue'
export default {
data() {
return {
refresher_triggered_boolean: false,
refresher_enabled_boolean: false,
scroll_with_animation_boolean: false,
show_scrollbar_boolean: true,
rebound_boolean: true,
scroll_y_boolean: true,
scroll_x_boolean: false,
upper_threshold_input: 50,
lower_threshold_input: 50,
scroll_top_input: 0,
scroll_left_input: 0,
refresher_background_input: "#FFF",
scrollData: [] as Array<string>,
size_enum: [{ "value": 0, "name": "item---0" }, { "value": 3, "name": "item---3" }] as ItemType[],
scrollIntoView: "",
refresherrefresh: false,
refresher_default_style_input: "black",
text: ['继续下拉执行刷新', '释放立即刷新', '刷新中', ""],
state: 3,
reset: true
}
},
onLoad() {
let lists : Array<string> = []
for (let i = 0; i < 10; i++) {
lists.push("item---" + i)
}
this.scrollData = lists
},
methods: {
list_view_click() { console.log("组件被点击时触发") },
list_view_touchstart() { console.log("手指触摸动作开始") },
list_view_touchmove() { console.log("手指触摸后移动") },
list_view_touchcancel() { console.log("手指触摸动作被打断,如来电提醒,弹窗") },
list_view_touchend() { console.log("手指触摸动作结束") },
list_view_tap() { console.log("手指触摸后马上离开") },
list_view_longpress() { console.log("如果一个组件被绑定了 longpress 事件,那么当用户长按这个组件时,该事件将会被触发。") },
list_view_refresherpulling(e : RefresherEvent) {
console.log("下拉刷新控件被下拉")
if(this.reset) {
if(e.detail.dy > 45) {
this.state = 1
} else {
this.state = 0
}
}
},
list_view_refresherrefresh() {
console.log("下拉刷新被触发 ")
this.refresherrefresh = true
this.refresher_triggered_boolean = true
this.state = 2
this.reset = false;
setTimeout(function(){
this.refresher_triggered_boolean = false
}, 1500)
},
list_view_refresherrestore() {
this.refresherrefresh = false
this.state = 3
this.reset = true
console.log("下拉刷新被复位")
},
list_view_refresherabort() { console.log("下拉刷新被中止") },
list_view_scrolltoupper(e:ScrollToUpperEvent) { console.log("滚动到顶部/左边,会触发 scrolltoupper 事件 direction="+e.detail.direction) },
list_view_scrolltolower(e:ScrollToLowerEvent) { console.log("滚动到底部/右边,会触发 scrolltolower 事件 direction="+e.detail.direction) },
list_view_scroll() { console.log("滚动时触发,event.detail = {scrollLeft, scrollTop, scrollHeight, scrollWidth, deltaX, deltaY}") },
list_item_click() { console.log("list-item组件被点击时触发") },
list_item_touchstart() { console.log("手指触摸list-item组件动作开始") },
list_item_touchmove() { console.log("手指触摸list-item组件后移动") },
list_item_touchcancel() { console.log("手指触摸list-item组件动作被打断,如来电提醒,弹窗") },
list_item_touchend() { console.log("手指触摸list-item组件动作结束") },
list_item_tap() { console.log("手指触摸list-item组件后马上离开") },
list_item_longpress() { console.log("list-item组件被绑定了 longpress 事件,那么当用户长按这个组件时,该事件将会被触发。") },
change_refresher_triggered_boolean(checked : boolean) { this.refresher_triggered_boolean = checked },
change_refresher_enabled_boolean(checked : boolean) { this.refresher_enabled_boolean = checked },
change_scroll_with_animation_boolean(checked : boolean) { this.scroll_with_animation_boolean = checked },
change_show_scrollbar_boolean(checked : boolean) { this.show_scrollbar_boolean = checked },
change_rebound_boolean(checked : boolean) { this.rebound_boolean = checked },
change_scroll_y_boolean(checked : boolean) { this.scroll_y_boolean = checked },
change_scroll_x_boolean(checked : boolean) { this.scroll_x_boolean = checked },
confirm_upper_threshold_input(value : number) { this.upper_threshold_input = value },
confirm_lower_threshold_input(value : number) { this.lower_threshold_input = value },
confirm_scroll_top_input(value : number) { this.scroll_top_input = value },
confirm_scroll_left_input(value : number) { this.scroll_left_input = value },
confirm_refresher_background_input(value : string) { this.refresher_background_input = value },
item_change_size_enum(index : number) { this.scrollIntoView = "item---"+index },
//自动化测试例专用
check_scroll_height(): Boolean {
var listElement = this.$refs["listview"] as Element
console.log("check_scroll_height--"+listElement.scrollHeight)
if(listElement.scrollHeight > 2000) {
return true
}
return false
},
//自动化测试例专用
check_scroll_width(): Boolean {
var listElement = this.$refs["listview"] as Element
console.log("check_scroll_width"+listElement.scrollWidth)
if(listElement.scrollWidth > 2000) {
return true
}
return false
},
change_refresher_style_boolean(checked : boolean) {
if(checked) {
this.refresher_default_style_input = "none"
} else {
this.refresher_default_style_input = "black"
}
}
}
}
</script>
<template>
<view class="main">
<list-view :scroll-x="scroll_x_boolean" :scroll-y="scroll_y_boolean" :rebound="rebound_boolean"
:upper-threshold="upper_threshold_input" :lower-threshold="lower_threshold_input" :scroll-top="scroll_top_input"
:scroll-left="scroll_left_input" :show-scrollbar="show_scrollbar_boolean" :scroll-into-view="scrollIntoView"
:scroll-with-animation="scroll_with_animation_boolean" :refresher-enabled="refresher_enabled_boolean"
:refresher-background="refresher_background_input" :refresher-triggered="refresher_triggered_boolean"
:refresher-default-style="refresher_default_style_input"
@click="list_view_click" @touchstart="list_view_touchstart" @touchmove="list_view_touchmove"
@touchcancel="list_view_touchcancel" @touchend="list_view_touchend" @tap="list_view_tap"
@longpress="list_view_longpress" @refresherpulling="list_view_refresherpulling"
@refresherrefresh="list_view_refresherrefresh" @refresherrestore="list_view_refresherrestore"
@refresherabort="list_view_refresherabort" @scrolltoupper="list_view_scrolltoupper" ref="listview" id="listview"
@scrolltolower="list_view_scrolltolower" @scroll="list_view_scroll" style="width:100%;">
<list-item
v-for="key in scrollData" :key="key" :id="key" @click="list_item_click" @touchstart="list_item_touchstart"
@touchmove="list_item_touchmove" @touchcancel="list_item_touchcancel" @touchend="list_item_touchend"
@tap="list_item_tap" @longpress="list_item_longpress"
class="list-item">
<text>{{key}}</text>
</list-item>
<list-item slot="refresher" class="refresh-box">
<text class="tip-text">{{text[state]}}</text>
</list-item>
</list-view>
</view>
<!-- #ifdef APP -->
<scroll-view style="flex:1">
<!-- #endif -->
<view class="content nvue">
<boolean-data :defaultValue="false" title="设置当前下拉刷新状态,true 表示下拉刷新已经被触发,false 表示下拉刷新未被触发"
@change="change_refresher_triggered_boolean"></boolean-data>
<boolean-data :defaultValue="false" title="开启下拉刷新" @change="change_refresher_enabled_boolean"></boolean-data>
<boolean-data :defaultValue="false" title="开启自定义样式" @change="change_refresher_style_boolean"></boolean-data>
<boolean-data :defaultValue="false" title="是否在设置滚动条位置时使用滚动动画,设置false没有滚动动画"
@change="change_scroll_with_animation_boolean"></boolean-data>
<boolean-data :defaultValue="true" title="控制是否出现滚动条" @change="change_show_scrollbar_boolean"></boolean-data>
<boolean-data :defaultValue="true" title="控制是否回弹效果" @change="change_rebound_boolean"></boolean-data>
<boolean-data :defaultValue="true" title="允许纵向滚动" @change="change_scroll_y_boolean"></boolean-data>
<boolean-data :defaultValue="false" title="允许横向滚动" @change="change_scroll_x_boolean"></boolean-data>
<input-data defaultValue="50" title="距顶部/左边多远时(单位px),触发 scrolltoupper 事件" type="number"
@confirm="confirm_upper_threshold_input"></input-data>
<input-data defaultValue="50" title="距底部/右边多远时(单位px),触发 scrolltolower 事件" type="number"
@confirm="confirm_lower_threshold_input"></input-data>
<input-data defaultValue="0" title="设置竖向滚动条位置" type="number" @confirm="confirm_scroll_top_input"></input-data>
<input-data defaultValue="0" title="设置横向滚动条位置" type="number" @confirm="confirm_scroll_left_input"></input-data>
<input-data defaultValue="#FFF" title="设置下拉刷新区域背景颜色" type="text"
@confirm="confirm_refresher_background_input"></input-data>
<enum-data :items="size_enum" title="通过id位置跳转" @change="item_change_size_enum"></enum-data>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<style>
.main {
max-height: 500rpx;
padding: 10rpx 0;
border-bottom: 1px solid rgba(0, 0, 0, .06);
flex-direction: row;
justify-content: center;
}
.main .list-item {
width: 750rpx;
height: 480rpx;
border: 1px solid #666;
background-color: #66ccff;
align-items: center;
justify-content: center;
}
.tip-text {
color: #888;
font-size: 12px;
}
.refresh-box {
justify-content: center;
align-items: center;
flex-direction: row;
height: 45px;
width: 100%;
}
</style>
refresher-default-style
属性为 none 不使用默认样式slot="refresher"
,需要设置刷新元素宽高信息否则可能无法正常显示!<template>
<list-view refresher-default-style="none" :refresher-enabled="true" :refresher-triggered="refresherTriggered"
@refresherpulling="onRefresherpulling" @refresherrefresh="onRefresherrefresh"
@refresherrestore="onRefresherrestore" style="flex:1" >
<list-item v-for="i in 10" class="content-item">
<text class="text">item-{{i}}</text>
</list-item>
<!-- 自定义下拉刷新元素 -->
<list-item slot="refresher" class="refresh-box">
<text class="tip-text">{{text[state]}}</text>
</list-item>
</list-view>
</template>
注意:
安卓系统版本 | 安卓 uni-app | 安卓 uni-app-x | iOS 系统版本 | iOS uni-app | iOS uni-app-x | |
---|---|---|---|---|---|---|
list-view | 5.0 | x | 3.9+ | 9.0 | x | - |
scroll-x | 5.0 | x | 3.9+ | - | - | - |
scroll-y | 5.0 | x | 3.9+ | - | - | - |
rebound | 5.0 | x | 3.9+ | - | - | - |
upper-threshold | 5.0 | x | 3.9+ | - | - | - |
lower-threshold | 5.0 | x | 3.9+ | - | - | - |
scroll-top | 5.0 | x | 3.9+ | - | - | - |
scroll-left | 5.0 | x | 3.9+ | - | - | - |
show-scrollbar | 5.0 | x | 3.9+ | - | - | - |
scroll-into-view | 5.0 | x | 3.9+ | - | - | - |
scroll-with-animation | 5.0 | x | 3.9+ | - | - | - |
refresher-enabled | 5.0 | x | 3.9+ | - | - | - |
refresher-threshold | 5.0 | x | 3.9+ | - | - | - |
refresher-max-drag-distance | 5.0 | x | 3.9+ | - | - | - |
refresher-default-style | 5.0 | x | 3.9+ | - | - | - |
refresher-background | 5.0 | x | 3.9+ | - | - | - |
refresher-triggered | 5.0 | x | 3.9+ | - | - | - |
custom-nested-scroll | 5.0 | x | 3.9+ | 9.0 | x | - |
@refresherpulling | 5.0 | x | 3.9+ | 9.0 | x | - |
@refresherrefresh | 5.0 | x | 3.9+ | 9.0 | x | - |
@refresherrestore | 5.0 | x | 3.9+ | 9.0 | x | - |
@refresherabort | 5.0 | x | 3.9+ | 9.0 | x | - |
@scrolltoupper | 5.0 | x | 3.9+ | 9.0 | x | - |
@scrolltolower | 5.0 | x | 3.9+ | 9.0 | x | - |
@scroll | 5.0 | x | 3.9+ | 9.0 | x | - |
吸顶布局容器
注意:暂时仅支持作为list-view的子节点, sticky-header不支持css样式!当一个容器视图设置多个sticky-header时,后一个sticky-header会停靠在前一个sticky-header的末尾处。
<template>
<list-view :scroll-y="true" class="page" rebound="false" :scroll-top="scroll_top_input" :refresher-enabled="refresher_enabled_boolean"
:refresher-triggered="refresher_triggered_boolean" @refresherrefresh="list_view_refresherrefresh">
<list-item type = 1>
<swiper indicator-dots="true" circular="true">
<swiper-item v-for="i in 3" :item-id="i">
<image src="/static/shuijiao.jpg" style="height: 240px;"></image>
<text style="position: absolute;">{{i}}</text>
</swiper-item>
</swiper>
</list-item>
<list-item class="content-item" type = 2>
<text class="text">向上滑动页面,体验sticky-header吸顶效果。</text>
</list-item>
<sticky-header>
<scroll-view style="background-color: #f5f5f5; flex-direction: row;" :scroll-x="true" :scroll-y="false" :show-scrollbar="false">
<view class="flex-row" style="align-self: flex-start; flex-direction: row;">
<text ref="swipertab" class="sift-item"
v-for="(name,index) in sift_item" @click="clickTH(index)">
{{name}}
</text>
</view>
</scroll-view>
</sticky-header>
<list-item v-for="(item,index) in list_item" :key="index" class="content-item" type = 3>
<text class="text">{{item}}</text>
</list-item>
</list-view>
</template>
<script>
export default {
data() {
return {
sift_item: ["排序", "筛选"],
list_item: [] as Array<string>,
refresher_enabled_boolean: true,
refresher_triggered_boolean: false,
scroll_top_input: 0
}
},
onLoad() {
let lists : Array<string> = []
for (let i = 0; i < 40; i++) {
lists.push("item---" + i)
}
this.list_item = lists
},
methods: {
list_view_refresherrefresh() {
console.log("下拉刷新被触发 ")
this.refresher_triggered_boolean = true
setTimeout(function(){
this.refresher_triggered_boolean = false
}, 1500)
},
confirm_scroll_top_input(value : number) {
this.scroll_top_input = value
},
clickTH(index:number){
console.log("点击表头:" + index);
}
}
}
</script>
<style>
.page {
flex: 1;
background-color: #f5f5f5;
}
.content-item {
padding: 15px;
margin: 5px 0;
background-color: #fff;
}
.text {
font-size: 14px;
color: #666;
line-height: 20px;
}
.sift-item {
color: #555;
font-size: 16px;
padding: 12px 15px;
}
</style>
安卓系统版本 | 安卓 uni-app | 安卓 uni-app-x | iOS 系统版本 | iOS uni-app | iOS uni-app-x | |
---|---|---|---|---|---|---|
sticky-header | 5.0 | x | 3.93 | 9.0 | x | - |
list-view组件的唯一合法子组件。每个item是一行
名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
type | number | 0 | 对应list-item的类型 list-view 将对同类型条目进行复用,所以合理的类型拆分,可以很好地提升 list-view 性能 |
type属性定义list-item组件类型。不赋值type属性默认值为0,每一个type类型都会有对应的list-item组件缓存池。
list-view组件加载list-item组件时,会优先查询对应type缓存池是否存在可复用的list-item组件。有则复用没有则创建新的list-item组件。
list-item组件被滑动出屏幕则会优先添加到对应类型的list-item缓存池,每个类型缓存最大5个(不同平台缓存最大值不固定),如果缓存池已满则进行组件销毁!
部分list-item组件存在子元素个数差异或排版差异时。请尽可能的配置不同的type,这样可以规避获取相同type类型的list-item组件后。由于子元素差异导致list-item无法正常复用问题。具体可参考示例:
<template>
<view class="content">
<list-view ref="listView" class="list" :scroll-y="true">
<list-item v-for="(item,index) in list" :key="index" class="content-item1" type=1>
<text class="text">title-{{item}}</text>
<text class="text">content-{{item}}</text>
</list-item>
<list-item v-for="(item,index) in list" :key="index" class="content-item2" type=2>
<image class="image" src ="/static/test-image/logo.png"></image>
</list-item>
<list-item type=3>
<text class="loading">{{text}}</text>
</list-item>
</list-view>
</view>
</template>
示例中有三种类型的list-item组件。如果都不赋值type,list-item组件滑动出屏幕后都归类到type=0的缓存池。当触发list-item组件重新加载时,获取type=0的缓存池的组件,获取到的list-item组件可能是两个text子组件也可能是一个image子组件或一个text子组件,底层复用判断时则认为该情况异常不复用,重新创建新的list-item组件!复用失败未能优化性能。正确的方式则是不同的类型设置不同的type。加载时则会获取对应type类型缓存池中的list-item组件实现复用。
安卓系统版本 | 安卓 uni-app | 安卓 uni-app-x | iOS 系统版本 | iOS uni-app | iOS uni-app-x | |
---|---|---|---|---|---|---|
list-item | 5.0 | x | 3.9+ | 9.0 | x | - |
type | 5.0 | x | 3.9+ | 9.0 | x | - |