ソースコード(Eclipse WTPプロジェクト) : fileExamWebService.zip
こちらで実際に動く物を試せます : http://hondou.homedns.org/ExamWebService/suggestion.html

概要

下図のように、テキストボックスに百人一首の一部を入れると、前方一致する百人一首が候補として提示されるような AJAX AJAX しているアプリを作ってみる

 
suggestionTextBox.png

Webサービス側

package com.snail.exam;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class WakaSuggestionWebService {

  private static String FILE_NAME = "/srv/ftp/data/hyakunin.txt";
  
  /** 和歌の対応表のキャッシュ. */
  private static Map<String, WakaBean> cache = null;

  /** キャッシュが有効な時刻. */
  private static long cacheUpdate = 0;

  /**
   * 和歌の対応表("かな" -> "漢字+詠み人")を読み出します. 一旦読み込むと5分間はキャッシュを返します。
   * 
   * @return 和歌の対応表
   */
  private synchronized Map<String, WakaBean> getWakaMap() {
    
    if (cacheUpdate > System.currentTimeMillis()) {
      // キャッシュ有効時間内
      return cache;
    }

    // 出現順を保存するために、LinkedHashMapを使う
    cache = new LinkedHashMap<String, WakaBean>();

    try {
      BufferedReader br = 
        new BufferedReader(
            new InputStreamReader(
                new FileInputStream(FILE_NAME),"UTF-8"));
      String kanji, kana, author;
      while (((kanji = br.readLine()) != null)
          && ((kana = br.readLine()) != null)
          && ((author = br.readLine()) != null)) {
        cache.put(kana, new WakaBean(kana, kanji, author));
      }
      br.close();
      cacheUpdate = System.currentTimeMillis() + 5 * 60 * 1000;
    } catch (IOException e) {
      // 読み込みに失敗した場合は何もしない
      // ただし、キャッシュ有効時間の更新も行わない
      e.printStackTrace();
      e = null;
    }

    return cache;
  }

  /**
   * fragment で始まる和歌を返します
   * 
   * @param fragment
   *          和歌の一部
   * @return fragment で始まる和歌の配列
   * @throws Exception
   */
  public WakaBean[] suggestWaka(final String fragment) {
      List<WakaBean> retList = new LinkedList<WakaBean>();

      for (Map.Entry<String, WakaBean> entry : getWakaMap().entrySet()) {
        String kana = entry.getKey();
        if (kana.startsWith(fragment)) {
          retList.add(entry.getValue());
        }
      }
      
      return retList.toArray(new WakaBean[0]);
  }

  public WakaSuggestionWebService() {
    super();
  }
}
 
package com.snail.exam;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

