Androidはワンツーパンチ 三歩進んで二歩下がる

Android卵プログラマーの記録ブログ

RubyでExcelから出力したShift_JISのcsvをutf-8に変換する

RubyExcelから出力した、文字コードShift_JIScsvutf-8に変換する方法のメモです。
私が作成しているRailsのアプリで、
seeds.rbでExcelファイルから出力したcsvファイルを読み込んで、
DBの初期データ投入をしています。

Excelから出力したcsvファイルはMacExcelを使用していても、Shift_JISになっています。
最近のExcelではUTF-8で出力出来るらしいのですが、私が持っているExcelが古いのか出来ないみたいです。

Shift_JIScsvを直接読み込むために、次の方法をとっています。

require 'csv'

(略)
csv_data = CSV.read('db/food.csv', headers: true,
    external_encoding: "Shift_JIS",
    internal_encoding: "utf-8"
  )
csv_data.each do |data|
  Food.create!(data.to_hash)
end

参考
string - Ruby read CSV file as UTF-8 and/or convert ASCII-8Bit encoding to UTF-8 - Stack Overflow

Nexus5Xのバッテリーを交換した。互換バッテリーは噂通りすぐ電池がなくなる。

Nexus5xをいくら充電をしても充電切れの赤いランプが付いてしまうようになってし
まいました。
ここ最近満充電してもすぐに電池がなくなってしまうのでバッテリーが寿命のようです。
ついに起動も満足に行えなくなってしまいました。

iPhone7をメインで使っているので、すぐに困るというわけではないのですが
Android開発用に実機が使えないのは大変困ります。(現在1台しか実機がないのです)
私のNexus5Xはストレージが16Gしかないので、すぐに容量がいっぱいになるし
3Dゲームもやるので、買い換えることにしました。
Nexus5Xはサブ機にすることにしました。

そこで自力でバッテリー交換をすることにしました。
一応スマフォ修理のお店にバッテリー交換の見積もりをしていただいたのですが、
13000円近くかかることがわかり、これだったらオンボロNexus5Xが
最悪壊れてもいいから自分でやろうと決めたのでした。

バッテリーに使うことにしたのはAmazonで売っている互換品です。

2980円が1780円になっているので、40%OFFでなかなか安いですね。
しかし評価が悪い(TдT)

必要だったら作業用のオープナーやドライバーも用意します。

電池と一緒にサジェストされているスマフォのフタをあけたりするプラスチックのオープナー
を使うのは一般的ですが、
私は精密ドライバーセットで全部やっちゃいました。
マイナスドライバーを隙間に挿して開けていく要領ですね。
マイナスドライバーをあんまり深く差し込んでグリグリすると傷が付いたりするので気をつけてください。


私が使っているのはこれ↓です。
今のところこれで困ったことはありません。
プラスチックのオープナーってプラスチックかすが出てしまうのが微妙に嫌です。
オープナーの爪ってすぐ折れるし。
って私のやり方がいけないのかな!


実際の交換ですが、参考サイト様を参考にやっていきます。
参考サイト様ありがとうございました!

sftt.jp

androgundan.blue


電源オフして、SIMカードを抜き、その穴あたりからマイナスドライバーを差し込み
ベキベキ裏蓋を剥がしまして、10個のネジを外します。プラスドライバーを使います。

さらにバッテリーを覆っている2つのパーツを外します。

f:id:sakura_bird1:20170903161748j:plain


電池を外します。
電池の上部の黄色っぽいベロのようなやつが充電端子になっているので、まず外します。
それから電池の下にマイナスドライバーを差し込んで接着剤を剥がしつつ外します。
電池は柔らかく、折れ曲がります。

f:id:sakura_bird1:20170903162054j:plain


新旧電池の比較。右が古い方。
古い電池が少しむくんでいます。

f:id:sakura_bird1:20170903162456j:plain


後は電池を交換して元に戻します。
電池を入れるとき、充電端子を先に挿した方が私はやりやすかったです。
パーツをはめる前に一旦充電できるか確認します。

f:id:sakura_bird1:20170903194310j:plain


そして作業終了後、充電は出来ることは出来るのですが、
実際に使ってみると50%ぐらい充電できているのに10%ぐらいになるのが異様に早いです。
Amazonのレビューでも散々ですね。
私は新端末が来るまでのつなぎであることと、端末オールリセットしたかったので
それが出来るようになったことで納得しています。
人にはおすすめしません。

