default eye-catch image.

Collection…Sequence,Slice,List,Tupple

Sequence,Slice 初めてPythonを触ると地味に取っつきにくい構文が現れるけども。 序数によるアクセスの他、範囲を指定してリストを得られる。 負の序数を指定すると後ろから数える。負のスライスも可 スライスの開始位置、終了位置はSequenceの前の方->後の方の順に書く。 長さがゼロ、またはマイナスの場合、空のリストを返す。 L = [0,1,2,3,4] a = L[1:3] print(a) # [1,2] b = L[-1:-3] print(b) # [2,3] k = K[3:1] print(k) # [] 増分値を指定することもできる。 L = [0,1,2,3,4] c = L[0:5:2] print(c) # [0,2] スライス自体もオブジェクト L = [0,1,2,3,4] s = slice(0,3) d = L[s] print(d) # [0,1,2] s2 = slice(0,None,1) print(L[s2]) # [0,1,2,3,4] スライスを構成する3要素は省略できる。 増分値に負を与えると逆順になるのを利用して要素を反転できる。 L = [0,1,2,3,4] print(L[::]) # = L[0:4:1] = [0,1,2,3,4] print(L[:]) # = L[0:4:1] = [0,1,2,3,4] print(L[::2]) # = L[0:4:2] = [0,2] print(L[::-1]) # = [4,3,2,1,0] スライスを指定して代入できる。 L = [0,1,2,3,4] L[1,4] = [\'hoge\',\'fuga\'] print(L) # [0,\'hoge\',\'fuga\',4] スライスを指定して削除できる。リストから削除するだけで要素自体を破棄しない。 L = [0,1,2,3,4] del L[0,2] print(L) # [3,4] 算術演算子、比較演算子、メンバーシップ演算子が用意されている。 Q = [1,2] * 3 print(Q) # [1,2,1,2,1,2] R1 = [1,2] R2 = [3,4] RX = R1 < R2 print(RX) # True P = [0,1,2,3,4] PX = 7 in P print(PX) # False PXX = 15 not in P print(PXX) # True Sequenceの長さはlen()で取得。 L = [0,1,2,3,4] print(len(L)) # 5 List リストの基本的な使い方は以下。 L1 = [0,1,2,3,4,5] L2 = [] for E in L1: L2.append(E) print(L2) # [0,1,2,3,4,5] 上記は以下のように1回で書ける。リスト内包とか言うらしい。 L1 = [0,1,2,3,4,5] L2 = [value for value in L1] print(L2) # [0,1,2,3,4,5] リスト内包には条件をつけられる。例えば偶数だけを集めるのは以下。 L1 = [0,1,2,3,4,5] L2 = [value for value in L1 if value % 2 == 0] # [0,2,4] iteratableを分解できる。 L = [(\'a\',1),(\'b\',2),(\'c\',3)] L1 = [c*i for c,i in L] print(L1) # [\'a\', \'bb\', \'ccc\'] iteratableの分解時に要素をリストで受けられる。 以下、タプルの第2要素以降をリストで受けて総和を求めて第1要素に掛けている。 L = [(1,2),(3,4,5),(6,7,8,9)] L1 = [car*sum(cdr) for car,*cdr in L] print(L1) # [2, 27, 144] リスト内包を入れ子にすることもできる。 years = [2013,2014] months = [1,2,3,4,5,6,7,8,9,10,11,12] dest1 = [] for year in years: for month in months: dest1.append((year,month)) print(dest1) dest2 = [(year,month) for year in years for month in months] print(dest2) Listオブジェクトの代表的なメソッド達。 L1 = [0,1,2,3,4] L1.append(5) print(L1) # [0,1,2,3,4,5] L2 = L1.copy() print(id(L1)) # 140415098962824 print(id(L2)) # 140415098984840 L2.clear() print(L2) # [] print(L1.count(2)) # 1 L3 = [\'a\',\'b\',\'c\'] L1.extend(L3) print(L1) # [0, 1, 2, 3, 4, 5, \'a\', \'b\', \'c\'] L4 = L1.index(0) print(L4) # 0 L1.insert(4,\'hoge\') print(L1) # [0, 1, 2, 3, \'hoge\', 4, 5, \'a\', \'b\', \'c\'] p = L1.pop(3) print(p) # 3 print(L1) # [0, 1, 2, \'hoge\', 4, 5, \'a\', \'b\', \'c\'] L1.remove(4) print(L1) # [0, 1, 2, \'hoge\', 5, \'a\', \'b\', \'c\'] L1.reverse() print(L1) # [\'c\', \'b\', \'a\', 5, \'hoge\', 2, 1, 0] Tuple 更新不能なリスト。要素を列挙するだけでTupleを作れる。更新できない以外はListと変わらない感じ。 \"更新できない\"という特徴があるため、辞書のキーとして使うことができる。 リストは要素としてそれぞれ独立した値が設定されることを想定。 タプルは全体で1つの要素になることを想定。タプルは「複数の要素から構成される値」。 \"更新できない\"という特徴は、メモリ割り当て時に動的確保を想定しなくて良いため、 リストよりもタプルの方がメモリ使用量を削減できるし、GCの負荷軽減に寄与する。 t1 = (1,2,3) print(t1) # (1,2,3) t2 = 3,4,5 print(t2) # (3, 4, 5) t3 = 1, print(t3) # (1,)

