2014年11月5日水曜日

AndroidのArrayAdapter#notifyDataSetChangedの謎

どうも。うにあです。

 ArrayAdapter#notifyDataSetChangedのソースコードを見たことありますか??

 /**
 * {@inheritDoc}
 */
@Override
 public void notifyDataSetChanged() {
    super.notifyDataSetChanged();
    mNotifyOnChange = true;
}
{@inheritDoc}となっているのでBaseAdapterのJavaDocを見てみる。
 
    /**
     * Notifies the attached observers that the underlying data has been changed
     * and any View reflecting the data set should refresh itself.
     */

データ変更をオブザーバに通知してViewに反映するメソッドですよね。ご存知の通り。

でも僕の疑問はなぜ、
mNotifyOnChange = true;
があるのかということなんですよねー。
notifyDataSetChangedを呼ぶとフラグがtrueにされてしまう。そのフラグはsetNotifyOnChangeというメソッドで操作できる。

setNotifyOnChange(boolean notifyOnChange)
Control whether methods that change the list (add(T)insert(T, int)remove(T)clear()) automatically call notifyDataSetChanged().


個人的に複数のデータを仕方なくfor文で登録しようとすると

1. setNotifyOnChange(false)
2. for文内でaddやinsertを複数回行う
3. setNotifyOnChange(true)
4. notifyDataSetChanged

というイメージで実装するのかなと思っていたのに。。いや、これで正しいとは思うんですが、notifyDataSetChangedを一度呼び出してしまうとfalseになっていたものがtrueになってしまうので注意が必要だというお話でした。

なぜこのようになっているのか疑問。。。誰か教えてください。。。






2014年10月29日水曜日

AndroidのArrayAdapterのnotifyDataSetChangedについて

AndroidのArrayAdapterのnotifyDatasetChangedについて

notifyDatasetChangedをAdapter外で使うのはよくないよなぁ。ってかそもそもnotifyDatasetChangedとはなんぞやって所から始まった話。

notifyDataSetChanged()とは

     /**
     * Notifies the attached observers that the underlying data has been changed
     * and any View reflecting the data set should refresh itself.
     */
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }

データが変更された時にViewに対してリフレッシュを通知するメソッド。とりあえず、Viewの更新を行うと。。。

notifyDataSetChangedはadapter内で使用する!

notifyDatasetChangedはあくまでもadapterがデータ更新された時にそれを知らせるために使用するべきだと思います。とりあえず、困ったときのAndroidReferenceを見てみましょうー。

  • setNotifyOnChange(boolean notifyOnChange)
    • Control whether methods that change the list (add(T), insert(T, int), remove(T), clear()) automatically call notifyDataSetChanged().

addやinsertやremoveやclearなどListViewに何かしらの操作があった時は自動で呼ばれるということです。例えばArrayAdapterのaddメソッドを見てみる。

    /**
     * Adds the specified object at the end of the array.
     *
     * @param object The object to add at the end of the array.
     */
    public void add(T object) {
        synchronized (mLock) {
            if (mOriginalValues != null) {
                mOriginalValues.add(object);
            } else {
                mObjects.add(object);
            }
        }
        if (mNotifyOnChange) notifyDataSetChanged();
    }

確かにnotifyDataSetChangedが呼ばれてる。

以上のことを踏まえると、Adapterクラスの操作(add,insert,remove,clearなど)の時に意識せずに呼ばれるメソッドであるべきだと思います。(本当はprotectedなメソッドにしてほしい。)
ってことで、Adapter(もしくはAdapterを継承したもの)以外でnotifyDataSetChangedを呼ぶのは良くないように思いました。

@Override
public void notifyDataSetChanged() {
    super.notifyDataSetChanged();
    mNotifyOnChange = true;
}

2014年4月21日月曜日

Javaのenumの使い方。

Javaのenumと戯れてたらいつの間にかstaticブロックと戯れていました。

enumの勉強してたので、学んだことをまとめます。

サンプルとしてジャンケンプログラムを作成する場合を考えてみます。

enum Hand{
 //まずは定数の列挙
 ROCK("グー", 0),
 SCISSORS("チョキ", 1),
 PAPER("パー", 2);
 //フィールド
 private final int num;
 private final String name;
 //コンストラクタ
 Hand(String name, int num){
  this.name = name;
  this.num = num;
 }
 //メソッド
 public static Hand parseHandFromNum(int num){
  switch(num){
  case 0: return ROCK;
  case 1: return SCISSORS;
  case 2: return PAPER;
  default: return null;
  }
 }
}
System.out.println(Hand.ROCK);
System.out.println(Hand.ROCK.name);
System.out.println(Hand.ROCK.num);
とすると出力は
ROCK
グー
0
となります。
列挙型について調べてたら逆コンパイルしたら面白いとあったので、してみました。

static final class Hand extends Enum
    {

        public static Hand parseHandFromNum(int num)
        {
            switch(num)
            {
            case 0: // '\0'
                return ROCK;

            case 1: // '\001'
                return SCISSORS;

            case 2: // '\002'
                return PAPER;
            }
            return null;
        }

        public static Hand[] values()
        {
            Hand ahand[];
            int i;
            Hand ahand1[];
            System.arraycopy(ahand = ENUM$VALUES, 0, ahand1 = new Hand[i = ahand.length], 0, i);
            return ahand1;
        }

        public static Hand valueOf(String s)
        {
            return (Hand)Enum.valueOf(EnumSample$Hand, s);
        }

        public static final Hand ROCK;
        public static final Hand SCISSORS;
        public static final Hand PAPER;
        private final int num;
        private final String name;
        private static final Hand ENUM$VALUES[];

        static 
        {
            ROCK = new Hand("ROCK", 0, "\u30B0\u30FC", 0);
            SCISSORS = new Hand("SCISSORS", 1, "\u30C1\u30E7\u30AD", 1);
            PAPER = new Hand("PAPER", 2, "\u30D1\u30FC", 2);
            ENUM$VALUES = (new Hand[] {
                ROCK, SCISSORS, PAPER
            });
        }

        private Hand(String s, int i, String name, int num)
        {
            super(s, i);
            this.name = name;
            this.num = num;
        }
    }