Androidアプリ「ポケット糖質量」をリリースいたしました

こんにちは。さくらです。
Androidアプリ「ポケット糖質量」をリリースいたしました。
play.google.com

このアプリが何を出来るかといいますと、1100種類以上の食品データから糖質量などの栄養素をリスト表示・検索することが出来ます。
f:id:sakura_bird1:20170816222524p:plain

以前このブログでRailsで「ポケット糖質量サイト」を作ったという記事を書きましたが、
そのAndroidアプリ版です。
sakura-bird1.hatenablog.com


最初はサイトがレスポンシブ対応になっているので、WebViewアプリみたいなものを作ろうかなと思っていたのですが、スマホブラウザで見るとどうしても表示が間延びして使いづらいなという印象でした。
ちなみにサイトは「Material Design for BootStrap」というテンプレートを使用していて、
レスポンシブのレイアウトも自動で表示してくれます。
それはそれで大変ありがたいのですが、専用のネイティブアプリの使い勝手には敵わない
ということと、レスポンシブ部分のコーディングスキルがないので作ることにしたのでした。

シンプルなアプリなので使い方はすぐにわかると思います。
検索したり、絞り込んだり、お気に入り登録したり、コピーしたり出来ます!

アプリを作り始めたのは2017年5月ぐらいなのですが、途中モチベが下がってしまったりしてダラダラしてたら8月になってしまいました。
モチベが下がってしまったのは、昨年末ぐらいから全体的に元気が無く年齢的な不調も加わって人生全般灰色に感じていたと言いますか・・
すぐ陳腐化してしまうITスキルについても、もう追いついていけないし
やったこともみんな無駄になってしまうんじゃないかと虚しくなったりしたのでした。
(というカミングアウトw)

というわけで、ゴミアプリかもしれませんが、細々作りました。
今回は自分の学習として最近Androidでもよく見かけるモダン開発をフルで取り入れようと思って

DI
RxJava
DataBinding

を全体的に取り入れました。
すでに新しい技術でもなくなっているので、今までも部分的にこれらを取り入れたり
簡単なサンプルを作ったりしたことはあるのですが、
最初からこうした方法を全体に使ったのは初めてです。
設計概念に関わる部分のやり方を変えるときには自分のアプリだといいですよね。
気軽に試行錯誤できるし失敗したっていい!

上記の手法を取り入れるにあたって、
プロジェクト内のフォルダの構成やどんな部分でどのクラスを使うのか、
DroidKaigiの公式アプリをものすごく参考にさせていただきました。
っていうかフォルダ名とか同じにしてるし、恥ずかしいぐらい真似してます。

おかげでなんとか書けたですよ。
DroidKaigiさんありがとう、そして貴重な情報をネットに書いてくれてるみなさんありがとう!

検証はテスト端末今1台しか無いので、ちょっと心配ですがうまく動くかな〜
アプリ内で使っている素材は大体Sketchで単純なものを作りました。
Sketchでアプリ素材書き出すのはほんとに楽でよかったです。

今後はまあ気楽にやっていきます。
RailsももうちょっとやりたいしAndroidアプリも作りたいです。
iOSまで手が回らないかな。
IT系じゃなく全く他のこともやってみたいな。

シニアプログラミングネットワーク #1勉強会に行ってきました

f:id:sakura_bird1:20170429143515j:plain

こんにちは。さくらです。
先日こんな勉強会に行ってきました。

eventdots.jp

81歳のiPhoneアプリプログラマー若宮正子さんがアプリ「hinadan」を開発した件は朝日新聞、CNNをはじめとして世界中で報道されました!
だがしかし、81歳プログラマーはさらにいた!そして、さらに他にも「若い者には負けない」どころか「圧倒する」方達が!
今回はシニアプログラミングネットワーク第1回イベントとしてシニアなプログラマーの皆さんの活動を紹介していき、プログラミングを学ぶこと、そしてお年寄りに使いやすいということはどういうことなのかを考えて行きます。
最古参にして最先端のコンピューターおばあちゃん/おじいちゃんの活躍をぜひご覧ください!
81歳にしてアプリを開発して話題になった若宮正子さんの「hinadanのストーリー」を。同じく81歳にして既にアプリを3本もリリースしている方が既にいた!その鈴木富司さんのセッション。また、ハードウェアを絡めた話では遠く福井から谷川一男さんからなんとイノシシ90頭を仕留めた自作檻の話をしていただきます。そして、若宮さんが以前にアプリのレビューを行なった浪江町でのCode for Japanフェローシップの話もあって盛りだくさんです!

