2016年10月30日 星期日

WebService 基礎 ( WebService 一)

.WSDL:web service definition language
定義了web service的server端和client端請求和回應的格式,主要有5個,分別是types、message、portType、binding、service
一個web service對應唯一的wsdl文件

.SOAP:simple object access protocal
簡單的、基于HTTP和XML的、用在WEB上交換資料的
soap訊息:請求/回應訊息
http+xml片斷

.SEI:WebService EndPoint Interface
WebService server端用來處理請求的介面,java就是寫一個介面,上面加上@WebService

.CXF:Celtix + XFire
XFire原本就是webservice框架,可能有一些問題,修改bug後,加上Celtix變成新的框架,是apache開發的,用來開發webservice的server端和client端



※SEI和實作

package hello.test;
import javax.jws.WebService;
    
@WebService
public interface IHelloWS {
    public void hello();
}
--------------------
package hello.test;
import javax.jws.WebService;
    
@WebService(endpointInterface = "hello.test.IHelloWS")
public class HelloWS implements IHelloWS {
    @Override
    public void hello() {
        System.out.println("Hello WebService!");
    }
    
    public String add(Integer a, int b) {
        System.out.println("call add method!");
        return "Answer is " + (a + b);
    }
}




※啟動伺服器

package hello.test;
import javax.xml.ws.Endpoint;
    
public class MyServer {
    public static void main(String[] args) {
        String url = "http://localhost:8888/aaa/bbb/ccc";
        Endpoint.publish(url, new HelloWS());
        System.out.println("server is start!");
    }
}

※如有修改(SEI、SEI實作或這一隻測試類),必須重啟才會生效

※複製url到瀏覽器,最後加上「?wsdl」會有如下的xml格式,也就是wsdl

<definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://test.hello/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://test.hello/" name="HelloWSService">
    <types>
        <xsd:schema>
            <xsd:import namespace="http://test.hello/" schemaLocation="http://localhost:8888/aaa/bbb/ccc?xsd=1"/>
        </xsd:schema>
    </types>
    
    <message name="hello">
        <part name="parameters" element="tns:hello"/>
    </message>
    
    <message name="helloResponse">
        <part name="parameters" element="tns:helloResponse"/>
    </message>
    
    <portType name="IHelloWS">
        <operation name="hello">
            <input wsam:Action="http://test.hello/IHelloWS/helloRequest" message="tns:hello"/>
            <output wsam:Action="http://test.hello/IHelloWS/helloResponse" message="tns:helloResponse"/>
        </operation>
    </portType>
    
    <binding name="HelloWSPortBinding" type="tns:IHelloWS">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
        <operation name="hello">
        
            <soap:operation soapAction=""/>
            
            <input>
                <soap:body use="literal"/>
            </input>
            
            <output>
                <soap:body use="literal"/>
            </output>
        </operation>
    </binding>
    
    <service name="HelloWSService">
        <port name="HelloWSPort" binding="tns:HelloWSPortBinding">
            <soap:address location="http://localhost:8888/aaa/bbb/ccc"/>
        </port>
    </service>
</definitions>

※types最裡層還有一個import的網址,再開一個瀏覽器分頁,會發現它長如下的樣子

<xs:schema xmlns:tns="http://test.hello/" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0" targetNamespace="http://test.hello/">
    <xs:element name="hello" type="tns:hello"/>
    
    <xs:element name="helloResponse" type="tns:helloResponse"/>
    
    <xs:complexType name="hello">
        <xs:sequence/>
    </xs:complexType>
    
    <xs:complexType name="helloResponse">
        <xs:sequence/>
    </xs:complexType>
</xs:schema>

※格式內容下一篇再說,先說明SEI的部分

※@WebService有6個屬性,都是接收String,分別是
name、targetNamespace、serviceName、portName、wsdlLocation、endpointInterface

其中EI(也就是interface)只有3個合法值:name、targetNamespace、wsdlLocation,API有特別註明「This member-value is not allowed on endpoint interfaces.」,
其中name和targetNamespace屬性,如果EI和實作類的設定不同,會以EI為主
wsdlLocation只能設在SEI裡,如果實作類有設,會在執行時出「[failed to localize] cannot.load.wsdl(path/xxx.wsdl)」的錯


.endpointInterface
上面有用到,沒加也是可以run,但客戶端請求時有所不同,下面會說明
有加只會將有實作的部分暴露出來
沒加會將自己的方法暴露出來

一個方法會對應
兩個message
兩個schema(在import裡)
一個portType-->operation
一個binding-->operation

.targetNamespace:,指定targetNamespace,不加為「http://從右到左的包名/」,此例為「http://test.hello/」

.serviceName:指定serviceName,不加為「實作類別名+Service」,此例為「HelloWSService」

.name:指定portType的name屬性,不加為「介面名」,此例為「IHelloWS」
還會影響portType-->operation-->input/output的wsam:Action屬性,這個屬性是「targetNamespace名稱/name名稱/方法名稱+Request/Response」,input為Request;output為Response
,此例input為「http://test.hello/HelloWS/addRequest」;output為「http://test.hello/HelloWS/addResponse」

.portName:指定service-->port的name屬性,不加為「實作類別名+Port」,此例為「HelloWSPort」;
還會影響binding的name屬性,為「port的name屬性+Binding」,此例為「HelloWSPortBinding」

.wsdlLocation:指定wsdl的路徑

※還有@WebResult、@WebParam、@WebMethod,這三個annotation也很重要
@WebResult寫在方法上,name屬性可以設定xml的回傳值的名稱,不打為「return」
@WebParam寫在參數裡,name屬性可以設定xml的參數名稱,不打為「arg+數字(從0開始)」
@WebMethod 寫在方法上,有三個值operationName、action、exclude
operationName屬性可以設定message的name屬性和portType-->operation的name屬性,不打為方法名
exclude不能加在EI,預設為false,若為true,此方法不會暴露出來

※在definitions有很多屬性,其中targetNamespace和name下面程式的QName會用到,分別對應到第一和第二個參數



※Eclipse 的 Web Services Explorer

其實Eclipse內鍵也有一個瀏覽的工具,也類似於在網址上打XXX~?wsdl,裡面可以對wsdl做request、response,如下畫面