default eye-catch image.

Reference,unpack,同時代入,変数の削除,3項演算子

Reference 変数はオブジェクトの参照。ということなので、オブジェクトのidを確認してみる。 hoge = [1,2,3,4] fuga = hoge print(hoge) # [1,2,3,4] print(fuga) # [1,2,3,4] print(id(hoge)) # 4359573320 print(id(fuga)) # 4359573320 hoge.append(5) print(hoge) # [1,2,3,4,5] print(fuga) # [1,2,3,4,5] 代入したときにどうなるか。新しい箱が作られてそちらを指すようになる。 foo = 1 bar = foo print(foo) # 1 print(bar) # 1 print(id(foo)) # 4359573320 print(id(bar)) # 4359573320 foo = 2 print(foo) # 2 print(bar) # 1 print(id(foo)) # 4476302432 print(id(bar)) # 4359573320 Unpack,同時代入 代入の右辺にiterableを置き、左辺に複数の変数を置くと、 左辺の各変数にiterableの各要素が代入される。 数が合わないとエラー。 foo = [1,2,3,4,5] a1,a2,a3,a4,a5 = foo print(a1) # 1 print(a2) # 2 print(a3) # 3 print(a4) # 4 print(a5) # 5 左辺にリストを置けるが、Unpackの評価においてリストも分解される。 c1,[c2,c3],c4 = [1,[2,3],4] print(c1) # 1 print(c2) # 2 print(c3) # 3 print(c4) # 4 リストでなくても同時代入は可能。 d1,d2 = 100,200 print(d1) # 100 print(d2) # 200 左辺と右辺の個数が合わないケース。通常は下記の通りエラーになる。 ただし左辺に*があるとよしなにやってくれる。 bar = [1,2,3] b1,b2 = bar # ValueError: too many values to unpack (expected 2) e1,*e2 = bar print(e1) # 1 print(e2) # [2,3] *f1,f2 = bar print(f1) # [1,2] print(f2) # 3 オブジェクトの削除 オブジェクトは明示的に削除できる。 foobar = 1024 print(id(foobar)) # 4348092368 del(foobar) print(id(foobar)) # NameError: name \'foobar\' is not defined 3項演算子 ?ではなくif..elseで書く。 a if b else c の場合、bが真ならa,偽ならc。 片方だけ評価される(short circuit)。 a = 100 b = 200 y = True x = a if y else b z = a if not y else b print(x) # 100 print(z) # 200

default eye-catch image.

PythonでUpcast/Downcast出来ない件と多態性のサンプル

