92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线

首頁

bootstrap table實現x-editable的行單元格編輯及解決數據Empty和支持多樣式

seo達人

如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

前言

  • 最近在研究bootstrap table的表格的單元格編輯功能,實現點擊單元格修改內容,其中包括文本(text)方式修改,下拉選擇(select)方式修改,日期(date)格式修改等。
  • 本文著重解決x-editable編輯的數據動態添加和顯示數據為Empty的問題,還有給表格單元格的內容設置多樣式,使得顯示多樣化。
  • 由于官網給的demo的數據都是html文件里寫好的,select類型的不能動態添加(所以網上的大多都是官網的類似例子,本篇博客就是在這種情況下以自己的經驗分享給大家,有問題可以留言哦),一旦動態添加就會出現顯示數據為Empty,我表格原本是有數據的,但是一用這個插件就把數據變成Empty了,這可不是我想要的,所以筆者就自行解決了這個問題。

對比網上的例子

  • 比如以下這種數據不是Empty的例子,但是是由于在html中寫死了數據(awesome),不適合動態添加。
<a href="#" id="username" data-type="text" data-pk="1">awesome</a> <script> $(function(){ $('#username').editable({
        url: '/post',
        title: 'Enter username' });
}); </script>
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 另外一種就是使用bootstrap table動態添加的,但是select類型就會出現數據為Empty的情況。
$('#db_dependences').bootstrapTable({
        method:'POST',
        dataType:'json',
        contentType: "application/x-www-form-urlencoded",
        cache: false,
        striped: true, //是否顯示行間隔色 sidePagination: "client", //分頁方式:client客戶端分頁,server服務端分頁(*) showColumns:true,
        pagination:true,
        minimumCountColumns:2,
        pageNumber:1, //初始化加載第一頁,默認第一頁 pageSize: 10, //每頁的記錄行數(*) pageList: [10, 15, 20, 25], //可供選擇的每頁的行數(*) uniqueId: "id", //每一行的唯一標識,一般為主鍵列 showExport: true,                    
        exportDataType: 'all',
        exportTypes:[ 'csv', 'txt', 'sql', 'doc', 'excel', 'xlsx', 'pdf'], //導出文件類型 onEditableSave: function (field, row, oldValue, $el) { $.ajax({
                success: function (data, status) { if (status == "success") {
                        alert("編輯成功");
                    }
                },
                error: function () { alert("Error");
                },
                complete: function () { }
            });
        },
        data: [{
            id: 1,
            name: '張三',
            sex: '男',
            time: '2017-08-09' }, {
            id: 2,
            name: '王五',
            sex: '女',
            time: '2017-08-09' }, {
            id: 3,
            name: '李四',
            sex: '男',
            time: '2017-08-09' }, {
            id: 4,
            name: '楊朝來',
            sex: '男',
            time: '2017-08-09' }, {
            id: 5,
            name: '蔣平',
            sex: '男',
            time: '2017-08-09' }, {
            id: 6,
            name: '唐燦華',
            sex: '男',
            time: '2017-08-09' }],
        columns: [{
            field: 'id',
            title: '序號' }, {
            field: 'name',
            title: '姓名',
            editable: {
                type: 'text',  
                validate: function (value) { if ($.trim(value) == '') { return '姓名不能為空!';  
                    }  
                }
            } 
        }, {
            field: 'sex',
            title: '性別',
            editable: {
                type: 'select',
                pk: 1,
                source: [
                    {value: 1, text: '男'},
                    {value: 2, text: '女'},
                ]
            }
        },  {
            field: 'time',
            title: '時間',
            editable: {
                type: 'date',
                format: 'yyyy-mm-dd',    
                viewformat: 'yyyy-mm-dd',    
                datepicker: {
                    weekStart: 1 }
            } 
        }]
    });
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101

結果圖如下:

這里寫圖片描述

由于開源,很快就找到原因,由于formatter我們沒有寫這個function導致調用的默認的formatter,默認的沒有把表格的值傳入html中,bootstrap-table-editable.js源碼如下,初始定義_dont_edit_formatter為false,我們沒有實現noeditFormatter的function就會執行第二個if語句,其中的標簽中沒有對內容賦值,導致最后顯示結果為它默認的Empty:

column.formatter = function(value, row, index) { var result = column._formatter ? column._formatter(value, row, index) : value;

                $.each(column, processDataOptions);

                $.each(editableOptions, function(key, value) {
                    editableDataMarkup.push(' ' + key + '="' + value + '"');
                }); var _dont_edit_formatter = false; if (column.editable.hasOwnProperty('noeditFormatter')) {
                    _dont_edit_formatter = column.editable.noeditFormatter(value, row, index);
                } if (_dont_edit_formatter === false) { return ['<a href="javascript:void(0)"', ' data-name="' + column.field + '"', ' data-pk="' + row[that.options.idField] + '"', ' data-value="' + result + '"',
                        editableDataMarkup.join(''), '>' + '</a>' ].join('');
                } else { return _dont_edit_formatter;
                }

            };
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

由于要實現多樣式,則把上面的代碼改變,并在使用的時候實現noeditFormatter:function(value){…}就是了。將上面的代碼改為如下(此為我自己改的,你可以根據自己的需要做修改):

column.formatter = function(value, row, index) { var result = column._formatter ? column._formatter(value, row, index) : value;

                $.each(column, processDataOptions);

                $.each(editableOptions, function(key, value) {
                    editableDataMarkup.push(' ' + key + '="' + value + '"');
                }); var _dont_edit_formatter = false; if (column.editable.hasOwnProperty('noeditFormatter')) { var process = column.editable.noeditFormatter(value, row, index); if(!process.hasOwnProperty('class')){
                        process.class = '';
                    } if(!process.hasOwnProperty('style')){
                        process.style = '';
                    }
                    _dont_edit_formatter = ['<a href="javascript:void(0)"', ' data-name="'+process.filed+'"', ' data-pk="1"', ' data-value="' + process.value + '"', ' class="'+process.class+'" style="'+process.style+'"', '>' + process.value + '</a>' ].join('');
                } if (_dont_edit_formatter === false) { return ['<a href="javascript:void(0)"', ' data-name="' + column.field + '"', ' data-pk="' + row[that.options.idField] + '"', ' data-value="' + result + '"',
                        editableDataMarkup.join(''), '>' + value + '</a>' ].join('');
                } else { return _dont_edit_formatter;
                }

            };
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

結果如下:

這里寫圖片描述

這里寫圖片描述

然后是bootstrap table的使用js文件,在其中實現noeditFormatter函數。返回的result必須包含filed和value,class和style可以不需要,class可以額外用其它插件之類,比如badge,style是增加樣式(背景,顏色,字體等)。