1.首先先切換到Java EE視圖
2.按Launch the Web Services Explorer,或者Run-->Launch the Web Services Explorer也是一樣
3.右上角有個WSDL Page
4.將剛剛網址的wsdl貼上後按Go,每按一次Go,左邊的Navigator都會出現一個你打的網址,就算你打的網址一樣也會出現
想刪除,只要在左框(Navigator)的右上角Clear即可


※JavaClientCallWebService.java

package xxx.ooo;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
    
public class JavaClientCallWebService {
    public static void main(String[] args) {
        Url url = null;
        try {
            url = new URL("http://localhost:8888/aaa/bbb/ccc?wsdl");
            QName qname = new QName("http://test.hello/", "HelloWSService");
            Service service = Service.create(url, qname);
            IHelloWS call = service.getPort(IHelloWS.class);
            call.hello();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
}

※上面是用Eclipse內鍵的工具呼叫WebService,這裡是用寫程式的方式呼叫

※將wsdl裡面的definitions的targetNamespace和name放在QName的建構子裡即可

※如果實作類的@WebService沒有endpointInterface屬性,會報「javax.xml.ws.WebServiceException: 未定義的連接埠類型: {http://test.hello/}IHelloWS」或者「javax.xml.ws.WebServiceException: {http://test.hello/}HelloWSService 不是有效的連接埠. 有效的連接埠為: {http://test.hello/}HelloWSPort」,應該是一定要有interface了

※Eclipse會多一個Console,一個是伺服器端的;一個是用戶端的
但hello()是伺服器端的程式碼,所以「Hello WebService!」會印在伺服器端


※wsimport

在安裝java的路徑裡,例如C:\Program Files\Java\jdkXXX\bin,有一個wsimport工具(jre沒有)
它可以將我們剛剛寫的程式顯示出完整的程式碼,因為有很多預設值,所以上面的程式碼才會這麼少

此時在命令提示字元下使用這個命令:
wsimport -d d:\ws_practice -keep -verbose wsdl網址
-d後面接路徑,表示會將生成出來的.class檔放在這個路徑裡,不指定會在目前的目錄生成
-keep表示還會保留.java檔
-verbose表示生成的過程有畫面可以看



此時切換好路徑後,輸入 wsimport -keep http://localhost:8888/aaa/bbb/ccc?wsdl
新增一個project,package和之前一樣,然後將生成的程式碼複製進去,.java部分即可
後面的網址也可以是wsdl格式的檔案,例如將這段網址的結果複製到一個文字文件,然後下
wsimport -keep -d d:\xxx d:\wsdl_file.wsdl,也是可以,副檔不一定要wsdl,只要是純文字即可

※test.java

HelloWSService hws = new HelloWSService();
IHelloWS hw = hws.getHelloWSPort();
System.out.println(hw.getClass());
hw.hello();

※這支是使用wsimport幫我們生成的程式碼,測試用戶端的程式

※首先先用wsdl最下面的service的name屬性名稱,然後一定有個方法可以返回介面
service下有個port標籤,他的name屬性就是getXXX
所以以這個例子就是getHelloWSPort

※可以發現 hw.getClass()是用代理的



※TCP/IP Monitor

Eclipse有內鍵一個TCP/IP監控工具
首先將網址的wsdl存成一個檔案,然後將最下面的代碼改成自己監聽的port
<service name="HelloWSService">
    <port name="HelloWSPort" binding="tns:HelloWSPortBinding">
        <soap:address location="http://localhost:9999/aaa/bbb/ccc"/>
    </port>
</service>

※我是將8888改成9999

最後在Window-->Preferences,然後如下畫面設定:

※綠框是新改的port,紅框的部分只要打IP和port即可



設定後還要啟動才可以
如果要修改,必需先按左邊的設定,然後右邊的Stop,再按Edit

※程式執行和wsimport的test.java一樣,執行後會出現一個叫TCP/IP Monitor的View
如果沒出現,就要選Window-->Show View-->Other...後,會出現如下的畫面:


TCP/IP Monitor畫面如下:

左上的紅框是只要有一次請求就會出現一次,不管錯誤還是一樣的請求都會出現
左邊為Request;右邊為Response
選了XML就會出現最下面的SOAP格式的標籤,這個標籤在上面介紹的Web Services Exporer也有,如果選Web Browser會比較好看



※SOAP UI 工具

此工具的功能和Eclipse 內鍵的 Web Services Explorer一樣,可以分析wsdl的結構,但工作中還蠻流行的,在此介紹一下,這裡是官網,可到此下載

P.S 上面介紹的wsimport將wsdl轉成java代碼,可以在網路上搜尋「免費 webservice」,可以找到xxx.wsdl的網址,但不一定能成功轉成java代碼,因為有可能是其他語言產生出來的
但用Eclipse 內鍵的 Web Services Explorer 和 SOAP UI 都可以執行

※為了要測試有參數的情形,所以介面增加一個方法並實作
public String add(Integer a, int b);
--------------------
@Override
public String add(Integer a, int b) {
    System.out.println("call add method!");
    return "Answer is " + (a + b);
}

※安裝好後如下設定:

有以下四種方法可以開啟打xxx.wsdl的地方
1.按紅框的SOAP,
2.Project按右鍵--> New SOAP Project
3.File--> New SOAP Project
4.Ctrl+N




在紅框打上xxx.wsdl後,Project Name會自動代出值來,預設是帶最後的「/」之後的字串,可以改




最左邊可看到兩個方法,add是我剛才加的,按下去會出現右邊的圖
argX是打參數的地方,打完後按下綠色的三角形就會執行
最右邊就會出現結果,如果有在控制台打印字串,也會出現在 server 端的 console

2016年10月25日 星期二

日期加減、星期

活用setXXX和getXXX即可,連潤年也都可以解決,以下還加上民國年的轉換
但檢核沒什麼判斷

※日期加減(使用API)

window.onload = function(){
    mingoDateStringAddSubstract('105/10/30', '/', 'm', -2);
    mingoDateStringAddSubstract('1051030', '', 'm', -2);
}
    
/*
    mingoDateString:民國年月日字串
    split:要分割的符號,可以為空
    ymd:要針對年、月、日作用
    number:加減年、月、日數
*/
function mingoDateStringAddSubstract(mingoDateString, split, ymd, number){
    if(mingoDateString.length != 7 && mingoDateString.length != 9){
        return 'yyymmdd or yyy?mm?dd';
    }
    
    var year, month, date;
    split = split.trim();
    if(split != ''){
        var dateArray = mingoDateString.split(split);
        year = dateArray[0];
        month = dateArray[1];
        date = dateArray[2];
    } else {
        year = mingoDateString.substring(0, 3);
        month = mingoDateString.substring(3, 5);
        date = mingoDateString.substring(5);
    }
    
    // 月-1是因為月是從0開始
    var d = new Date(Number(year) + 1911, Number(month) - 1, date);
    console.log('before=' + d.toDateString());
    if('y' == ymd.toLowerCase()){
        d.setYear(d.getFullYear() + number);
    } else if('m' == ymd.toLowerCase()){
        d.setMonth(d.getMonth() + number);
    } else if('d' == ymd.toLowerCase()){
        d.setDate(d.getDate() + number);
    }
    console.log('after=' + d.toDateString());
    
    // 轉民國年,月+1是因為月是從0開始
    var rtnYear = Number(d.getFullYear()) - 1911;
    var rtnMonth = Number(d.getMonth()) + 1;
    var rtnDate = d.getDate();
    
    if(year.length == 2){
        year = '0' + year;
    } else if(year.length == 1){
        year = '00' + year;
    }
    rtnMonth = rtnMonth < 10 ? '0' + rtnMonth : rtnMonth;
    rtnDate = rtnDate < 10 ? '0' + rtnDate : rtnDate;
    
    console.log(rtnYear + split + rtnMonth + split + rtnDate);
    console.log('==========');
}



※日期加減(不用API)

window.onload = function(){
    console.log(mingoDateCalc('105/10/30', '/', 'm', -2));
    console.log(mingoDateCalc('1051030', '', 'm', -2));
}
    
// 是否閏年
function isLeapYear(year){
    /* 網路上找到的閏年判斷邏輯  
    4的倍數-->非100的倍數-->閏
           -->  100的倍數-->非400的倍數-->非閏
                         -->  400的倍數-->閏
    y/4 == 0 && y/100 != 0 
    y/4 == 0 && y/100 == 0 && y/400 == 0 
    
    */
    
    /*
    if(
        (year % 4 == 0 && year % 100 != 0) || 
        (year % 4 == 0 && year % 100 == 0 && year % 400 == 0)
    ){
        return true;
    } else {
        return false;
    }
    */
    
    // 簡化後
    if(year % 4 == 0){
        if(year % 100 != 0){
            return true;
        } else {
            return year % 400 == 0 ? true : false;
        }
    } else {
        return false;
    }
}
    
// 回傳當月天數
function judgeDateOfMonth(year, month){
    switch(month) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            return 31
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            return 30
            break;
        default:
            if(isLeapYear(Number(year) + 1911)){
                return 29;
            } else {
                return 28;
            }
    }
}
    
function mingoDateCalc(mingoDateString, split, ymd, number){
    if(mingoDateString.length != 7 && mingoDateString.length != 9){
        return 'yyymmdd or yyy?mm?dd';
    }
    
    var year, month, date;
    split = split.trim();
    if(split != ''){
        var dateArray = mingoDateString.split(split);
        year = Number(dateArray[0]);
        month = Number(dateArray[1]);
        date = Number(dateArray[2]);
    } else {
        year = Number(mingoDateString.substring(0, 3));
        month = Number(mingoDateString.substring(3, 5));
        date = Number(mingoDateString.substring(5));
    }
    
    if(ymd == 'y'){
        year = year + number;
        if(month == 2 && date == 29){
            // 非潤年,加1或減1看需求而定(API是加1)
            if(number > 0 && !isLeapYear(Number(year) + 1911)){
                // 減1
                //date = date - 1;
    
                // 加1
                month = 3;
                date = 1;
            }
        }
    } else if(ymd == 'm'){
        if(number > 0) {
            year = year + Math.floor(number / 12);
            month = month + (number % 12);
            if(month > 12){
                year = year + 1;
                month = month - 12;
            }
        } else {
            number = Math.abs(number);
            year = year - Math.floor(number / 12);
            month = month - (number % 12);
            if(month <= 0){
                year = year - 1;
                month = month + 12;
            }
        }
    } else if(ymd == 'd'){
        if(number > 0) {
            date = date + number;
            var ymdArray = calcPlusDate(year, month, date);
            year = ymdArray[0];
            month = ymdArray[1];
            date = ymdArray[2];
        } else {
            date = date + number;
    
            // 一開始要判斷上個月的天數,所以先扣一個月
            month = month - 1;
            if(month == 0){
                year = year - 1;
                month = 12;
            }
            var ymdArray = calcMinusDate(year, month, date);
            year = ymdArray[0];
            month = ymdArray[1];
            date = ymdArray[2];
        }
    }
    
    if(year.length == 2){
        year = '0' + year;
    } else if(year.length == 1){
        year = '00' + year;
    }
    month = month < 10 ? '0' + month : month;
    date = date < 10 ? '0' + date : date;
    return year + split + month + split + date;
}
    
// 計算天數為正
function calcPlusDate(y, m, d){
    var day = judgeDateOfMonth(y, m);
    if(d > day){
        d = d - day;
        m = m + 1;
        if(m == 13){
            y = y + 1;
            m = 1;
        }
        return calcPlusDate(y, m, d);
    }
    return new Array(y, m, d);
}
    
// 計算天數為負
function calcMinusDate(y, m, d){
    var day = judgeDateOfMonth(y, m);
    if(d < 0){
        d = d + day;
        m = m - 1;
        if(m == 0){
            y = y - 1;
            m = 12;
        }
        return calcMinusDate(y, m, d);
    }
    
    // 一開始先扣了一個月,這裡要加回來
    m = m + 1;
    if(m == 13){
        y = y + 1;
        m = 1;
    }
    return new Array(y, m, d);
}

※雖然沒有測很仔細,但應該和API一樣

※檢核可用正則表達式加強一下



※星期

※API有getDay()可取得星期,自己寫的話就要設定一個寫死的日期是星期幾,然後根據這一個來判斷,如下:

window.onload = function(){
    var y = 2011; var m = 11; var d = 30;
    
    // 兩個日期相差的天數,使用new Date API
    var d1 = new Date(2012, 1 - 1, 1);//月減1是因為API是從0開始
    var d2 = new Date(y, m - 1, d);
    var oneDay = 60 * 60 * 24;
    console.log('用API算相差的天數=' + Math.abs((d1-d2) / (oneDay * 1000)));
    
    apiWeek(y, m, d);
    noApiWeek(y, m, d);
}
    
function apiWeek(year, month, date) {
    // 使用API
    // 月-1是因為月是從0開始
    var d = new Date(year, month - 1, date);
    console.log('用API算出的日期=' + d.getDay());// 0為星期日
}
    
function noApiWeek(year, month, date) {
    // 不使用API
    // 2012/1/1 星期日
    // 2010 12 13 星期一
    var baseYear = 2012;
    var baseMonth = 1;
    var totalDay = 0;
    
    //月日一定>=1,所以不用判斷
    if(year >= baseYear){
        totalDay = date;
        while(true){
            if(baseMonth > 12){
                baseYear = baseYear + 1;
                baseMonth = baseMonth - 12;
            }
    
            if(baseYear == year && baseMonth == month){
                break;
            }
    
            totalDay = totalDay + judgeDateOfMonth(baseYear, baseMonth);
            baseMonth = baseMonth + 1;
        }
        totalDay = Math.abs(totalDay - 1);
        console.log('不用API算相差的天數=' + totalDay);
        getPositiveWeek(totalDay % 7);
    } else {
        // 判斷上個月的日期
        baseMonth = 12;
        baseYear = 2011;
    
        var lastMonthDay = judgeDateOfMonth(baseYear, baseMonth);
        totalDay = Math.abs(lastMonthDay - date + 1); 
    
        while(true){
            if(baseYear == year && baseMonth == month){
                break;
            }
    
            baseMonth = baseMonth - 1;
            if(baseMonth == 0){
                baseYear = baseYear - 1;
                baseMonth = 12;
            }
            totalDay = totalDay + judgeDateOfMonth(baseYear, baseMonth);
        }
        console.log('不用API算相差的天數=' + totalDay);
        getNegativeWeek(totalDay % 7);
    }
}
    
// 是否閏年
function isLeapYear(year){
    if(year % 4 == 0){
        if(year % 100 != 0){
            return true;
        } else {
            return year % 400 == 0 ? true : false;
        }
    } else {
        return false;
    }
}
    
function getPositiveWeek(weekNumber){
    var display = '不用API算出的日期=星期';
    switch(weekNumber) {
        case 0:
            console.log(display + '日');
            break;
        case 1:
            console.log(display + '一');
            break;
        case 2:
            console.log(display + '二');
            break;
        case 3:
            console.log(display + '三');
            break;
        case 4:
            console.log(display + '四');
            break;
        case 5:
            console.log(display + '五');
            break;
        case 6:
            console.log(display + '六');
            break;
    }
}
    
function getNegativeWeek(weekNumber){
    var display = '不用API算出的日期=星期';
    switch(weekNumber) {
        case 0:
            console.log(display + '日');
            break;
        case 1:
            console.log(display + '六');
            break;
        case 2:
            console.log(display + '五');
            break;
        case 3:
            console.log(display + '四');
            break;
        case 4:
            console.log(display + '三');
            break;
        case 5:
            console.log(display + '二');
            break;
        case 6:
            console.log(display + '一');
            break;
    }
}
    
// 回傳當月天數
function judgeDateOfMonth(year, month){
    switch(month) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            return 31
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            return 30
            break;
        default:
            if(isLeapYear(year)){
                return 29;
            } else {
                return 28;
            }
    }
}

※已2012/1/1為基準,我故意選星期天為0的,因為這樣比這個日期大的日期,結果會和API一樣

※回傳當月天數和日期加減的程式碼稍有不同,因為不用民國年了,所以將+1911拿掉了

XmlSerializer、XmlPullParser (Android)

XmlSerializer:寫XML
XmlPullParser:讀XML

※Chess.java

public class Chess {
    private String chessName;
    private Integer price;
    