継承・カプセル化・多態性 オブジェクト指向の必須要素はこれ。継承、カプセル化・多態性。 異種リストを書けば全部試せるので試してみる。 多態性実現のためにオブジェクトのUpcast/Downcastを調べようとしたら存在しない!。 結論として、PythonにおいてはオブジェクトのUpcast/Downcastは出来ない。 動的型付けなんで、Upcastは勝手に上位クラスだと思って触れば良いのだけど、 形だけでも上位クラスにキャストしておいた方が可読性は上がると思うので残念。 abstractメソッドを定義するだけでパッケージを要求するとか切ない。 from abc import ABCMeta, abstractmethod # 抽象基底クラス class Base: __attr = 100 @abstractmethod def hoge(self): pass class Derived1(Base): __attr = 200 def hoge(self): print(self.__attr) class Derived2(Base): __attr = 300 def hoge(self): print(self.__attr) vals = [Derived1(), Derived2()] for val in vals: val.hoge() なお、Downcastについては、以下のようにしている例がある。 class SubDerived2(Derived1): __attr = 500 def hoge(self): print(self.__attr) def myfunc(self): print(\"myfunc is called\") derived = Derived2() derived.__class__ = SubDerived2 derived.myfunc() 確かに、Upcastは動的型付けの範囲内だが、実現可能でDowncastは何らかの指示が必要で、 必要な方にだけ手段が用意されているのは合理的な気もする。 関数 defで定義する。最初の行に書いた文字列リテラルはdocstring。PHPDoc的なやつだろうか。 関数のスコープからローカル->グローバル->ビルトインの順にシンボルを参照できる。 関数スコープから外のスコープのシンボルは書けない。(global指定して初めて書ける)。 フィボナッチ数列。 def fib(n): \"\"\"Print a Fibonacci series up to n.\"\"\" a,b = 0,1 while a < n: print(a,end=' ') a,b=b,a+b print() fib(100) # 0 1 1 2 3 5 8 13 21 34 55 89 キーワード引数も使える。 def myfunc(hoge,fuga=1): print(hoge) print(fuga)

default eye-catch image.

インデントとブロック,比較と、比較の連接,辞書オブジェクト,リストとタプル,if … elif … else,for,while,iteratableオブジェクト

