シニアプログラミングネットワーク #1勉強会に行ってきました
こんにちは。さくらです。
先日こんな勉強会に行ってきました。
81歳のiPhoneアプリプログラマー若宮正子さんがアプリ「hinadan」を開発した件は朝日新聞、CNNをはじめとして世界中で報道されました!
だがしかし、81歳プログラマーはさらにいた!そして、さらに他にも「若い者には負けない」どころか「圧倒する」方達が!
今回はシニアプログラミングネットワーク第1回イベントとしてシニアなプログラマーの皆さんの活動を紹介していき、プログラミングを学ぶこと、そしてお年寄りに使いやすいということはどういうことなのかを考えて行きます。
最古参にして最先端のコンピューターおばあちゃん/おじいちゃんの活躍をぜひご覧ください!
81歳にしてアプリを開発して話題になった若宮正子さんの「hinadanのストーリー」を。同じく81歳にして既にアプリを3本もリリースしている方が既にいた!その鈴木富司さんのセッション。また、ハードウェアを絡めた話では遠く福井から谷川一男さんからなんとイノシシ90頭を仕留めた自作檻の話をしていただきます。そして、若宮さんが以前にアプリのレビューを行なった浪江町でのCode for Japanフェローシップの話もあって盛りだくさんです!
このすごさ・・・!
と、言ってもシニアでプログラミングをされている方は私の身近にもいらっしゃいますのでそこまでは驚かなかったですが。
(その方は70歳ぐらいですが、Androidアプリを300本ぐらいリリースされています。
東京アプリ開発者もくもく会にいつも来てくれる方で、すごーく元気をもらっています。)
客席も結構シニアでしたね。47歳の私なぞ小娘でしたね。
今日来てた人達が気軽に集まれる勉強会とかまたやってくれたら行きたいです。
写真も撮ったのですが、後ろの方に座っていたので全体的に下のほうがうまく写っていなくてすみません。
メディアの方がたくさん来ていたようなので、別途詳しい記事が上がると思います。
と思ってたら記事がありました!
フリック!ニュース /Flick!News : 何歳になってもプログラミングにチャンレンジできる! #シニアプログラミング
登壇者の方は実際にアプリを開発されたおばあさまおじいさまだったのですが、どの方もよどみなく喋ってプレゼン力があり、お若かったです。
わたしはあんなに喋れないですよ。
元々聡明でいらっしゃる感じでした。
みんな英語も得意なんだって(✽ ゚д゚ ✽)
若宮正子さん
82歳になったばかりだそうです。リリースしたiOSアプリ「hinadan」について。Swiftで書かれたそうです。
反響がすごく色々なメディアに取り上げられてテレビにも出演したそうです。
韓国でNAVERのカンファレンスで講演をされたそうですよー。すごいですね。
若宮さんのお話はユーモアたっぷりですごくウケていました。
主催の小泉さんとSkype(でしたっけ?すみません物忘れが・・)かなにかでやりとりしてマンツーマンで教えてもらいながら作ったそうです。
アプリのイメージ。年寄りが勝てるゲームを作りたい!という気持ちから始まったビッグプロジェクト!
小泉さんの開発裏話も楽しく参考になりました。
お年寄り向けのUIの話や教える時のコツとか。出来ることと出来ないことを明確に区別して、捨てるべきところを捨ててゴールに向かっていったそうです。
今後は作りたいものから逆算して教育する仕組みを作りたいそうです。
小泉さんは小学生の頃からプログラミングをされていたそうなので、すごいと思いました。
また若宮さんはコードフォージャパンの陣内一樹さんとともに浪江町のアプリ開発にも関わったそうです。
陣内さんのプレゼンも素晴らしく、利用者を置き去りにしたアプリ開発を反省させられました。
写真撮れなくてごめんなさい。
鈴木富司さん
勉強会当日が誕生日だそうで82歳になられたそうです🎉
既に3本もiOSアプリをリリースされてるそうです。
鈴木さんはプログラミングのスクールに通って勉強されたそうですが、一度挫折して2度目の挑戦だったそうです。
現在はiOSのもくもく会によく顔を出されているそうで、若者との交流を楽しみにしているそうです。
鈴木さんのアプリ開発は趣味という言葉より使命感という言葉の方があっている感じです。
高齢者向けスマホの勉強アプリを開発されてます。
アップルのリジェクトとも戦ったそうです。すごい。
谷川一夫さん
Ichigo Jamって初めて知ったんですが、BASICでプログラミングできる子供用パソコンだそうです。
ichigojam.net
色々揃えても数千円で済む感じ。
このモジュールと赤外線センサーを組み合わせてイノシシの捕獲檻を自作して年間90頭のイノシシを捕まえたというすごいお話でした。
渋谷のお洒落なIT勉強会でイノシシの捕まえ方を聞くというシュールさとか色々あり過ぎるんですが、
谷川さんの問題解決力のすごさに脱帽しました。
お住いの福井ではイノシシに農作物を荒らされる被害に悩まされているそうです。
子供プログラミング教室(当日その教室の素敵な先生もいらしてましたよ)でIchigo Jamをイノシシ捕獲に使うことを思いついたそうです。
元々設計士だそうなので、CADを使った設計などもそうですが素養がおありになるなーって思いました。
こんな感じで驚きつつ楽しい時間を過ごしました。
登壇者様、開催者様ともに本当にありがとうございました。
素晴らしい活動を拝見してやる気をたくさんいただけました。
最後に、私も地頭悪い系で趣味プログラミングをやっている者ですが、自分の実感からネガティブ面もちょっと書いておこうと思います。
年取ってもやる気と好奇心があればプログラミング出来るんだー!わーわー!と盛り上がってもやっぱり難しい局面が多いと思うのです。
対象は全くのプログラミング初心者の高齢者の方です。
学習用プログラミング言語やハローワールド以上のことをやろうとすると難易度が高くなる
安定した環境で学習用に作られたプログラミング言語で学習したり、
他の言語でも画面にハローワールドを表示する(のに毛が生えた程度)といったことをやってるうちは平和に進むと思いますが、
「自分の作りたいアプリ」はそれでは実現出来なかったりするのでなんだかんだで難しくなってきます。
年をとると新しいことを覚えるのが辛くなる問題
全くの初心者が始めるのはやっぱりめちゃめちゃ大変だと思います。覚えることが膨大です。
やっと覚えても変化が早いので古い知識になっていて世間ははるか先をいってたりします。
お金がかかるかもしれない
プログラミング技術の本は一冊3000円ぐらいします。また、本だけだと問題が起きた時に一人で解決するのが難しいことがあります。
不定期で集まる勉強会のようなところに参加するのは非常によいことですが、
現役エンジニアは多忙なので、聞いたことには答えてくれてもガッツリ教えてもらうことは期待できません。
何がわからないのか言語化出来れば無料のQAサイトを頼ることが出来ますが、
その段階に至っていなければスクールなどに通ったり有料学習サービスを利用するという選択肢になるかもしれません。
英語と切っても切れない
この勉強会でも話題に出ましたが、プログラミングをすると英語のエラーメッセージを解読する必要があります。
英文を無視せず翻訳サイトで翻訳するといった手間は必要です。
プラットフォームによってはアプリ申請やリジェクトなどのやり取りを英語で行う必要があります。
その他、互換性のないアップデートやモジュール間のバージョン依存など
OSや開発ツールやライブラリのバージョンを上げると動かなくなったり、
新しいバージョンのモジュールを使いたければ今まで作ったプログラミングを修正しなければならなかったり
ということが日常茶飯事のように感じられるかもしれません。
なーんて、いろいろとやる気を削ぐようなことを書いてごめんなさい。
でも今回の勉強会の登壇者様達のようにやる人はやるので、面白そうだとかこんなのが作りたいだとかあったらとりあえず飛び込んでみてもいいですよね。
無料でリッチな開発環境が手に入ったり、世界中の凄腕エンジニアのコードを読めたり、良いことも山ほどある世界ですし!
小泉さんがおっしゃるような作りたいゴールから逆算して教育する仕組みが出来たら、もっと多くの高齢の方が気軽にプログラミングを楽しめる時代が来るかもしれません。
そうなったら楽しいですね♡
伸び縮みするCardViewを作成する(cachapa/ExpandableLayout + RecyclerView + CardViewのサンプル)
RecyclerViewを使ってリスト表示しているレイアウトがあり、その中でタップするとViewが伸縮するCardViewを表示するレイアウトのサンプルを作ったのでメモです。
ExpandableListViewと似たような表示方法です。
こちらに全体のソースがあります。短いコードですが、ブログを読んでいてサンプルのプロジェクト全体を見たいなあって思うことがあるので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
上記のサンプルに伸縮を表すアイコンを追加しました。
アイコンは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ってありますよね。
今までこれを使って便利だったのですが訳あって解約することにしました。
退会する方法はこれですね。
で画面の指示に従って「お支払い停止手続きに進む」を押したのですが、次のような画面が表示されました。
画像は公式サイトからです。
ちゃんと読めば書いてあるんだけど、どうも当時の私は向こうから電話がかかってくるんだなと思って忘れてしまいました。
手続きのリンクを踏んだことでとりあえず退会の申し込みはしたような気になってしまいました。
それで電話を待てばいいのだろうとのんきに構えていました。
当然何事も起こらず電話もかかってくることなくばっちり会費を引き落とされていました。
あーもったいない。
ぼんやりしすぎな自分に対する怒りがこみ上げてなんだか落ち込みました。
受付できませんでしたとか自動配信メールくれてもいいのに、とかちょっと思いましたが、
退会はどこもわかりにくかったり面倒くさいことが多いので気を引き締めていかなければなりませんね。
さっきあらためて停止手続きをしたのですが、確認の自動配信メールとか何も来ていないです。
あと、公式サイトにある「お支払い停止リクエストフォーム」が表示されなかったのですが、
チャットで質問したほうがいいのかどうか迷いますが、これから電話かかってくると思うのでその時に聞いてみようと思います。
こういうものなのでしょうかね。