    // setter/getter...
}

※讀寫XML會用到的java bean


※XmlSerializer

public void btn(View view){
    List<Chess> chs = new ArrayList<>();
    String[] chessNames = {"象棋", "西洋棋", "五子棋"};
    Integer[] prices = {55, 60, 65};
    for (int i = 0; i < chessNames.length; i++) {
        Chess ch = new Chess();
        ch.setChessName(chessNames[i]);
        ch.setPrice(prices[i]);
        chs.add(ch);
    }
    
    System.out.println("寫檔");
    XmlSerializer xml = Xml.newSerializer();
    File f = new File(getApplicationContext().getFilesDir() + File.separator, "serializer.xml");
    try (OutputStream out = new FileOutputStream(f)){
        xml.setOutput(out, "UTF-8");
    
        xml.startDocument("UTF-8", true);
        xml.startTag(null, "map"); // null表示沒有namespace
        for (Chess chess : chs) {
            // chess node
            xml.startTag(null, "chess");
            xml.text(chess.getChessName());
            xml.endTag(null, "chess");
    
            // price node
            xml.startTag(null, "price");
            xml.text(chess.getPrice().toString());
            xml.endTag(null, "price");
        }
        xml.endTag(null, "map");
        xml.endDocument();
    } catch (FileNotFoundException e) {
            e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

※try有圓括號(java7)要在build.gradle(Module:app)這支檔案裡的
android->;defaultConfig->;minSdkVersion-->要在19(含)以上



※XmlPullParser

public void btn(View view){
    XmlPullParser xml = Xml.newPullParser();
    File f = new File(getApplicationContext().getFilesDir() + File.separator, "serializer.xml");
    try (InputStream in = new FileInputStream(f)){
        xml.setInput(in, "UTF-8");
    
        int type = xml.getEventType();
        while (type != XmlPullParser.END_DOCUMENT) {
            if(type == XmlPullParser.START_DOCUMENT) {
                System.out.println("Start document");
            } else if(type == XmlPullParser.START_TAG) {
                System.out.println("Start tag " + xml.getName());
            } else if(type == XmlPullParser.END_TAG) {
                System.out.println("End tag " + xml.getName());
            } else if(type == XmlPullParser.TEXT) {
                System.out.println("Text " + xml.getText());
            }
            type = xml.next();
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (XmlPullParserException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}



2016年10月24日 星期一

Context 的 openFileOutput/openFileInput、SharedPreferences (Android)

※Context的openFileOutput/openFileInput

從data/data/com.example~~~/files資料夾裡,讀或寫
其中com.example~~~是getApplicationContext().getPackageName()


※strings.xml

<resources>
    <string name="login_practice">登入練習</string>
    <string name="enter_account">請輸入帳號</string>
    <string name="enter_password">請輸入密碼</string>
    <string name="remember_account_and_password">記住帳號密碼</string>
    <string name="login">登錄</string>
    <string name="account_and_password_require">帳號密碼必須輸入</string>
</resources>

※需要用到的字串,集中在這個檔案管理


※activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.bruce_java.myapplication.MainActivity">
    
    <EditText
        android:id="@+id/account"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/enter_account"
        android:layout_marginTop="80dp"/>
    
    <EditText
        android:id="@+id/password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textPassword"
        android:hint="@string/enter_password"
        android:layout_marginBottom="10dp"/>
    
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <CheckBox
            android:id="@+id/cb"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/remember_account_and_password"
            android:layout_alignParentLeft="true"/>
    
        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/login"
            android:layout_alignParentRight="true"
            android:onClick="login"/>
    </RelativeLayout>
</LinearLayout>

※模擬一個登入頁面


※AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.bruce_java.myapplication20161022">
    
    <!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>-->
    <!--<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>-->
    
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/login_practice"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivityButton">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

※注意android:label本來是@string/app_name,我改了,要對應到strings.xml

※如果需要加權限,就打開註解的部分,當然還有很多


※MainActivity.java

public class MainActivity extends AppCompatActivity {
    EditText accText = null;
    EditText passText = null;
    CheckBox cb = null;
    final String fileName = "login.txt";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_button);
    
        accText = (EditText) findViewById(R.id.account);
        passText = (EditText) findViewById(R.id.password);
        cb = (CheckBox) findViewById(R.id.cb);
    
        Map<String, String> map = this.readAccountAndPassword();
        if (!map.isEmpty()) {
            accText.setText(map.get("account"));
            passText.setText(map.get("password"));
            cb.setChecked(true);
        } else {
            cb.setChecked(false);
        }
    }
    
    private Map<String, String> readAccountAndPassword() {
        Map<String, String> map = new HashMap<>();
        BufferedReader in = null;
        try {
            in = new BufferedReader(new InputStreamReader(super.getApplicationContext().openFileInput(fileName)));
            map.put("account", in.readLine());
            map.put("password", in.readLine());
        } catch (FileNotFoundException fnfe) {
            fnfe.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return map;
    }
    
    public void login(View view){
        String account = accText.getText().toString().trim();
        String password = passText.getText().toString().trim();
    
        if(TextUtils.isEmpty(account) || TextUtils.isEmpty(password)){
            Toast.makeText(getApplicationContext(), R.string.account_and_password_require, Toast.LENGTH_SHORT).show();
            return;
        } else {
            if(cb.isChecked()) {
                OutputStream out = null;
                try {
                    //data/data/com.example~~~/files
                    out = super.getApplicationContext().openFileOutput(fileName, super.MODE_PRIVATE);
                    out.write((account + "\r\n" + password).getBytes());
                } catch (FileNotFoundException fnfe) {
                    fnfe.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (out != null) {
                            out.close();
                            Toast.makeText(getApplicationContext(), "已儲存帳號和密碼", Toast.LENGTH_SHORT).show();
                        }
                    } catch (IOException ioe) {
                        ioe.printStackTrace();
                    }
                }
            }
        }
    }
}

※MODE_PRIVATE會寫入檔案,如果發現已有檔案會覆蓋,
如果改成MODE_APPEND,會附加資料在最後面

※readAccountAndPassword方法是讀;login方法是寫



※SharedPreferences 讀寫XML

從data/data/com.example~~~/shared_prefs資料夾裡,讀或寫
會自動加副檔名xml(不管你有沒有加xml,都會在後面多個.xml)
只有MainActivity.java和openFileOutput/openFileInput不一樣

※MainActivity.java

public class MainActivity extends AppCompatActivity {
    EditText accText = null;
    EditText passText = null;
    CheckBox cb = null;
    final String fileName = "login";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_button);
    
        accText = (EditText) findViewById(R.id.account);
        passText = (EditText) findViewById(R.id.password);
        cb = (CheckBox) findViewById(R.id.cb);
    
        Map<String, String> map = this.readAccountAndPassword();
        if (map.get("account") != null && map.get("password") != null) {
            accText.setText(map.get("account"));
            passText.setText(map.get("password"));
            cb.setChecked(true);
        } else {
            cb.setChecked(false);
        }
    }
    
    public void login(View view){
        String account = accText.getText().toString().trim();
        String password = passText.getText().toString().trim();
    
        if(TextUtils.isEmpty(account) || TextUtils.isEmpty(password)){
            Toast.makeText(getApplicationContext(), R.string.account_and_password_require, Toast.LENGTH_SHORT).show();
            return;
        } else {
            if(cb.isChecked()) {
                // data/data/com.example~~~/shared_prefs/login.xml
                // fileName後會自動加副檔名xml
                SharedPreferences sp = super.getSharedPreferences(fileName, super.MODE_PRIVATE);
                SharedPreferences.Editor editor = sp.edit();
                editor.putString("account", account);
                editor.putString("password", password);
                editor.commit();
                Toast.makeText(getApplicationContext(), "已儲存帳號和密碼", Toast.LENGTH_SHORT).show();
            }
        }
    }
    
    private Map<String, String> readAccountAndPassword() {
        Map<String, String> map = new HashMap<>();
        SharedPreferences sp = super.getSharedPreferences(fileName, super.MODE_PRIVATE);
        // 第二個參數是取不到時的預設值
        map.put("account", sp.getString("account", null));
        map.put("password", sp.getString("password", null));
        return map;
    }
}

※readAccountAndPassword方法是讀;login方法是寫

2016年10月23日 星期日

Button的兩種方式 (Android)

官網連結
有兩種方式可以使按鈕起作用,View.OnClickListener 和 XML layout,其中View.OnClickListener有三種方法


※一、View.OnClickListener

※activity_main.xml

<Button
    android:id="@+id/btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="test button"/>

※View.OnClickListener的三種方法都是用這個xml


※1.MainActivity.java

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_button);
    
