React Native之ListView使用

来源:http://www.cnblogs.com/miaomiaoshen/archive/2016/11/17/6075097.html
-Advertisement-
Play Games

前言 學習本系列內容需要具備一定 HTML 開發基礎,沒有基礎的朋友可以先轉至 "HTML快速入門(一)" 學習 本人接觸 React Native 時間並不是特別長,所以對其中的內容和性質瞭解可能會有所偏差,在學習中如果有錯會及時修改內容,也歡迎萬能的朋友們批評指出,謝謝 文章第一版出自簡書,如果 ...


前言

  • 學習本系列內容需要具備一定 HTML 開發基礎,沒有基礎的朋友可以先轉至 HTML快速入門(一) 學習

  • 本人接觸 React Native 時間並不是特別長,所以對其中的內容和性質瞭解可能會有所偏差,在學習中如果有錯會及時修改內容,也歡迎萬能的朋友們批評指出,謝謝

  • 文章第一版出自簡書,如果出現圖片或頁面顯示問題,煩請轉至 簡書 查看 也希望喜歡的朋友可以點贊,謝謝

ListView組件介紹


  • ListView組件是React Native中一個比較核心的組件,用途非常廣,設計初衷就是用來高效的展示垂直滾動的列表數據
  • ListView 繼承了 ScrollView 的所有屬性
  • 使用步驟:
    • 創建一個ListView.DataSource數據源,然後給它傳遞一個普通的數組數據


        getInitialState(){
            // 初始化數據源(rowHasChanged是優化的一種手段,只有當r1 !== r2的時候才會重新渲染)
            var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
            return{
                // 給dataSource傳遞一組 數組
                dataSource: ds.cloneWithRows(['內容0', '內容1', '內容2', '內容3', '內容4', '內容5'])
            }
        },
    
    • 使用數據源實例化一個ListView組件,定義一個renderRow回調函數,這個函數會接受數組中的每個數據作為參數,並返回一個可渲染的組件(也就是該列表的每一行Item)


        render() {
            return (
                <View style={styles.container}>
                    // 根據數據源實例化一個ListView
                    <ListView style={{backgroundColor:'yellow'}}
                        // 獲取數據源
                        dataSource={this.state.dataSource}
                        // 根據數據源創建一個Item
                        // 註:這裡的this.renderRow是隱式寫法,系統會根據函數的需要,將對應的參數傳遞過去(共有4個參數:rowData, sectionID, rowID, highlightRow)
                        renderRow={this.renderRow}
                    />
                </View>
            );
        },
    
        // 返回一個Item
        renderRow(rowData,sectionID,rowID) {
            return(
                // 實例化Item
                <View>
                    <Text style={{backgroundColor:'red', height:44}}>內容{rowData},在第{sectionID}組第{rowID}行</Text>
                </View>
            )
        }
    

    效果:
    ListView初體驗.gif

  • ListView 同樣支持一些高級特性,包括設置每一組的粘性的頭部、支持設置列表 header 和 footter 視圖、當數據列表滑動到最底部的時候支持 onEndReached 方法回調、設備屏幕列表可見的視圖數據發生變化的時候回調 onChangeVisibleRows 以及一些性能方面的優化特性