$('#db_dependences').bootstrapTable({
        method:'POST',
        dataType:'json',
        contentType: "application/x-www-form-urlencoded",
        cache: false,
        striped: true, //是否顯示行間隔色 sidePagination: "client", //分頁方式:client客戶端分頁,server服務端分頁(*) showColumns:true,
        pagination:true,
        minimumCountColumns:2,
        pageNumber:1, //初始化加載第一頁,默認第一頁 pageSize: 10, //每頁的記錄行數(*) pageList: [10, 15, 20, 25], //可供選擇的每頁的行數(*) uniqueId: "id", //每一行的唯一標識,一般為主鍵列 showExport: true,                    
        exportDataType: 'all',
        exportTypes:[ 'csv', 'txt', 'sql', 'doc', 'excel', 'xlsx', 'pdf'], //導出文件類型 onEditableSave: function (field, row, oldValue, $el) { $.ajax({
                success: function (data, status) { if (status == "success") {
                        alert("編輯成功");
                    }
                },
                error: function () { alert("Error");
                },
                complete: function () { }
            });
        }, //      onEditableHidden: function(field, row, $el, reason) { // 當編輯狀態被隱藏時觸發 //          if(reason === 'save') { //              var $td = $el.closest('tr').children(); //          //    $td.eq(-1).html((row.price*row.number).toFixed(2)); //          //    $el.closest('tr').next().find('.editable').editable('show'); //編輯狀態向下一行移動 //          } else if(reason === 'nochange') { //              $el.closest('tr').next().find('.editable').editable('show'); //          } //      }, data: [{
            id: 1,
            name: '張三',
            sex: '男',
            time: '2017-08-09' }, {
            id: 2,
            name: '王五',
            sex: '女',
            time: '2017-08-09' }, {
            id: 3,
            name: '李四',
            sex: '男',
            time: '2017-08-09' }, {
            id: 4,
            name: '楊朝來',
            sex: '男',
            time: '2017-08-09' }, {
            id: 5,
            name: '蔣平',
            sex: '男',
            time: '2017-08-09' }, {
            id: 6,
            name: '唐燦華',
            sex: '男',
            time: '2017-08-09' }, {
            id: 7,
            name: '馬達',
            sex: '男',
            time: '2017-08-09' }, {
            id: 8,
            name: '趙小雪',
            sex: '女',
            time: '2017-08-09' }, {
            id: 9,
            name: '薛文泉',
            sex: '男',
            time: '2017-08-09' }, {
            id: 10,
            name: '丁建',
            sex: '男',
            time: '2017-08-09' }, {
            id: 11,
            name: '王麗',
            sex: '女',
            time: '2017-08-09' }],
        columns: [{
            field: 'id',
            title: '序號' }, {
            field: 'name',
            title: '姓名',
            editable: {
                type: 'text',  
                validate: function (value) { if ($.trim(value) == '') { return '姓名不能為空!';  
                    }  
                }
            } 
        }, {
            field: 'sex',
            title: '性別',
            editable: {
                type: 'select',
                pk: 1,
                source: [
                    {value: 1, text: '男'},
                    {value: 2, text: '女'},
                ],
                noeditFormatter: function (value,row,index) { var result={filed:"sex",value:value,class:"badge",style:"background:#333;padding:5px 10px;"}; return result;
                }
            }
        },  {
            field: 'time',
            title: '時間',
            editable: {
                type: 'date',
                format: 'yyyy-mm-dd',    
                viewformat: 'yyyy-mm-dd',    
                datepicker: {
                    weekStart: 1 },
                noeditFormatter: function (value,row,index) { var result={filed:"time",value:value,class:"badge",style:"background:#333;padding:5px 10px;"}; return result;
                }
            } 
        }]
    });
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143

關于bootstrap table的導出及使用可以看我另外一篇博客

下載和引用

下載x-editable,并如下引用。

<link href="js/bootstrap_above/x-editable-develop/dist/bootstrap-editable/css/bootstrap-editable.css" rel="stylesheet"> <script src="js/bootstrap_above/x-editable-develop/dist/bootstrap-editable/js/bootstrap-editable.js"></script> <script src="js/bootstrap_above/bootstrap-table-develop/dist/extensions/editable/bootstrap-table-editable.js"></script>
    
  • 1
  • 2
  • 3

然后講上訴的一些文件修改添加,就完成了。

另外項目的結果展示

這里寫圖片描述

這里寫圖片描述

其中的樣式都是自行在x-editable的基礎上添加的。如配置出問題,以下是源碼鏈接。

藍藍設計m.skdbbs.com )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。


HTML2.1表單標簽及屬性介紹

seo達人

如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

<!DOCTYPE html>

<html>

   <head>

       <meta charset="UTF-8">

       <title>表單標簽及屬性介紹</title>

   </head>

   <body>

   <!--form:表單標簽,在html頁面創建一個表單(瀏覽器上不顯示),若要提交數據到服務器則負責收集數據的標簽要放到form內 ;-->

       <!--action:(確定表單提交的路徑);-->

       <!--method提交方式:get(默認值)有內容 ,post沒有-->

       <form action="#" method="get">


           <!--input:輸入域標簽,獲取用戶輸入信息;-->

           <!--type值不同收集方式不同:hidden(隱藏字段,數據會發送到服務器但瀏覽器不顯示),text(文本框),password(密碼框),radio(單選框),checkbox(復選框),

               file(文件上傳組件),submit(提交按鈕),button(普通按鈕),reset(重置按鈕);-->

           <!--name:元素名(表單數據需提交到服務器必提供name屬性值),服務器通過屬性值獲取提交數據;-->

           <!--readonly:只讀-->

           <!--value:設置input默認值。submit和reset為按鍵上顯示數據-->

           <!--size:大小-->

           <!--maxlength:允許輸入的最大長度-->

           隱藏字段:<input type="hidden" name="id" value=""/><br/>

           用戶名:<input type="text" name="username" readonly="readonly" value="zhangsan" size="40px" maxlength="20"/><br/>

           密碼:<input type="password" name="password"/><br/>

           確認密碼:<input type="password" name="repassword"/><br/>

           性別:<input type="radio" name="sex" value="man"/>男

           <input type="radio" name="sex" value="woman"/>女<br/>

           愛好:<input type="checkbox" name="hobby" value="釣魚"/>釣魚

           <input type="checkbox" name="hobby" value="打電動"/>打電動

           <!--checked:單選或復選框默認勾選-->

           <input type="checkbox" name="hobby" value="畫畫" checked="checked"/>畫畫<br/>

           頭像:<input type="file" /><br/>

           <!--select:下拉列表標簽-->

           籍貫:<select name="province">

               <!--option:子標簽,下拉列表中的一個選項-->

               <option>---請選擇---</option>

               <!--value:發送得服務器的選項值-->

               <option value="北京">北京</option>

               <option value="上海">上海</option>

               <!--selected:勾選當前列表項-->

               <option value="廣州" selected="selected">廣州</option>

           </select><br/>

           自我介紹:

               <!--textarea:文本域-->

               <textarea>


               </textarea><br/>

           提交按鈕:<input type="submit" value="注冊"/><br/>

           普通按鈕:<input type="button" value="普通按鈕"><br/>

           重置按鈕:<input type="reset"/>

       </form>

   </body>