public class WakaBean implements Serializable {
  private static final long serialVersionUID = -8204110091872591615L;
  private String kana;
  private String kanji;
  private String author;
  public String getKana() {
    return kana;
  }
  public WakaBean(){
    super();
  }
  public WakaBean(String kana, String kanji, String author){
    super();
    this.kana = kana;
    this.kanji = kanji;
    this.author = author;
  }
  public void setKana(String kana) {
    try {
      this.kana = URLEncoder.encode(kana, "UTF-8");
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
  }
  public String getKanji() {
    return kanji;
  }
  public void setKanji(String kanji) {
    try {
      this.kanji = URLEncoder.encode(kanji, "UTF-8");
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
  }
  public String getAuthor() {
    return author;
  }
  public void setAuthor(String author) {
    try {
      this.author = URLEncoder.encode(author, "UTF-8");
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
  }
}

クライアント(Javascript)側

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Suggestion Example</title>
<style type="text/css">
.suggestionListON {
  width: 500pt;
  border: ridge;
}

.suggestionListOFF {
  border: none;
}

.candidateON {
  color: yellow;
  background-color: black;
}

.candidateOFF {
  color: black;
  background-color: white;
}
</style>
<script type="text/javascript" src="scripts/prototype.js"></script>
<script type="text/javascript" src="scripts/ws.js"></script>
<script type="text/javascript">
// <!--
//------------------------------------------------------------------------
function init(){
  Event.observe('tBox', 'keydown', keyPress);
}
//------------------------------------------------------------------------
var lastEvaluated = null;
function updateSuggestionList(){
  var input = $('tBox').value;
  if(input == ''){
    return true;
  }
  if(input == lastEvaluated){
    return true;
  }
  lastEvaluated = input;

  var MyHandler = Class.create();
  Object.extend(MyHandler.prototype,WS.Handler.prototype);
  Object.extend(MyHandler.prototype,{     
    on_request : function(envelope){
    },
    on_response : function(envelope){
    },
    on_error : function(call,envelope){
      alert("ERROR");
    }
  });
  var call = new WS.Call('/ExamWebService/services/WakaSuggestionWebService');

  call.add_handler(new MyHandler());
  var nsuri = 'http://exam.snail.com';
  var qn_op = new WS.QName('suggestWaka', nsuri);
  call.invoke_rpc(qn_op, [ {
    name  : 'fragment',
    value : $('tBox').value
  } ], null, function(call, envelope) {
    var wakalist = envelope.get_body().get_all_children()[0].get_all_children();
    var kana = new Array(wakalist.length);
    
    for( var wakaNo = 0 ; wakaNo < wakalist.length ; wakaNo++ ){
      var wakaBean = wakalist[wakaNo].get_all_children();
      for( var elemNo = 0 ; elemNo < wakaBean.length ; elemNo++ ){
        var name = wakaBean[elemNo].element.tagName;
        var value = wakaBean[elemNo].get_value();
        if( name == 'kana' ){
          kana[wakaNo] = value;
        }else if( name == 'kanji' ){
          kanji = value;
        }else if( name == 'author' ){
          author = value;
        }        
      }
    }

    var inputText = $('tBox').value;
    if( kana.length == 1 ){
      var candidate = kana[0];
      if( inputText == candidate ){
        $('suggestionList').innerHTML = '';
        $('suggestionList').className = "suggestionListOFF";
        return;
      }
    }
    
    var suggestion = '';
    for(var cnt = 0 ; cnt < kana.length ; cnt++){
      suggestion += '<div';
      suggestion += ' onmouseover="mousein(this);"';
      suggestion += ' onmouseout="mouseout(this);"';
      suggestion += ' class="candidateOFF"';
      suggestion += ' onClick="setValue(this.innerHTML);">';
      suggestion += kana[cnt];
      suggestion += '</div>';
    }
    $('suggestionList').innerHTML = suggestion;
    $('suggestionList').className = "suggestionListON";
    // $('soap').innerHTML = "RAW SOAP ENVELOPE:<br/>" + arguments[2].escapeHTML();
  });
}

//------------------------------------------------------------------------
// Mouse Operation

function setValue(selectedValue){
  $('tBox').value = selectedValue;
  $('suggestionList').innerHTML = "";
  $('suggestionList').className = "suggestionListOFF";
}

function mousein(row){
  var candidateList = $('suggestionList').childNodes;
  for( var cnt = 0 ; cnt < candidateList.length ; cnt++ ){
    candidateList[cnt].className = "candidateOFF";
  }
  row.className = 'candidateON';
}

function mouseout(row){
  row.className = 'candidateOFF';
}

//------------------------------------------------------------------------
// Key operation

function keyPress(event){
  var candidateList = $('suggestionList').childNodes;
  var cnt;

  if( candidateList.length == 0 ){
    return;
  }
  
  switch(event.keyCode){
  case Event.KEY_RETURN :
    // RETURN Key (13) is pressed
      for( cnt = 0 ; cnt < candidateList.length ; cnt++ ){
          if(candidateList[cnt].className == "candidateON"){
            // find selected row
            $('tBox').value = candidateList[cnt].innerHTML;
            $('suggestionList').innerHTML = '';
            return;
          }
      }
      break;
  case Event.KEY_UP :
    // UP Key (38) is pressed
      for( cnt = 0 ; cnt < candidateList.length ; cnt++ ){
          if(candidateList[cnt].className == "candidateON"){
            // find selected row
            candidateList[cnt].className = "candidateOFF";

            if( (cnt-1) == -1 ){
              candidateList[candidateList.length - 1].className = "candidateON";
            }else{ 
              candidateList[cnt-1].className = "candidateON";
            }
            return;
          }
      }
      candidateList[candidateList.length - 1].className = "candidateON";
      break;
  case Event.KEY_DOWN :
    // DOWN Key (40) is pressed
    for(cnt = 0 ; cnt < candidateList.length ; cnt++ ){
        if(candidateList[cnt].className == "candidateON"){
          // find selected row
          candidateList[cnt].className = "candidateOFF";

          if( (cnt+1) == candidateList.length ){
            candidateList[0].className = "candidateON";
          }else{ 
            candidateList[cnt+1].className = "candidateON";
          }
          return;
        }
    }
    candidateList[0].className = "candidateON";
    break;
  }   
}
//------------------------------------------------------------------------
// -->
</script>
</head>
<body onLoad="init();">
<div><input type="text" id="tBox"
  onkeyup="updateSuggestionList();" autocomplete="off"
  style="width: 500pt" /> <br />
<div id="suggestionList" class="suggestionListOFF"></div>
</div>
</body>
</html>

HTML

基本的に、テキストボックスと、候補を展開する <div> のみ

<body>
<div><input type="text" id="tBox"
	onkeyup="updateSuggestionList();" autocomplete="off"
	style="width: 500pt" onfocus="isWakaInputBoxOnFocus=true"
	onblur="isWakaInputBoxOnFocus=false" /> <br />
<div id="suggestionList" class="suggestionListOFF"></div>
</div>
</body>

テキストボックスに、何かが入力されたときの振る舞い

マウスで候補を選んだときの振る舞い

矢印キーで候補を選んだときの振る舞い


参考文献


Java#AJAX


添付ファイル: filesuggestionTextBox.png 696件 [詳細] fileExamWebService.zip 699件 [詳細] filehyakunin.txt 2221件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS   sitemap
Last-modified: 2009-04-16 (木) 00:07:38 (2789d)
ISBN10
ISBN13
9784061426061