ListView常用屬性


  • ScrollView 全部屬性

  • dataSource:設置ListView的數據源

  • initialListSize:指定在組件剛掛載的時候渲染多少行數據。用這個屬性來確保首屏顯示合適數量的數據,而不是花費太多幀逐步顯示出來

  • onChangeVisibleRows:((visibleRows, changedRows) => void)當可見的行的集合變化的時候調用此回調函數。visibleRows 以 { sectionID: { rowID: true }}的格式包含了所有可見行,而changedRows 以{ sectionID: { rowID: true | false }}的格式包含了所有剛剛改變了可見性的行,其中如果值為true表示一個行變得可見,而為false表示行剛剛離開可視區域而變得不可見

  • onEndReached:當所有的數據都已經渲染過,並且列表被滾動到距離最底部不足onEndReachedThreshold個像素的距離時調用。原生的滾動事件會被作為參數傳遞。譯註:當第一次渲染時,如果數據不足一屏(比如初始值是空的),這個事件也會被觸發

  • onEndReachedThreshold:調用onEndReached之前的臨界值,單位是像素

  • pageSize:每次事件迴圈(每幀)渲染的行數

  • removeClippedSubviews:用於提升大列表的滾動性能。需要給行容器添加樣式overflow:'hidden'。(Android已預設添加此樣式)此屬性預設開啟

  • renderFooter:(() => renderable)頁頭與頁腳會在每次渲染過程中都重新渲染(如果提供了這些屬性)。如果它們重繪的性能開銷很大,把他們包裝到一個StaticContainer或者其它恰當的結構中。頁腳會永遠在列表的最底部,而頁頭會在最頂部

  • renderHeader: 在每一次渲染過程中Footer(尾)該會一直在列表的底部,header(頭)該會一直在列表的頭部

  • renderRow:【(rowData, sectionID, rowID, highlightRow) => renderable
    • 從數據源(Data source)中接受一條數據,以及它和它所在section的ID。返回一個可渲染的組件來為這行數據進行渲染。預設情況下參數中的數據就是放進數據源中的數據本身,不過也可以提供一些轉換器
    • 如果某一行正在被高亮(通過調用highlightRow函數),ListView會得到相應的通知。當一行被高亮時,其兩側的分割線會被隱藏。行的高亮狀態可以通過調用highlightRow(null)來重置
  • renderScrollComponent:【(props) => renderable】指定一個函數,在其中返回一個可以滾動的組件。ListView將會在該組件內部進行渲染。預設情況下會返回一個包含指定屬性的ScrollView

  • renderSectionHeader:【(sectionData, sectionID) => renderable】
    • 如果提供了此函數,會為每個小節(section)渲染一個粘性的標題。
    • 粘性是指當它剛出現時,會處在對應小節的內容頂部;繼續下滑當它到達屏幕頂端的時候,它會停留在屏幕頂端,一直到對應的位置被下一個小節的標題占據為止
  • renderSeparator:【(sectionID, rowID, adjacentRowHighlighted) => renderable】
    • 如果提供了此屬性,一個可渲染的組件會被渲染在每一行下麵,除了小節標題的前面的最後一行。在其上方的小節ID和行ID,以及鄰近的行是否被高亮會作為參數傳遞進來
  • scrollRenderAheadDistance:當一個行接近屏幕範圍多少像素之內的時候,就開始渲染這一行

  • stickyHeaderIndices(iOS):一個子視圖下標的數組,用於決定哪些成員會在滾動之後固定在屏幕頂端。舉個例子,傳遞stickyHeaderIndices={[0]}會讓第一個成員固定在滾動視圖頂端。這個屬性不能和horizontal={true}一起使用

方法


  • getMetrics():導出一些用於性能分析的數據

  • scrollTo(...args):滾動到指定的x, y偏移處,可以指定是否加上過渡動畫。
    • 參考 ScrollView#scrollTo.

ListView簡單優化建議


  • ListView 設計的時候,當需要動態載入非常大量或者渲染複雜的數據時,下麵有一些方法可以提高 ListView 的性能
    • 只渲染更新數據變化的那個Item,rowHasChange方法會告訴ListView組件是否需要重新渲染當前Item
    • 選擇渲染的頻率,預設情況下,每一個event-loop(事件迴圈)只會渲染一行(可以同pageSize自定義屬性設置)這樣可以把大工作量進行分隔,提供整體渲染性能

ListView 基本佈局


  • 這邊我們就按照下圖中的佈局實現一個簡單的列表數據展示
    基本佈局

  • 分析上圖整體佈局,我這邊就將其劃分為幾個模塊,首先需要有個 大的View 來包裝內部所有的內容,其次再給標題部分分配一個 小View 以方便維護,具體如下圖
    基本佈局分析

  • 接下來就可以開始幹活啦~
    • 首先,ListView 需要數據源,那麼我們就先來自定義一下數據源的 Json 數據,


        [
            {"title" : "icon", "img" : "icon"},
            {"title" : "lufei", "img" : "lufei"},
            {"title" : "icon", "img" : "icon"},
            {"title" : "lufei", "img" : "lufei"},
            {"title" : "icon", "img" : "icon"},
            {"title" : "lufei", "img" : "lufei"},
            {"title" : "icon", "img" : "icon"},
            {"title" : "lufei", "img" : "lufei"},
            {"title" : "icon", "img" : "icon"},
            {"title" : "lufei", "img" : "lufei"},
            {"title" : "icon", "img" : "icon"},
            {"title" : "lufei", "img" : "lufei"},
            {"title" : "icon", "img" : "icon"},
            {"title" : "lufei", "img" : "lufei"}
        ]
    
    • 有了數據後,我們就可以根據數據來實例化 ListView
      • 獲取數據


          var newData = require('./Data/localData.json');
      
      • 初始化數據源


          getInitialState(){
              var ds = new ListView.DataSource({rowHasChanged:(r1, r2) => r1 != r2});
                  return{
                      // 將獲得的數組傳遞給dataSource
                      dataSource : ds.cloneWithRows(newData)
              }
          },
      
      • 接著就是根據數據源實例化 ListView
        • 視圖部分


            render(){
                return(
                    <View style={styles.container}>
                        <ListView
                            dataSource={this.state.dataSource}
                            renderRow={this.renderRow}
                        />
                    </View>
                );
            },
        
            // 返回一個Item
            renderRow(rowData){
                return(
                    <View style={styles.itemStyle}>
                        <Image source={{uri:rowData.img}} style={styles.imageStyle}/>
                        <View style={styles.subItemStyle}>
                            <Text style={{marginTop:5, fontSize:17}}>{rowData.title}</Text>
                            <Text style={{marginBottom:5, fontSize:13, color:'green'}}>簡介</Text>
                        </View>
                    </View>
                );
            }
        
        • 樣式部分


            var styles = StyleSheet.create({
                container: {
                    flex:1
                },
        
                itemStyle: {
                    // 主軸方向
                    flexDirection:'row',
                    // 下邊框
                    borderBottomWidth:1,
                    borderBottomColor:'gray'
                },
        
                imageStyle: {
                    // 尺寸
                    width:60,
                    height:60,
                    // 邊距
                    marginLeft:10,
                    margin:10
                },
        
                subItemStyle: {
                    // 對齊方式
                    justifyContent:'space-around'
                }
            });
        
        效果:
        ListView案例一.gif