</html>


藍藍設計m.skdbbs.com )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。


Android MVP極限封裝(一)

seo達人

如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

MVP架構在Android這一塊已經盛行依舊,對于一些學習能力比較強的人來說,已經能夠運用自如甚至改造優化了,對于吾等菜鳥,卻是如此的陌生,今日這篇博客,算是小弟在學習和應用上的一點總結罷了,如有不足,還請各位大神不吝指教。

MVP架構是什么就不多說了,博主主要很大家分享的是,如何設計MVP架構。

先來分析一下MVP如何使用:M-V-P三層之間,P作為中間層,負責M,V之間的數據交互的中介,將數據從M層獲取,處理之后提交到V層,換句話說,V需要持有P的實例,P層需要持有V的實例。原理很簡單,使用泛型對數據進行封裝處理: 
1.定義一個V層的空接口,主要是方便封裝:

/**
 * V層接口
 */ public interface IView { }
            
  • 1
  • 2
  • 3
  • 4
  • 5

2.定義一個P層的接口:

/**
 * 抽象為接口
 * 
 */ public interface IPresenter<V extends IView> { /**
     * 綁定視圖
     * 
     * @param view
     */ void attachView(V view); /**
     * 解除綁定(每個V記得使用完之后解綁,主要是用于防止內存泄漏問題)
     */ void dettachView();

}
            
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

3.封裝P基類:綁定解綁V實例

/**
 * 抽象類 統一管理View層綁定和解除綁定
 *
 * @param <V>
 */ public class BasePresenter<V extends IView, M extends IModel> implements IPresenter<V> { private WeakReference<V> weakView; protected M model;

    public V getView() { return proxyView;
    } /**
     * 用于檢查View是否為空對象
     *
     * @return */ public boolean isAttachView() { return this.weakView != null && this.weakView.get() != null;
    } @Override public void attachView(V view) { this.weakView = new WeakReference<V>(view);
    } @Override public void dettachView() { if (this.weakView != null) { this.weakView.clear(); this.weakView = null;
        }
    }
}
            
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

4.M層封裝:

/**
 * M層
 */ public interface IModel { } /**
 * 登錄model
 * Created by admin on 2018/2/5.
 */ public interface ILoginModel extends IModel { void login();
} /**
 * 登錄
 * Created by admin on 2018/2/5.
 */ public class LoginModel implements ILoginModel { @Override public void login() { // TODO: 2018/2/5 發起登錄請求  }
}
            
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

之后,將數據提交到activity或者fragment就行了。 
最基本的鋪墊已經做好了,接下來就該封裝View了:

/**
 * Created by admin on 2018/2/5.
 */ public abstract class MvpActivity<V extends IView, P extends BasePresenter<V>> extends AppCompatActivity implements IView { private P presenter;

    @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState);
        ...
        presenter=getPresenter();
        presenter.attachView(this);
    } protected P getPresenter() { return presenter;
    } protected void setPresenter(P presenter) { this.presenter = presenter;
    } protected V getView() { return (V) this;
    }
    ...
    @Override protected void onDestroy() {
        presenter.dettachView();
        ... super.onDestroy();
    }
}
            
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

收工,MVP基礎框架搭建完成了。沒錯,就是基礎框架,但是能不能用呢,讓我們拭目以待吧。 
先來寫一個View:

public interface ILoginView extends IView { void onLoginSuccess(); void onFailed();

}
            
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然后是Presneter:

/**
 * Created by admin on 2018/2/5.
 */ public class LoginPresenter extends BasePresenter<ILogin, LoginModel> { public LoginPresenter() {
        model = new LoginModel();
    }

    public void login(){
        model.login(new LoginCallBack() { @Override public void onSuccess() { if(null!=(ILogin)getView()){
                    weakView.onLoginSuccess();
                }
            } @Override public void onFailure() { if(null!=(ILogin)getView()){
                    weakView.onFailure();
                }
            }
        });
    }

}
            
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

最后來完成Activity的邏輯:

public class LoginActivity extends MvpActivity<ILoginView, LoginPresenter> implements ILoginView { ...
    @Override public LoginPresenter getPresenter() { return new LoginPresenter();
    } public void login(View view) {
        String name = etUserName.getText().toString();
        String pwd = etUserPwd.getText().toString();
        getPresenter().login(name, pwd);
    }

    @Override public void onLoginSuccess() {

    }

    @Override public void onFailed(){

    ...
}


    




    


藍藍設計m.skdbbs.com )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。


Retrofit源碼分析

seo達人

如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

1、簡介

retrofit是一個封裝okhttp請求的網絡請求庫,可以通過Rxjava適配返回信息。

2、原理分析

我們通過Retrofit.Builder建造者模式創建一個Retrofit實例對象

public static final class Builder {
    /**
      *Android線程切換的類 
      */
    private final Platform platform;
    private @Nullable okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    private @Nullable Executor callbackExecutor;
    private boolean validateEagerly;

    Builder(Platform platform) {
      this.platform = platform;
    }

    public Builder() {
      this(Platform.get());
    }

    Builder(Retrofit retrofit) {
      platform = Platform.get();
      callFactory = retrofit.callFactory;
      baseUrl = retrofit.baseUrl;

      converterFactories.addAll(retrofit.converterFactories);
      // Remove the default BuiltInConverters instance added by build().
      converterFactories.remove(0);

      callAdapterFactories.addAll(retrofit.callAdapterFactories);
      // Remove the default, platform-aware call adapter added by build().
      callAdapterFactories.remove(callAdapterFactories.size() - 1);

      callbackExecutor = retrofit.callbackExecutor;
      validateEagerly = retrofit.validateEagerly;
    }

    public Builder client(OkHttpClient client) {
      return callFactory(checkNotNull(client, "client == null"));
    }

    public Builder callFactory(okhttp3.Call.Factory factory) {
      this.callFactory = checkNotNull(factory, "factory == null");
      return this;
    }

    public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
      if (httpUrl == null) {
        throw new IllegalArgumentException("Illegal URL: " + baseUrl);
      }
      return baseUrl(httpUrl);
    }

    public Builder baseUrl(HttpUrl baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      List<String> pathSegments = baseUrl.pathSegments();
      if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
        throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
      }
      this.baseUrl = baseUrl;
      return this;
    }

    public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

    public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      callAdapterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

    public Builder callbackExecutor(Executor executor) {
      this.callbackExecutor = checkNotNull(executor, "executor == null");
      return this;
    }

    public List<CallAdapter.Factory> callAdapterFactories() {
      return this.callAdapterFactories;
    }

    public List<Converter.Factory> converterFactories() {
      return this.converterFactories;
    }

    public Builder validateEagerly(boolean validateEagerly) {
      this.validateEagerly = validateEagerly;
      return this;
    }

    public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories =
          new ArrayList<>(1 + this.converterFactories.size());

      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
 } 
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129