        Button btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(new MyOnClickListener());
    }
    
    private class MyOnClickListener implements View.OnClickListener{
        @Override
        public void onClick(View v) {
            Toast.makeText(getApplicationContext(), "extends Listener button", Toast.LENGTH_SHORT).show();
        }
    }
}

※寫一支class繼承View.OnClickListener,然後實作onClick方法


※2.MainActivity.java

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_button);
    
        Button btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Toast.makeText(getApplicationContext(), "Listener button", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

※和第一種方法一樣,只是變成匿名類別


※3.MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_button);
    
        Button btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(this);
    }
    
    @Override
    public void onClick(View view) {
        Toast.makeText(getApplicationContext(), "class Listener button", Toast.LENGTH_SHORT).show();
    }
}

※不寫class繼承View.OnClickListener,直接繼承View.OnClickListener,一樣實作onClick方法



※二、XML layout 

這種方式是用設定的方式來達成,較常用


※activity_main.xml

<Button
    android:id="@+id/btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="test button"
    android:onClick="xxx"/>

※xxx是要實限的方法名稱,當然也可以叫onClick


※MainActivity.java

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_button);
    }
    
    public void xxx(View view) {
        Button btn = (Button) view;
        String[] display = {"別按我", "按屁啊", "按一次五元", "誰叫你亂按的", "林老師咧"};
        btn.setText(display[(int) (Math.random() * display.length)]);
        Toast.makeText(getApplicationContext(), "setting button", Toast.LENGTH_SHORT).show();
    }
}