他言語からの流入組が最小工数でPythonを学ぶシリーズです。 Python文法詳解を読みながらアウトプットしていきます。 [arst_toc tag=\"h4\"] いくら何でも公式を上から順番に読むのは時間がかかり過ぎるし、 550ページとかある本なんて読みたくないので、なるべく薄くて枠だけ書いてある本が良いなと。 Python文法詳解posted with amazlet at 19.05.25石本 敦夫 オライリージャパン 売り上げランキング: 432,601Amazon.co.jpで詳細を見る インデントとブロック ブロックはbeginや { などの予約語ではなく、インデントを使う。 公式は以下。 [clink implicit=\"false\" url=\"https://docs.python.org/2.0/ref/indentation.html\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"2.1.7 Indentation\" excerpt=\"Leading whitespace (spaces and tabs) at the beginning of a logical line is used to compute the indentation level of the line, which in turn is used to determine the grouping of statements.\"] 公式に面白い例が載ってた。 見た目上、インデントが無茶苦茶だけれども文法的には合理性がある例。 要するにインデントとインデント解除のconsistencyがあればOK。 def perm(l): # Compute the list of all permutations of l if len(l) <= 1: return [l] r = [] for i in range(len(l)): s = l[:i] + l[i+1:] p = perm(s) for x in p: r.append(l[i:i+1] + x) return r コーデイング規約によると、お作法としてスペースの個数は4個。だそうだ。 タブではなくスペースでインデントするなんて信じられない...。 エディタにタブ=スペース4個の設定を追加しないと。 言語仕様上の制約はインデントのconsistencyだけなので、 実際のインデント数はプロジェクトによるとのこと。ほうほう。 ただ、スペースとタブの混在禁止はこの際ハッキリさせましょう、という流れ。 比較と、比較の連接 比較演算子について公式は以下。 [clink implicit=\"false\" url=\"https://docs.python.org/ja/3/library/stdtypes.html#comparisons\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"2.1.7 Indentation\" title=\"比較\" excerpt=\"Python には 8 種の比較演算があります。比較演算の優先順位は全て同じです (ブール演算より高い優先順位です)。比較は任意に連鎖できます; \"] 比較演算子を連接できる。PHPとRubyには無い感じ。 a=50 # 通常 if (0 < a) and (a < 100): print("OK") else: print("NG") # 連接 if 0 < a < 100: print("OK") else: print("NG") 辞書オブジェクト KeyValue型。本家は以下。Keyは変更不能で一意でなければならない、と書いてある。 [clink implicit=\"false\" url=\"https://docs.python.org/ja/3/tutorial/datastructures.html#dictionaries\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"5.5. 辞書型 (dictionary)\" excerpt=\"もう一つ、有用な型が Python に組み込まれています。それは 辞書 (dictionary)です。辞書は他の言語にも 連想記憶 (associated memory) や 連想配列 (associative array) という名前で存在することがあります。ある範囲の数でインデクス化されているシーケンスと異なり、辞書は キー (key) でインデクス化されています。\"] a = {1:\'one\',2:\'two\'} print(a) # {1:\'one\',2:\'two\'} # 代入 print(a[1]) # \'one\' a[1] = \'壱\' print(a[1]) # \'壱\' # 削除 del a[1] print(a[1]) # エラー キーを文字列にして連想配列みたくできる。 連想配列のキーにできるかどうかは、その型が更新可能であるかどうか? b = {\'hoge1\':\'fuga1\',\'hoge2\':\'fuga2\'} print(b) # {\'hoge1\':\'fuga1\',\'hoge2\':\'fuga2\'} リストとタプル 似て非なる配列的データ構造、リストとタプル。 リストの方は更新可能。タプルは更新不能。 任意の型を要素としてとれるので、配列ではなくリスト。 L1 = [1,2,3,4,5] print(L1) # [1,2,3,4,5] L2 = (1,2,3,4,5) print(L2) # (1,2,3,4,5) L1[0] = \'hoge\' print(L1) # [\'hoge\', 2, 3, 4, 5] L2[0] = 100 # TypeError: \'tuple\' object does not support item assignment 序数を使ってシーケンシャルアクセスできる。 ary = [1,5,3,4,9] print(ary[3]) # 4 str = \'abcdef\' print(str[1]) # b 負の序数を使うと後ろから数える。 ary = [1,5,3,4,9] print(ary[-1]) # 4 レンジも使える。 ary = [1,5,3,4,9] print(ary[1:3]) # [5,3,4] print(ary[-3:-1]) # [5,3,4] print(ary[:3]) # [1,5,3] レンジ指定で要素を更新できる。 ary = [1,5,3,4,9] ary[2:3] = [100,200] print(ary)# [1, 5, 100, 200, 4, 9] if ... elif ... else elseif が elif。 論理式が偽になるのは\"数値の0\"、空のコレクション、Noneオブジェクト。それ以外は真。 a = 50 if 0 < a < 35: print("OK") elif 35 <= a < 100: print("So much") else: print("NG") for,while forループとwhileループがある。iteratableオブジェクトを指定できる。 whileループについて。 str = \"aiueo\" stop = len(str) i=0 while i < stop: print(str[i]) i += 1 # a # i # u # e # o forループについて。 str = \'abcde\' for c in str: print(c) # a # b # c # d # e # continue, break str = \'abcde\' for c in str: if c == \'c\': continue if c == \'e\': break print(c) # a # b # d whileループ、forループの終了時に実行するブロックをelseで定義できる。 breakで中断した場合は実行されない。 str = \'abcde\' for c in str: print(c) else: print(\'ループ終了\') # a # b # c # d # e # ループ終了 iteratableオブジェクト よくあるパターン。要素の順序を走査するためのインターフェースを実装したオブジェクト。 本家によるとこんな感じ。 [clink implicit=\"false\" url=\"https://docs.python.org/ja/3/glossary.html#term-iterable\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"iterable\" excerpt=\"(反復可能オブジェクト) 要素を一度に 1 つずつ返せるオブジェクトです。 反復可能オブジェクトの例には、(list, str, tuple といった) 全てのシーケンス型や、 dict や ファイルオブジェクト といった幾つかの非シーケンス型、 あるいは Sequence 意味論を実装した __iter__() メソッドか __getitem__() メソッドを持つ任意のクラスのインスタンスが含まれます。\"]

default eye-catch image.

とりあえずチャットアプリを作ってみる

Kotlin製のChat UI Libraryを写経しながらAndroidアプリを作る練習。 もちろんサーバ側は無いので、文字列決め打ちで出してるだけ。 昔作ったJava製のものと基本的なことは変わっていない様子。 https://github.com/bassaer/ChatMessageView.git null安全性の絶対的な安心感。これに尽きる。。 自分のような素人が書いてもぬるぽで落ちないというのは奇跡!。 Swiftとそっくりという噂なので、 もしかするとKotlinからSwiftに移植するのは簡単なのかも。

default eye-catch image.

一覧から詳細の起動_一覧側(MainActivity)

一覧から詳細を起動する一覧側(MainActivity.kt)の実装。 ListViewのsetOnItemClickListener()の書き方。 setOnItemClickListener()の引数はAdapterView.OnItemClickListenerオブジェクトを取る。 OnItemClickListenerオブジェクトは1つの仮想関数(Single Abstract Method)を持っていて、 それを実装したものを渡すことになる。 本来、OnItemClicListenerを派生したオブジェクトでSAMを実装したものを渡す、という とても長い記述になるのだけれども、仮想関数が1個だけならば代わりにラムダ式を1個渡せば良い。 (SAM変換)。 ActivityからIntentを取得してIntentからActivityを起動する、という書き方。 Intentを取得するときにパラメタ(今回の場合はArticleインスタンス)が渡る。 package com.example.ikuty.myapplication import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.widget.ListView import com.example.ikuty.myapplication.model.Article class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val listAdapter = ArticleListAdapter(applicationContext) val dummyArticle1 = dummyArticle(\"ikuty.com記事1\",\"ikuty1\") val dummyArticle2 = dummyArticle(\"ikuty.com記事2\",\"ikuty2\") listAdapter.articles = listOf(dummyArticle1,dummyArticle2) val listView: ListView = findViewById(R.id.list_view) as ListView listView.adapter = listAdapter listView.setOnItemClickListener { adapterView,view, position, id -> val article = listAdapter.articles[position] ArticleActivity.intent(this, article).let { startActivity(it) } } } }

default eye-catch image.

一覧から詳細の起動 – ParcelableなインスタンスをIntentに付与してActivityを起動する

記事一覧画面でアイテムを触るとそのアイテムの詳細画面が開くというのをやりたい。 記事詳細画面のController(Activity)を書く。 Intent まずIntent。Activityを起動する際にパラメタの役割を果たす。 Intentには「意図」「目的」の意味がある。Activityを起動する意図とか目的とか。 ベースとなるIntentに付加的な情報を付与(putExtra)することで「意図」「目的」となる。 Intentには基本型の他にカスタムオブジェクトを付与することができる。 ただし、カスタムオブジェクトはParcelableインターフェースを実装している必要がある。 あるActivityから別のActivityを起動する際のテクニックとして、 起動される側のActivityが持つintent()メソッドを使ってIntentを作成し(つまりパラメタを渡し)、 作成したIntent経由でActivityを起動する。 companionオブジェクトにfun intent(context: Context, article: Article): Intentを作る。 このintent()を経由すれば起動に必要なパラメタ(今回はarticle: Article)を付与したIntentを取得できる。 intent()内ではputExtra()を使ってarticleとIntentを紐づけている。 onCreate(savedInstanceState: Bundle?) onCreate()が発火したときにはIntentにParcalableなArticleインスタンスが付与済み。 Activityが持つintentからgetParcelableExtra()によりArticleインスタンスを取得して、 ビューにそのArticleインスタンスを表示する。 package com.example.ikuty.myapplication import android.content.Context import android.content.Intent import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.webkit.WebView import com.example.ikuty.myapplication.model.Article import com.example.ikuty.myapplication.view.ArticleView class ArticleActivity: AppCompatActivity() { companion object { private const val ARTICLE_EXTRA: String = \"article\" fun intent(context: Context,article: Article): Intent = Intent(context, ArticleActivity::class.java) .putExtra(ARTICLE_EXTRA, article) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_article) val articleView = findViewById(R.id.article_view) as ArticleView val webView = findViewById(R.id.web_view) as WebView val article: Article = intent.getParcelableExtra(ARTICLE_EXTRA) articleView.setArticle(article) webView.loadUrl(article.url) } }

default eye-catch image.

リストビューを表示するためにListViewAdapterを実装する

ListViewAdapter リストビューを表示するためにListViewAdapterを実装する。 BaseAdapterを継承して必要なメソッドを実装する。 実装が必要なのは以下。 fun getCount(): Int fun getItem(position: Int): Any? fun getItemId(position: Int): Long fun getView(position:Int,convertView: View?,parent: ViewGroup?): View fun getCount(): Int 名前の通り、リストビューが保持するアイテムの個数を返す。 ListViewAdapterが保持するList<Article&gtのサイズを返すようにする。 fun getItem(position: Int): Any? 名前の通り、指定した位置のアイテムを返す。 保持するList<Article&gtの要素を返すようにする。 fun getItemId(position: Int): Long 指定した位置のアイテムに関するアプリケーション固有のIDを返す。 ListViewの外から「位置->固有ID」を取得できる。 アイテムを取得してアイテムからIDを取るんじゃダメなのか...。 Get the row id associated with the specified position in the list. fun getView(position:Int,convertView: View?,parent: ViewGroup?): View 指定した位置のアイテムを表示するためのビューを取得する。 convertViewには、画面から表示しきれなくなったViewが来る。 既に画面に表示されているならばnullが来る。 画面から表示しきれなくなったView(convertView)を使い回すことで負荷削減する。 Get a View that displays the data at the specified position in the data set. convertViewをArticleViewにダウンキャストした結果をViewとする。 ダウンキャストがnullならばエルビス演算子(?:)の右辺が評価され、新しいArticleViewが作られる。 View = ((convertView as? ArticleView) ?: ArticleView(context)).apply { setArticle(articles[position]) package com.example.ikuty.myapplication import android.content.Context import android.view.View import android.view.ViewGroup import android.widget.BaseAdapter import com.example.ikuty.myapplication.model.Article import com.example.ikuty.myapplication.view.ArticleView class ArticleListAdapter(private val context: Context): BaseAdapter() { var articles: List = emptyList() override fun getCount(): Int = articles.size override fun getItem(position: Int): Any? = articles[position] override fun getItemId(position: Int): Long = 0 override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View = ((convertView as? ArticleView) ?: ArticleView(context)).apply { setArticle(articles[position]) } }

default eye-catch image.

ListViewの1行を表示するViewControllerを書いてみる

ボキャブラリーが貧弱でアレなんだけども、やりたいことはそんな感じ。 リスとビューの1行に表示するアイテムのViewCOntrollerを書いてみる。 1行に前回定義したArticleを表示する予定なので、そういう文脈になっている。 findViewById()ではないKotlin風のViewの取り方 findViewById()を使うことで、IDから該当するViewを取ることができるけれど、 その応答値はnullableなので、findViewById()を使って取ったViewを使いまわそうとすると、 nullableに対するケアがつきまとうようになる。 Kotlinのlazy()を使うことでnullableでないViewの取り方を実現できる! lazy()は移譲プロパティの移譲先を書く書き方。公式の仕様はこちら。 遅延プロパティ、つまりアクセスされたときに初めて評価されるプロパティを実現する。 以下、Viewに<T: View&gt bindView(@IdRes id:Int)という拡張関数を追加している。 追加した拡張関数の戻りがnullを許容しないから、 これを経由すればnullのケアをしなくて良くなる(という話) lazy()の中で評価したfindViewById()がnullを返した場合、ダウンキャストの失敗を どう扱うんだろうか。 package com.example.ikuty.myapplication import android.support.annotation.IdRes import android.view.View fun View.bindView(@IdRes id: Int): Lazy = lazy { findViewById(id) as T } 実装 この拡張関数を使ったArticleViewは以下。 プロパティとして保持するViewがnull非許容になっている。 それにより、setArticle(artilce:Article)において、 プロパティにアクセスする際にnullのケアをしないで済んでいる。 これは便利なんじゃなかろうか。 package com.example.ikuty.myapplication.view import android.content.Context import android.graphics.Color import android.util.AttributeSet import android.view.LayoutInflater import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView import com.example.ikuty.myapplication.R import com.example.ikuty.myapplication.model.Article import com.example.ikuty.myapplication.bindView class ArticleView : FrameLayout { constructor(context: Context?) : super(context) constructor(context: Context?, attrs: AttributeSet?) : super(context,attrs) constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context,attrs,defStyleAttr) constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context,attrs,defStyleAttr,defStyleRes) val profileImageView: ImageView by bindView(R.id.profile_image_view) val titleTextView: TextView by bindView(R.id.title_text_view) val userNameTextView: TextView by bindView(R.id.user_name_text_view) init { LayoutInflater.from(context).inflate(R.layout.view_article,this) } fun setArticle(article: Article) { titleTextView.text = article.title userNameTextView.text = article.user.name profileImageView.setBackgroundColor(Color.RED) } }

default eye-catch image.

Percelableを実装してIntentに乗せてBundleに渡せるモデルを書く

Percelable interface モデルをActivity間でやりとりしたり、再生成時の復元処理を書いたり、など、 Percelableというインターフェースを実装すれば、面倒みてくる。 Bundleには基本的な型以外に、Percelableを実装したインスタンスを渡すことができる。 (つまり、ActivityとFragmentに渡すことができる。) Percelableを実装するとIntentに載せることができる。 Parcelable interfaceは2つの仮想関数を持っていてそれぞれ実装する。 CREATORというstaticメンバを実装する。 abstract describeContents() abstract writeToPercel(Percel dest, int flags) CREATOR abstract describeContents() describeContents()はひとまず0を返すようにする。 0以外の応答値が必要な場合についてひとまず省略。 abstract writeToPercel(Percel dest, int flags) writeToPercel(Percel dest, int flags)はPercelに保存するデータを列挙する。 通常クラスが持つプロパティを列挙する。 CREATOR CREATORという名前のstaticフィールドを実装する必要がある。 Interface for classes whose instances can be written to and restored from a Parcel. Classes implementing the Parcelable interface must also have a non-null static field called CREATOR of a type that implements the Parcelable.Creator interface. Kotlinはstaticフィールドに対応していない(companionオブジェクトが対応する)ため、 companionオブジェクトとして実装する必要がある。 Percelable.Create<T>というobject式(シングルトンオブジェクト)を継承して作る。 Kotlinから呼ぶだけなら、companionオブジェクトを用意するだけで外からstaticメンバのように扱えるけれども、 Javaから呼ぶなら、さらに@JvmFieldを付ける必要がある。 Kotlnでプロパティを書くと、Javaに変換する際にgetXXX()やsetXXX()のように 自動的にgetter/setterが付く(らしい)。 プロパティに@JvmFieldアノテーションをつけると、getXXX()/setXXX()ではなく、 直接フィールドを触るように変換される。 @JvmFieldを付けずにstaticフィールドを書こうとすると、 勝手にgetXXX()/setXXX()が付いてしまい要求を満たせなくなる。 (JVM言語っぽいところを初めて理解した図...) 実装例は以下参照。 スコープ関数,run ここ見ながら理解。 任意の型Tの拡張関数で、そのTをレシーバとする関数Rを引数にとる。 public inline fun T.run(f: T.() -> R): R = f() 例えば、文字列\"AIUEO\"を小文字にする例。 val str = \"AIUEO\".run { toLowerCase() } println(s) // aiueo 実装例 Kotlinの文法の基本を詰めていれば問題なし。 package com.example.ikuty.myapplication.model import android.os.Parcel import android.os.Parcelable data class Article(val id: String, val title: String, val url: String, val user: User) : Parcelable { companion object { @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { override fun createFromParcel(source: Parcel): Article = source.run { Article(readString(),readString(),readString(), readParcelable(Article::class.java.classLoader)) } override fun newArray(size: Int): Array = arrayOfNulls(size) } } override fun describeContents(): Int = 0 override fun writeToParcel(dest: Parcel, flags: Int) { dest.run { writeString(id) writeString(title) writeString(url) writeParcelable(user, flags) } } }