通過Retrofit.Builder中build方法創建一個Retrofit實例對象,在創建Retrofit時會判斷用戶創建OkhttpClient對象,沒有創建Retrofit會創建一個默認okhttpClient對象,然后設置Platform中的主線程線程池,設置線程池處理器交給主線程Looper對象。然后創建一個Retrofit對象。我們通過Retrofit.create創建一個接口代理類

 public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });
  } 
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

在調用Creater方法時,通過代理類創建Service實例對象,當我們通過接口實例對象調用方法時,通過invoke方法時,通過Method創建一個ServiceMethod對象,然后把ServiceMethod存儲起來

 public ServiceMethod build() {
          callAdapter = createCallAdapter();
          responseType = callAdapter.responseType();
          if (responseType == Response.class || responseType == okhttp3.Response.class) {
            throw methodError("'"
                + Utils.getRawType(responseType).getName()
                + "' is not a valid response body type. Did you mean ResponseBody?");
          }
          responseConverter = createResponseConverter();

          for (Annotation annotation : methodAnnotations) {
            parseMethodAnnotation(annotation);
          }

          if (httpMethod == null) {
            throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
          }

          if (!hasBody) {
            if (isMultipart) {
              throw methodError(
                  "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
            }
            if (isFormEncoded) {
              throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
                  + "request body (e.g., @POST).");
            }
          }

          int parameterCount = parameterAnnotationsArray.length;
          parameterHandlers = new ParameterHandler<?>[parameterCount];
          for (int p = 0; p < parameterCount; p++) {
            Type parameterType = parameterTypes[p];
            if (Utils.hasUnresolvableType(parameterType)) {
              throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
                  parameterType);
            }

            Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
            if (parameterAnnotations == null) {
              throw parameterError(p, "No Retrofit annotation found.");
            }

            parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
          }

          if (relativeUrl == null && !gotUrl) {
            throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
          }
          if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
            throw methodError("Non-body HTTP method cannot contain @Body.");
          }
          if (isFormEncoded && !gotField) {
            throw methodError("Form-encoded method must contain at least one @Field.");
          }
          if (isMultipart && !gotPart) {
            throw methodError("Multipart method must contain at least one @Part.");
          }

          return new ServiceMethod<>(this);
        }

    private CallAdapter<T, R> createCallAdapter() {
            /**
             *獲取方法返回值類型
             */
          Type returnType = method.getGenericReturnType();
          if (Utils.hasUnresolvableType(returnType)) {
            throw methodError(
                "Method return type must not include a type variable or wildcard: %s", returnType);
          }
          if (returnType == void.class) {
            throw methodError("Service methods cannot return void.");
          }
          //獲取注解信息
          Annotation[] annotations = method.getAnnotations();
          try {
            //noinspection unchecked
            return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
          } catch (RuntimeException e) { // Wide exception range because factories are user code.
            throw methodError(e, "Unable to create call adapter for %s", returnType);
          }
        } 
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85

在創建ServiceMethod時,獲取我們okhttp請求是否有返回值,沒有返回值拋出異常,然后獲取注解信息,然后獲取retrofit中CallAdapter.Factory,然后調用get方法,我們在通過rxjavaFactoryAdapter.create創建的就是實現CallAdapter.Factory對象,然后調用CallAdapter.Factory中respenseType方法,然后通過我們傳遞converter對數據進行序列化,可以通過gson和fastjson進行實例化對象,然后通過parseMethodAnnomation解析請求類型

 private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
          if (this.httpMethod != null) {
            throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
                this.httpMethod, httpMethod);
          }
          this.httpMethod = httpMethod;
          this.hasBody = hasBody;

          if (value.isEmpty()) {
            return;
          }

          // Get the relative URL path and existing query string, if present.
          int question = value.indexOf('?');
          if (question != -1 && question < value.length() - 1) {
            // Ensure the query string does not have any named parameters.
            String queryParams = value.substring(question + 1);
            Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
            if (queryParamMatcher.find()) {
              throw methodError("URL query string \"%s\" must not have replace block. "
                  + "For dynamic query parameters use @Query.", queryParams);
            }
          }

          this.relativeUrl = value;
          this.relativeUrlParamNames = parsePathParameters(value);
        } 
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

通過注解類型獲取到請求類型時,通過調用相關方法解析獲取到請求url,然后通過注解獲取方法中是否有注解字段,有注解信息存儲到Set集合中。然后創建一個OkhttpCall對象,通過調用serviceMethod.adapt方法做網絡請求,serviceMethod.adapt調用是callAdapter中的adapt方法,如果用戶沒有設置callAdapter模式使用的是ExecutorCallAdapterFactory中的adapt方法

 public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
            if (getRawType(returnType) != Call.class) {
                return null;
            } else {
                final Type responseType = Utils.getCallResponseType(returnType);
                return new CallAdapter<Object, Call<?>>() {
                    public Type responseType() {
                        return responseType;
                    }

                    public Call<Object> adapt(Call<Object> call) {
                        return new ExecutorCallAdapterFactory.ExecutorCallbackCall(ExecutorCallAdapterFactory.this.callbackExecutor, call);
                    }
                };
            }
        } 
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在ExectorCallAdapterFactory中調用組裝的Call方法中enqueue方法調用異步網絡請求,成功后通過Platform中MainThreadExecutor切換到主線程。在調用callback中的enqueue,onResponse和onFairlure方法時實際是調用到OkhttpCall方法的onResponse方法,在OkHttpCall.enqueue中重新組建OkHttp.Call url和參數信息,然后封裝請求,請求成功后通過parseResponse解析返回信息狀態,然后把返回信息狀態成ResponseBody對象,調用ServiceMethod.toResponse解析,在toResponse中實際是我們設置ConverterFactory對象解析數據,完成后調用callBack中onSuccess方法。

 @Override public void enqueue(final Callback<T> callback) {
        checkNotNull(callback, "callback == null");

        okhttp3.Call call;
        Throwable failure;

        synchronized (this) {
          if (executed) throw new IllegalStateException("Already executed.");
          executed = true;

          call = rawCall;
          failure = creationFailure;
          if (call == null && failure == null) {
            try {
              call = rawCall = createRawCall();
            } catch (Throwable t) {
              throwIfFatal(t);
              failure = creationFailure = t;
            }
          }
        }

        if (failure != null) {
          callback.onFailure(this, failure);
          return;
        }

        if (canceled) {
          call.cancel();
        }

        call.enqueue(new okhttp3.Callback() {
          @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
            Response<T> response;
            try {
              response = parseResponse(rawResponse);
            } catch (Throwable e) {
              callFailure(e);
              return;
            }

            try {
              callback.onResponse(OkHttpCall.this, response);
            } catch (Throwable t) {
              t.printStackTrace();
            }
          }

          @Override public void onFailure(okhttp3.Call call, IOException e) {
            callFailure(e);
          }

          private void callFailure(Throwable e) {
            try {
              callback.onFailure(OkHttpCall.this, e);
            } catch (Throwable t) {
              t.printStackTrace();
            }
          }
        });
      }
