支付寶效果圖
- 實現的效果:
- 頁面向上滑動有吸頂的效果。
-
便民服務,查詢服務 ...
所在的tabBar
與頁面下方對應的模塊內容一一對應。 - 頁面滑動對應的
tabBar
的title
居中偏移。 - 編輯模式下:數據的
新增
與刪除
。
- 效果展示:
還原支付寶效果圖
-
實現吸頂的代碼(噗~感覺這個沒啥好寫 ...... 很尷尬):
—— 段子手是運用ScrollView
==>onScroll
==>e.nativeEvent.contentOffset.y
獲取頁面滑動的當前坐標;
然后和頁面初次加載時tabBar
==>onLayout
==>e.nativeEvent.layout.y
的值做比較獲取狀態。this.moveHeight = e.nativeEvent.contentOffset.y; if (this.moveHeight >= typeY) { this.setState({ positionType: true, }) } else { this.setState({ positionType: false }) }
-
實現滑動模塊和標題一一對應代碼(這個有點篇幅):
-
先說:點擊標題,對應的模塊自動置頂
- 首先初次加載時,我把
tabBar
下的各個模塊的y
坐標都存下來,push
到數組中,并且對應的模塊下標i
也存起來。
//保存 模塊的下標和 y 軸 let params = { key: i, tmpY: e.nativeEvent.layout.y }; //數組去重(判斷命名的變量不建議用 type) let typePis = tmpArr.some(v => v.key == params.key); if (typePis) { tmpArr.filter((v) => { return v.key != params.key }) } else { tmpArr.push(params); } break;
- 去重是因為吸頂的原因,會導致頁面的
onLayout
重新加載。 - 這里還有一個小坑:模塊返回的坐標順序并不是按頁面展示順序來的。
- 拿到模塊的坐標數組
tmpArr
后,根據點擊tabBar
的下標index
和tmpArr
的key
去匹配,一致時取出tmpArr
對應的tmpY
值,根據該值去計算頁面的偏移量。
let y; tmpArr.map((v, i) => { if (v.key == index) { y = v.tmpY; } }); // 頁面中模塊的 y 軸移動 (typeY: tabBar 的坐標, // y:模塊的坐標,頭部 固定位置的搜索: autoHeight(45)) this.refs.refMoveHeight.scrollTo({y: typeY + y - autoHeight(45)});
- 在點擊
tabBar
最后的標題,模塊的內容的高度不夠去偏移到置頂的位置的處理(根據已有的內容高度,去自適應填充空白區域)。- 保存最后一個模塊的
y
軸
// i:就是點擊標題的下標;typeList:是標題數組。 if (i == typeList.length - 1) { this.setState({ listCellHeight: e.nativeEvent.layout.y }) }
- 在最后的一個模塊后面添加一個高度為
1
的View
,然后保存它的坐標footHeight = e.nativeEvent.layout.y
,接著再設置一個填充空白頁面View
,高度為屏幕全高 - (footHeight - listCellHeight - typeY + autoHeight(45))
。
- 保存最后一個模塊的
- 首先初次加載時,我把
-
再說下滑動模塊和
tabBar
的標題對應- 獲取手勢在屏幕上的滑動方向,把
<=
當前頁面滑動高度的模塊都塞選出來,然后取出最大的下標,然后和tabBar
中的標題下標去匹配,一致則標題顯示高亮。let maxValue = 0; if (this.moveHeight > e.nativeEvent.contentOffset.y) { tmpArr.map((v, i) => { if (e.nativeEvent.contentOffset.y >= (typeY + v.tmpY - autoHeight(45))) { // console.log(i + '下下下'); //因為模塊高度的下標不是按在頁面中的位置返回的,所以和 tabBar 的下標并不能一一對應,所以要塞選出下標的最大值 if (tmpArr[i].key >= maxValue) { maxValue = tmpArr[i].key } // console.log(maxValue + 'maxValue下========='); } } ) } else { tmpArr.map((v, i) => { if (e.nativeEvent.contentOffset.y >= (typeY + v.tmpY - autoHeight(45))) { // console.log(i + '下下下'); //因為模塊高度的下標不是按在頁面中的位置返回的,所以和 tabBar 的下標并不能一一對應,所以要塞選出下標的最大值 if (tmpArr[i].key >= maxValue) { maxValue = tmpArr[i].key } // console.log(maxValue + 'maxValue上======'); } } ) } this.state.currentIndex = maxValue;
- 獲取手勢在屏幕上的滑動方向,把
-
-
實現頁面滑動對應的
tabBar
的title
居中偏移代碼-
獲取屏幕
width
的寬度的一半。let widthHalf = 屏幕寬度 / 2;
-
獲取到
tabBar
中各個標題位置的width
,同時保存對應的挑剔位置的下標,數組為tmpArrX
。//保存 tabBar 的下標和 width let param = { index: i, tmpX: e.nativeEvent.layout.width }; //數組去重 let tmpType = tmpArrX.some(v => v.index == param.index); if (tmpType) { tmpArrX.filter((v) => { return v.index != param.index }) } else { tmpArrX.push(param); }
-
拿到點擊
tabBar
的下標,獲取到這個下標之前的模塊width
。//獲取 index 之前模塊 width let widthX = 0; //獲取選中的 width let indexWidth; for (let i = 0; i <= index; i++) { // console.log('i===' + i); tmpArrX.map((item, key) => { if (item.index == i) { widthX += item.tmpX } if (index == item.index) { indexWidth = item.tmpX } }) }
-
根據判斷出來是不是
tabBar
中最后一個標題,然后拿widthX
和indexWidth
然后去判斷編寫邏輯。let moveX; // index*20 是每個模塊的空隙 20 if (widthX + index * 20 > widthHalf && index != tmpArrX.length - 1) { moveX = (widthX - widthHalf + indexWidth) / 2 } else if (index == tmpArrX.length - 1) { //index + 1 :間距比個數多一個; 10 :marGinLeft = 10 moveX = (index + 1) * 20 + widthX - SCREEN_WIDTH + 10 } // 頁面中 tabBar 的 x 軸移動 this.refs.moveX.scrollTo({x: autoWidth(moveX)})
-
-
實現編輯模式下:數據的
新增
與刪除
的代碼(段子手快餓死了,寫不動注釋了 ......)//數組的加減 _addOrDelete(keyType, data, i) { console.log('data===' + JSON.stringify(data) + '===' + i); //更改 severListType 的數據 const {severListType, headList}=this.state; let tmpType = severListType.some(item => item.id === data.id); switch (keyType) { case 1: // severListType.splice(i, 1); if (tmpType) { // tmpSeverId :用來存儲選中的截取的 id this.state.severListType = severListType.filter((item) => { return item.id != data.id }); //編輯模式下,是否為已有模塊, true:加號,false:減號 data.select = !tmpType; } break; case 2: case 3: if (!tmpType) { if (severListType.length > 10) { RootToast.show('首頁最多添加 11 個應用') } else { severListType.push(data) } } else { //獲取輸入的值和在另一個模塊數組中的下標,然后刪除 console.log('data1===' + JSON.stringify(data) + '===ss' + i); console.log('severListType===' + JSON.stringify(severListType)); this.state.severListType = severListType.filter((item) => { return item.id != data.id }); data.select = !tmpType; } break; } //刷新數據 this.setState({ severListType: this.state.severListType }, () => { // console.log('severListType===' + JSON.stringify(this.state.severListType)); }); }
TIP:
- 吸頂效果在
android
低配中會出現卡頓現象。 - 頁面滑動對應的
tabBar
的title
居中偏移(我的寫法還是有問題的,還有就是模塊滑動時tabBar
對應居中也會有卡頓)。
新增手勢
-
TouchableHighlight
屬性
名稱 | 屬性 | 注釋 |
---|---|---|
accessibilityComponentType | View.AccessibilityComponentType | 設置可訪問的組件類型 |
accessibilityTraits | View.AccessibilityTraits | 設置訪問特征 |
accessible | bool | 設置當前組件是否可以訪問 |
delayLongPress | View.AccessibilityTraits | 設置當前組件是否可以訪問 |
accessibilityTraits | number | 設置延遲的時間,單位為毫秒。從 onPressIn 方法開始,到 onLongPress 被調用這一段時間 |
delayPressIn | number | 設置延遲的時間,單位為毫秒,從用戶觸摸控件開始到 onPressIn 被調用這一段時間 |
delayPressOut | number | 設置延遲的時間,單位為毫秒,從用戶觸摸事件釋放開始到 onPressOut 被調用這一段時間 |
onLayout | function | 當組件加載或者改組件的布局發生變化的時候調用。調用傳入的參數為 {nativeEvent:{layout:{x,y,width,height}}} |
onLongPress | function | 當用戶長時間按壓組件(長按效果)的時候調用該方法 |
onPress | function | 當用戶點擊的時候調用(觸摸結束)。 但是如果事件被取消了就不會調用。(例如:當前被滑動事件所替代) |
onPressIn | function | 用戶開始觸摸組件回調方法 |
onPressOut | function | 用戶完成觸摸組件之后回調方法 |
pressRetentionOffset | {top: ,left: ,bottom: ,right: } | 該設置當視圖滾動禁用的情況下,可以定義當手指距離組件的距離。當大于該距離該組件會失去響應。當少于該距離的時候,該組件會重新進行響應。確保你傳入一個常量來減少內存分配。 |
段子手不才,歡迎來補充
- 由于篇幅原因,具體想要知道整個效果圖的代碼或者有補充地方的可以加技術群:631730313