ListView 九宮格佈局實現


  • 先來看下大概的佈局

九宮格概念佈局

  • 從上面可以看出,這個案例是為了實現類似 CollectionView 效果(比如常見的瀑布流),通常情況下,ListView 是縱向排列的,而此案例我們需要它橫向排列,那麼就需要使用到上面提到的 contentContainerStyle 屬性,向裡面添加 flexDirection:'row'和flexWrap:'wrap'` 兩個屬性

        contentViewStyle: {
            // 主軸方向
            flexDirection:'row',
            // 換行
            flexWrap:'wrap'
        },
    
  • 當然了,我們還是需要自定義一組數據供 ListView 使用,這邊就使用上面案例的數據
  • 根據數據實例化 ListView ,參考上面案例,這裡只粘貼 Item部分,其它的就不重覆了
    • 視圖部分


        var ListViewDemo = React.createClass({
            getInitialState(){
                // 初始化數據源
                var ds = new ListView.DataSource({rowHasChanged:(r1, r2) => r1 != r2});
                return{
                    dataSource : ds.cloneWithRows(newData)
                }
            },
    
            render(){
                return(
                    <ListView
                        dataSource={this.state.dataSource}
                        renderRow={this.renderRow}
                        // 設置contentContainerStyle
                        contentContainerStyle={styles.contentViewStyle}
                    />
                );
            },
    
            // 返回一個Item
            renderRow(rowData){
                return(
                {/* 實例化Item */}
                <View style={styles.itemStyle}>
                    <Image source={{uri:rowData.img}} style={styles.itemImageStyle}/>
                    <Text>{rowData.title}</Text>
                </View>
                );
            }
        });
    
    • 樣式部分


        var styles = StyleSheet.create({
    
            contentViewStyle: {
                // 主軸方向
                flexDirection:'row',
                // 換行
                flexWrap:'wrap'
            },
    
            itemStyle: {
                // 對齊方式
                alignItems:'center',
                justifyContent:'center',
                // 尺寸
                width:itemWH,
                height:itemWH,
                // 左邊距
                marginLeft:vMargin,
                marginTop:hMargin
            },
    
            itemImageStyle: {
                // 尺寸
                width:60,
                height:60,
                // 間距
                marginBottom:5
            }
        });
    

    效果:
    ListView九宮格佈局

ListView 分組樣式的實現分析


  • 在移動設備裡面,經常會看到 sticky效果,比如常見的通訊錄

  • 在React Native中,ScrollView組件要實現 sticky效果 很簡單,只需要使用
    stickyHeaderIndices 就可以了,但對於 ListView 來說,stickyHeaderIndices是無效的,下麵我們就來分析怎樣才能使 ListView 實現吸頂效果

  • 首先,ListView要實現 sticky效果 需要使用到 cloneWithRowsAndSections 方法將 dataBlob(object), sectionIDs (array), rowIDs (array) 三個值傳遞出去
    • dataBlob:包含ListView所需的所有數據(section header 和 rows),在ListView渲染數據時,使用getSectionData 和 getRowData 來渲染每一行數據。 dataBlob 的 key 值包含 sectionID + rowId,參考下麵模擬的數據結構


        var dataBlob = {
            'sectionID1' : {section1 data},
            'sectionID1:rowID0' : {row0 data},
            'sectionID1:rowID1' : {row1 data},
            'sectionID2' : {section1 data},
            'sectionID2:rowID0' : {row0 data},
            'sectionID2:rowID1' : {row1 data},
            'sectionID2:rowID2' : {row2 data},
            ...
        };
    
    • sectionIDs:sectionIDs 用於標識每組section,參考下麵模擬的數據結構


        var sectionIDs = ['sectionID0','sectionID1','sectionID2', ...];
    
    • rowIDs:rowIDs 用於描述每個 section 里的每行數據的位置及是否需要渲染。在ListView渲染時,會先遍歷 rowIDs 獲取到對應的 dataBlob 數據,參考下麵模擬的數據結構


        var rowIDs = [['rowID0', 'rowID1', 'rowID2'...], ['rowID0', 'rowID1', ...], ['rowID0', 'rowID1'], ...];
    

ListView 分組樣式實現


  • 上面我們大概地分析了下 ListView 實現分組的原理,接下來就根據上面的分析加上實際的案例,來更直觀地體驗下 ListView分組功能的實現

  • 首先,因為要分組,所以數據肯定比之前的案例使用到的要複雜,但是不用擔心,這邊會儘量詳細地將數組的處理表述出來,先來看下我們需要使用到的數據

        {
            "data":[
                {
                    "title":"A",
                    "icons":[
                        {
                            "icon" : "icon"
                            },
                        {
                            "icon" : "lufei"
                        },
                        {
                            "icon" : "icon"
                        }
                    ]
                },
                {
                    "title":"B",
                    "icons":[
                        {
                            "icon" : "icon"
                        },
                        {
                            "icon" : "lufei"
                        },
                        {
                            "icon" : "icon"
                        },
                        {
                            "icon" : "lufei"
                        }
                    ]
                },
                {
                    "title":"C",
                    "icons":[
                        {
                            "icon" : "icon"
                        },
                        {
                            "icon" : "lufei"
                        }
                    ]
                },
                {
                    "title":"D",
                    "icons":[
                        {
                            "icon" : "icon"
                        },
                        {
                            "icon" : "lufei"
                        },
                        {
                            "icon" : "icon"
                        },
                        {
                            "icon" : "lufei"
                        }
                    ]
                },
                {
                    "title":"E",
                    "icons":[
                        {
                            "icon" : "icon"
                        },
                        {
                            "icon" : "lufei"
                        },
                        {
                            "icon" : "lufei"
                        }
                    ]
                },
                {
                    "title":"F",
                    "icons":[
                        {
                            "icon" : "icon"
                        },
                        {
                            "icon" : "lufei"
                        }
                    ]
                }
    
            ]
        }
    
  • 結合上面的分析,我們先來初始化數據源

        getInitialState(){
            // 初始化getSectionData
            var getSectionData = (dataBlob, sectionID) => {
                return dataBlob[sectionID];
            };
    
            // 初始化getRowData
            var getRowData = (dataBlob, sectionID, rowID) => {
                return dataBlob[sectionID + ':' + rowID];
            };
    
            return {
                // 初始化數據源
                dataSource: new ListView.dataSource({
                    getSectionData : getSectionData,
                    getRowData : getRowData,
                    rowHasChanged : (r1, r2) => r1 !== r2,
                    sectionHeaderHasChanged : (s1, s2) => s1 !== s2
                })
            }
        },
    
  • 接著是數組的解析,然後將解析好的數據提供給 dataSource 進行更新,需要註意的是在實際開發中,數據的複雜程度遠遠要大於我們上面的數據,這是比較耗時的操作,所以我們會選擇在非同步線程中執行,之前的文章中也提到過 —— 在React Native中,我們一般將耗時複雜的操作放到 componentDidMount 中執行

        // 耗時、複雜操作放到這裡處理
        componentDidMount(){
            // 載入數據
            this.loadData();
        },
    
        // 載入數據
        loadData(){
            // 拿到json數據中的數組
            var jsonData = iconData.data;
            // 定義變數
            var dataBlob = {},
                sectionIDs = [],
                rowIDs = [],
                icons = [];
            // 遍曆數組中對應的數據並存入變數內
            for (var i = 0; i<jsonData.length; i++){
                // 將組號存入 sectionIDs 中
                sectionIDs.push(i);
                // 將每組頭部需要顯示的內容存入 dataBlob 中
                dataBlob[i] = jsonData[i].title;
                // 取出該組所有的 icon
                icons = jsonData[i].icons;
                rowIDs[i] = [];
                // 遍歷所有 icon
                for (var j = 0; j<icons.length; j++){
                    // 設置標識
                    rowIDs[i].push(j);
                    // 根據標識,將數據存入 dataBlob
                    dataBlob[i + ':' + j] = icons[j];
                }
            }
            // 刷新dataSource狀態
            this.setState({             dataSource:this.state.dataSource.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs)
            });
        }
    
  • 最後,就是設置樣式,將佈局調到我們想要的效果就可以了
    • 視圖部分


        render(){
            return(
                <View style={styles.container}>
                    // 實例化頂部View
                    <View style={styles.topViewStyle}>
                        <Text style={{fontSize:21}}>分組樣式</Text>
                    </View>
                    // 實例化ListView
                    <ListView
                        dataSource={this.state.dataSource}
                        renderRow={this.renderRow}
                        renderSectionHeader={this.renderSectionHeader}
                    />
                    </View>
            );
        },
    
        // 返回一個Item
        renderRow(rowData, sectionID, rowID){
            return(
                <View style={styles.itemStyle}>
                    <Image source={{uri:rowData.icon}} style={{width: 60, height:60, marginTop:10, marginLeft:10}}></Image>
                    <Text style={{marginTop:15, marginLeft:10}}>示例</Text>
                </View>
            );
        },
    
        // 返回一個SectionHeader
        renderSectionHeader(sectionData, sectionID){
            return(
                <Text style={{backgroundColor:'yellow'}}>{sectionData}</Text>
            );
        },
    
    • 樣式部分


        var styles = StyleSheet.create({
            contairn:{
                flex:1
            },
    
            topViewStyle: {
                // 尺寸
                height:44,
                // 邊距
                marginTop:20,
                // 對齊方式
                justifyContent:'center',
                alignItems:'center'
            },
    
            itemStyle: {
                // 尺寸
                height:80,
                // 主軸方向
                flexDirection:'row',
                // 下邊框
                borderBottomWidth:1,
                borderBottomColor:'gray'
            },
        });
    

    效果:
    ListView分組樣式


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 定義:正則用於規定在文本中檢索的內容,它是對字元串執行模式匹配的強大工具 RegExp(正則表達式) 對象的語法: new RegExp(pattern, attributes); pattern為一個字元串或匹配規則 attributes為可選字元串,包含屬性g、i 和 m g:代表全局匹配 (繼 ...
  • 關於Turtle Online Turtle online 是Turtle框架的PC前端架構,包括組件和API兩大部分,可以快速的搭建PC前端開發環境。組件包括日曆、分頁、圖片輪播/圖片瀏覽、各類提示彈框/自定義彈層、氣泡提示、圖標icon、表單等。API包括常用JS方法封裝(cookie操作、aj ...
  • 1、tortoisegit Git下載地址: https://tortoisegit.org/download/ SVN下載地址: https://tortoisesvn.net/downloads.html 2、sourcetree https://www.sourcetreeapp.com/ ...
  • AysncTask相信大家都不陌生,它是為了簡化非同步請求、更新UI操作而誕生的。使用它不僅可以完成我們的網路耗時操作,而且還可以在完成耗時操作後直接的更新我們所需要的UI組件。這使得它在android開發中成為炙手可熱的網路請求工具類。 ...
  • //通過NSNumberFormatter,同樣可以設置NSNumber輸出的格式。例如如下代碼: NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; formatter.numberStyle = NSNumberFor ...
  • 前面兩篇分別介紹了通過腳本和C代碼讀寫/sys/class/gpio以控制GPIO。實際項目調試時經常還需要在Java代碼里控制GPIO,其實現與C代碼類似,唯一不同是Android許可權。本文重點介紹Android6.0許可權的配置並對在Java層控制GPIO的耗時做簡單分析。 以高通平臺為例,許可權配 ...
  • 向下傳遞活動很簡單,可以我採用putExtra()方法的重載,把我們想要傳遞的數據暫時放在intent中,啟動活動時從這裡取就可以了。 首先我們在MainActivity(主活動)顯式聲明intent的方式來啟動SecondActivity,通過PutExtra()方法來傳遞數據。 ...
  • 一、前言: 安卓(Android):是一種基於Linux的自由及開放源代碼的操作系統,主要用在移動設備上,如手機、平板電腦。其他的設備也有使用安卓操作系統,比如:電視機,游戲機、數位相機等等。 二、具體: 1、首先需要有開發安卓的環境,安卓SDK。在此本人提供Android4.4的版本下載鏈接:ht ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...