藍藍設計m.skdbbs.com )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。

PYTHON爬蟲——必應圖片關鍵詞爬取

seo達人

如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

圖片三個網站的圖片搜索結果進行爬取和下載。 
首先通過爬蟲過程中遇到的問題,總結如下: 
1、一次頁面加載的圖片數量各個網站是不定的,每翻一頁就會刷新一次,對于數據量大的爬蟲幾乎都需要用到翻頁功能,有如下兩種方式: 
1)通過網站上的網址進行刷新,例如必應圖片:

url = 'http://cn.bing.com/images/async?q={0}&first={1}&count=35&relp=35&lostate=r
&mmasync=1&dgState=x*175_y*848_h*199_c*1_i*106_r*0'
    
  • 1
  • 2

2)通過selenium來實現模擬鼠標操作來進行翻頁,這一點會在Google圖片爬取的時候進行講解。 
2、每個網站應用的圖片加載技術都不一樣,對于靜態加載的網站爬取圖片非常容易,因為每張圖片的url都直接顯示在網頁源碼中,找到每張圖片對應的url即可使用urlretrieve()進行下載。然而對于動態加載的網站就比較復雜,需要具體問題具體分析,例如google圖片每次就會加載35張圖片(只能得到35張圖片的url),當滾動一次后網頁并不刷新但是會再次加載一批圖片,與前面加載完成的都一起顯示在網頁源碼中。對于動態加載的網站我推薦使用selenium庫來爬取。

對于爬取圖片的流程基本如下(對于可以通過網址實現翻頁或者無需翻頁的網站): 
1. 找到你需要爬取圖片的網站。(以必應為例)

這里寫圖片描述
2. 使用google元素檢查(其他的沒用過不做介紹)來查看網頁源碼。

這里寫圖片描述
3. 使用左上角的元素檢查來找到對應圖片的代碼。

這里寫圖片描述
4. 通過觀察找到翻頁的規律(有些網站的動態加載是完全看不出來的,這種方法不推薦)

這里寫圖片描述
從圖中可以看到標簽div,class=’dgControl hover’中的data-nexturl的內容隨著我們滾動頁面翻頁first會一直改變,q=二進制碼即我們關鍵字的二進制表示形式。加上前綴之后由此我們才得到了我們要用的url。 
5. 我們將網頁的源碼放進BeautifulSoup中,代碼如下:

url = 'http://cn.bing.com/images/async?q={0}&first={1}&count=35&relp=35&lostate=r&mmasync=1&dgState=x*175_y*848_h*199_c*1_i*106_r*0' agent = {'User-Agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.165063 Safari/537.36 AppEngine-Google."}
page1 = urllib.request.Request(url.format(InputData, i*35+1), headers=agent)
page = urllib.request.urlopen(page1)
soup = BeautifulSoup(page.read(), 'html.parser')
    
  • 1
  • 2
  • 3
  • 4
  • 5

我們得到的soup是一個class ‘bs4.BeautifulSoup’對象,可以直接對其進行操作,具體內容自行查找。 
首先選取我們需要的url所在的class,如下圖: 
這里寫圖片描述
波浪線是我們需要的url。 
我們由下面的代碼得到我們需要的url:

if not os.path.exists("./" + word):#創建文件夾 os.mkdir('./' + word) for StepOne in soup.select('.mimg'):
    link=StepOne.attrs['src']#將得到的<class 'bs4.element.Tag'>轉化為字典形式并取src對應的value。 count = len(os.listdir('./' + word)) + 1 SaveImage(link,word,count)#調用函數保存得到的圖片。
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

最后調用urlretrieve()函數下載我們得到的圖片url,代碼如下:

 try:
        time.sleep(0.2)
        urllib.request.urlretrieve(link,'./'+InputData+'/'+str(count)+'.jpg') except urllib.error.HTTPError as urllib_err:
        print(urllib_err) except Exception as err:
        time.sleep(1)
        print(err)
        print("產生未知錯誤,放棄保存") else:
        print("圖+1,已有" + str(count) + "張圖")
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

這里需要強調是像前面的打開網址和現在的下載圖片都需要使用try except進行錯誤測試,否則出錯時程序很容易崩潰,大大浪費了數據采集的時間。 
以上就是對單個頁面進行數據采集的流程,緊接著改變url中{1}進行翻頁操作繼續采集下一頁。 
數據采集結果如下: 
這里寫圖片描述

有問題請留言。 

藍藍設計m.skdbbs.com )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。

微信小程序實現倒計時,蘋果手機不顯示

seo達人

如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

JS頁面代碼段:


    
  1. const app = getApp()
  2. let goodsList = [
  3. { actEndTime: '2018-07-21 21:00:34' },
  4. { actEndTime: '2028-07-17 21:00:37' },
  5. { actEndTime: '2018-09-21 05:00:59' },
  6. { actEndTime: '2018-08-19 07:00:48' },
  7. { actEndTime: '2018-08-28 03:00:11' }
  8. ]
  9. Page({
  10. data: {
  11. countDownList: [],
  12. actEndTimeList: []
  13. },
  14. onLoad: function () {
  15. let endTimeList = [];
  16. // 將活動的結束時間參數提成一個單獨的數組,方便操作
  17. goodsList.forEach(o => { endTimeList.push(o.actEndTime) })
  18. this.setData({ actEndTimeList: endTimeList });
  19. // 執行倒計時函數
  20. this.countDown();
  21. },
  22. //當時間小于兩位數時十位數補零。
  23. timeFormat: function (param) {//小于10的格式化函數
  24. return param < 10 ? '0' + param : param;
  25. },
  26. //倒計時函數
  27. countDown: function () {
  28. // 獲取當前時間,同時得到活動結束時間數組
  29. let newTime = new Date().getTime();//當前時間
  30. let endTimeList = this.data.actEndTimeList;//結束時間的數組集合
  31. let countDownArr = [];//初始化倒計時數組
  32. // 對結束時間進行處理渲染到頁面
  33. endTimeList.forEach(o => {
  34. let endTime = new Date(o).getTime();
  35. let obj = null;
  36. // 如果活動未結束,對時間進行處理
  37. if (endTime - newTime > 0) {
  38. let time = (endTime - newTime) / 1000;
  39. // 獲取天、時、分、秒
  40. let day = parseInt(time / (60 * 60 * 24));
  41. let hou = parseInt(time % (60 * 60 * 24) / 3600);
  42. let min = parseInt(time % (60 * 60 * 24) % 3600 / 60);
  43. let sec = parseInt(time % (60 * 60 * 24) % 3600 % 60);
  44. obj = {
  45. day: this.timeFormat(day),
  46. hou: this.timeFormat(hou),
  47. min: this.timeFormat(min),
  48. sec: this.timeFormat(sec)
  49. }
  50. } else {//活動已結束,全部設置為'00'
  51. obj = {
  52. day: '00',
  53. hou: '00',
  54. min: '00',
  55. sec: '00'
  56. }
  57. }
  58. countDownArr.push(obj);
  59. })
  60. //每隔一秒執行一次倒計時函數, 渲染
  61. this.setData({ countDownList: countDownArr })
  62. setTimeout(this.countDown, 1000);
  63. }
  64. })