※官網有說要public,而且要有View參數,表示自己的這一個按鈕

2016年10月21日 星期五

公用:proxy (jQuery31)

※proxy

代理事件的東西
有四種方式


※第一個參數是functon,第二個參數是context(物件)

$(function(){
    var me = {
        type: "me",
        test: function(event) {
            $(event.target).css("background-color", "red");
            $("div").append(this.type + "<br>" );
            $("button").unbind("click", this.test);
        }
    };
    
    var you = {
        type: "you",
        test: function(event) {
            $("div").append(this.type + " ");
        }
    };
    
    $("button")
        .on("click", $.proxy(me.test, me)) // 只執行一次,因為被unbind了
        .on("click", $.proxy(you.test, you)) // you-->都是自己的,很好理解
        .on("click", $.proxy(you.test, me)) // me-->右邊是me,type抓的是me的
        .on("click", you.test); // button-->按鈕的type
});
----------
<button type="button">Test</button>
<div></div>




※第一個參數是context(物件),第二個參數是string

$(function(){
    var obj = {
        name: "John",
        xxx: function() {
            $("p").append(this.name);
            $("button").off("click", obj.test);
        }
    };
    
    $("button").on("click", $.proxy(obj, "xxx"));
});
----------
<button>Test</button>
<p></p>