結果はそのまんまクラス。

じゃ、クラス使ってやればよくない?と思うかもしれないけども自分はこのコードのここに注目した。
        public static final Hand ROCK;
        public static final Hand SCISSORS;
        public static final Hand PAPER;
        private final int num;
        private final String name;
        private static final Hand ENUM$VALUES[];

        static 
        {
            ROCK = new Hand("ROCK", 0, "\u30B0\u30FC", 0);
            SCISSORS = new Hand("SCISSORS", 1, "\u30C1\u30E7\u30AD", 1);
            PAPER = new Hand("PAPER", 2, "\u30D1\u30FC", 2);
            ENUM$VALUES = (new Hand[] {
                ROCK, SCISSORS, PAPER
            });
        }
これをクラス使って自分で実装する手間を省く程度に考えると、クラスかenumを使うかを自分で判断できるのかなと。

以下の記事が参考になりました。
列挙型を使う Java 5.0を使ってタイプセーフな方法で定数を表現する
Switch文の条件式にenumじゃなくてnullを入れてみた

(追記 2014/04/22)

 クラスと比較したenumの利点
 public static final int ROCK = 0;
 public static final int SCISSORS = 1;
 public static final int PAPER = 2;
 public static void showHand(int hand){
  switch (hand) {
  case ROCK:
   System.out.println("グー");
   break;
  default:
   break;
  }
 }
上記のようにクラスで実装を行うとshowHandを使う人はint型のものは全て引数に取れるということになります。 この場合showHand()内で0,1,2以外はエラー処理を行わないといけなくなりますよね。
しかし、引数showHand(Hand hand)のようなメソッドを定義するとHand型以外のものは渡せなくなります。 そういう意味でenumはタイプセーフな使い方をできるようです。


(おまけ)

staticブロック(初期化演算子)というものを初めて知ったのですが、面白い!

実際は使っちゃだめだと思うんですが、下のように宣言の前に代入するみたいな書き方もできました。配列やリストなどの組み合わせでもいろいろおもしろいことができそうです!

  static {
      num = 50;
  }
 
  public static final int num;



2014年4月20日日曜日

MacでJava標準APIを見る

Java標準APIを見てハムハムしたいと思ったんですが、src.zipなるものがどこかに入ってるらしい。
Windowsの場合の記事は多かったんですが、Macの場合の記事が少なかったためsrc.zipの場所をメモっておきます。

/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home/src.zip


2014年3月8日土曜日

電子書籍と紙媒体の本、何が違うのかを個人的に考えてみた話(結論出てません)

今更感がすごいのですが、電子書籍って僕は結構使うんですよねー。

僕はどっちでもいいんですが、「電子書籍はダメ、紙媒体の本じゃないと嫌」という意見もすごく多い。

そこで、電子書籍と紙媒体の本の違いを考えたことをメモっておきたいと思います。


電子書籍の何が悪い??

僕は電子書籍が好きです。紙媒体も好きです。

しかし、電子書籍嫌いで紙媒体派の友人に聞いたのですが、電子書籍は「使いにくい」「目が疲れる」「紙の質感を楽しみたい」「なんかいや」などなどの意見が・・・・。笑

確かに同じようなことがKindleは悪みたいなことを言ってる本に書いてあった気がする。
(使いにくければ本の世界に没頭できない、シンプルなUIが必要みたいなこと)

じゃ、そこで「近未来、すごい使いやすいUIが開発されて、まるで紙を触っているような感覚で目も疲れないような電子書籍リーダが出てきたらどう?」という意地悪な質問をしてみたところ。

「んー、それでもいや。なんか違う。」

と言われてしまいました・・・。

そのまま議論を続けたところ、僕の中でいくつかの仮説が・・

「思い出の保存先による大切さ」

データよりは実物体の方が思い出を保存しやすいと思います。
スマホに入ったままの写真データよりは、印刷した写真をアルバムとかに閉じていたほうが思い出が蓄積されて大切な感じがしませんか??

電子書籍の中にある本に思い出が蓄積されるよりは、紙媒体の本の方が思い出を蓄積しやすく、本自体を大切に感じるのではないかと思いました。

「思い出の分散保存」

思い出を紙媒体に保存するときはその本ごとに保存する。
しかし、電子書籍の場合は思い出の保存先は1つのデバイス。

人間は思い出を1箇所にまとめて保存するよりも、複数箇所に分散して保存したほうが幸せに感じるんじゃないのかなぁというのが仮説です。

思い出を分散して保存することで周りにたくさんの思い出が溢れ、思い出を感じられる空間ができる。
それが幸せに感じられるような気がするんですよ。




以上、電子書籍と紙媒体の違いを「思い出」という観点から考えてみました。

-----
電子書籍(透過光)と紙媒体(反射光)を見る際の脳の動きの違いなどがあり、それぞれ利点と欠点があるなど様々な研究がされてるそうですが、今回は無視しました