wxml頁面代碼段


    
  1. <view class='tui-countdown-content' wx:for="{{countDownList}}" wx:key="countDownList">
  2. 距結束
  3. <text class='tui-conutdown-box'>{{item.day}}</text>天
  4. <text class='tui-conutdown-box'>{{item.hou}}</text>時
  5. <text class='tui-conutdown-box'>{{item.min}}</text>分
  6. <text class='tui-conutdown-box tui-countdown-bg'>{{item.sec}}</text>秒
  7. </view>

 

wxss頁面代碼段


    
  1. page{
  2. background: #f5f5f5;
  3. }
  4. .tui-countdown-content{
  5. height: 50px;
  6. line-height: 50px;
  7. text-align: center;
  8. background-color: #fff;
  9. margin-top: 15px;
  10. padding: 0 15px;
  11. font-size: 18px;
  12. }
  13. .tui-conutdown-box{
  14. display: inline-block;
  15. height: 26px;
  16. width: 26px;
  17. line-height: 26px;
  18. text-align: center;
  19. background:#ccc;
  20. color: #000;
  21. margin: 0 5px;
  22. }
  23. .tui-countdown-bg{
  24. background: red;
  25. color: #fff;
  26. }
  27. .container{
  28. width: 100%;
  29. display: flex;
  30. justify-content: center;
  31. }
  32. .backView{
  33. width:690rpx;
  34. background: #fff;
  35. display: flex;
  36. flex-direction: column;
  37. margin-bottom: 30rpx;
  38. }
  39. .createDate
  40. {
  41. background: #f5f5f5;
  42. padding:15rpx 15rpx 10rpx 15rpx;
  43. line-height: 50rpx;
  44. font-size: 28rpx;
  45. color: gainsboro;
  46. text-align: center;
  47. }
  48. .backViewitem1{
  49. display: flex;
  50. flex-direction: row;
  51. height: 55rpx;
  52. align-items: center;
  53. padding:8rpx 40rpx;
  54. border-bottom: 2rpx solid #f5f5f5;
  55. }
  56. .ico
  57. {
  58. width:35rpx;
  59. height:35rpx;
  60. }
  61. .name
  62. {
  63. color: #c13176;
  64. margin-left: 20rpx;
  65. font-size: 28rpx;
  66. }
  67. .details
  68. {
  69. font-size:24rpx;
  70. letter-spacing: 2rpx;
  71. }
  72. .backViewitem2{
  73. display: flex;
  74. flex-direction: row;
  75. line-height: 35rpx;
  76. min-height: 70rpx;
  77. padding: 15rpx 40rpx 10rpx 40rpx;
  78. border-bottom: 2rpx solid #f5f5f5;
  79. }
  80. .details1
  81. {
  82. color:#888;
  83. font-size:23rpx;
  84. letter-spacing: 2rpx;
  85. }

 藍藍設計m.skdbbs.com )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 平面設計服務。

從前端和后端兩個角度分析jsonp跨域訪問(完整實例)

seo達人

如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

一、什么是跨域訪問

舉個栗子:在A網站中,我們希望使用Ajax來獲得B網站中的特定內容。如果A網站與B網站不在同一個域中,那么就出現了跨域訪問問題。你可以理解為兩個域名之間不能跨過域名來發送請求或者請求數據,否則就是不安全的??缬蛟L問違反了同源策略,同源策略的詳細信息可以點擊如下鏈接:Same-origin_policy; 
總而言之,同源策略規定,瀏覽器的ajax只能訪問跟它的HTML頁面同源(相同域名或IP)的資源。

二、什么是JSONP

JSONP(JSON with Padding)是JSON的一種“使用模式”,可用于解決主流瀏覽器的跨域數據訪問的問題。

由于同源策略,一般來說位于 server1.example.com 的網頁無法與不是 server1.example.com的服務器溝通,而 HTML 的<script> 元素是一個例外。利用<script>元素的這個開放策略,網頁可以得到從其他來源動態產生的 JSON 資料,而這種使用模式就是所謂的 JSONP。用 JSONP 抓到的資料并不是 JSON,而是任意的JavaScript,用 JavaScript 直譯器執行而不是用 JSON 解析器解析。更具體的原理需要更多篇幅的講解,小伙伴可以自行去百度。

三、JSONP的使用

前端的使用示例

JQuery Ajax對JSONP進行了很好的封裝,我們使用起來很方便。前端示例:

 $.ajax({
            type:"GET",
            url:"http://www.deardull.com:9090/getMySeat", //訪問的鏈接 dataType:"jsonp", //數據格式設置為jsonp jsonp:"callback", //Jquery生成驗證參數的名稱 success:function(data){ //成功的回調函數 alert(data);
            },
            error: function (e) { alert("error");
            }
        }); 
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

需要注意的地方是:

  • dataType,該參數必須要設置成jsonp
  • jsonp,該參數的值需要與服務器端約定,詳細情況下面介紹。(約定俗成的默認值為callback)

后端的配合示例

JQuery Ajax Jsonp原理

后端要配合使用jsonp,那么首先得了解Jquery Ajax jsonp的一個特點: 
Jquery在發送一個Ajax jsonp請求時,會在訪問鏈接的后面自動加上一個驗證參數,這個參數是Jquery隨機生成的,例如鏈接 
http://www.deardull.com:9090/getMySeat?callback=jQuery31106628680598769732_1512186387045&_=1512186387046 
中,參數callback=jQuery31106628680598769732_1512186387045&_=1512186387046就是jquery自動添加的。 
添加這個參數的目的是唯一標識這次請求。當服務器端接收到該請求時,需要將該參數的值與實際要返回的json值進行構造(如何構造下面講解),并且返回,而前端會驗證這個參數,如果是它之前發出的參數,那么就會接收并解析數據,如果不是這個參數,那么就拒絕接受。 
需要特別注意的是這個驗證參數的名字(我在這個坑上浪費了2小時),這個名字來源于前端的jsonp參數的值。如果把前端jsonp參數的值改為“aaa”,那么相應的參數就應該是 
aaa=jQuery31106628680598769732_1512186387045&_=1512186387046

后端接收與處理

知道了Jquery Ajax Jsonp的原理,也知道了需要接受的參數,我們就可以來編寫服務器端程序了。 
為了配合json,服務器端需要做的事情可以概括為兩步:

