ラベル java の投稿を表示しています。 すべての投稿を表示
ラベル java の投稿を表示しています。 すべての投稿を表示

2014年3月31日月曜日

poiを使ってみた

気が付きゃ3月も終わりだ('A`)

poiを使ってみた

javaで走らせたり、というかSeleniumでのテスト時に一緒にデータを取って、その結果がExcelとかに出力できたらなー、とか考えたのが始まり

1. poi

ここからダウンロードできるライブラリの一種。簡単に言うと、「Microsoft Documentsにアクセスしやすくするライブラリ」 とのこと。 今回はそのうちの一つであるExcelへのアクセスを手伝ってもらう。
今回は poi-bin-3.10-FINAL-20140208.tar.gz を使用。

2. 環境設定

とりあえずよくわからないので、 
docs以下
LICENCE
NOTICE
以外は全部プロジェクトに放り込んだうえでパスを通した。 examplesとかいるのかなぁ…

3. WorkBookの作成

一つずつ順に。WorkBookはその名の通り作業する場所。ここにExcelファイルの1シートをコピーして作業する。 あくまでコピーなので、最後に書き込まないとExcelファイル自体は更新、書き込みされない点に注意。
    try {

      /* エクセルファイルはどこですか */
      File f = new File(filepath);
      InputStream is = new FileInputStream(f);

      /* ワークブックにアクセス */
      XSSFWorkbook wb = new XSSFWorkbook(is);

    } catch (FileNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
filepathはExcelファイルの在り処。「ファイルが無ければ作る」処理を追加してもいいかも。 WorkBookの作成時には2つもExceptionが発生するので、きちんと捕まえるなど対処が必要。

4. シート、行、列からセルの指定

WorkBookにシートを読み込ませて、徐々に絞っていく感じ。
    try {

      /* エクセルファイルはどこですか */
      File f = new File(filepath);
      InputStream is = new FileInputStream(f);

      /* ワークブックにアクセス */
      XSSFWorkbook wb = new XSSFWorkbook(is);

      /* シートの指定 */
      XSSFSheet sheet = wb.getSheet(sheetname);

      /* シートの定義の最初の行 */
      int firstRow = sheet.getFirstRowNum() + 1;

      /* シートの定義の最終行 */
      int lastRow = sheet.getLastRowNum();

      /* 行を順に上から見ていく。 */
      for (int i = firstRow; i <= lastRow; i++) {

        /* 行情報を取得 */
        XSSFRow row = sheet.getRow(i);

        /* 列の読み込み。 とりあえず 1 で */
        XSSFCell cell = row.getCell(columnno);

      }

    } catch (FileNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
sheetnameとcolumnnoは別途指定。
sheetnameはそのままシートにつける名前。デフォルトは【Sheet1】とかなってるアレ。実はintで順番指定も可能らしい(未検証)。
columnnoは列の番号。 A列が0、B列が1、といった順で対応しているらしい。

作業の順番は、
シートの指定 → 行の指定 → 列の指定
行の指定の際に、シート中にて、【何かしらが列のどこかに記入されている行】の行数が取得できる。今回は事前に項番を振っておいたExcelを読み込んでいるため、項番の分だけ回ってくれる寸法。 【最初の行】の指定の際 +1 しているのは、各列のラベルを設定した結果、その行まで含めて【最初の行】とされてしまったのか、うまくいかなかったため。

もちろん行数をピンポイントに指定してやることも可能。

5. セルの内容の確認

既に記入されていないか確認。
 /* チェック。 いろいろステータスのサポートがあるらしい */
        if (cell != null && cell.getCellType() == Cell.CELL_TYPE_STRING) {

          /* セルの内容がStringなら */
          String cellval = cell.getStringCellValue();

          /* 取得してみる */
          System.out.println(cellval);

        } else if (cell == null || cell.getCellType() == Cell.CELL_TYPE_BLANK) {

          /* ブランクなら */
          System.out.println("からっぽ(・д・`)");
        }
セルの読み込み直後から
Cell.CELL_TYPE_STRING はいわゆるセルの型。数字や日付なんかの指定もあり、読み込み時などに型のチェックができる。「数値が取り出せた場合のみ」なんて条件も可能。

6. 書き込み

実際に書き込む。

/* 指定された列番号のセルを準備する */
    XSSFCell cell = row.getCell(columnNum, Row.CREATE_NULL_AS_BLANK);

    /* 値を適当に書き込む */
    /* TODO Stringじゃなくて数字でよくないかなこれ */
    cell.setCellValue(String.format(" %1$d", 12345));
少し巻き戻って、指定の行から書き込むセルの指定のし直し。columnNumはまた別の列番号。
Row.CREATE_NULL_AS_BLANK は【空のセルとする】的な宣言...?('A`)
setCellValueで値を書き込む。TODOにある通り、なぜ数値をStringで書き込んでいるかは謎(他のとこからコピペしてきたからなんだがな…('A`))

7. Excelに書き込み

先述の通り、ここまでは WorkBook上での出来事。きちんとExcelファイルに放り込んでやらないと更新されない。

      /* 最後にワークブックをファイルに書き込み */
      OutputStream os = new FileOutputStream(f);
      wb.write(os);
これだけ。だけどこれをやらないとExcelファイルには何も起きない…('A`)

8.番外編 -色の指定-

ちょっとだけ戻ってセルの取得あたり。フォントの色やセルの色を付けてみる。
 /* style準備 */
    XSSFCellStyle style = wb.createCellStyle();
    style.setFillPattern(XSSFCellStyle.SOLID_FOREGROUND);

    /* font準備 */
    XSSFFont font = wb.createFont();

    /* 背景色設定 */
    style.setFillForegroundColor(backgroundColor);

    /* フォント設定 */
    font.setColor(fontColor);
    style.setFont(font);

    /* セルにスタイルを反映 */
    cell.setCellStyle(style);
styleがセルの色やら、fontが文字周り。 styleの準備をして、 fontの準備をして、最後にセルにその設定を反映する、という手順。
backgroundColorやfontColorは別宣言。色の指定は HSSFColorクラスが使用可能。たとえば赤であれば、HSSFColor.RED.index で使用できる。
文字の書き込みなどとは別で宣言できるが、当然 7. Excelに書き込み の処理の前にやらないと反映されない。




|ω・`) |Ю .。o0 ( 次は型やら何やらをきれいにしないとな )

2013年4月30日火曜日

Enumノススメ

ふとしたチャンスで借りて読んだ EffectiveJava. ずっとEnumの使い方を勘違いしていたので矯正を兼ねて
メモメモφ(・ω・ )

Enumノススメ

なんという今更感.
だが, それがいい.

1. Enumって?

日本語に直すと 列挙型. 要するに関連する定数をまとめたもの. もともとは基本的に int値らしい. あらかじめ要素数がわかっている際に効果を発揮し, 別の要素が混ざらなくなる.

たとえば実際 EffectiveJavaにある例から. 
public static final int APPLE_FUJI = 0;
public static final int APPLE_PIPPIN = 1;
public static final int APPLE_GRANNY_SMITH = 2;

public static final int ORANGE_NAVEL = 0;
public static final int ORANGE_TEMPLE = 1;
public static final int ORANGE_BLOOD = 2;
リンゴ, オレンジの種類を定数にしておきたいとする. なにが困るかというと, たとえば品種改良を表すメソッド
public void breed(int variety1, int variety2){}
なんてのがあったとする. このメソッドは引数の2つの品種を掛け合わせる, なんてメソッドだとした時, ちょっとした問題が発生する.
それは, リンゴ×オレンジなんていう異種交配が始まってしまうパターン.
引数は「intかどうか」しかチェックしないため, 間違って (リンゴ,オレンジ) と別の種類の引数を入れてもコンパイルどころか実行まで通ってしまう. おおこわいこわい.
また, 上記例なら,
APPLE_FUJI == ORANGE_NAVEL
が trueを返すミラクル. これを回避するために, いちいち数値が被っていないかチェックが必要だったりとなかなか骨が折れる.
そこでEnum.

2. Enumの作り方

作り方はいたって簡単. 上記例ならリンゴ, オレンジはそれぞれ別物としてほしいので,
  public enum Apple {
    FUJI, PIPPIN, GRANNY_SMITH;
  }

  public enum Orange {
    NAVEL, TEMPLE, BLOOD;
  }
classや interfaceの代わりに enumを使う, 後は名前を列挙してやる. 「,(カンマ)」区切りで並べ, 必ず最後は「;(セミコロン)」でしめる.
こうすると, たとえばFUJIを呼び出したいなら,
Apple.FUJI
で呼び出せる. こうするとbreedメソッドも
public void appleBreed(Apple variety1, Apple variety2){}
とすれば, このメソッドはリンゴ同士でしかコンパイルが通らなくなる. 比較も,
Apple.FUJI == Orange.NAVEL;
Apple.FUJI == Apple.PIPPIN;
上はそもそも比較するものの型が違うとコンパイルエラーを吐いてくれるし, 下はもちろん falseが返る.
後からの追加も名前を足すだけで, 値の被りは名前の被りだけ気にすればいい.

3. 応用

こんな Enumの作り方もできちゃう.
public enum Skill {
  KAMEHAMEHA("かめはめ波", 1000);

  public String name;
  public int power;

  Skill(String name, int power) {
    this.name = name;
    this.power = power;
  }

  public String toString() {
    return name;
  }

}
唐突すぎる例( ゚д゚ )
たとえばかめはめ波という技の持つデータがすべて定数だった場合. これを Enumを使わずに管理するのはおそらく骨.
String KAMEHAMEHA_NAME ="かめはめ波";
int KAMEHAMEHA_POWER = 1000;
うん. 絶対事故る('A`).

そこで 「Enumにデータと振る舞いを持たせる」 ことをやってみる. KAMEHAMEHAは Skill型なので, 7行目からのコンストラクタが使える. 名前 KAMEHAMEHAが()で引数を持つのは, このコンストラクタを使うため. かめはめ波の powerを取り出したいなら,
int power = Skill.KAMEHAMEHA.power;
で取り出せる. 読みやすいし変にいろいろ参照しなくても OKなのは事故が起きにくいはず.
ちなみに12行目からのtoString()はEffectiveJava第 3章 10項「toStringを常にオーバーライドする」参照. 要するに,
System.out.println(Skill.KAMEHAMEHA);
としたときに, 「かめはめ波」と返ってきてくれた方が, 使う人にも優しい, ということ. 技を増やすときは同じように,
KAMEHAMEHA("かめはめ波", 1000),
TAIYOUKEN("太陽拳", 500);
とでも増やしてやればいい.



他にもメソッド持たせたりさらに応用もあるけどまだここまで.



かめはめ波とか出てきたのはだいたいここのせい.
たまに振り返ると固まっていなかった部分が矯正できて気分がいい+(・ω・0)*








まぁこの固めたのもいつまでもつやら…('A`)

2013年3月15日金曜日

Wait.until()で指定できる状態の種類

Selenium2でテストを書いていく際, 何かとお世話になるのが Wait<WebDriver>.until(). 「引数に指定した状態になるまで」待機してくれるのだが, この状態の指定のメソッドが多すぎる上に, いちいち英語読むのがダルげふんげふん説明が単調なのでまとめてメモメモφ(・ω・ )
ちなみに 「何言ってんだコイツ?(・д・`)」と感じられたら, こちらの本家へジャンプ.
またこちらでメソッドの中身を見るのもいいかも.

Wait.until()の指定できる「状態」


1. 準備


・インポートと宣言

import org.openqa.selenium.support.ui.Wait;
import static org.openqa.selenium.support.ui.ExpectedConditions.*;

WebDriver driver = new ChromeDriver();
Wait<webdriver> wait = new WebDriverWait(driver, 10);
まずは準備. newする際に, 待機を指示する WebDriverの指定と, 最大待機時間を宣言. 今回は 10秒を指定. もちろん 10を 20に置き換えれば最大 20秒待機となる. この待機時間を過ぎると,
org.openqa.selenium.TimeoutException
が発生し, テストはそこで failとなる.
逆にこの最大待機時間内に conditionが整えば, わざわざ最大待機時間まで待機せずに先に進んでくれる.
Thread.sleep(10000);
とかするよりよっぽどスマート.


・until

wait.until(ExpectedCondition condition);
wait.until()を呼び出す. この引数に以下に紹介する conditionを設定して渡してやればOK.
たとえば titlesで状態を設定したいなら,
wait.until(titles("期待するタイトル"));
としてやる.
使い方自体はシンプルなのだが, 問題はこの「期待する状態の設定」の方になる.


2. conditionの設定

・titles

public static ExpectedCondition<java.lang.Boolean> titleIs(java.lang.String title)
「ページのタイトルが titleに一致するまで」待機.
・titleContains

public static ExpectedCondition<java.lang.Boolean> titleContains(java.lang.String title)
「ページのタイトルに titleが含まれるまで」待機.
・presenceOfElementLocated

public static ExpectedCondition<WebElement> presenceOfElementLocated(By locator)
「引数に指定した locatorがDOMに現れるまで」待機. ただし待機終了時に, 必ずしもそれが画面に表示されているとは限らない.
・visibilityOfElementLocated

public static ExpectedCondition<WebElement> visibilityOfElementLocated(By locator)
↑に近く, 「引数に指定した locatorがDOMに現れるまで」待機.こちらは画面に表示されたうえで, それが0より大きい幅高さを持って存在している状態になるまで待機する.
・visibilityOf

public static ExpectedCondition<WebElement> visibilityOf(WebElement element)
visibilityOfElementLocatedに近いが, こちらは「引数に指定した elementがDOMに現れるまで」待機. ”画面に表示される”の定義も visibilityOfElementLocatedと同じ.
・presenceOfAllElementsLocatedBy

public static ExpectedCondition<java.util.List<WebElement>> presenceOfAllElementsLocatedBy(By locator)
「引数に指定した locatorが, ページ上に少なくとも一つ存在するまで」待機. 見えるかどうかは問わないらしい.
・textToBePresentInElement

public static ExpectedCondition<java.lang.Boolean> textToBePresentInElement(By locator, java.lang.String text)
「引数に指定した textが, 指定した locatorに textとして現れるまで」待機. 要するに指定した locatorでgetText()したら textがとれるようになるまで待機?
・textToBePresentInElementValue

public static ExpectedCondition<java.lang.Boolean> textToBePresentInElementValue(By locator, java.lang.String text)
↑に近いが, こちらは「引数に指定した textが, 指定した locatorの valueとして現れるまで」待機.
・frameToBeAvailableAndSwitchToIt

public static ExpectedCondition<WebDriver> frameToBeAvailableAndSwitchToIt(java.lang.String frameLocator)
「指定した frameLocatorに WebDriverが移動するまで」待機?そもそもframeがよくわかってないし, ここを見た分には, このメソッドでそのまま移動しそうな switchTo()が呼ばれてるけどどういうことなんだろう…
・invisibilityOfElementLocated

public static ExpectedCondition<java.lang.Boolean> invisibilityOfElementLocated(By locator)
今度は「引数に指定した locatorが, 見えなくなるかDOM上からいなくなるまで」待機. "見えなくなる"の定義はないのかな…(・ω・`)
・invisibilityOfElementWithText

public static ExpectedCondition<java.lang.Boolean> invisibilityOfElementWithText(By locator, java.lang.String text)
「 textで指定した要素が, locatorで指定したエレメントから, 見えなくなるかDOM上からいなくなるまで」待機, …でいいのかな.
・elementToBeClickable

public static ExpectedCondition<WebElement> elementToBeClickable(By locator)
「 locatorで指定したエレメントが, 画面上に表示され, かつクリックできる状態になる」まで待機.
・stalenessOf

public static ExpectedCondition<java.lang.Boolean> stalenessOf(WebElement element)
「 elementがDOMに付属しなくなるまで」待機. ページのリロードなどでDOMが置き換わるなどするときに, "指定した elementがDOMからちゃんと消えるまで"待つ, ってこと? 参考
・refreshed

public static <T> ExpectedCondition<T> refreshed(ExpectedCondition<T> condition)
このメソッド…無くなってる…?まぁ正直どういう動きか微妙に想像つかないけど(・ω・`)
・elementToBeSelected

public static ExpectedCondition<java.lang.Boolean> elementToBeSelected(WebElement element)
「指定した elementが選択された状態になるまで」待機. チェックボックスとかに使うんだろうけど, どこで判断してるんだろう…
・elementSelectionStateToBe

public static ExpectedCondition<java.lang.Boolean> elementSelectionStateToBe(WebElement element, boolean selected)
ほぼ↑と同じ気が…. 「指定した elementが selectedで指定した状態に選択 された/されていない 状態になるまで」待機. こっちで統一してもいい気がする…. 説明文とかコピペを疑うし…
・elementToBeSelected

public static ExpectedCondition<java.lang.Boolean> elementToBeSelected(By locator)
↑の locator版?「 locatorで指定したエレメントが選択された状態になるまで」待機, 当たりか. 説明すら放棄されましたorz
・elementSelectionStateToBe

public static ExpectedCondition<java.lang.Boolean> elementSelectionStateToBe(By locator, boolean selected)
「 locatorで指定したエレメントの選択状態が, selectedで指定した状態になるまで」待機, でいいのかな? もはや説明不要といわんばかりの勢い
・alertIsPresent

public static ExpectedCondition<Alert> alertIsPresent()
おそらく「アラートが出るまで」待機. 見たまんまでも説明がないと不安になりますよ…('A`)
・not

public static ExpectedCondition<java.lang.Boolean> not(ExpectedCondition<?> condition)
シンプルに否定. 「引数に指定した conditionの notの状態になるまで」待機. …ってこのメソッドも無くなってるし( ゚д゚ )使えそうとか思ったのに…(;ω;`)


とりあえず以上. なんか無くなってるのか違うとこ見てるのかわからないのが2つほどあったけど…






枠とか試してみたけど…レイアウトって難しい(・ω・`)

2013年3月13日水曜日

プルダウンメニューをWebDriverに選んでもらう

いわゆる

こーいうの
これをWebDriverに選んでもらいたいけどちょっと詰まったのでメモメモφ(・ω・ )

プルダウンメニューをWebDriverで選択する

たとえば, 
<select name="preview">
    <option value="0">おはよう</option>
    <option value="1">こんにちわ</option>
    <option value="2">こんばんわ</option>
    <option value="3">おやすみ</option>
</select>
こんな <Select>があったとすると
ただの Select- optionのプルダウンメニューならサポートしてくれているらしい.
Select selector = new Selector(driver.findElement(By.name("preview")));
これで準備完了. 試しに 「こんにちわ」 を選びたいなら,
selector.selectByValue("1");
selector.selectByIndex(1);
selector.selectByVisibleText("こんにちわ");
など, optionの value, id, 表示テキストで選択できる親切設計.

…ってここやこことかここを参考にしてみたけれど…(・д・`)できない

なんでだろうとみてみると
Element should have been "select" but was "input"
なんて警告.
なんか 「inputよこしてるぞゴルァ!щ(゚Д゚#щ) =3」的な話?

こことか眺めていると, どうやらカスタムしたような<select>には対応していないみたいな空気.
…べつにカスタムしてないと思うんだけどなぁ…(・ω・`)

とか思ってたら原因は
nameで locaterを指定していたら, <select>ではなく別の<input>を指定していた
模様. くだらなすぎる…orz
今一度 classや nameに被りがないかちゃんと確認を…(・ω・`)

ちなみにできないと思って探してみた違うやり方.
WebElement selector = driver.findElement(By.name("preview"));
selector.findElement(By.xpath("//option[@value='1']")).click();
いちおうこれでも選択はできた. できたけど…('A`)
まぁ 最後の手段 的なやり方ってことで…





というかプルダウンメニューがここに貼れたことにびっくり( ゚д゚ )

WebElementをもっと楽に探したい

テストを作っているとよく出てくる問題が
「同じようなタグの要素がいっぱいある…」「どう1個だけを指定したらいいんだ…」
なんて状態. リストが複数あるなかその中の要素を1個だけ選べとか言われても下手するとBy.xpath()使っても20や30も Listになって出てきたり, そもそもリスト以外の要素が引っかかったり…
そんな時だいぶ楽になる要素の探し方.

子要素の探索

要するにリストのように全く同じ要素が複数作成されるときは, 親要素に入れて管理している場合がほとんど(だと思う(・ω・`)). ならば親要素を特定してからその中を探索してやればすっごい楽になるよね!というお話. まぁ単純な話です. ちなみにコード中に出てくる Byについてはこちらにもメモがございます(宣伝
たとえば, 
<div class="parent1">
  <span class="child"></span>
  <span class="child"></span>
</div>
<div class="parent2">
  <span class="child"></span>
  <span class="child"></span>
  <span class="child"></span>
</div>
なんて要素の階層があったとする.
2つ目の divの中の2つ目の spanを探したい時, 直接探そうとするとかなりめんどくさい.
なので,
WebElement parent = driver.findElement(By.className("parent2"));
WebElement child = parent.findElemens(By.className("child")).get(1);
と, 先に親エレメントを特定してから, その下のエレメントを探してやることができる. コード的にだいぶ意味が通りやすくなるはず. なおもちろん driverは WebDriver.
これを「class="child"だけで見れば 4番目だから Listで取得して .get(3)だ!」なんて考えて
WebElement child = driver.findElement(By.className("child")).get(3);
こんなコードを書くとまず他の人には伝わらない. まして上の divに要素増えた時には…
Σ(||゚Д゚)ヒィィィィ

また, まとめて
WebElement child = driver.findElement(new ByChained(By.className("parent2"),By.className("child"))).get(1);
なんていう書き方も. これは
new ByChained(By locator, …)
を用いることで, 順に探している(らしい).


ただ問題はここまでのこと,  By.xpath()を使うとなんかうまくいかない…(・ω・`)なんでだろ







一番楽かつ安定なのは classなりわかりやすくつけて指定してやることなのよね…

2013年2月26日火曜日

fakeApplicationを使ってテストの初期値を作ろう

Selenium2でテストを続けるうちに出てきた問題.
DBの初期値やらリセットやらどうしよう…

前にjUnitでテストの段階わけのようなことができたので
「じゃぁ@Beforeでデータ入れて@Afterで消せばいっかなー(・з・)」
なんて考えてたら…

fakeApplication()で本体のメソッドを使う

当然本体ではDBにアクセス→参照やら挿入やらやってるもんだから, このメソッドもらえないかなーなんて考えてとりあえずテストの中で呼ぶ

→ There is no started application

…('A`)なんだこれ
どうやらテストは jUnit, 本体は Playの領分らしく, そのままではまたいで呼び出すことはできないらしい. あらめんどくさい. 呼び出せるアプリが起動していないから There is no started application(開始しているアプリはありません) ってことなのか?
そこで見つけたのが fakeApplication()など play.test.Helpersシリーズ. どうやら本体を仮に動かして中のメソッドをいじれるようにしてくれるっぽいという印象だけの理解('A`)
これ使えばわざわざテスト用に起動しなくていいのかも…
import static play.test.Helpers.fakeApplication;
import static play.test.Helpers.running;

running(fakeApplication(), new Runnable() {

      @Override
      public void run() {
         // 初期値作るメソッドやらなんやら
      }
    });
こんな感じ.

ただし今回は, confファイルで使うテーブルを指定しており, これをテスト用のテーブルを使う, と一緒に宣言を変えたいという追加設定.
最初は「別の confを宣言するのか?」なんて考えてまた迷走.
結局たどり着いたのが,
Map<String String> map = new HashMap<String String>();
map.put("confの中で使うテーブルを宣言している項目", "テスト用テーブルの設定");

running(fakeApplication(map), new Runnable() {
// 以下略
Mapを使ってアプリ起動時の application.confを一部書き換えて読み込む方法.
例えば db.default.driverなんていう, 使用する DBの Driverの宣言. これをテスト時には別のものに書き換えたい!なんてときは
map.put("db.default.driver", "別のドライバ")
としてやれば, fakeApplication()で呼ばれるアプリ中では "別のドライバ" を使ってくれる. まぁテスト時だけドライバ変えるのも珍しすぎる気はするけど… いい例えが思いつかなかった…
なお複数 putしていっぱい書き換えることも可能.
そして, 元になるのは通常起動時に読まれる application.confのはず.


そして git嫌い('A`)

OSによる分岐

いろいろいじってたSelenium2
共同開発でいじっていたので中にはMac等Win以外のユーザーもいるわけで.
それはいいのだけど
「chromedriver.exeとかMacじゃ動かないんだけど(・ω・ )っ/凵⌒☆チン」
「chromedriver.shにしてくんね?マダ-?(・ω・  )っ/凵⌒☆チン」

…ぉおぅ('A`)

というわけでOSによって呼び出すchromedriverを変えたいなー, というのが始まり

Java, OSの参照と分岐

chromedriverについてはこちらあたりで. 
特に難しいことはせず, シンプルにif文.
  public static void setChromeProperty() {
    if (SystemUtils.IS_OS_LINUX) {
      // Linux用処理
      return;
    }
    if (SystemUtils.IS_OS_MAC) {
      // Mac用処理
      return;
    }
    if (SystemUtils.IS_OS_WINDOWS) {
      // Windows用処理
      return;
    }
  }
ifの中身
SystemUtils
を参照する.
わりとわかりやすい名前で助かります.∩( ・ω・)∩

OSを判定し booleanが返ってくるのでそのまま ifに入れられるのもわかりやすい.

これだけ.

ちなみにこの SystemUtils. 結構細かいとこまで見てくれているらしく.
SystemUtils.IS_OS_WINDOWS_95
SystemUtils.IS_OS_WINDOWS_98
SystemUtils.IS_OS_WINDOWS_2000
SystemUtils.IS_OS_WINDOWS_ME
SystemUtils.IS_OS_WINDOWS_XP
などOSのバージョンまで見てくれたり.





…ここまでするアプリ作る予定は無いけどネ(・ω・`)

2013年2月25日月曜日

By.xpath中心のメモ

Selenium2でテスト作成時, 嫌でも使わなければならないものが findElement(By).
このByについてメモメモφ(・ω・ )

Byの指定

WebDriver.findElementは今開いているページからその引数で指定した要素( WebElement)を返すメソッド. なのでこの引数を正しく指定しないとエラーに泣くことになる.


By.id("id")
そのままidで要素を探す. なので必ずひとつで返るはず. 安定感は抜群.
By.className("class")
classで探すときは .class()ではなくこれ. っていうか .class()なんてない. 入力補完を使っていればまず無いけど, .id()等で考えが固まっているとつまらないミスでモニターとにらめっこすることになる.
By.name("name")
nameで探す. id以外は複数返る恐れがあるので注意. 複数あるはずなのに .findElement()で探した場合, ページソースを上から探して最初の要素だけ返ってくる. ちゃんと全部みたいなら
List<webdriver> elements = WebDriver.findElements(By by)
を使いましょう.
By.linkText("text")
ちょっと注意. 名前の通り<a>タグで囲まれたリンクのテキストだけを探す. ボタンの文字なんかは検索対象外なので注意.

By.xpath(String path)
この String pathの部分書き方が今回のメイン. たとえばこんなボタンが html上にあったとする.
<button class="btn" onclick='history.back()'>キャンセル</button>
なんてことは無いキャンセルボタン. ただidが振られておらず, classもありきたりな名前でもしかしたら複数取得されるかもしれない.
これを指定したいときは
WebElement element = driver.findElement(By.xpath("//button[@class='btn' and @onclick='history.back()']"));
となる
// → \\ではないので注意
button[条件] → 探したいのが<button>なのでbutton[条件]. もちろん<input>を探したいなら input[条件]となる.
@class='btn' → そのまま, <button>の中の要素 class="btn"を指す. @が無いとバグるので注意.
and → 複数の条件を設定する際に使う. すなおに and.
@onclick='history.back()' → メソッドだろうとお構いなしに条件に指定できる. まぁ文字列見てるとかなんだろうけど.
以降も and @~で条件をどんどん絞り込める.

これで指定できるはず. 一番いいのは idつけてやれば一意に定まるだろうけど, 自分じゃ修正に手が出せないときなんかはこれで. もちろん条件を増やせば絞り込めるけど, 特に classを複数設定しているような要素の場合, スペースや全半角でも条件がずれるので注意.



読みにくっ('A`)

2013年2月22日金曜日

Selenium2のテスト時間短縮?

前々回あたりでSelenium2のテストを書き始めたが, 毎回毎回ブラウザを立ち上げては消すのがなんとも時間がかかる.
なのでせめて一つのテストファイルの中ではブラウザの立ち上げ→終了は1回にして, 同じウィンドウを使い回せないかと考えた.
これでいいんだろうか…('A`)


Selenium2のテストの余計な時間の短縮


正直8割 jUnit4による解決

  private static WebDriver driver;

  @BeforeClass
  public static void setUpBeforeClass() throws Exception {
    driver = new FirefoxDriver();
  }

  @Before
  public void setUp() throws Exception {
    driver.get(initUrl);
  }

  @After
  public void tearDown() throws Exception {
    driver.manage().deleteAllCookies();
  }

  @AfterClass
  public static void tearDownAfterClass() throws Exception {
    driver.quit();
  }

Firefox版. 単純にjUnitのアノテーションに頼れば何とかなんじゃね?というだけのお話.
アノテーションのおかげで

@BeforeClass → (@Before → @Test → @After) * n → @AfterClass

という順序で呼ばれるので

ブラウザ起動 → (初期状態作成 → テスト → 事後処理) * n → ブラウザ閉じる

とすればいい.
コード的にも前々回使ったコードがほとんど.
driver.manage().deleteAllCookies();
このメソッドで作られてしまったクッキーを削除しているくらい. これを@Afterで行なうことで各テストで作られたクッキーを削除してやる.
@Beforeのところでテストしたい画面の初期状態を作ってやれば, 一つのテストファイル中は同じウィンドウを使いまわしてテストができるはず.

これで毎回テストメソッドごとにブラウザ立ち上げては閉じるなんて時間が大幅にカットできる.
実際変更前のテストごとにウィンドウを開く方法だと6分かかっていたテストも, 3分強程度まで短縮できている.
せいぜい30~40程度ののテストでこれなのだからさらにテストが増えたときの差は大きいはず!
+(・ω・0)*



どうみてもSelenium2の問題ではありません. 本当にありがとうございましたorz

2012年12月18日火曜日

Play! ~チュートリアルをやってみた後編

前回からのさらに続き. いい加減終わりたい(;ω;`)

7. Taskフォームの作成

今回はTaskクラスなのでTaskフォーム. UserクラスならUserフォーム. まんまですね(・ω・`)
イメージは中身のクラスを運ぶコンテナ的な何か.
準備としてはTaskクラスの方に
import play.data.*
が必要とのこと. しかし例ではいきなり必要な部分だけimportしてるし('A`)
準備ができたら今度はコントローラの方に,
 Form<Task> taskForm = form(Task.class);
を宣言. これにより, ビューとコントローラの間でのデータのやり取りができるほか, 制約を簡単に加えることができる.
制約に関しては, 例えばTaskでは必須入力項目としてタスクのラベルに制約をかけたいなら, Taskクラスの方で
@Required
public String label;
としてやるだけで, やれデータが入っているかー, なんてif文とか書かなくて済むのである.

8. トップページ描画

Taskフォームを用いて実際にページを見てみる. Application.java のtasks()の戻り値を,
return ok(views.html.index.render(Task.all(), taskForm));
Task.all()はstaticな関数, Taskのリストを返す. これとFormを投げて準備完了.
これでここまできちんとコピペしてきていたら, 適当にブラウザを立ち上げ 「localhost:9000」で見られるはず. (ちゃんと play run していれば)
ただしこの時点ではTaskのCreate機能を積んでいないので, 次の項目で作る.

9. Createボタンの実装

もう訳とかどうでもよくnこの項目でタスクの新規作成処理を積み込む.
Application.javaのnewTask(), この内容を作成する.
public static Result newTask() {
  Form<Task> filledForm = taskForm.bindFromRequest();
  if(filledForm.hasErrors()) {
    return badRequest(views.html.index.render(Task.all(), filledForm));
  } else {
    Task.create(filledForm.get());
    return redirect(routes.Application.task());
  }
}
さらっと書かれていてコピペしてスルーしてしまいそうなところだが, 実はイロイロ大事な部分が含まれているという罠. チュートリアル汚い.

2行目 bindFromRequest() これはリクエストからFormを取り出すメソッド. これをしないといつまでもこのクラスの最初に作成した空のtaskFormを参照してしまい, なにも表示されないことに折れるまで首を傾げるハメになる. もちろんリクエストが無いときは取れません.

3行目 filledForm.hasErrors() 2行目で取得したものにエラーが無いかチェックするメソッド. エラーがあったらtrueが返り, 4行目のようにバッドリクエストを返すようにする大事な処理.

6行目 filledForm.get() これでFormの中に含まれるTaskを取り出す. Formのまま投げないように. この6行目でTaskクラスから新規タスクを作成する.


10. データベースによるTaskの保存

正直わかんないorz なんでいきなりH2DBが絡んでくるのよ…('A`) 前に使ってH2DBの準備してたからいいけど, これ準備無かったらどうなるんだろう…
超簡単に.
conf/application.conf, ここまでずっと横をスルーされてきたこいつをいじる. 中身はいろいろ必要なことが書かれているので下手にいじると爆死します. "#" がその行のコメントアウト. チュートリアルではaddと言っているが, 既に書かれた上でコメントアウトされているので, 混乱を避けるためにも#を外して対処しましょう. ちなみにその下にパスワード設定なんかもありますがいじって無いので省略.  このちょっと下に次の設定もコメントアウトされているのでこちらも対処.
その後Taskクラスをチュートリアルのように書き換えると, 見事タスクが増えるようになりました.
うん, よくわからんorz.

11. タスクの削除

これ逆に章分ける必要あるのか?
DBへの接続の設定は済んでいるので, 削除部分をApplication.javaへ書き込む.
以上.


デプロイはやっていないので今回は省略. なのでここで終了.
こっちを見るとより詳しく見ることができる.





最後グダグダだぁ…('A`)



2012年12月17日月曜日

Play! ~チュートリアルをやってみた中編②

はたしてここで終われるのか…終われなかった…orz
前回からの続き.


3. 開発ワークフロー

直訳でも気にしない. 要は app/controll/Application.java をいじるとこうなるよ! ってお話. 前回の1. でやっちゃった気がしなくも無い(・з・)
1. では他のViewを呼び出していたけど, Stringを直に放り込むことも可能. この場合Content-Typeは text/plain となりなんとも味気ないページがでてきます.
また, コードを変えたときにコンパイルとサーバ再起動を自動でやってくれるのはPlayの利点の一つ. 特にエラーが起きたときや構文エラーがあったときは, ブラウザ上にその箇所を表示してくれる親切設計. ただそのことを,
「As you can see, errors are beautifully displayed directly in your browser.(ご覧の通り, エラーはあなたのブラウザ上に直接美しく表示されます)」
美しくって…. しかもエラーによっては文字化けするし('A`)

4. アプリケーションの準備

直訳でも(ry. conf/routesを編集することで, アクションの振り分けができるよー, ってお話.
また最終行の,
POST    /tasks/:id/delete       controllers.Application.deleteTask(id: Long)
ここで, アクションに引数が持たせられる他,  URL部分に「:id」と入れると, 引数に使ったidをURLに埋め込むことができるよ!(・∀・)といったことも.
当然このconf/routesだけをいじってもアクションのほうを書かないとエラー吐かれるよ, ってことをわざわざ3. で出たブラウザ表示を用いて例示している. 当然ですね('A`)
なのでapp/controller/Application.javaにアクションを書いていきましょう. この時, まだ内容が決まっていないなら,
return TODO
としておくことで, とりあえずコンパイルは通るようになるよ, ということらしい.
ちなみにこのまま走らせると 「501 Not Implemented(未実装)」 が返ります. 便利.
最後はリダイレクトのお話. Viewを通さず, 別のアクションを呼びたいときは,
return redirect(routes.Application.tasks());
と書くことで, 直接conf/routesに飛び, アクションの振り分けから再開できるよ!(・∀・)ってこと.
このときのステータスはちゃんと 「303 See Other(他参照)」 でリダイレクトされる. これまた便利.

5. Taskモデルの準備

直(ry. ようやっとModelの登場. しかしデフォルトではディレクトリは作ってくれないので, まずは app/models ディレクトリを作る必要がある.
ディレクトリを作ったら中に Task.javaを作成. それだけ. ( ゚д゚ )
このあたりで今回では終わらないことを悟り 中編②とか妙なタイトルに変更. どうしてこうなったorz.


6. アプリケーションテンプレート

逆に訳すに訳せない('A`) Todoリストを表示するためのHTMLを作成する…のだが.
なんかやたらすっきりしてる. 2. でもちょっと書いたが, @~というのはPlay!フレームワークが関数のように扱ってくれて, 自動でHTML文を生成してくれる便利なツールとの事. ただし使うためには,
@import helper._
を宣言しておく必要がある.
中でも, @form は重要. この宣言だけで<form>タグを生成してくれる. マジ便利. もちろん例のように引数を放り込むことも可能.
また, 他にもテキストボックスなど<input>タグシリーズを生成する@input等もある. これもまた便利.
チュートリアルにある最後の数行,
@form(routes.Application.newTask()){
  @inputText(taskForm("label"))
<input type="submit" value="Create">
}

この宣言で,
<form action="/tasks" method="POST">
  <dl class=" " id="label_field">
    <dt><label for="label">label</label></dt>
    <dd><input type="text" id="label" name="label" value=""></dd>
  </dl><input type="submit" value="Create">
</form>
とこれくらい作ってくれる. idなんかを追加で指定してやることも可能.
ちなみに生成するHTMLをカスタムすることもできるらしい.
@inputは他にも@inputPasswordなんてのもあるが, ボタンに関してはまだ見つかっていない.
無いのかな…(・ω・`)






次だ…次で終わらせる…('A`)


2012年12月13日木曜日

Play! ~チュートリアルをやってみた中編

前回に引き続きPlay!チュートリアル.
ついにコードをいじり始める


Playちゅーとりある後編


0. 準備いろいろ

とりあえず前回の3. でサーバが run している状態から続き.
run していなければとりあえず run.
そしてもうひとつ必要なことが, MVCモデルの理解.
理解といっても詳細に説明できるほど, なんてことはなく, 
「まぁ役割に応じてそれぞれ管理するところを分けてんだへぇー('A`)」
くらいの認識があれば十分.(なはず(・ω・`)大丈夫かな)
詳しくはグーグル先生あたりに聞くといろいろ教えてくれるはず.
まぁMVCが
「M(モデル), V(ビュー), C(コントローラ)」
を表現してることだけ知ってれば自分はどうにかなってる('A`)たぶん

1. アクションとコントローラ

まずはアクションとそれをまとめるコントローラのお話らしい. もう少し構造について話してからでも遅くない気がするんだけどなぁ…(・д・`)
conf/routesファイルをテキストエディタあたりで開くとチュートリアルにあるような文が見られるはず.
GET /       controllers.Application.index()
いきなりなんじゃいorz特に右のお前.
これだけだとらちが明かないのでapp/controllers/Application.javaも隣に開いてみる.
すると中にはjavaコード. よく見てみるとなにやらメソッドが入ってる.
  public static Result index() {
    return ok(index.render("Your new application is ready."));
  }
このindex(), これがちょうど先ほどroutesファイルに入ってた右のあいつにあたる.
なのでApplication.javaではなく, Hoge.javaのindex()を呼びたいなら,
GET /       controllers.Hoge.index()
になるはず.
そして左はリクエストの情報. なのでチュートリアルのほうで和訳すると,
「GETメソッドで localhost:9000 でアクセスしてきた時, controller.Application.javaのindex()を呼び出すよ!(・∀・)」
という設定を書いてることになる.

そしてApplication.index(), こちらが返すResult型がresponseとして扱われる, との事.
return しているものも簡単に和訳すると,
「index に"Your new application is ready."というStringを与えて, ステータス200 OK で返すよヽ(・ω・)/」
って事らしい, ok の部分を badrequest などに変えるとステータスを変えてreturnできます, index.render()についてはもうちょっと後に出てきます.

簡単にまとめると,
 conf/routes -> リクエストに対するアクションの振り分けをします.
  app/controllers/ -> アクションを内包するコントローラクラスを入れます.
ってことらしい.
ちなみに今更だけど, ここではアクションとはindex()なんかのメソッドにあたります. たぶん(・з・)

2. ビュー

別にチュートリアルでは分かれて無いけど. だいぶ冗長なので.
こんどはapp/views/index.scala.htmlをテキストエディタで開いてみましょう. ファイル名気持ちわる('A`)
ちなみにこいつが1. ででてきた index.render() のindexにあたる.
なので main.render() にするとすぐしたの main.scala.html を指定することになる.
さてindex.scala.html の中身はというと…
@(message: String)

@main("Welcome to Play 2.0") {
    
    @play20.welcome(message)
    
}
これだけ.
これまた @ だらけで良くわからん.
少しずつ見ていくと, まず最初の
@(message: String)
いきなりわけがわからないが, なにやら変数の宣言にも見えなくは無いか.
しかしこれはどちらかというと引数の宣言に当たる.
すなわち, 1. のApplication.index() で与えられたString「"Your new application is ready."」はここで message に格納される.
ここで引き受けたら後は5行目のように, コード内で使えるようになる.
そして
@main("Welcome to Play 2.0") {
    
    @play20.welcome(message)
    
}
mainはちょうどmain.scala.htmlにあたる, 後ろの2つは引数. 
 @play20.welcome(message)
こいつは良くわからない('A`)が働きとしては message を引数にデフォルトのページのhtmlを返すメソッドとして働く.
ということは@main以下は,
「main.scala.htmlに, String型 と Html型 2つの引数を投げてここに貼るよ!(・з・)」といったところ.
なのでmain.scala.htmlも開いて確認してみる.
@(title: String)(content: Html)

<!DOCTYPE html>

<html>
    <head>
        <title>@title</title>
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
        <script src="@routes.Assets.at("javascripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
    </head>
    <body>
        @content
    </body>
</html>

はい, 一番上にStringとhtmlを受け取る準備ができていました.

これが大体の流れ. チュートリアルの内容にだいぶいろいろ足したけど('A`)
おかげで終わらなかったorz

続きはまた次回にでも.







次で…終わらない気がするーヽ(;´Д`)ノ

2012年11月28日水曜日

ViewFlipperでページめくり ~おまけ編

ここからはおまけ.

前回の続きでフリックでページをめくる処理を実装する.
要するにフリックイベントを取りに行くわけでもはやViewFlipperほとんど関係ない気がする('A`)

OnGestureListenerをもちいたフリックイベントの取得

ついにタイトルすらViewFlipperから遠のいたけど気にしない.
実装するのはActivity. ここに

implements OnGestureListener

してやる. 当然 eclipse「メソッドのオーバーライド足りてねぇぞゴルァ(゚Д゚#)」と怒られるので言われるままに作成.
ただし今回使うメソッドは2つのみ.

① onFling
② dispatchTouchEvent

特に②が忘れられがちなので注意

① onFling

実際に受け取ったフリックイベントから処理を行なうクラス. 

@Override
public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) {
    float dx = Math.abs(e1.getX() - e2.getX());
    float dy = Math.abs(e1.getY() - e2.getY());
    if (dx > dy) {
      if (velocityX > 0) {
        flipper.setInAnimation(slideInFromLeft);
        flipper.setOutAnimation(slideOutToRight);
        flipper.showPrevious();
      } else {
        flipper.setInAnimation(slideInFromRight);
        flipper.setOutAnimation(slideOutToLeft);
        flipper.showNext();
      }
      return true;
    }
    return false;
}

たまにはsyntaxhighlighterの色を変えてみたかった(言い訳)ので参考サイトをほぼコピペを更にコピペというだめっぷりorz
3行目, 4行目でフリックのX方向, Y方向の移動距離をそれぞれ取得,
5行目で比較してX方向のほうが大きい=横にフリックする気でフリックした 時のみ処理を行なう.
6行目でフリックのベクトルが右方向か左方向かを判定, 右なら1ページ戻る, 左なら1ページ進む, を処理させる. 処理の内容は前回参照.
それだけ. これでイベントが飛んでくれば処理してくれる.

②dispatchTouchEvent

一度ここを設定し忘れて「あっれーうごかないぞー(゜д゜;三;゜д゜)」とか言ってたことがあるのでorz
まずonCreateあたりで

GestureDetector gestureDetector = new GestureDetector(this, this);

を取得.
そして問題のdispatchTouchEvent
@Override
public boolean dispatchTouchEvent(final MotionEvent event) {
  return gestureDetector.onTouchEvent(event) || super.dispatchTouchEvent(event);
}

eclipseの自動生成に出てこない引っ掛けっぷり. ひどすぎる('A`)
コイツを作っておかないとそもそもイベントを取ってくれませんorz


これらを設定してやると, おめでとう!ボタンで動いていた画面がフリックで移動するようになったぞ!
となる.

onFlingの3行目, 4行目でとったように, e1.getX()でとった座標で条件分岐してやれば, 特定の範囲でフリックしたときのみイベントを取るなんてのもできそう. やってないけど(・ω・`)





長かった…('A`)

ViewFlipperでページめくり ~動作編


前回からの続き. ようやくページめくりのお話.

B. コード上で動きを指定

とりあえず

ViewFlipper flipper = (ViewFlipper) findViewById(R.id.flipper)

で ViewFlipperを取得.
ページを送る/戻す処理は

flipper.showNext()/flipper.showPrevious()

これだけ.
これをボタンのクリックイベントだったり画面のフリックイベントだったりに呼ぶようにしてやれば, それだけで次の/前の子ビューを表示してくれる.便利!

ただこれだけだと一瞬でページが切り替わる非常に味気ないページめくりに…
なので折角だしアニメーションを仕込んでみる.

手順は
① アニメーションの情報を仕込んだxmlを作成
② ①を基にflipperにアニメーションをセット
の2本立てでお送りします

①xmlを作成

まずはres/animを作成. 基本自動生成はされていないので自作のはず.
ここにAndroid XML File を作成します. RootElementは「translate」.
内容は,

<?xml version="1.0" encoding="utf-8"?>
<translate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:fromXDelta="-100%p"
    android:toXDelta="0%p"
    android:fromYDelta="0%p"
    android:toYDelta="0%p">
</translate>

ためしに左からページが画面に入ってくるアニメーション.

android:durationがアニメーションに書ける時間(ミリ秒)
XDelta, YDeltaはそれぞれViewの左上の座標の位置を指定. 0%pで今写っている画面の左上を指す.
fromがアニメーション開始時の位置, toがアニメーション終了時の位置を指す.
のでこのアニメーションは,
画面外ちょうど画面1枚分左から, 500ミリ秒かけて画面ぴったりに収まるように入ってくる
アニメーションとなる.
ちょっと指定を変えれば出ていく, 逆方向の動きはもちろん, 斜めからスライドイン/アウトだって指定できちゃう(゚∀゚)ラクチン!

これを,
1. 左から入ってくる
2. 右へ出て行く
3. 左へ出て行く
4. 右から入ってくる
の4タイプ分用意します.

②flipperにアニメーションをセット

xmlを用意できたらいざセット

Animation slideInFromLeft = AnimationUtils.loadAnimation(this, R.anim.slide_in_from_left);
Animation slideOutToRight = AnimationUtils.loadAnimation(this, R.anim.slide_out_to_right);
flipper.setInAnimation(slideInFromLeft);
flipper.setOutAnimation(slideOutToRight);

slide_in_from_leftがちょうど①に挙げたxml. 同様にAnimation を作成し, InとOutそれぞれflipperにセットしてやる.
新しいページの入り方と古いページの出方…のはず(・ω・`)
これでOK.
ただしIn, Outそれぞれアニメーションは1個ずつしか登録できないため, 上記のコード全部で右にページを送る処理ひとつ.
左にページを送る逆向きの処理を同じ flipperにセットしたい場合, 処理を呼び出すごとにセットしてやらないと, 最後にセットしたアニメーションのみ実行することになります.
なので1ページ戻るアニメーションを実現するなら,

 flipper.setInAnimation(slideInFromLeft);
 flipper.setOutAnimation(slideOutToRight);
 flipper.showPrevious();

これ全部を「戻る」ボタンあたりのonClickで呼んでやる必要がある.逆もまた然り.


これで一応アニメーションしながらページめくりはできる. できるけど…なんか違う.
やっぱりフリックでページをめくりたい!


なのでさらに続けてみます.







あともうちょっとだけ続くんじゃ('A`)

ViewFlipperでページめくり ~ページ作成編

ホーム画面やらiPhoneやらでおなじみ横フリックでページめくり.
これを利用して, 数ページに分割したランキングを作りたい! と調べてみたら, ViewFlipperなんて便利そうなViewを見つけたヽ(・ω・)/.

ViewFlipperで簡単ページめくり


必要なものは
A. <ViewFlipper>タグを用意
B. コード上で動きを指定

まずはxml

A. <ViewFlipper>タグを用意

使いたいActivityのレイアウトに

<ViewFlipper
       android:id="@+id/flipper"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" >
</ViewFlipper>

これだけ. 実に簡単. ちなみにこの外にボタンやらなにやら置いてもOK. Layout内で<ViewFlipper>タグが陣取ったエリアだけでページめくりしてくれます.

ただしこのままだとめくる「ページ」が無いので, 中に子ビューを入れる.
入れる方法は2通り.

①. このままxmlに突っ込むぜ!
② いやコード上で動的に作りたいんで

①はまた楽. <ListView>なんかと同じようにタグの間に宣言するだけ.
例えば…

<ViewFlipper
       android:id="@+id/flipper"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" >
       <include android:id="@+id/flipper_child" layout="@layout/flipper_child"/>
</ViewFlipper>

<include>を使うと見やすくまとまってよろしいかと.flipper_child.xmlはまた別にLayoutを作って用意しておけばOK. ただ<include>は2個以上入れないとページめくりになりません. まぁ当たり前だけど('A`)
ページ数が決まっているならばこっちがオススメ.

②はちょっとめんどい. 追加自体は

ViewFlipper flipper = (ViewFlipper) findViewById(R.id.flipper);
flipper.addView(childLayout)

問題はこのchildLayout.すでにLayout自体ができているならひたすらViewを作成→addViewで良いのだが, それなら<include>使ったほうが誤爆は少ない.
また今回は順位表のように決まったLayoutを使いまわして作りたかったうえに, 何ページになるかが決まっていない始末.仕方が無いので子ビューのLayout.xmlを一枚テンプレート的に用意して動的に内容を作って addしていく.


View childLayout = this.getLayoutInflater().inflate(FLIPPER_CHILD_LAYOUT_ID, null);
ListView list = (ListView) childLayout.findViewById(R.id.list);
list.setAdapter(listAdapter);
list.setScrollingCacheEnabled(false);
list.setOnItemClickListener(itemListener);


完璧今回使ったもののコピペ. 手抜きではありません. ええ決して.
1行目で子ビューを新規生成. FLIPPER_CHILD_LAYOUT_IDは子ビューのLayout.xmlのid. 第二引数は今回は特に親のViewGroupをとらないのでnull…で良いのかな(・ω・`)
2行目は子ビューのLayout.xml内に自分で設置したListViewを取得. childLayoutからfindViewByIdしている点には注意.
3行目はそのListに値をぶち込む処理. Adapterに関しては今回はノータッチ. ただ何ページも作る際はAdapterを毎回新しく作るなどちゃんとしておかないと同じ内容のデータが延々と並ぶ苦行のFlipperが完成するので注意.
4行目は完全にListViewの設定. これをしておくとListViewスクロール時, 背景の色が変わるアレを無くすことができます. 背景色を指定しているときなんかに合わせてどうぞ. Flipper関係ねー.
5行目もListの OnItemClickListener のセット. ここで毎回ちゃんとしておくとどのページのListViewもイベントが取れるようになります. ただListenerの中でgetitemする際は,

ListView list = (ListView) parent;
Object item = (Object) list.getItemAtPosition(position);


しておかないとうまく取れないかも…
ともあれこれでFlipperにページを収められたはず.

…長くなっちゃったのでページを動かす処理は次回…


2012年10月29日月曜日

Androidで設定を参照, 操作するあれこれ -画面の輝度編-

忙しかった…余計な仕事は背負い込むもんじゃないね('A`)


だいぶ間が開いてしまったけど
久々にAndroidさんに怒られたので書いてみる.


画面の輝度の操作


簡単に言ってしまえば「画面の明るさ変えるよ(゚∀゚)」ってだけなこと.
一昔前にこれ利用したライトアプリなんかあったとか無かったとか.

いつもどおりAndroidさんの設定に手を加えるべく.お邪魔するのは

import android.provider.Settings.System;

Androidの設定は基本ここにお願いなことはいい加減わかってきた.

1. 設定の変更


値が入っているのは

System.SCREEN_BRIGHTNESS

今回はここ.
ここの値をputしてやれば良いのだが値に注意.

まず値は
0 ~ 255
であり, また機種によっては
0 = ブラックアウト
と認識して何も見えなくなってしまうらしい点.
なので値は
1 ~ 255
にしておくといろいろ安心.

また0% ~ 100% で表記したいなら自分で計算してやるよろし.
というわけで


System.putInt(ContentResolver, System.SCREEN_BRIGHTNESS, "値");

当然値はint型でok.

2. 画面の明るさの変更


お前1. でやったじゃん と言う無かれ.
1. はあくまで 設定の変更 だけで, すぐにその設定が反映されるかといえば答えはNO.
なのですぐに反映してやりたいとき, また1. を利用する際, デモンストレーション的に明るさを変えたいときにはこちら.

ただしここで怒られた.
実は1. も2. もAsyncTask使ってUIスレッドの裏で処理をさせようとしたところ

おまUIスレッド以外からUIイジってんじゃねーよ ゴルァ!!щ(・Д・´#щ) =3

と怒られましてorz
1. はともかくこれからいじる2. に関してはUIスレッド上でやりましょう, ってお話.

ひとまずソース

LayoutParams layoutParams = getWindow().getAttributes();
layoutParams.screenBrightness = "値";
getWindow().setAttributes(layoutParams);

やってることは割りと単純.
1行目で現在の画面の情報を取得.
2行目でその情報のうち 画面の輝度 に好きな値を投入(ただしfloat型かつ0.0 ~ 1.0の間という統一感の無さ)
3行目で反映…のはずなんだけど…なぜか無くても変わる!不思議!

…なんでだろ(・ω・`)

まぁとりあえずそのActivityくらいしか明るさは保たないので一時的なしのぎにでも.
最初に述べたライトアプリなんかにはむしろ都合が良いとか何とか.



まただらだらと書いてしまった…('A`)



2012年10月15日月曜日

Androidで設定を参照, 操作するあれこれ -バッテリーwithService編-

以前のバッテリー情報取得のその後.
Activityの内部クラスにしとけばいいや, 参照しやすいし.
などと考えていたら

Serviceに乗せなさい



orz

通達が来ましたとさ.


Serviceを用いてBattery情報を得る

当初は
「ServiceからActivityを書き換えるってどうやるんだ…?Binder?ServiceConnection?Messengerを使えば値を渡せるのか!」
などと迷走を続けていたところ

sendBroadcastでえぇやん(意訳
でないと汎用性下がるし

と2度目のorz
ほぼ一日かけてたどり着いたMessengerは無に返り, 晴れてsendBroadcastによる実装が残りましたとさorz

という愚痴を書きたかっただけ.

1. Service側

まずはService側. いたって単純で, Intent.ACTION_BATTERY_CHANGEDを受け取ったらオリジナルのIntentに必要な情報を載せて発信するだけのもの.


public class BatteryService extends Service {


    public static final String ORIGN_ACTION = "orignbatteryaction";

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(final Context context, final Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
                int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
                // オリジナルのActionでintentを作成
                Intent sendIntent = new Intent(BatteryService.ORIGN_ACTION);
                // levelを載せる
                sendIntent.putExtra("level", level);
                sendBroadcast(sendIntent);
            }        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
        registerReceiver(mReceiver, filter);
    }

    @Override
    public void onStart(final Intent intent, final int startId) {
        super.onStart(intent, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mReceiver);
        Log.i("hogeBatteryService", "unregisterReceiver");
    }

    @Override
    public IBinder onBind(final Intent arg0) {
        return null;
    }
}

たまにはまるっと載せてみる.mReceiverの中, コメントにある通りACTIONの部分を自作してやるだけ. 名前はユニークになるように. 万一被ると大変なことになる…かも?('A`)
後はintentを送る際,

startActivity(sendIntent)

ではなく
sendBroadcast(sendIntent)

を用いれば, 行き先を指定しないIntentの出来上がり.

2. Activity側


こちらもほぼ同様.


public class ResidureBatteryCheckerActivity extends Activity {

    private static TextView textView;

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_residure_battery_checker);
        textView = ((TextView) findViewById(R.id.hoge_battery_check));
    }

    @Override
    public void onResume() {
        super.onResume();
        // バッテリー情報更新のサービスを開始する
        Intent batteryIntent = new Intent(this, BatteryService.class);
        if (startService(batteryIntent) == null) {
            Log.e("hogeMakeFirstSetting", "cannot Start Service");
        } else {
            Log.i("hogeMakeFirstSetting", "Service is Start!");

            // ServiceからのBroadcastを受け取るReceiverを立てる
            IntentFilter filter = new IntentFilter();
            filter.addAction(BatteryService.ORIGN_ACTION);
            registerReceiver(orignReceiver, filter);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(orignReceiver);
        stopService(new Intent(this, BatteryService.class));
    }

    public static void changeText(final String message) {
        if (textView != null) {
            textView.setText(message);
        }
    }

    // オリジナルのIntentを受け取るReceiver
    private final BroadcastReceiver orignReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(final Context context, final Intent intent) {
            if (intent.getAction().equals(BatteryService.ORIGN_ACTION)) {
                int level = intent.getIntExtra("level", 0);
                String message = "level is " + level;
                changeText(message);
            }
        }
    };
}

なんてことは無く, ただReceiverを立て, ただオリジナルIntentを拾い, 情報を取り出して好きに使うだけ.
なおReceiverはonDestroyなどできちんとunregisterReceiverしましょう. でないとちゃんと休ませろって警告が来ます. 主にLogに.Serviceはお好みで. ただ止める手段がないと走り続けてバッテリーがマッハ.ヽ(;´Д`)ノ








…Messengerを使ったものはこの倍はコードがあったわけで, 結果としては良かった…のかな.
Handlerとか良くわかんなかったしorz

2012年10月14日日曜日

Androidで設定を参照, 操作するあれこれ -Wi-fiの小ネタ編-

DeveloperのSettings.Systemのあたりを漁っていたら見つけたので


Wi-fiのスリープ設定

正直こんなことも無ければ知らなかったであろう機能.
簡単に言ってしまえば「本体がスリープしたときWi-fiもスリープする?」という設定ができる機能が本体の設定の中にあったわけで…


一生懸命画面のOffのIntentとってなんとかしようとか考えてたのは一体…orz


ちなみに設定までの行き方は
「Wi-fi設定」でメニューを開き「詳細設定」へ…

メニューて…('A`)

とまぁなかなかエキセントリック遠い所に設定があるのでボタン一発で書き換えてやろう, と考えた次第.


とりあえずコード.まずは設定の取得から.


android.provider.Settings.System.getInt(contentresolver,android.provider.Settings.System.WIFI_SLEEP_POLICY,-1);

長い…
画面の設定編でもやった通り,

import android.provider.Settings.System;
しておくと楽かと
parmissionも WRITE_SETTINGS だけでOK(なはず).
内容もいたって簡単.WIFI_SLEEP_POLICYの今の設定をintで返すだけ.
設定は3種類.


0 = WIFI_SLEEP_POLICY_DEFAULT : 画面のスリープと一緒にスリープする
2 = WIFI_SLEEP_POLICY_NEVER : スリープしない
1 = WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED : 電源につながっていればスリープしない

の3つ.

設定の書き換えも
android.provider.Settings.System.putInt(contentresolver,android.provider.Settings.System.WIFI_SLEEP_POLICY,values)

のvaluesに0から2の好きな値を入れてやればOK.
boolean型が返ってくるので, ifで囲って書き込み成功/失敗をとってやればより安全にできるかと.



…parmissionあってるかな…(・ω・`)

2012年10月10日水曜日

Androidで設定を参照, 操作するあれこれ -画面の設定編-

今回はいったいいつ使うのかわからないネタ


android端末の画面の設定を操作


うん…普通に設定開けば良いじゃんとか言わない.アーアー<'(;´Д`)>キコエナイー
見つけて使ってみたから書く…うん


0. 下準備

まず始めに準備するのが.ManifestのUse-Parmission.
ここに

android.permission.WRITE_SETTINGS

これを許可してやる.
許可しないと基本設定を書き込めません.
一部読むだけなら要らないのもあるけれど…

そして


import android.provider.Settings.System;

をimportしておくことをお勧めする.
でないとSystemと一口に言っても, System.out.printlnでおなじみのjava.langや, android.osなどにもSystemがある.
そのため, 以降別のSystemを眺めながら「そんなメソッド見つからない!」とかIDEさんに理不尽に怒られる可能性があります.('A`)コレハヒドイ

1. 画面の点灯時間の設定

いわゆるオートスリープまでの時間の設定.
まずは, 今の設定を呼び出すメソッド.

System.getInt(getContentResolver(),System.SCREEN_OFF_TIMEOUT)

これだけ.
もしくは,
System.getInt(getContentResolver(),System.SCREEN_OFF_TIMEOUT, -1)

これだけ.
最初の引数はContentRe
最後の引数の-1は今回は特に関係ないようです.getIntによくあるデフォルト値?
ちなみに-1は「消灯しない」の設定みたい
第一引数のContentResolverはともかく
第二引数のSystem.SCREEN_OFF_TIMEOUT, これでどの情報にアクセスするかを設定しているみたい.

これで現在の設定が取得可能. 取得はミリ秒単位.

取得したら今度は設定.


System.putInt(getContentResolver(), System.SCREEN_OFF_TIMEOUT, 設定したい時間(ミリ秒));

これだけ.
設定したい時間に好きな値を放り込めばOK.
本体の設定アプリにない値も設定できます.



だれか一年とか設定してみた人いないのかな|・ω・`)チラッ


2. 画面の回転の設定

Notification Barを引っ張れば良いとか言わない.
Widget出せば良いとか言わない.

System.getInt(getContentResolver(),System.ACCELEROMETER_ROTATION)

まずは設定の取得. 既視感?少し上を見れば解決するかと.
基本Settings.Systemはこのメソッドで何とかなりそう.
返ってくる値は0(ロック状態), 1(ロック解除状態)の2種類.
表示するなら一工夫必要.

そして設定.

System.putInt(getContentResolver(),System.ACCELEROMETER_ROTATION, 0 or 1);

大方の予想通りでございます. はい.
0 or 1 でロック or アンロック を指定できます.

非常にシンプルなのでボタン一個で設定できるのもうなずけます.

とりあえずはこのあたり.











コード少ないなぁ…(・ω・`)

2012年10月9日火曜日

Androidで設定を参照, 操作するあれこれ -バッテリー編-

久々のAndroidキター
ただいまjava!私は帰ってきた!+(・ω・0)*




まあメモるのは初めてなんですがね(;´Д`)


内容は

androidの設定を操作するアプリの作成

ということでそのパーツ毎にちらほらとメモしていく予定


まずはバッテリー編からφ(・ω・`)

android端末のバッテリー情報を取得


端末のバッテリー情報を取るならIntent.ACTION_BATTERY_CHANGEDのお世話になるのが楽.
…他にも方法あるのかな

このIntentは, システムが「バッテリーの残量や状態が変更された時」, その情報を載せてIntentを発行していてくれている(らしい)

なのでこれを受信する受信機を用意してやればいい, とのこと

そこで登場するのがBroadcastReceiverクラス
ブロードキャスト化されたIntentを拾えるこのクラスを継承して, BatteryReceiverなるクラスを作ります

1. Receiverの作成


public static final class BatteryReceiver extends BroadcastReceiver {

  @Override
  public void onReceive(final Context context, final Intent intent) {
                        // actionの取得
   String action = intent.getAction();
                        // actionの確認. Intent.ACTION_BATTERY_CHANGED以外のintentは排除する
   if (!action.equals(Intent.ACTION_BATTERY_CHANGED)) {
    Log.d("BatteryReceiver ", "action is " + action);
    return;
   }

   // 状態, 「充電中」等の状態がintで取れる
   int status = intent.getIntExtra("status", 0);
   // 電池残量, デフォルトは0.
   int level = intent.getIntExtra("level", 0);

   Log.d("level", level + "%");
  }
 }

とりあえずシンプルにバッテリーの状態と残量(%)だけを取得するクラス.
このレシーバーを立てておく. 立て方は後々
Intent.ACTION_BATTERY_CHANGEDを受け取るとonReceiveが呼ばれて処理スタート.
まずはコメントにあるとおり, 余計なIntentはreturnで排除.
・・・余計なIntentって入ってくるのかな.
その後はもうお好きに.
Intentからお好みの情報を引き出して操作するだけ.
Intentに含まれる値に関しては参考を参照.

参考:http://www.adakoda.com/android/000140.html

また, このonReceiveはオーバーライドメソッドなので, ココ以外に値を持ち出すときは一工夫必要.
上記をまるっと内部クラスにしてやるのが一番扱いやすい・・・?

ほかにもServiceクラスに乗せるなどの使い方も.
ここで得られる値もWidget向きだったりするのでServiceで使うのもアリ・・・?

疑問系ばかりであるorz

2. Filterの登録


BatteryReceiver receiver = new BatteryReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(receiver, filter);

Receiverが出来たら, 今度はIntentFilterをかけてやる. こちらはActivityならonCreateやonResumeなどで宣言してやるといい.
1,2行目で作ったReceiverとIntentFilterをインスタンス化.
3行目の addAction メソッドで件の Intent.ACTION_BATTERY_CHANGED をfilterが受け取ることを宣言.
4行目でそのfilterとreceiverを関連付けて宣言は終了.
あとはこのメソッドを通るように起動させればReceiver起動.


IntentがくるたびにReceiverが動いてくれる.

3. Receiverの破棄

unregisterReceiver(receiver);
これだけ.
これをonDestoryなど, もう要らないと判断される時に呼べばいい.
ただonPauseがいいのかonDestoryがいいのかは微妙….
もっと他にいいとこあるかも…?

これで必要なパーツはそろい踏み.

Widgetなんかにしてバッテリー残量によってアクションが変わるアプリなんかが作りやすいかも.








…アプリ自体がバッテリー食うのは内緒( ´д)ヒソ(´д`)ヒソ(д` )

AWS CDKで立てたEC2インスタンスのTimeZoneとかいじりたかった話

EC2を立てることはできたけど、立てたインスタンスは UTCのままだし設定ファイルとかいちいちscpしてくるのはダルい。 当初UserDataでなんとかしようとしたものの、「書く量がヤバいしメンテしにくい」と悩んでいたところ見かけたのが  AWS::CloudFormation:...