このすごさ・・・!
と、言ってもシニアでプログラミングをされている方は私の身近にもいらっしゃいますのでそこまでは驚かなかったですが。
(その方は70歳ぐらいですが、Androidアプリを300本ぐらいリリースされています。
東京アプリ開発者もくもく会にいつも来てくれる方で、すごーく元気をもらっています。)


客席も結構シニアでしたね。47歳の私なぞ小娘でしたね。
今日来てた人達が気軽に集まれる勉強会とかまたやってくれたら行きたいです。

写真も撮ったのですが、後ろの方に座っていたので全体的に下のほうがうまく写っていなくてすみません。
メディアの方がたくさん来ていたようなので、別途詳しい記事が上がると思います。
と思ってたら記事がありました!
フリック!ニュース /Flick!News : 何歳になってもプログラミングにチャンレンジできる! #シニアプログラミング


f:id:sakura_bird1:20170429140518j:plain

登壇者の方は実際にアプリを開発されたおばあさまおじいさまだったのですが、どの方もよどみなく喋ってプレゼン力があり、お若かったです。
わたしはあんなに喋れないですよ。
元々聡明でいらっしゃる感じでした。
みんな英語も得意なんだって(✽ ゚д゚ ✽)

若宮正子さん

82歳になったばかりだそうです。リリースしたiOSアプリ「hinadan」について。Swiftで書かれたそうです。
反響がすごく色々なメディアに取り上げられてテレビにも出演したそうです。
韓国でNAVERのカンファレンスで講演をされたそうですよー。すごいですね。
若宮さんのお話はユーモアたっぷりですごくウケていました。
主催の小泉さんとSkype(でしたっけ?すみません物忘れが・・)かなにかでやりとりしてマンツーマンで教えてもらいながら作ったそうです。


f:id:sakura_bird1:20170429141219j:plain

f:id:sakura_bird1:20170429141353j:plain

アプリのイメージ。年寄りが勝てるゲームを作りたい!という気持ちから始まったビッグプロジェクト!
f:id:sakura_bird1:20170429141620j:plain

f:id:sakura_bird1:20170429142054j:plain

f:id:sakura_bird1:20170429143118j:plain

小泉さんの開発裏話も楽しく参考になりました。
お年寄り向けのUIの話や教える時のコツとか。出来ることと出来ないことを明確に区別して、捨てるべきところを捨ててゴールに向かっていったそうです。
今後は作りたいものから逆算して教育する仕組みを作りたいそうです。
小泉さんは小学生の頃からプログラミングをされていたそうなので、すごいと思いました。

f:id:sakura_bird1:20170429143239j:plain

f:id:sakura_bird1:20170429143306j:plain

また若宮さんはコードフォージャパンの陣内一樹さんとともに浪江町のアプリ開発にも関わったそうです。
陣内さんのプレゼンも素晴らしく、利用者を置き去りにしたアプリ開発を反省させられました。
写真撮れなくてごめんなさい。

f:id:sakura_bird1:20170429160655j:plain


鈴木富司さん

勉強会当日が誕生日だそうで82歳になられたそうです🎉
既に3本もiOSアプリをリリースされてるそうです。
鈴木さんはプログラミングのスクールに通って勉強されたそうですが、一度挫折して2度目の挑戦だったそうです。
現在はiOSもくもく会によく顔を出されているそうで、若者との交流を楽しみにしているそうです。
鈴木さんのアプリ開発は趣味という言葉より使命感という言葉の方があっている感じです。
高齢者向けスマホの勉強アプリを開発されてます。
アップルのリジェクトとも戦ったそうです。すごい。

f:id:sakura_bird1:20170429144139j:plain

f:id:sakura_bird1:20170429144320j:plain

f:id:sakura_bird1:20170429144548j:plain

f:id:sakura_bird1:20170429145102j:plain

f:id:sakura_bird1:20170429145351j:plain

f:id:sakura_bird1:20170429150748j:plain