第一步、接收驗證參數

根據與前端Ajax約定的jsonp參數名來接收驗證參數,示例如下(使用SpringMVC,其他語言及框架原理類似)

 @ResponseBody @RequestMapping("/getJsonp") public String getMySeatSuccess(@RequestParam("callback") String callback){
    
  • 1
  • 2
  • 3
第二步、構造參數并返回

將接收的的驗證參數callback與實際要返回的json數據按“callback(json)”的方式構造:

 @ResponseBody
    @RequestMapping("/getMySeat") public String getMySeatSuccess(@RequestParam("callback") String callback){
        Gson gson=new Gson(); //google的一個json工具庫 Map<String,String> map=new HashMap<>(); map.put("seat","1_2_06_12"); return callback+"("+gson.toJson(map)+")"; //構造返回值 }
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

四、總結

最終,前后端的相應代碼應該是這樣的: 
前端

 $.ajax({
            type:"GET",
            url:"http://www.deardull.com:9090/getMySeat", //訪問的鏈接 dataType:"jsonp", //數據格式設置為jsonp jsonp:"callback", //Jquery生成驗證參數的名稱 success:function(data){ //成功的回調函數 alert(data);
            },
            error: function (e) { alert("error");
            }
        });
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

后端

 @ResponseBody
    @RequestMapping("/getMySeat") public String getMySeatSuccess(@RequestParam("callback") String callback){
        Gson gson=new Gson(); Map<String,String> map=new HashMap<>(); map.put("seat","1_2_06_12");
        logger.info(callback); return callback+"("+gson.toJson(map)+")";
    }
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

需要注意的是:

  • 前端注意與后端溝通約定jsonp的值,通常默認都是用callback。
  • 后端根據jsonp參數名獲取到參數后要與本來要返回的json數據按“callback(json)”的方式構造。
  • 如果要測試的話記得在跨域環境(兩臺機器)下進行。

完整的示例就是上面兩段代碼,這里就不提供Github連接了。上面的示例親測有效,如果有遇到問題的,歡迎留言提問。

藍藍設計m.skdbbs.com )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。

小程序----頁面兼容h5標簽

seo達人

如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

有時候當小程序向后臺拿數據是一篇html標簽的文章時,把它放進小程序會發現很多標簽就不兼容,如果要一個個改又很麻煩,有沒有方法可以很快地兼容html標簽呢? 
有個工具可以做到:wxParse 
這里寫圖片描述 
下載了它的壓縮包后解壓,復制wxParse文件夾放到小程序pages頁面里: 
這里寫圖片描述 
在wxml里引入,這里的路徑僅供參考:

<import src="../../../../wxParse/wxParse.wxml" /> <view> //在需要放置html文本的地方使用wxParse模板 <template is="wxParse" data="{{wxParseData:content.nodes}}" /> </view>
    
  • 1
  • 2
  • 3
  • 4
  • 5

在js里引入:

let wxparse = require("../../../../wxParse/wxParse.js");
Page({ /**
   * 頁面的初始數據
   */ data: {
      content: '' },
   onLoad: function(options) { var that = this;
     ..... /**
* WxParse.wxParse(bindName , type, data, target,imagePadding)
* 1.bindName綁定的數據名(必填)
* 2.type可以為html或者md(必填)
* 3.data為傳入的具體數據(必填)
* 4.target為Page對象,一般為this(必填)
* 5.imagePadding為當圖片自適應是左右的單一padding(默認為0,可選)
*/ wxparse.wxParse('content', 'html', result.data.content, that);
   }
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在wxss引入:

@import "../../../../wxParse/wxParse.wxss";
藍藍設計m.skdbbs.com )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務

JS:獲取驅動器的大小和可用空間

seo達人

如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

親測必須將代碼放在記事本中,改成html格式,在用IE運行(必須)



<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<title>顯示指定驅動器的大小及可用空間</title>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

</head>

<body>

<form name="form1" method="post" action="">

盤符:

<input type="text" name="text1">

&nbsp;&nbsp;&nbsp;

<input type="button" name="Button1" value="磁盤空間" onclick="DriveSize(document.form1.text1)">

</form>

<script language="javascript">

<!--

function DriveSize(Drivename)

{

var fso=new ActiveXObject("Scripting.FileSystemObject");

var s=fso.GetDrive(Drivename.value);

if (s.IsReady)

{

var str,str1,AllSize=0.0;

str="當前驅動器的名稱為:"+s.DriveLetter+"\n";

AllSize=s.TotalSize/1024/1024/1024;

str=str+"當前驅動器的大小為:"+parseInt(AllSize*10)/10+"\n";

AllSize=s.FreeSpace/1024/1024/1024;

str=str+"當前驅動器的可用空間為:"+parseInt(AllSize*10)/10;

alert(str);

}

else

alert("該驅動器無效。")

}

//-->

</script>

</body>

</html>

運行:


結果:單位為g

藍藍設計m.skdbbs.com )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務


Bootstrap Table實現定時刷新數據

seo達人

如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

Bootstrap Table實現定時刷新數據

推薦第二種方法

  • 令表格的id為realTimeTable

1、毀掉表格,再查詢數據后append,如果你查大量的數據(例如:查詢很多渠道的信息),而獲取服務器數據又太慢,你就會看到表格在一行一行的增加

  • 定時器,多長時間執行一次,自己定義,此處是30S
setInterval(function() { queryAll();
}, 30000);
    
  • 1
  • 2
  • 3
  • 先定義一個函數,里面放入查詢的方法updateRealTimeData和你所自定義使用的方法
function queryAll() { updateRealTimeData();
        .
        .
        .
        .
}
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 方法updateRealTimeData
 function updateRealTimeData() { if(errorFlag || appid == -1) return; //把表格的tbody移除,不然后面會一直添加 $("#realTimeTable").bootstrapTable('removeAll'); //獲取數據 $.ajax({
            data: { //向服務器發送的一些參數,像日期,游戲ID什么的 .
                        .
                        .
                        .
                        .
                },
                    type: "post", //url不用說了吧,否則不知道向服務器哪個接口發送并請求 url: *******,
                    async: true, //超時時間 timeout:30000,
                    success: function(msg) { if(msg.code == '1') { //定義的函數實現對表格賦值,自定義想傳的參數,但別忘了msg,不然搞個屁 showTableData(msg,……);
                        }

                    }
                });
            }
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 方法showTableData
function showTableData(msg,……) { tableData = []; for(var i = 0; i < json.length; i++) {
                tableData.push({ //這里也就是data-field的名稱,getDate是服務器返回的字段名 date: json[i].getDate,
                       .
                       .
                       .
                       .
                }) //數組反向排列,看情況使用 tableData.reverse(); //向tbody里面添加數據 $("#realTimeTable").bootstrapTable('append', tableData);
            }
}
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2、使用updateRow方法

  • 首先,得存在表格,里面有數據,才能更新行,否則沒作用。此方法不會像上面的方法表格消失再增加,這個是整體不變,里面的數據會自動更新

  • 定時器,和上面一樣,多長時間執行一次,自己定義,此處是30S