※第二個參數要對應到第一個參數物件裡的名字才有效



※其他方式

第三種和第四種方式是給第三個參數(可以給很多,類似java的「…」),是給第二個參數用的
這裡以第二個參數是物件為例
$(function(){
    var me = {
        type: "dog",
        test: function(one, two, event) {
            $("p")
                .append(one.type + '<br />') // cat
                .append(two.type + '<br />') // fish
                .append(event.type + '<br />') // click
                .append(this.type + '<br />') // dog
                .append(event.target.type + '<br />'); // submit
        }
    };
    
    var you = {type: "cat"};
    var they = {type: "fish"};
    $("button").on("click", $.proxy(me.test, me, you, they));
});
----------
<button>Test</button>
<p></p>

※event.target.type 這個我不懂

公用:grep、contains、merge、map、noop、now、trim、type、unique (jQuery30)

※grep

$(function(){
    var rtn = $.grep([1,2,3,4,5,6,7,8,9,8,7,6,5,4,3,2,1], function(element, index){
        console.log(index + '=' + element);
        return (element != 5 && index > 4);
    },false);
    alert(rtn);
    // 第三個參數是false或不給,結果為 6,7,8,9,8,7,6,4,3,2,1
    // 第三個參數是true,結果為 1,2,3,4,5,5 也就是上一行的相反
    
    var rtn = $.grep([{'a':1, 'b':2}, {'c':3, 'd':4}, 'e', 5], function(element, index){
        console.log('index=' + index);
        if($.isPlainObject(element)){
            for(e in element){
                console.log(e); // a b c d
            }
        } else {
            console.log(element); // e 5
        }
    });
});

※過濾陣列用的,如果過濾物件不會出錯,但好像沒有執行一樣



※contains

$(function(){
    console.log($.contains(document.documentElement, document.body)); // true
    console.log($.contains(document.body, document.documentElement)); // false
    console.log($.contains(document.getElementsByTagName("div"), document.getElementsByTagName("span"))); // false
});
----------
<div>
    <span></span>
</div>

※右邊的參數是否被左邊的參數包括,這個我不太懂



※merge

$(function(){
    console.log($.merge([0,1,2], [2,3,4])); // [0, 1, 2, 2, 3, 4]
    console.log($.merge([], [2,3,4] ));  // [2, 3, 4]
    console.log($.merge($.merge([0,1,2], [2,3,4]), [4,5,6])); // [0, 1, 2, 2, 3, 4, 4, 5, 6]
    
    var test = $.merge([0,{1:function(){return 'x'}},{2:'two'}], [{2:'owt'},3,{1:function(){return 'o'}}]);
    for(t in test){
        if($.isPlainObject(test[t])){
            for(tt in test[t]){
                console.log(test[t][tt]);
            }
        } else {
            console.log(test[t]); 
        }
    }
});

※將兩個陣列合併



※map

$.map(['a','b','c'], function(val, index) {
    console.log('val=' + val); // a b c
    console.log('index=' + index); // 0 1 2
});
    
$.map({'a':1,'b':2,'c':3}, function(val, index) {
    console.log('val=' + val); // 1 2 3
    console.log('index=' + index); // a b c
});

※用法和each一樣



※noop、now、trim

console.log($.noop);// function(){}
console.log($.noop()); // undefined
console.log($.now); // function(){return (new Date()).getTime();}
console.log($.now()); // long的數字
    
var $trim = $.trim('  1 2 3  ');
console.log($trim); // 1 2 3
console.log($trim.length); // 5





※type

console.log($.type()); // undefined
console.log($.type(undefined)); // undefined
console.log($.type(window)); // object
console.log($.type(null)); // null
    
console.log('----------');
console.log($.type(true)); // boolean
console.log($.type(3)); // number
console.log($.type("test")); // string
console.log($.type(function(){} )); // function
console.log($.type([])); // array
    
console.log('----------');
console.log($.type({})); // object
console.log($.type(new Date())); // date
console.log($.type(new Error())); // error
console.log($.type(/test/)); // regexp