f:id:sakura_bird1:20170429150548j:plain

谷川一夫さん

Ichigo Jamって初めて知ったんですが、BASICでプログラミングできる子供用パソコンだそうです。
ichigojam.net
色々揃えても数千円で済む感じ。
このモジュールと赤外線センサーを組み合わせてイノシシの捕獲檻を自作して年間90頭のイノシシを捕まえたというすごいお話でした。
渋谷のお洒落なIT勉強会でイノシシの捕まえ方を聞くというシュールさとか色々あり過ぎるんですが、
谷川さんの問題解決力のすごさに脱帽しました。
お住いの福井ではイノシシに農作物を荒らされる被害に悩まされているそうです。
子供プログラミング教室(当日その教室の素敵な先生もいらしてましたよ)でIchigo Jamをイノシシ捕獲に使うことを思いついたそうです。
元々設計士だそうなので、CADを使った設計などもそうですが素養がおありになるなーって思いました。

f:id:sakura_bird1:20170429152824j:plain

f:id:sakura_bird1:20170429152922j:plain

f:id:sakura_bird1:20170429153159j:plain

f:id:sakura_bird1:20170429153416j:plain

f:id:sakura_bird1:20170429153604j:plain

f:id:sakura_bird1:20170429153920j:plain

f:id:sakura_bird1:20170429154401j:plain

f:id:sakura_bird1:20170429154909j:plain

f:id:sakura_bird1:20170429155311j:plain

f:id:sakura_bird1:20170429155335j:plain

f:id:sakura_bird1:20170429155453j:plain

f:id:sakura_bird1:20170429160235j:plain


こんな感じで驚きつつ楽しい時間を過ごしました。
登壇者様、開催者様ともに本当にありがとうございました。
素晴らしい活動を拝見してやる気をたくさんいただけました。

最後に、私も地頭悪い系で趣味プログラミングをやっている者ですが、自分の実感からネガティブ面もちょっと書いておこうと思います。
年取ってもやる気と好奇心があればプログラミング出来るんだー!わーわー!と盛り上がってもやっぱり難しい局面が多いと思うのです。
対象は全くのプログラミング初心者の高齢者の方です。

学習用プログラミング言語やハローワールド以上のことをやろうとすると難易度が高くなる

安定した環境で学習用に作られたプログラミング言語で学習したり、
他の言語でも画面にハローワールドを表示する(のに毛が生えた程度)といったことをやってるうちは平和に進むと思いますが、
「自分の作りたいアプリ」はそれでは実現出来なかったりするのでなんだかんだで難しくなってきます。

年をとると新しいことを覚えるのが辛くなる問題

全くの初心者が始めるのはやっぱりめちゃめちゃ大変だと思います。覚えることが膨大です。
やっと覚えても変化が早いので古い知識になっていて世間ははるか先をいってたりします。

お金がかかるかもしれない

プログラミング技術の本は一冊3000円ぐらいします。また、本だけだと問題が起きた時に一人で解決するのが難しいことがあります。
不定期で集まる勉強会のようなところに参加するのは非常によいことですが、
現役エンジニアは多忙なので、聞いたことには答えてくれてもガッツリ教えてもらうことは期待できません。
何がわからないのか言語化出来れば無料のQAサイトを頼ることが出来ますが、
その段階に至っていなければスクールなどに通ったり有料学習サービスを利用するという選択肢になるかもしれません。

英語と切っても切れない

この勉強会でも話題に出ましたが、プログラミングをすると英語のエラーメッセージを解読する必要があります。
英文を無視せず翻訳サイトで翻訳するといった手間は必要です。
プラットフォームによってはアプリ申請やリジェクトなどのやり取りを英語で行う必要があります。

その他、互換性のないアップデートやモジュール間のバージョン依存など

OSや開発ツールやライブラリのバージョンを上げると動かなくなったり、
新しいバージョンのモジュールを使いたければ今まで作ったプログラミングを修正しなければならなかったり
ということが日常茶飯事のように感じられるかもしれません。