setInterval(function() { queryAll(); for (var j = 0; j < 請求的數據的總條數(也就等于表格的行數); j++) {
                changeAllChannelRealTime(j, .....);
            } }, 30000);
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
function changeAllChannelRealTime(j, .....) {
        $.ajax({
            data: { //向服務器發送的一些參數,像日期,游戲ID什么的 .
                        .
                        .
                        .
                        .
                },
                    type: "post", //url不用說了吧,否則不知道向服務器哪個接口發送并請求 url: *******,
                    async: true, //超時時間 timeout:30000,
                    success: function(msg) { if (msg.code == '1') {
                            changeData(j, msg, .....);
                    }
                },
                error: function () { msgToast.error("查詢數據出錯");
                }
            });
        }
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
function changeData(i,msg,......){ $('#realTime_Table').bootstrapTable('updateRow', { //i表示第幾行,從0開始 index: i,
                row: { //這里也就是data-field的名稱,*表示字段名 date: msg.*
                        .
                        .
                        .
                        .
                }
            });         
}
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

大象~ 大象~ 你的鼻子怎么那么長~~ 



其他相關:

藍藍設計m.skdbbs.com )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。

日歷

鏈接

個人資料

藍藍設計的小編 http://m.skdbbs.com

存檔

92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线
99亚洲男女激情在线观看| 免费久久精品视频| 国产三区精品| 午夜精品国产精品大乳美女| 99视频热这里只有精品免费| 另类国产ts人妖高潮视频| 成人av午夜影院| 一区二区三区国产豹纹内裤在线| 国产精品国产三级国产三级人妇| 91精品欧美一区二区三区综合在| 亚洲欧美视频一区| 国产精品一区二区三区四区在线观看| 67194成人在线观看| 亚洲精品1区| 中文字幕亚洲激情| 国产一区二区三区黄网站| 一区av在线播放| 秋霞成人午夜伦在线观看| 久久久不卡网国产精品二区| 精品国产不卡一区二区三区| 日本大片在线播放| 久久久久97| 色婷婷亚洲综合| 大片网站久久| 成人免费视频免费观看| 国产劲爆久久| 欧美亚洲二区| 亚洲午夜影视影院在线观看| 国产一区二区三区四区hd| 69**夜色精品国产69乱| 国产精品私人影院| 国产精品中文字幕日韩精品| 欧美性视频在线| 国产精品免费不| 国产做a爰片久久毛片| 欧美电影网站| 人妖欧美1区| 国产无一区二区| 97久久超碰| 石原莉奈一区二区三区在线观看| 亚洲黄色录像片| 欧美日韩亚洲一区二区三区在线观看| 日韩一区二区三区色| 欧美美女黄视频| 美女扒开腿让男人桶爽久久软| 亚洲精品不卡| 91免费观看视频在线| 999热视频| 国产女优一区| 精品久久久久久久久久久久久| 日本91福利区| 一区二区三区短视频| 久久国产成人| 亚洲天堂免费看| 欧美不卡1区2区3区| av激情成人网| 亚洲美女黄网| 亚洲三级理论片| 一本一道久久综合狠狠老| 在线不卡免费av| 欧洲av在线精品| 欧美大片在线免费观看| 91网址在线看| 欧美日韩一区二区视频在线观看| 亚洲人成网站999久久久综合| 不卡在线视频中文字幕| 亚洲成av人片在线观看香蕉| 日韩国产高清影视| 国产精品高潮在线| 久久久女女女女999久久| 亚洲欧洲av另类| 国产精品久久午夜| 欧美高清一级片在线观看| 国产亚洲一级高清| 亚洲精品久久久久久久久久久| 美国成人xxx| 成人精品一区二区三区四区| 国产精品美女无圣光视频| 一区二区三区日韩精品视频| 国产亚洲欧洲一区高清在线观看| 日韩av一区二区三区在线观看| 欧美成人app| 黄色资源网久久资源365| 国产精品视频一区国模私拍| 国产在线观看一区二区三区| 91丨porny丨蝌蚪视频| 亚洲小说春色综合另类电影| 亚洲欧美日本国产有色| 免费黄色电影在线观看| 亚洲视频狠狠| 国产一区二中文字幕在线看| 成人精品视频99在线观看免费| 美女在线视频一区| 国产精品亚洲片夜色在线| 成人黄色av网址| 粉嫩av国产一区二区三区| 5278欧美一区二区三区| 亚洲人www| 午夜精品久久一牛影视| 欧美日韩精品系列| 精品视频在线观看免费观看| xxxxx91麻豆| 欧美疯狂性受xxxxx另类| 亚洲一区二区三区四区五区黄| 欧美日韩视频在线一区二区| 欧美人与动xxxxz0oz| 久久久国产精品不卡| 亚洲高清久久网| 国产精品jk白丝蜜臀av小说| 97久久香蕉国产线看观看| 久久影院一区二区三区| 国产va免费精品观看精品| 欧美激情综合五月色丁香| 久久成人国产精品| 亚洲精品高清在线观看| 超碰在线97国产| 欧美色另类天堂2015| 亚洲欧美小说色综合小说一区| 96成人在线视频| 亚洲亚洲免费| 欧美gay男男猛男无套| 欧美国产日韩视频| 亚洲第一在线| 国产一区二区在线观| 精品88久久久久88久久久| 日韩欧美国产一区在线观看| 一区二区中文字幕| 一本色道久久综合亚洲精品高清| 91精品国产综合久久福利| 懂色av一区二区三区免费看| 亚洲白拍色综合图区| 亚洲日韩第一页| xvideos亚洲人网站| 亚洲成a人在线观看| 国产精品一区二区三区观看| 久久av在线看| 午夜视黄欧洲亚洲| 影音先锋欧美激情| 日韩中文字幕在线免费观看| 国产一区二区福利| 国产探花在线精品一区二区| 色综合久久天天综线观看| 91制片在线观看| 男女男精品视频| 国产三区二区一区久久| 久久精品夜色噜噜亚洲aⅴ| 不用播放器成人网| 午夜精品久久久久久久99水蜜桃| 日韩免费高清在线观看| 欧美日韩国产综合一区二区三区| 欧美久久一区二区三区| 国产精品久久久久久久久动漫| 亚洲视频日本| 成人精品久久一区二区三区| 亚洲国产精品一区二区第四页av| 国产乱码精品1区2区3区| 亚洲欧美日韩在线一区| 一本一本久久a久久综合精品| 色婷婷精品久久二区二区蜜臀av| 国产在线精品一区二区三区| 污视频在线免费观看网站| 成人av资源在线| 欧美黄色片免费观看| 99国产精品久久久久|