2005
6
7
8
9
10
11
12
2006
1
2
3
4
5
6
7
8
9
10
11
12
2007
1
2
3
4
5
6
7
8
9
10
11
12
2008
1
2
3
4
5
6
7
8
9
10
11
12
2009
1
2
3
4
5
6
7
8
9
10
11
12
2010
1
2
3
4
5
6
7
8
9
10
11
12
2011
1
2
3
4
5
6
7
8
9
10
11
12
2012
1
2
3
4
5
6
7
8
9
10
11
12
2013
1
2
3
4
5
6
7
8
9
10
11
12
2014
1
2
3
4
5
6
7
8
9
10
11
12
2015
1
2
3
4
5
6
7
8
9
10
11
12
2016
1
2
3
4
5
6
7
8
9
10
11
12
2017
1
2
3
4
5
6
7
8
9
10
11
12
2018
1
2
3
4
5
6
7
8
9
10
11
12
2019
1
2
3
4
5
6
7
8
9
10
11
12
2020
1
2
3
4
5
6
7
8
9
10
11
12
2021
1
あのコード、Androidのですよね?Androidだったらデフォルト 値はリソースで持つのが王道ですけど。 まぁ、いいか。ちょっと面倒なんでRubyで書きますけど: text1 = TextView.new(context) text1.setTextSize(TypedValue::COMPLEX_UNIT_DIP, 24) text2 = TextView.new(context) text2.setTextSize(TypedValue::COMPLEX_UNIT_DIP, 24) text1.setTextColor(Color::RED) text2.setTextColor(Color::BLACK) text1.setText(@name) text2.setText(@title) こういうコードをどう整理するかっていう話ですよね。で、 やっぱり、自分は、コンポーネントごとに整理します。 text1 = TextView.new(context) text1.setTextSize(TypedValue::COMPLEX_UNIT_DIP, 24) text1.setTextColor(Color::RED) text1.setText(@name) text2 = TextView.new(context) text2.setTextSize(TypedValue::COMPLEX_UNIT_DIP, 24) text2.setTextColor(Color::BLACK) text2.setText(@title) そうすると、メソッドに切り出したくなる。 text1 = newText1(context) text2 = newText2(context) def newText1(context) text1 = TextView.new(context) text1.setTextSize(TypedValue::COMPLEX_UNIT_DIP, 24) text1.setTextColor(Color::RED) text1.setText(@name) return text1 end def newText2(context) text2 = TextView.new(context) text2.setTextSize(TypedValue::COMPLEX_UNIT_DIP, 24) text2.setTextColor(Color::BLACK) text2.setText(@title) return text2 end そうすると、今度は似た部分を切り出したくなる。 text1 = newText1(context) text2 = newText2(context) def newText1(context) return newText(context, Color::RED, @name) end def newText2(context) return newText(context, Color::BLACK, @title) end def newText(context, color, text) text = TextView.new(context) text.setTextSize(TypedValue::COMPLEX_UNIT_DIP, 24) text.setTextColor(color) text.setText(text) return text end そうすると、今度は引数をパラメータ化したくなる。 text1 = newText1(context) text2 = newText2(context) def newText1(context) return newText(context, {:color => Color::RED, :text => @name}) end def newText2(context) return newText(context, {:color => Color::BLACK, :text => @title}) end def newText(context, params) text = TextView.new(context) text.setTextSize(TypedValue::COMPLEX_UNIT_DIP, 24) text.setTextColor(params[:color]) if params.key?(:color) text.setText(params[:text]) if params.key?(:text) return text end そうすると、今度はパラメータをまとめたくなる: texts = [ {:color => Color::RED, :text => @name}, {:color => Color::BLACK, :text => @title} ] text1 = newText(context, texts[0]) text2 = newText(context, texts[1]) def newText(context, params) text = TextView.new(context) text.setTextSize(TypedValue::COMPLEX_UNIT_DIP, 24) text.setTextColor(params[:color]) if params.key?(:color) text.setText(params[:text]) if params.key?(:text) return text end lots of small piecesからonce and only onceへと流れて いくわけです。 パラメータを別ファイルにしてrequireすれば、それも一種の リソースファイルということになります。Rubyのような インタプリタ言語ではよくやるんじゃないかと思います。 やっちゃいけないのが、横に切るやり方: text1 = TextView.new(context) text2 = TextView.new(context) text1.setTextSize(TypedValue::COMPLEX_UNIT_DIP, 24) text2.setTextSize(TypedValue::COMPLEX_UNIT_DIP, 24) text1.setTextColor(Color::RED) text2.setTextColor(Color::BLACK) text1.setText(@name) text2.setText(@title) 一見これもlots of small piecesにできそうなんですけど、 実は非常にリファクタリングしにくいんですね。このまま リファクタリングしていくと: textViews = [TextView.new(context), TextView.new(context)] textViews.each{|text| text.setTextSize(TypedValue::COMPLEX_UNIT_DIP, 24)} colors = [Color::RED, Color::BLACK] texts = [@name, @title] textViews.each_with_index{|text, i| text.setTextColor(colors[i])} textViews.each_with_index{|text, i| text.setText(texts[i])} みたいな形になって、新しいTextViewを増やすのが面倒に なるんです。パラメータをまとめたくなるようなコードにも ならない。
AndroidのEditTextがアンドゥをサポートしてなくって。 自前でやるしかない。TextWatcherでイベントは拾える。 それがまたわかりにくいAPIなんだけど。ハマったのはそこ じゃなくって、エミュレータでの動き。 たとえば"ab"と入力した後にアンドゥすると"a"になる。 そこまでは思惑どおり。でも、その後、"c"を入力すると、 "abc"となる。アンドゥで消したはずの"b"が、キーボード から入力するとなぜか復活する。 なんでだろう、なんでだろう?Spannableとか関係してる? 他のエディタはどうやってるんだろう?あれ?EditText 使ってないエディタが結構多い?あれ?やっぱりこの エディタのコードでも復活しちゃうな。おいおい、4.2にも なって、まだこんなマヌケなバグが残ってんのかよ? はい、エミュレータのバグでした。Nexus7で試すと、そんな 現象は起きませんでした。 https://github.com/tkojitu/UndoDemo 最初は「アンドゥといえばCommandパターンだろ、jk」 みたいな感じで実験してたんだけど、それだとコンテキスト メニューからの編集には対応できないわけで。 TextWatcherでイベント拾って。TextWatcherっていうのは、 beforeの部分がafterに置き換えられるというイベント。 なので、アンドゥはその逆置換をやってやればいい。 リドゥは、もう一度同じ置換をやってやればいい。ただし、 アンドゥもリドゥもTextWatcherがイベントを拾うので、 それは無視しないといけない。 -- しかし、Androidのアプリでソースが公開されてるのは、 そのまま持ってきてもビルドできないのが多い。 -- Googleは、Androidのドキュメントをそろえるつもりが ないらしい。developer.android.comは、MSDN並みに よく変わり、MSDN並みに役立たず。APIドキュメントは しっかり書かれているのが救いなんだけど。 ちょっと前まではチュートリアルがあったんだけど、 一掃された。ガイドはあるけど、ボンヤリした話しか 載ってない。コード見たけりゃSDKのサンプル見ろって 方針らしい。ところが、そのサンプルも、1つのプロジェクト にたくさん詰め込みすぎてて、何が何やら。 ググれば引っかかるのはStackOverflowばかり。それを 信じていいかどうかもよくわからない。そういえば、 MSDNもまた変わって、StackOverflowを検索する オプションがついた。ウザいだけ。 StackOverflowのようなQAサイトは、いってみればバッド ノウハウの集積所なわけで、Javaチュートリアルのような 正当性や全体としての整合性というものがない。日本でも 後追いサイトがいくつか出てきたけど、そんなことやる くらいならStackOverflowの日本語化に協力したほうが まだしも建設的じゃないのかね。ニコ動ほど市場が広い わけでもなかろうし、日本固有の事情があるわけでもないし、 さほど差別化ができると思えないし。
Copyright © 1905 tko at jitu.org