※unique

$(function(){
    var divs = $("div").get();
    console.log(divs.length); // 5
    console.log($.unique(divs).length); // 5
    
    divs = divs.concat($(".d").get());
    console.log(divs.length); // 7
    var xxx = $.unique(divs);
    console.log(xxx.length); // 5
    
    $('input[type="button"]').one('click', function(){
        $('p').append(xxx);
    });
});
----------
<div>d1</div>
<div class="d">d2</div>
<div>d3</div>
<div class="d">d4</div>
<div>d5</div>
<input type="button" value="click me"/>
<p>p</p>



2016年10月20日 星期四

公用:globalEval、makeArray、inArray (jQuery29)

※globalEval、eval 的差異

和javascript的eval很像,但是全域的

$(function(){
    test();
});
    
function test(){
    $.globalEval('var xxx = 2;');
    eval('var ooo = 3;');
    console.log('init xxx=' + xxx);
    console.log('init ooo=' + ooo);
}
    
function after(){
    console.log('after xxx=' + xxx);
    // console.log('after ooo=' + ooo); // 執行時錯誤
}
----------
<input type="button" value="click me" onclick="after()" />

※註解那行打開會發生錯誤,因為並不是全域的



※makeArray

故名思義,就是用來產生陣列的

$(function(){
    var arr = $.makeArray($('span'));
    // alert(arr[0].childNodes[0].data);
    // debugger;
    $.each(arr, function(i, v){
        console.log('v=' + v.childNodes[0].data); // s1-->s2
    });
});
----------
<span>s1</span>
<div>d1</div>
<span>s2</span>
<div>d2</div>



※makeArray 使用多層時

$(function(){
    $.each($.makeArray($('li')).reverse(), function(i, v){
        console.log('v=' + v.childNodes[0].data); // s1-->s2
    });
});
----------
<ul>
    U1
    <li>L1</li>
    <li>L2</li>
    <li>
        L3
        <ul>
            U2
            <li>L4</li>
            <li>L5</li>
        </ul>
    </li>
</ul>

※結果如以下畫面


※會發現L3前後都空很大,這是因為HTML有換行的關係,改成以下就不會了

<ul>
    U1
    <li>L1</li>
    <li>L2</li>
    <li>L3<ul>
            U2
            <li>L4</li>
            <li>L5</li>
        </ul>
    </li>
</ul>



※inArray、indexOf 的差異

var str = "abc cba array abc cba";
console.log($.inArray("b", str)); // 1
console.log($.inArray("b", str, 5)); // 5
console.log($.inArray("b", str, -8)); // 15
console.log($.inArray("x", str)); // -1
    
console.log(str.indexOf('b')); // 1
console.log(str.indexOf('b', 5)); // 5
console.log(str.indexOf('b', -8)); // 1
console.log(str.indexOf('x')); // -1
    
console.log($.inArray("ab", str)); // -1
console.log($.inArray("ab", str, 5)); // -1
console.log($.inArray("ab", str, -8)); // -1
    
console.log(str.indexOf('ab')); // 0
console.log(str.indexOf('ab', 5)); // 14
console.log(str.indexOf('ab', -8)); // 0
    
console.log($.inArray("abc", str)); // -1
console.log($.inArray("abc", str, 5)); // -1
console.log($.inArray("abc", str, -8)); // -1
    
console.log(str.indexOf('abc')); // 0
console.log(str.indexOf('abc', 5)); // 14
console.log(str.indexOf('abc', -8)); // 0
    
console.log($.inArray("John", [ 4, "Pete", 8, "John" ])); // 3
console.log($.inArray("Joh", [ 4, "Pete", 8, "John" ])); // -1
    
console.log([ 4, "Pete", 8, "John" ].indexOf("John")); // 3
console.log([ 4, "Pete", 8, "John" ].indexOf("Joh")); // -1

※inArray可以有第三個參數,沒有寫預設是0,表示從第一個字開始找

※用在搜尋字串且找一個字時:相同的地方是
使用正數時,還有找不到時都是-1,只有負數不同

※搜尋兩個字或一個單字時,inArray都是-1,也就是不支援;但indexOf有支援

※搜尋陣列時,inArray和indexOf都一樣

公用:each、isXXX、parseXXX (jQuery28)

※$.each 

就是迴圈

$(function(){
    $.each(['a', 'b', 'c'], function(i, v){
        console.log('i=' + i);
        console.log('v=' + v);
    });
    //0 a-->1 b-->2 c
    
    $.each({'a':'one', 'b':'two', 'c':'three'}, function(i, v){
        console.log('i=' + i);
        console.log('v=' + v);
    });
    //a one-->b two-->c three
    
    $.each({'a':{a1:1}, 'b':'two', 'c':{c3:3}}, function(i, v){
        console.log('i=' + i);
        if($.isPlainObject(v)){
            for(vc in v){
                console.log('vc=' + vc);
                console.log('v[vc]=' + v[vc]);
            }
        } else {
            console.log('v=' + v);
        }
    });
    // a a1 1-->b two-->c c3 3
});

※如果只有value,那第一個參數就是index



※$.isXXX 

isXXX都是判斷用的方法,目前有以下七種

1.$.isArray():是否是陣列
2.$.isEmptyObject():是否是空物件
3.$.isFunction():是否是function
4.$.isNumeric():是否是數字
5.$.isPlainObject():是否是物件
6.$.isWindow():是否是window物件
7.$.isXMLDoc():是否是XML文件



※isPlainObject、isArray、isEmptyObject

console.log($.isPlainObject({a:1, b:2})); // true
console.log($.isPlainObject([1,2,3])); // false
console.log($.isPlainObject({})); // true
console.log($.isPlainObject([])); // false
console.log($.isPlainObject({a:[4,5,6], b:[7,8,9]})); // true
console.log($.isPlainObject([{c:3}, {d:4}])); // false
console.log($.isPlainObject(function(){})); // false
    
console.log($.isArray({a:1, b:2})); // false
console.log($.isArray([1,2,3])); // true
console.log($.isArray({})); // false
console.log($.isArray([])); // true
console.log($.isArray({a:[4,5,6], b:[7,8,9]})); // false
console.log($.isArray([{c:3}, {d:4}])); // true
console.log($.isArray(function(){})); // false
    
