Procházet zdrojové kódy

feat(basic): 为 RefreshView 添加控制器并优化聊天页面

- 在 basic 中添加 RefreshController 接口,用于控制 RefreshView 的滚动
- 重构 RefreshView 组件,支持通过控制器进行滚动操作- 在 HosChatView 中集成 RefreshController,实现消息自动滚动到最新
- 优化 ChatItemView 的显示逻辑,修复系统消息的显示问题
- 调整 HosChatView 的数据加载和刷新逻辑
徐勤民 před 1 měsícem
rodič
revize
dcb822cc31

+ 1 - 0
CHANGELOG.md

@@ -1,6 +1,7 @@
 # [v1.0.9] 2025.xx.xx
 
 > - `RefreshView`参数不包含`onLoadMore`的时候,上划不应该提示`没有更多数据了`
+> - `RefreshView`添加一个`controller`,补充跳转到顶部,跳转到底部等方法,具体见文档`4.3`注释
 >
 
 # [v1.0.8] 2025.03.11

+ 1 - 1
Index.ets

@@ -55,7 +55,7 @@ export { WindowHelper } from './src/main/ets/utils/WindowHelper'
  */
 export { LoadingView } from './src/main/ets/view/LoadingView'
 export { SafeView } from './src/main/ets/view/SafeView'
-export { RefreshView } from './src/main/ets/view/RefreshView'
+export { RefreshView } from './src/main/ets/view/refresh/RefreshView'
 /**
  * 自定义view
  */

+ 16 - 6
README.md

@@ -466,7 +466,7 @@ build() {
 }
 ```
 
-### 4.3.[RefreshView](./src/main/ets/view/RefreshView.ets)
+### 4.3.[RefreshView](src/main/ets/view/refresh/RefreshView.ets)
 
 > 下拉刷新,上拉加载更多
 
@@ -487,18 +487,28 @@ build() {
     // ......
     RefreshView({
         isLoading: this.isLoading,
-        keyGenerator: (item: Item) => {
-            return item.id
+        // 是否自动调用`onRefresh`方法,默认为`true`,加载组件会自动调用一次`onRefresh`
+        init: false,
+        // 第一次加载完成,是否定位到底部,默认`false`,定位到顶部
+        positioningToBottom: true,
+        // 控制器,提供跳转到顶部、跳转到底部、是否已经在最底部方法
+        controller: this.msgController,
+        // 唯一标识,view的key
+        keyGenerator: () => {
+            return ToolsHelper.getUuid().toString()
         },
+        // 每页请求的数据大小,根据这个参数判断是否可以上拉加载更多
         pageSize: 10,
         data: this.list ?? [],
         // 这里要用箭头函数,否则buildItem里面的this指向不对
-        customBuilderParam: (item: Item, index: number) => {
-          this.buildItem(item, index)
+        customBuilderParam: (item: ImMessage, index: number) => {
+            this.buildItem(item, index)
         },
+        // 下拉刷新回调
         onRefresh: () => {
-            this.refresh()
+            this.getHistory()
         },
+        // 上拉加载更多回调
         onLoadMore: (pageNum) => {
             this.refresh(pageNum)
         }

+ 5 - 0
src/main/ets/view/refresh/RefreshController.ets

@@ -0,0 +1,5 @@
+export interface RefreshController {
+  toTop: (smooth?: boolean) => void
+  toBottom: (smooth?: boolean) => void
+  isAtEnd: () => boolean
+}

+ 68 - 3
src/main/ets/view/RefreshView.ets → src/main/ets/view/refresh/RefreshView.ets

@@ -1,24 +1,86 @@
-import { ToolsHelper } from '../utils/ToolsHelper'
+import { ToolsHelper } from '../../utils/ToolsHelper'
+import { inspector } from '@kit.ArkUI'
+import { RefreshController } from './RefreshController'
 
 @Component
 export struct RefreshView {
   @Link isLoading: boolean
-  @Prop data: Array<ESObject>
+  @Prop @Watch('change') data: Array<ESObject>
   @Require keyGenerator?: (item: ESObject, index: number) => string
+  //是否自动调用`onRefresh`方法,默认为`true`,加载组件会自动调用一次`onRefresh`
   init: boolean = true
+  // 第一次加载完成,是否定位到底部,默认`false`,定位到顶部
+  positioningToBottom: boolean = false
   pageSize: number = 10
   onLoadMore?: (pageNum: number) => void
   onRefresh?: () => void
+  controller: RefreshController | null = null
   private startY: number = 0
   private endY: number = 0
   private lastNum: number = 0
   private _openMore: boolean = false
   private _oTime: number = 0
+  private _listener: inspector.ComponentObserver = inspector.createComponentObserver('Refresh_View_List');
+  private _scroller: Scroller = new Scroller()
+  private onDrawComplete = () => {
+    this._isLoad = false
+    this.isLoading = false
+    if (!this._firstFinish && this.positioningToBottom) {
+      this._firstFinish = true
+      this.toBottom()
+    } else {
+      if (this._toTop) {
+        this.toTop()
+        this._toTop = false
+      } else if (this._toBottom) {
+        this.toBottom()
+        this._toBottom = false
+      }
+    }
+  };
+  private _firstFinish = false
+  private _isLoad = false
+  private _toTop = false
+  private _toBottom = false
+
+  change() {
+    this._isLoad = true
+  }
 
   aboutToAppear(): void {
     if (this.init) {
       this.onRefresh && this.onRefresh()
     }
+    if (this.controller) {
+      this.controller.toTop = (smooth?: boolean) => {
+        this.toTop(smooth)
+      }
+      this.controller.toBottom = (smooth?: boolean) => {
+        this.toBottom(smooth)
+      }
+      this.controller.isAtEnd = () => this._scroller.isAtEnd()
+    }
+    this._listener.on('draw', this.onDrawComplete)
+  }
+
+  toTop(smooth?: boolean) {
+    if (!this._isLoad) {
+      this._scroller.scrollToIndex(0, smooth, ScrollAlign.START)
+    } else {
+      this._toTop = true
+    }
+  }
+
+  toBottom(smooth?: boolean) {
+    if (!this._isLoad) {
+      this._scroller.scrollToIndex(this.data.length - 1, smooth, ScrollAlign.END)
+    } else {
+      this._toBottom = true
+    }
+  }
+
+  aboutToDisappear(): void {
+    this._listener.off('draw', this.onDrawComplete);
   }
 
   // 使用父组件@Builder装饰的方法初始化子组件@BuilderParam
@@ -28,7 +90,9 @@ export struct RefreshView {
 
     Refresh({ refreshing: $$this.isLoading }) {
       if (this.data && this.data.length > 0) {
-        List() {
+        List({
+          scroller: this._scroller,
+        }) {
           ForEach(this.data ?? [], (item: ESObject, index: number) => {
             ListItem() {
               this.customBuilderParam(item, index)
@@ -63,6 +127,7 @@ export struct RefreshView {
         .onScrollIndex((first: number, last: number) => {
           this.lastNum = last + 1
         })
+        .id('Refresh_View_List')
         .width('100%')
         .height('100%')
         .alignListItem(ListItemAlign.Center)