なーんて、いろいろとやる気を削ぐようなことを書いてごめんなさい。
でも今回の勉強会の登壇者様達のようにやる人はやるので、面白そうだとかこんなのが作りたいだとかあったらとりあえず飛び込んでみてもいいですよね。
無料でリッチな開発環境が手に入ったり、世界中の凄腕エンジニアのコードを読めたり、良いことも山ほどある世界ですし!
小泉さんがおっしゃるような作りたいゴールから逆算して教育する仕組みが出来たら、もっと多くの高齢の方が気軽にプログラミングを楽しめる時代が来るかもしれません。
そうなったら楽しいですね♡









Material DesignのsvgアイコンをAndroid StudioでVector Asset Studioを使ってVectorDrawableのxml形式に変換する方法

GoogleのマテリアルデザインのアイコンをAndroid Studioで簡単に使えるようにして普通の画像に近い感じでImageViewで使えるようにする方法です。
エントリの題名はマテリアルアイコンのことについてになっていますが、SVGまたはPSDフォーマットのベクター画像をVector Asset Studioでxml形式に変換できます。ベクター画像の制限についてはこちらをご覧ください。
公式サイトはこちらです。

Add Multi-Density Vector Graphics | Android Studio

マテリアルアイコンはこちらでも一覧が載っています。
material.io

前提

Gradle plugin v2.0以上。当エントリではv2.3.1を使っています。
AppCompat v23.2以上。当エントリではv25.3.1を使っています。

app/build.gradleに以下を加えておきます。
defaultConfig {
vectorDrawables.useSupportLibrary = true
}

作成

Asset Studioを起動します。

Android Studioのプロジェクトから/app/src/main/res/drawable のところで右クリックします。
New > Vector Assetをクリックします。

f:id:sakura_bird1:20170430102511p:plain:w300

Configure Vector Asset画面が表示されます。
Asset TypeはデフォルトでMaterial Iconが選択されています。

Iconボタンをクリックします。

f:id:sakura_bird1:20170430102806p:plain

Select Iconから使用したいアイコンを選択します。

Sizeは24dp固定ですがベクター画像なので拡大縮小による画像の劣化などを気にしなくていいので気にしないでいきます。

Nextボタンをクリックします。

保存先のフォルダを指定します。

Finishボタンをクリックします。

drawableフォルダにxmlが追加されています。
開いてみるとそれらしいコードになっています。画面右側のPreviewをクリックするとイメージが表示されるので確認するとちゃんと表示されました。

f:id:sakura_bird1:20170430103353p:plain


ImageViewで使用する時はsrcの代わりに
app:srcCompat="@drawable/ic_announcement_black_24dp"のように指定します。

f:id:sakura_bird1:20170430104009p:plain


参考にさせてもらったサイトさま
AppCompat — Age of the vectors – Chris Banes – Medium
VectorDrawable対応まとめ - Qiita



以上です。

伸び縮みするCardViewを作成する(cachapa/ExpandableLayout + RecyclerView + CardViewのサンプル)

RecyclerViewを使ってリスト表示しているレイアウトがあり、その中でタップするとViewが伸縮するCardViewを表示するレイアウトのサンプルを作ったのでメモです。
ExpandableListViewと似たような表示方法です。


f:id:sakura_bird1:20170425233635g:plain:w300

こちらに全体のソースがあります。短いコードですが、ブログを読んでいてサンプルのプロジェクト全体を見たいなあって思うことがあるのでGithubにアップしてます。
github.com


開いたり閉じたりするビューを実装するのにこちらのライブラリを使用させていただいています。
github.com


まずはライブラリの導入から
app/build.gradle

dependencies {
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support:cardview-v7:25.3.1'
    compile 'com.android.support:design:25.3.1'

    compile 'net.cachapa.expandablelayout:expandablelayout:2.8'
}


レイアウトです。
Activityのレイアウトファイルです。
res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginBottom="16dp" />

    </LinearLayout>
</layout>