console.log($.isEmptyObject('')); // true
console.log($.isEmptyObject("")); // true
console.log($.isEmptyObject({})); // true
console.log($.isEmptyObject([])); // true
console.log($.isEmptyObject(function(p){})); // true
console.log($.isEmptyObject(function(){var a = 5; return a + 5})); // true

※{}就是物件;[]就是陣列

※要注意判斷空物件時的function,裡面明明有東西,但還是true



※isFunction

function aFunction(){}
var bFunction = function(){}
var cFunction = new Function();
// var dFunction = (x) => {}
    
console.log($.isFunction(aFunction)); // true
console.log($.isFunction(bFunction)); // true
console.log($.isFunction(cFunction)); // true
// console.log($.isFunction(dFunction)); // true
console.log($.isFunction(function(){})); // true

※注解的部分是IE不支援



※isNumeric、isWindow

console.log($.isNumeric(+10)); // true
console.log($.isNumeric("-10")); // true
console.log($.isNumeric(0144)); // true 
console.log($.isNumeric(0xFF)); // true
console.log($.isNumeric("0xFF")); // true
console.log($.isNumeric("8e5")); // true
console.log($.isNumeric(3.1415)); // true
    
console.log($.isWindow(window)); // true
console.log($.isWindow(document)); // false

※注意判斷數字時,其他進位也有支援



※isXMLDoc、parseXML

console.log($.isXMLDoc('<xxx></xxx>')); // false
console.log($.isXMLDoc($.parseXML('<xxx></xxx>'))); // true
console.log($.isXMLDoc($.parseXML('<xxx />'))); // true

※經過parseXML後,才是XML,就算字串裡有加xml第一行的version1.0之類的結果也是一樣,反正裡面就是字串



※parseXXX

目前有三種
1. parseXML:轉成XML,上個範例已有,以下就不寫了
2.parseJSON:轉成JSON
3.parseHTML:轉成HTML


※parseJSON

// alert($.parseJSON('{a:1}').a); // 執行時錯誤
// alert($.parseJSON("{'a':1}").a); // 執行時錯誤
alert($.parseJSON('{"a":1}').a); // 1
// alert($.parseJSON('["a":1]').a); // 執行時錯誤
// alert($.parseJSON('{"a":"1\t2"}').a); // 執行時錯誤
alert($.parseJSON(null)); //null
alert($.parseJSON(undefined)); // 1.9之前null,之後執行時錯誤

※只有雙引號才可以,其他就只有null不會報錯


※parseHTML

<div id="log" />
----------
var html = $.parseHTML("hello, <b>my name is</b> jQuery.");
$("#log").append(html);
    
$.each(html, function(i, v) {
    console.log(i); // 0-->1-->2
    console.log(v); // hello,--><b>my name is</b>-->jQuery.
    console.log('-----');
});

※append時,轉不轉成HTML都可以,但如果不轉成HTML,用each會執行錯誤

※還有第二、三個參數可用,我沒研究

2016年10月13日 星期四

幫助方法、全域Ajax事件處理器 (jQuery25)

官網連結

※幫助方法

就是serialize和 serializeArray
serialize可將form變成字串的形式
serializeArray可將form變成name、value的形式,回傳JSON物件(不是JSON字串),

<form action="xxx/ooo" method="post">
    使用者:
    <input type="text" name="username" value="user" /><br />
    密碼:
    <input type="password" name="password" value="pass" /><br />
    
    <input type="radio" name="ra" value="r1" />
    <input type="radio" name="ra" value="r2" />
    <input type="radio" name="ra" value="r3" checked="checked" /><br />
    
    <input type="checkbox" name="ch" value="c1" checked="checked" />
    <input type="checkbox" name="ch" value="c2" checked="checked" />
    <input type="checkbox" name="ch" value="c3" /><br />
    
    <select name="se">
        <option>s1</option>
        <option selected="selected">s2</option>
        <option>s3</option>
    </select>
    <br />
    
    <input type="button" value="serialize" onclick="serialize()" /><br />
    <input type="button" value="serializeArray" onclick="serializeArray()" /><br />
</form>
    
<span id="span1"></span><br />
<span id="span2"></span>
-----
$(function(){
    $('#span1').text($("form").serialize());
    console.log($("form").serialize()); // username=user&password=pass&ra=r3&ch=c1&ch=c2&se=s2
    
    $('#span2').text($("form").serializeArray());
    console.log($("form").serializeArray()); // [Object, Object, Object, Object, Object, Object]
    
    $.each($("form").serializeArray(), function(i, f){
        console.log("name=" + f.name);
        console.log("value=" + f.value);
    });
});


因為serializeArray印出來是Object,所以可以用$.each印出來



官網連結

※全域Ajax事件處理器

目前共6種
由上往下執行,ajaxError、ajaxSuccess只會執行其中之一,global設false不會執行
.ajaxStart():一開始就會執行
.ajaxSend():開始發送會執行
.ajaxError():失敗時執行
.ajaxSuccess():成功時執行
.ajaxComplete():完成時執行
.ajaxStop():結束執行


var ajaxTest = function(){
    $.ajax({
        url : "xxx",
        type : "POST",
        // dataType: 'abc',
        data : $.param({ 
                    username: $('input[name="username"]').val(), 
                    password: 'p' 
                }),
        success : function(data, textStatus, jqXHR){
            console.log('data=' + data);
            console.log('textStatus=' + textStatus);
        }
    });
}
    
function setGlobalFalse(){
    $.ajaxSetup({
        global : false
    });
}
    
$(document).ajaxStart(function() {
    alert('start');
});
    
$(document).ajaxSend(function() {
    alert('send');
});
    
$(document).ajaxError(function() {
    alert('error');
});
    
$(document).ajaxSuccess(function() {
    alert('success');
});
    
$(document).ajaxComplete(function() {
    alert('complete');
});
    
$(document).ajaxStop(function() {
    alert('stop');
});
----------
<h3>${success}</h3>
<form action="xxx/ooo" method="post">
    使用者:
    <input type="text" name="username" value="user" /><br />
    密碼:
    <input type="password" name="password" value="pass" /><br />
    <input type="submit" />
</form>
    
<input onclick="ajaxTest()" type="button" value="test" />
<input onclick="setGlobalFalse()" type="button" value="globalFalse" />

※這範例是成功的,所以ajaxSuccess會跳出來;將dataType打開會失敗,所以ajaxError跳出來

※將globalFalse的按鈕按下後,都不會觸發了

※注意官網有說版本的問題,我沒有測