RecyclerViewに表示する1行分のレイアウトです。
CardViewの中にExpandableLayoutを入れ子にしています。
Data Bindingを使用しています。
res/layout/recycler_item.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="viewModel"
            type="com.sakurafish.expandablerecyclerview.sample.RecyclerItemViewModel" />
    </data>

    <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/card_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginTop="8dp"
        app:cardUseCompatPadding="true"
        card_view:cardCornerRadius="4dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/expand_button"
                style="@style/TextAppearance.AppCompat.Medium"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:elevation="6dp"
                android:onClick="@{viewModel::onClickExpandButton}"
                android:padding="16dp"
                android:text="@{viewModel.expandButtonText}" />

            <net.cachapa.expandablelayout.ExpandableLayout
                android:id="@+id/expandable_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="#eee"
                app:el_duration="300"
                app:el_expanded="false"
                app:el_parallax="0.5">

                <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <TextView
                        android:id="@+id/text1"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentTop="true"
                        android:gravity="center"
                        android:padding="10dp"
                        android:text="@{viewModel.text1}" />

                    <TextView
                        android:id="@+id/text2"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_below="@+id/text1"
                        android:gravity="center"
                        android:padding="10dp"
                        android:text="@{viewModel.text2}" />

                </RelativeLayout>

            </net.cachapa.expandablelayout.ExpandableLayout>

        </LinearLayout>
    </android.support.v7.widget.CardView>
</layout>

Activityです。
アダプターの中のonBindViewHolderでViewの伸び縮みの制御をしています。
expand()で伸び、collapse()で縮みます。

MainActivity.java

package com.sakurafish.expandablerecyclerview.sample;

import android.content.Context;
import android.databinding.DataBindingUtil;
import android.databinding.ObservableArrayList;
import android.databinding.ObservableList;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.sakurafish.expandablerecyclerview.sample.databinding.ActivityMainBinding;
import com.sakurafish.expandablerecyclerview.sample.databinding.RecyclerItemBinding;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
        ObservableList<RecyclerItemViewModel> list = new ObservableArrayList<>();
        for (int i = 0; i < 50; i++) {
            RecyclerItemViewModel viewModel = new RecyclerItemViewModel("text1 : " + i, "text2 : " + i);
            list.add(viewModel);
        }
        binding.recyclerView.setAdapter(new ListAdapter(this, list));
    }

    private static class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder> {

        public ListAdapter(@NonNull Context context, @NonNull ObservableList<RecyclerItemViewModel> list) {
            this.context = context;
            this.list = list;
        }

        private final Context context;
        private final List<RecyclerItemViewModel> list;

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new ViewHolder(context, parent);
        }

        @Override
        public void onBindViewHolder(final ViewHolder holder, int position) {
            final RecyclerItemViewModel viewModel = getItem(position);
            if (viewModel.isExpanded()) {
                holder.binding.expandButton.setSelected(false);
                holder.binding.expandableLayout.expand(true);
            } else {
                holder.binding.expandButton.setSelected(false);
                holder.binding.expandableLayout.collapse(true);
            }

            viewModel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (viewModel.isExpanded()) {
                        holder.binding.expandButton.setSelected(false);
                        holder.binding.expandableLayout.collapse(true);
                    } else {
                        holder.binding.expandButton.setSelected(true);
                        holder.binding.expandableLayout.expand(true);
                    }
                    viewModel.setExpanded(!viewModel.isExpanded());
                }
            });

            viewModel.setExpandButtonText(position + ". Tap to expand");

            holder.binding.setViewModel(viewModel);
            holder.binding.executePendingBindings();
        }

        @Override
        public int getItemCount() {
            return list.size();
        }

        public RecyclerItemViewModel getItem(int position) {
            return list.get(position);
        }

        public class ViewHolder extends RecyclerView.ViewHolder {

            RecyclerItemBinding binding;

            public ViewHolder(Context context, ViewGroup parent) {
                super(LayoutInflater.from(context).inflate(R.layout.recycler_item, parent, false));
                binding = DataBindingUtil.bind(itemView);
            }
        }
    }
}

DataBindingで使用しているビューモデルのクラスです。
RecyclerItemViewModel.java

package com.sakurafish.expandablerecyclerview.sample;


import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.support.annotation.NonNull;
import android.view.View;

public class RecyclerItemViewModel extends BaseObservable {

    private String expandButtonText;
    private String text1;
    private String text2;

    private View.OnClickListener onClickListener;
    private boolean expanded = false;

    RecyclerItemViewModel(@NonNull String text1, @NonNull String text2) {
        this.text1 = text1;
        this.text2 = text2;
    }

    public String getText1() {
        return text1;
    }

    public String getText2() {
        return text2;
    }

    @Bindable
    public String getExpandButtonText() {
        return expandButtonText;
    }

    public void setExpandButtonText(@NonNull String expandButtonText) {
        this.expandButtonText = expandButtonText;
    }

    public void onClickExpandButton(View view) {
        if (onClickListener != null) {
            onClickListener.onClick(view);
        }
    }

    public void setOnClickListener(@NonNull View.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }

    public void setExpanded(boolean expanded) {
        this.expanded = expanded;
    }

    public boolean isExpanded() {
        return this.expanded;
    }
}


以上です。

追記 2017/05/06

上記のサンプルに伸縮を表すアイコンを追加しました。
f:id:sakura_bird1:20170506212819p:plain:w200

f:id:sakura_bird1:20170506212839p:plain:w200

アイコンはGoogle製マテリアルデザインのアイコンで、ベクター画像を使用しています。
手前味噌で恐縮ですが、こちらのページの方法でxmlを追加しました。
sakura-bird1.hatenablog.com


ビューを開いた状態と閉じた状態で表示する画像を変更するのにselectorを使用しています。

res/drawable/expand_arrow.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_expand_more_black_24dp" android:state_selected="true" />
    <item android:drawable="@drawable/ic_expand_less_black_24dp" android:state_selected="false" />
</selector>

ImageViewでsrcで画像を指定する箇所を
app:srcCompat="@drawable/expand_arrow"
という風にselectorを定義したxmlファイルの名前にしておきます。

ビューを押すタイミングでsetSelected(boolean)メソッドでselectedの状態を切り替えます。
これでビューを押すたびに画像が変更されますが、せっかくなのでアニメーションも付けました。

        @Override
        public void onBindViewHolder(final ViewHolder holder, int position) {
            final RecyclerItemViewModel viewModel = getItem(position);
            if (viewModel.isExpanded()) {
                holder.binding.expandButton.setSelected(false);
                holder.binding.expandableLayout.expand(true);
            } else {
                holder.binding.expandButton.setSelected(true);
                holder.binding.expandableLayout.collapse(true);
            }

            viewModel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (viewModel.isExpanded()) {
                        holder.binding.expandButton.setSelected(false);
                        holder.binding.expandableLayout.collapse(true);
                    } else {
                        holder.binding.expandButton.setSelected(true);
                        holder.binding.expandableLayout.expand(true);
                    }
                    // 画像を180度回転させる
                    ObjectAnimator anim = ObjectAnimator.ofFloat(holder.binding.expandArrow, "rotation", 0, 180);
                    anim.setDuration(150);
                    anim.start();

                    viewModel.setExpanded(!viewModel.isExpanded());
                }
            });
(中略)
        }






超うっかりで落胆。freeeを退会しようとしてネットで手続きしたけど、営業時間内でないと再申し込みしないといけないのを忘れて会費を引き落とされた。

私が忘れたのがいけないのですが、共有しておけば誰かの役に立つかもしれないので書いておきます。
フリーランス青色申告に役立つ会計アプリケーションのfreeeってありますよね。

www.freee.co.jp

今までこれを使って便利だったのですが訳あって解約することにしました。
退会する方法はこれですね。

freeeから退会する – freee ヘルプセンター

で画面の指示に従って「お支払い停止手続きに進む」を押したのですが、次のような画面が表示されました。
画像は公式サイトからです。

f:id:sakura_bird1:20170410150243p:plain

ちゃんと読めば書いてあるんだけど、どうも当時の私は向こうから電話がかかってくるんだなと思って忘れてしまいました。
手続きのリンクを踏んだことでとりあえず退会の申し込みはしたような気になってしまいました。
それで電話を待てばいいのだろうとのんきに構えていました。

当然何事も起こらず電話もかかってくることなくばっちり会費を引き落とされていました。
f:id:sakura_bird1:20170410150743p:plain

あーもったいない。
ぼんやりしすぎな自分に対する怒りがこみ上げてなんだか落ち込みました。
受付できませんでしたとか自動配信メールくれてもいいのに、とかちょっと思いましたが、
退会はどこもわかりにくかったり面倒くさいことが多いので気を引き締めていかなければなりませんね。

さっきあらためて停止手続きをしたのですが、確認の自動配信メールとか何も来ていないです。
あと、公式サイトにある「お支払い停止リクエストフォーム」が表示されなかったのですが、
チャットで質問したほうがいいのかどうか迷いますが、これから電話かかってくると思うのでその時に聞いてみようと思います。
こういうものなのでしょうかね。