#dartlang_jp のFlutterハンズオンで再度Flutterを触ってみた
この前の日曜日にFlutterのハンズオンに参加してきました。
- 講師
- ntaooさん 読み方は特に決めてないそうですが、「えぬたおさん」でいいとのこと。
- 資料
dart自体から実際のFlutterアプリまで、かなり盛りだくさんな内容でした。
以前に一度公式のtutorialはこなしていたのですが、プラスαで学べる事が多かったのでまとめておきます。
講義
Dart
FlutterからDartを知ったのですが、Dart自体はもともとWeb用の言語だったそうです。2004年頃からWeb以外もターゲットにした汎用プログラミング言語としての道を歩み始めたとのこと。
また、開発時はJIT, リリース時はAOTコンパイルを実行する点、言語レベルで非同期処理を実装している点などが特徴みたいです。
全体的な文法はJavaとJSにそっくりです。言語自体は意図的に面白くなくしており、簡単に習得できるように設計されています。
Flutter
Flutter自体は前回も触れましたが、復習もかねて。
- 特徴
- クロスプラットフォームが可能なフレームワーク
- HotReloadによる開発の高速化
- staticなものを更新しない限りは差分を見てreloadしてくれる
- Reactiveなフレームワーク
- Reactの影響を受けている
- 状態をstateで管理し、
setState()
メソッドに反応して差分描画を行う
- Widget
- pluginによるデバイスのネイティブ機能との通信が可能
- 公式repo: https://github.com/flutter/plugins
- 自作も可能
- 開発環境サポートが充実
- 構造
- coreとなるEngine部分はC++で書かれている
- documentもしっかりある
- パッケージ管理システム
- Pub package manager
- https://pub.dartlang.org/
- ユニバーサルに使えるコアパッケージと各プラットフォーム向けに使えるパッケージがある
参照
- 公式サイト: https://flutter.io/
- サンプル集: https://github.com/flutter/flutter/tree/master/examples
- awesomeなリソースをまとめたリポジトリ: https://github.com/Solido/awesome-flutter
- 公式Twitter: https://twitter.com/flutterio?lang=en
ハンズオン
作ったもの
hacker news(https://news.ycombinator.com/)のニュースリーダーです。実際に外部APIを叩いています。
ニュースをタップすると、WebViewが開いてページを表示できます。
また、ニュースをお気に入りに登録できます。お気に入りリストも見ることが出来ます。
さらに、Pull(Swipe?)-To-Rrefreshも実装。ネイティブをやっていく自信がなくなってきますね。
ゆっくり調べながらやっていたら当日終わらなくて、後日改めてやり直したコードがこちら。コメント多めです。
学び
ほんとに何から何までWidgetであること
特にDividerなんて顕著ですが、marginから何から全部がWidgetとして扱われます。HTML書いている時にpaddingにするかmarginにするか悩むことが多かったのですが、Flutterだとその辺何も考えずに決められそうでいい感じです。
ネイティブのUIの充実
アイコンとPullToRefreshで実感しました。今回はボタンなどは使用していないためあんまりMaterial感を体験できていないのですが、ネイティブで書くより互換性を気にせずもりもり書けるのはちょっと爽快感があります。 ちなみに、AndroidのminSdkVersion
はデフォルトで16に設定されていました。環境によって違うのでしょうか。
また、アイコンですが、Icons
クラス配下に大量に用意されています。以下のサイトに一覧があるのですが、数えてみたら 985個もありました。笑う。
つらかったところ
書いていくうちに、インデントとカッコの対応が崩れた瞬間の絶望感。。他の言語よりも崩れたときの復帰がしんどい印象でした。Android StudioのDartAnalysisを使えばかなりわかりやすくなりますが、これがなかったらと思うと……恐ろしいですね。
正直初めは何やってるのか全く分かりませんでしたが、2回目をやり直したらだいぶ目が慣れてきました。 marginからdividerから何までWidgetであるということを忘れなければ大丈夫だと思います。
また、やっぱりネイティブと比べてしまうと不安定なところはあります。例えば、ListViewを勢い良くスクロールすると……たまに落ちたします。この辺のキャパシティというか、何も考えずに書いてもある程度いい感じにやってくれるのは、もしかするとネイティブの方なのかなぁ、などと。
lintについて
ハンズオンでは参考程度くらいでしか触れられていないのですが、Flutter(Dart)のlintについてです。
デフォルトでは、override時のアノテーション(@override
)は添えていなくてもエラーが出ないようになっています。ただ、慣れていないうちにタイポ等もろもろつらかったので、試しに追加してみました。手順は以下の通りです。
- projectのrootに
analysis_options.dart
を作成する。- これ、もとは
.analysis_options
だったみたいなのですが、変更加えるそうなので前者がおすすめだそうです。
- これ、もとは
- 中身をこんな感じに書く。
analyzer: strong-mode: true errors: todo: ignore exclude: - flutter/** - lib/api/*.dart linter: rules: - avoid_empty_else - cancel_subscriptions - close_sinks - annotate_overrides
以上です。簡単ですが、忘れず設定しておきたいです。
詳しくは、以下のリンクを参照してください。
感想
Flutter公式のtutorialにプラスした部分もあり、かなり実用的なハンズオンでした。このアプリ普通に使えますね……。そして、何故か忘れがちですがこれがAndroidとiOS両方で動いているというの、改めて恐ろしいです。
ただ、やっぱりそろそろファイル1枚に書いていくスタイルが厳しくなってくる分量だと思うので、まずはこれを分割していくところが次のステップでしょうか。
また、今後はチャットアプリなど外部のAPIにPOSTしていくようなものも触ってみたいです。次は話題のFirebaseですかね……!
講師のntaooさん、主催の方々、ありがとうございました!
追記
講師のntaooさんよりコメントいただきました!
コーディングの際に、プラグインのおかげで強力なalt+enter
の補完が使えるそうです。詳しくは以下のリンクとこの記事のコメント欄を参照してください。
GoogleI/O行ってないけどAndroid Jetpack気になったので調べてみた
先週はGoogle I/Oでしたね。現地にも行かずリアルタイムで見ることもしませんでしたが、TwitterのTLから現地のレポートを見てそわそわしていました。
KeynoteでのAndroid P発表も騒がれましたが、何やらJetpack
とやらが話題の様子。Googleがdocumentを揃えてくれているので、読んでみました。
なお、翻訳ではなくメモ程度です。
What’s
公式サイトによると、
Jetpack is a set of libraries, tools and architectural guidance to help make it quick and easy to build great Android apps.
とのこと。後述する内訳も見ると、これまでGoogleが提供していたArchitectureComponentを含んだコンポーネント群、のような感じでしょうか。
What’s included
4つの区分があるようです。
Foundation
Android開発の全領域に関わるコンポーネントが入っています。AppCompatよく分からんけど使っとくか、みたいな時期がすごく長かったので、個人的にこれがちゃんと分類されたことはすごく嬉しいです。
Architecture
- Data Binding
- Lifecycles
- LiveData
- Navigation
- Paging
- Room
ここがいわゆるArchitecture Componentが入るカテゴリのようですね。データの永続化、データ変更の通知などを行うものから、ライフサイクルのつらみをラッピングしてくれるものまで詰まっています。 正直まだ全部は分かってないので詳しくは書きません。
Navigationのところは、Androidアプリのデザイン自体とも絡んできそうなので、早めに読みたいです。
Behavior
- Download manager
- Media & playback
- Notifications
- Permissions
- Sharing
- Slices
制作しているアプリが、他のアプリやOSなどと上手くやっていくところをお手伝いしてくれるコンポーネント群、という理解です。
特に気になったのはNotificationsとSlicesです。前に業務でウィジェット(ホームスクリーンに出てくる方)を作ることがあったのですが、通知やらUI微妙に作りづらい問題やらで結構苦労しました。この辺がきれいに作れるようになってるといいなぁ……。(まだ見てない)
UI
- Animation & transitions
- Auto
- Emoji
- Fragment
- Layout
- Palette
- TV
- Wear OS by Google
当たり前なのですが、UIというだけあって、handset以外のモバイル端末から車までもをサポートするコンポーネントが揃っています。スマホだけがAndroidアプリ開発じゃないんだ。
加えて、おなじみFragmentから絵文字、アニメーションを対象としたコンポーネントが入っています。
How to use
具体的な使い方は各コンポーネント群をインストールして……となるのです が、Jetpackベースでアプリを作る時のハンズオン?のようなものも用意されていました。
Android Studioの3.2+が必須条件です。まだアイコンが緑色の人(2018/05/12時点)は、公式サイトからPreview版をインストールしましょう。 Stable版とは違う名前でインストールされたのでご安心を。
次に、新しくプロジェクトを作ります。
すると……
新顔がいますね。Activity & Fragment + ViewModel
だそうです。
ActivityやFragmentとそれらに紐づくlayout配下のXMLの名前を聞かれます。さらに、ViewModelの名前やFragment package pathを入力する項目がありますね。
とりあえずそのままにしてFinish
を押すと……
Activity, Fragment, ViewModelが出来ています。Activity以外はuiパッケージに押し込めてくれています。
ソースはかなりシンプルですが、以下のとおりです。
Activity
... class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main_activity) if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .replace(R.id.container, MainFragment.newInstance()) .commitNow() } } }
Fragment
... class MainFragment : Fragment() { companion object { fun newInstance() = MainFragment() } private lateinit var viewModel: MainViewModel override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { return inflater.inflate(R.layout.main_fragment, container, false) } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java) // TODO: Use the ViewModel } }
ViewModel
import android.arch.lifecycle.ViewModel class MainViewModel : ViewModel() { // TODO: Implement the ViewModel }
ページ上に進めていくと、ViewModelを実装したりArchitectureComponentのNavigation
の香りを感じたりできます。
Guide to App Architecture
Guide to App Architecture | Android Developers
ちょっと長いので箇条書きで。
- アプリのコンポーネントの中にアプリのデータや状態を保持してはいけない
- 全てのアプリのコンポーネントは、個別に起動されたりいつでも捨てられる可能性があるため
- コンポーネント間は依存せず独立しているべき
- 重要な原理は2つ
- 関心の分離をしよう
- ActivityやFragmentからUI操作やOSとのやり取り以外のコードを排除する
- UIを(できれば永続化した)モデルから操作しよう
- OSによるリソース解放でアプリのプロセスがdestroyされたとしてもユーザーがデータを失わないため
- ネットワークの状態がよくない時でもアプリを起動しておくため
- Modelとはアプリ内のデータを扱うコンポーネント
- 関心の分離をしよう
UIを作る時は?
ViewModel
を使おう- 表示するデータを保持しておくため
- Viewや設定の変更(画面の向きの変更によるactivityの再生成など)とは関係なしにデータを扱える
LiveData
を使おう- アプリの他のコンポーネントから保持しているデータの変更を検知するため
- データの変更をobserveしてUIを更新したいときには、propertyを
LiveData
で包んだ後、observe()
メソッド内で処理を行う
データを取得するときは?
- REST APIと通信を行うライブラリを使おう
- Serviceという形でメソッドを準備する
- 今回は
Retrofit
を使う
ViewModel
にデータの取得とオブジェクトへのマッピングの責務を追わせるのはやめよう- メンテナンス性向上のため
関心の分離のため
Repository
オブジェクトを作ろう- コンポーネント間の依存性を管理しよう
ViewModelとRepositoryを接続するときは?
ViewModelからrepositoryのメソッド呼ぶだけです。次!
データをキャッシュしたいときは?
- Fragmentに戻るたびにAPIをcallするのはやめよう
- 貴重なネットワークの帯域を浪費することになる
- ユーザーに通信完了まで待たせることになる
val cached = CacheObject.get(id) if (cached != nil) { return cached } // 以降、APIからの取得処理
そっか、インメモリキャッシュってこんなシンプルだったんだと考え直しました。SQLiteやらrealmやらライブラリ先行で技術を追っていると大事なことを忘れてしまいますね。
データを永続化したいときは?
Room
を使おう
テストしたいときは?
- UI & Interaction
- この時だけEspressoなどを使ってUIの実装のテストをしよう
- ViewModel
JUnit
でテストをしようRepository
をモックににする必要がある
- Repository
- DAO
- instrumentationテストを行う必要がある
JUnit
のSupportSQLiteOpenHelper
を使えばUnitテストができるが、端末とローカルPCとでSQLiteのバージョンに違いがあると困る
- Service
- Mock化しよう
- 外部に依存しないテストにするため、通信が走らないでテストをしよう
- ライブラリはたくさんあるけど、okhttp/mockwebserverとかおすすめ
- TestingArtifacts
TODO
- 古くなったデータの更新処理
- ネットワークの状態をユーザーに通知する
Note
- データの整合性を保つには、data sourceを1つにしよう
- 同じデータを返す複数のAPIのエンドポイントがあるとき、データの齟齬が発生する
- 全てのデータ取得を内部のDBなどに統一すれば解決できる
大筋は以上です!!長かった!~後半JetpackというよりはAACの話だけどまあいっか~
実際はコードラボ風味になっているので、手を動かしながら進めていただきたく!
AACの紹介ではなくて、直面した問題ベースでの設計の考え方をベースに進めてくれたので、かなり分かりやすいし研修の教材としてもいい感じなのではないでしょうか。
まだAACで触わりたりないところがあるので、各コンポーネントの内部もしっかり読んでいきたいです。
Android StudioのActivityTemplate全部試してみた!(2/2)
前回に引き続き、AndroidStudioのActivityのサンプルを試してみます。 残り6個!
目次
- LoginActivity
- Master/Detail Flow
- TabbedActivity
- SettingsActivity
- ScrollingActivity
- NavigationDrawer
LoginActivity
特徴
- 一般的なログインフォーム
- 補完機能などUIが充実
Source
xml
<!-- プログレスバー --> <ProgressBar android:id="@+id/login_progress" style="?android:attr/progressBarStyleLarge" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:visibility="gone" />
<!-- メールアドレスの補完機能がついたTextView --> <AutoCompleteTextView android:id="@+id/email" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/prompt_email" android:inputType="textEmailAddress" android:maxLines="1" android:singleLine="true" />
自動補完用のTextViewなんてあったんですね。
Activity
長いのでメソッド名だけ、
- permission処理
- mayRequestContacts()
- onRequestPermissionResult(Int, Array
, IntArray)
- 連絡帳アクセス
- onCreateLoader(Int, Bundle?)
- onLoadFinished(Loader>Cursor>, Cursor)
- textViewに補完
- addEmailsToAutoComplete(List
)
- addEmailsToAutoComplete(List
- 通信処理
- inner class UserLoginTask
- プログレスダイアログ
- showProgress(Boolean)
機能だけでなく、UX的な面でも参考になります……!
Master/Detail Flow
特徴
Source
基本は一般的なActivityとFragmentです。特徴的なのは、出し分けのロジックですね。
今回はこれを2つのitem_list.xml
の呼び分けで実現しています。app/src/main/res/layout
にあるものが呼ばれていればスマホサイズ、app/src/main/res/layout-w900dp
のものが呼ばれていればタブレットサイズとなります。
Activity
if (item_detail_container != null) { // The detail container view will be present only in the // large-screen layouts (res/values-w900dp). // If this view is present, then the // activity should be in two-pane mode. twoPane = true }
onClickListener = View.OnClickListener { v -> val item = v.tag as DummyContent.DummyItem // タブレットかどうか if (twoPane) { val fragment = ItemDetailFragment().apply { arguments = Bundle().apply { putString(ItemDetailFragment.ARG_ITEM_ID, item.id) } } parentActivity.supportFragmentManager .beginTransaction() .replace(R.id.item_detail_container, fragment) .commit() } else { val intent = Intent(v.context, ItemDetailActivity::class.java).apply { putExtra(ItemDetailFragment.ARG_ITEM_ID, item.id) } v.context.startActivity(intent) } }
TabbedActivity
特徴
3種類のスタイルがあります。
- Swipe Views
- Action Bar Tabs
- Action Bar Spinner
が、、、全体的に上手くいきませんでした。
SwipeViewsはデザインが違い、その他はそもそもビルドが出来ませんでした。
AndroidStudioのバージョンにもよるのでしょうが、別途自分で書いてみます。
SettingsActivity
特徴
アプリの設定画面を生成してくれます。
アプリ内で完結したいものは、OSの設定画面へと遷移してくれます。
Source
AppCompatPreferenceAcrivityでPreferenceごとにFragmentを定義しています。
@TargetApi(Build.VERSION_CODES.HONEYCOMB) class GeneralPreferenceFragment : PreferenceFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) addPreferencesFromResource(R.xml.pref_general) setHasOptionsMenu(true) // Bind the summaries of EditText/List/Dialog/Ringtone preferences // to their values. When their values change, their summaries are // updated to reflect the new value, per the Android Design // guidelines. bindPreferenceSummaryToValue(findPreference("example_text")) bindPreferenceSummaryToValue(findPreference("example_list")) } override fun onOptionsItemSelected(item: MenuItem): Boolean { val id = item.itemId if (id == android.R.id.home) { startActivity(Intent(activity, SettingsActivity::class.java)) return true } return super.onOptionsItemSelected(item) } }
の繰り返しです。
また、xml/pref_xxx
でpreferenceごとの画面を定義しています。
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to dismiss it. --> <!-- NOTE: ListPreference's summary should be set to its value by the activity code. --> <ListPreference android:defaultValue="180" android:entries="@array/pref_sync_frequency_titles" android:entryValues="@array/pref_sync_frequency_values" android:key="sync_frequency" android:negativeButtonText="@null" android:positiveButtonText="@null" android:title="@string/pref_title_sync_frequency" /> <!-- This preference simply launches an intent when selected. Use this UI sparingly, per design guidelines. --> <Preference android:title="@string/pref_title_system_sync_settings"> <intent android:action="android.settings.SYNC_SETTINGS" /> </Preference> </PreferenceScreen>
こうやって使うんですね。
ScrollingActivity
特徴
- CollapsingToolbarLayoutの中に配置したAppBarの大きさが可変になる
Source
そのままじゃ上手く表示されなかったのでちょっと修正……
以前紹介した本を参考にしました。
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="180dp" android:elevation="10dp"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/toolbarLayout" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/colorPrimary" app:layout_collapseMode="pin" /> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.v4.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ScrollingActivity" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_mar[f:id:t-miliya612:20180510205238p:plain]gin="@dimen/text_margin" android:text="@string/large_text" /> </android.support.v4.widget.NestedScrollView> </android.support.design.widget.CoordinatorLayout>
app:layout_behavior="@string/appbar_scrolling_view_behavior"
を忘れるとコンテントの文字が被ってしまうので要注意です。
NavigationDrawer
特徴
- 左側から出てくるタイプのナビゲーション
Source
設定は結構簡単でした。
Activity
val toggle = ActionBarDrawerToggle( this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) drawer_layout.addDrawerListener(toggle) toggle.syncState() nav_view.setNavigationItemSelectedListener(this)
activity_navigation_drawer.xml
... <android.support.design.widget.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" app:headerLayout="@layout/nav_header_navigation_drawer" app:menu="@menu/activity_navigation_drawer_drawer" /> ...
layout/nav_header_navigation_drawer.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="@dimen/nav_header_height" android:background="@drawable/side_nav_bar" android:gravity="bottom" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:theme="@style/ThemeOverlay.AppCompat.Dark"> <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:contentDescription="@string/nav_header_desc" android:paddingTop="@dimen/nav_header_vertical_spacing" app:srcCompat="@mipmap/ic_launcher_round" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="@dimen/nav_header_vertical_spacing" android:text="@string/nav_header_title" android:textAppearance="@style/TextAppearance.AppCompat.Body1" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/nav_header_subtitle" /> </LinearLayout>
menu/activity_navigation_drawer_drawer.xml
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:showIn="navigation_view"> <group android:checkableBehavior="single"> <item android:id="@+id/nav_camera" android:icon="@drawable/ic_menu_camera" android:title="Import" /> <item android:id="@+id/nav_gallery" android:icon="@drawable/ic_menu_gallery" android:title="Gallery" /> <item android:id="@+id/nav_slideshow" android:icon="@drawable/ic_menu_slideshow" android:title="Slideshow" /> <item android:id="@+id/nav_manage" android:icon="@drawable/ic_menu_manage" android:title="Tools" /> </group> <item android:title="Communicate"> <menu> <item android:id="@+id/nav_share" android:icon="@drawable/ic_menu_share" android:title="Share" /> <item android:id="@+id/nav_send" android:icon="@drawable/ic_menu_send" android:title="Send" /> </menu> </item> </menu>
まとめ
全体的に学習材料として面白かったです。ただ、Tab系のActivityは謎でした……結構使いそうな気がするんだけどなぁ。
初めて実装する際には、手直しのことも考えるとEmptyActivityで頑張った方がよさそうですね。
そして、 初めから全部で2記事って決めると大変つらいことになる という学びを得ました。以上です。
Android StudioのActivityTemplate全部試してみた!(1/2)
AndroidStudioでActivityを追加するとき、とりあえず何も考えずにEmptyActivityを選択していました。
これ他の選ぶとどうなってるんだろう、とふと気になってしまい、、、
とりあえず全部遊んでみることにしました。
リポジトリはこちら
目次(1/2)
- Basic Activity
- BottomNavigationActivity
- FullscreenActivity
- GoogleAdMobAdsActivity
- GoogleMapsActivity
- LoginActivity
Basic Activity
特徴
- Toolbarが設定されている
- FloatActionButtonが配置されている
onClickListener
でToastを表示
content_{ActivityName}
の名前で空のConstraintLayoutがincludeされている
特に難しいものはないですね。ToolbarやFABを生成してくれるのは使い勝手が良さそうです。
BottomNavigationActivity
- BottomNavigationの配置
- 遷移するとtextが変更
- 選択したアイコンは色が変わってサイズが少し大きくなる
res/menu
を使用したデータ受け渡し (あれ、menuなんて使ったことなかった……これめっちゃ便利……)
FullscreenActivity
- Fullscreen表示に対応
- タップしたらFullscreenの解除、設定のトグル
面白そうなのでソースも追っていきます。分解して見ていきましょう
Source
- onCreate()
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_fullscreen) supportActionBar?.setDisplayHomeAsUpEnabled(true) mVisible = true // Set up the user interaction to manually show or hide the system UI. fullscreen_content.setOnClickListener { toggle() } // Upon interacting with UI controls, delay any scheduled hide() // operations to prevent the jarring behavior of controls going away // while interacting with the UI. dummy_button.setOnTouchListener(mDelayHideTouchListener) }
- toggle()
private fun toggle() { if (mVisible) { hide() } else { show() } }
- hide()
private fun hide() { // Hide UI first supportActionBar?.hide() fullscreen_content_controls.visibility = View.GONE mVisible = false // Schedule a runnable to remove the status and navigation bar after a delay mHideHandler.removeCallbacks(mShowPart2Runnable) mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY.toLong()) }
- mShowPart2Runnable
private val mShowPart2Runnable = Runnable { // Delayed display of UI elements supportActionBar?.show() fullscreen_content_controls.visibility = View.VISIBLE }
- mHidePart2Runnable
private val mHidePart2Runnable = Runnable { // Delayed removal of status and navigation bar // Note that some of these constants are new as of API 16 (Jelly Bean) // and API 19 (KitKat). It is safe to use them, as they are inlined // at compile-time and do nothing on earlier devices. fullscreen_content.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION }
systemUiVisibility
については、こちらの記事が詳しいです。Android4.xの時期、ハードの物理ボタンが無くなった時期に生まれたんですね。Xperia acroとか懐かしい……。
GoogleAdMobAdsActivity
特徴
- GoogleのAdMobを使用する
- Interstitial(全画面)タイプの広告、Bannerタイプの広告の2種類がある
Source
Interstitialの場合もBannerの場合も、呼んでるAPIは変わらないみたいです。以下、Interstitialの例です。
build.gradle
dependencies { implementation 'com.google.android.gms:play-services-ads:15.0.1'
Activity
// Create the InterstitialAd and set the adUnitId (defined in values/strings.xml). interstitialAd = newInterstitialAd() loadInterstitial() ... private fun newInterstitialAd(): InterstitialAd { return InterstitialAd(this).apply { adUnitId = getString(R.string.interstitial_ad_unit_id) adListener = object : AdListener() { override fun onAdLoaded() { next_level_button.isEnabled = true } override fun onAdFailedToLoad(errorCode: Int) { next_level_button.isEnabled = true } override fun onAdClosed() { // Proceed to the next level. goToNextLevel() } } } } private fun showInterstitial() { // Show the ad if it's ready. Otherwise toast and reload the ad. if (interstitialAd?.isLoaded == true) { interstitialAd?.show() } else { Toast.makeText(this, "Ad did not load", Toast.LENGTH_SHORT).show() goToNextLevel() } } private fun loadInterstitial() { // Disable the next level button and load the ad. next_level_button.isEnabled = false val adRequest = AdRequest.Builder() .setRequestAgent("android_studio:ad_template") .build() interstitialAd?.loadAd(adRequest) } ...
基本的な流れとしては、
InterstitialAd(this).apply
でInterstitialの広告を生成AdRequest.Builder()
で広告リクエストを作成interstitialAd?.loadAd(adRequest)
で実際に広告をリクエスト
となっているようです。
GoogleMapsActivity
特徴
- GoogleMapを表示
- 事前にgoogle developer consoleでGoogle MapsのAPI keyの取得が必須
res/values/google_maps_api.xml
を参照
Source
コードはそんなに複雑ではありません。
build.gradle
dependencies { implementation 'com.google.android.gms:play-services-maps:15.0.1'
Activity
import android.support.v7.app.AppCompatActivity import android.os.Bundle import com.google.android.gms.maps.CameraUpdateFactory import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.OnMapReadyCallback import com.google.android.gms.maps.SupportMapFragment import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.MarkerOptions class MapsActivity : AppCompatActivity(), OnMapReadyCallback { private lateinit var mMap: GoogleMap override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_maps) // Obtain the SupportMapFragment and get notified when the map is ready to be used. val mapFragment = supportFragmentManager .findFragmentById(R.id.map) as SupportMapFragment mapFragment.getMapAsync(this) } /** * Manipulates the map once available. * This callback is triggered when the map is ready to be used. * This is where we can add markers or lines, add listeners or move the camera. In this case, * we just add a marker near Sydney, Australia. * If Google Play services is not installed on the device, the user will be prompted to install * it inside the SupportMapFragment. This method will only be triggered once the user has * installed Google Play services and returned to the app. */ override fun onMapReady(googleMap: GoogleMap) { mMap = googleMap // Add a marker in Sydney and move the camera val sydney = LatLng(-34.0, 151.0) mMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney")) mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney)) } }
一旦今回はここまで!
残り6つはまた次回。。。
kotlin-stdlib-jreがdeprecatedになっていたので深掘りしてみた
環境
- AndroidStudio 3.1.2
- Kotlin 1.2.41
起こったこと
AndroidStudioで新しくプロジェクトを作ってみたら、{project}/app.gradle
のorg.jetbrains.kotlin:kotlin-stdlib-jre7
がdeprecatedになっていました。
kotlin-stdlib-jre7 is deprecated since 1.2.0 and should be replaced with kotlin-stdlib-jdk7
言われた通りjre-7
からjdk-7
に変えたら直りました。よかったです。
……だけで終わるのもアレなので、しっかり調べてみました。
kotlin-stdlibについて
まさにStandardなlibraryで、Kotlinのコア部分の実装を担っています。
The Kotlin Standard Library provides living essentials for everyday work with Kotlin. These include:
- Higher-order functions implementing idiomatic patterns (let, apply, use, synchronized, etc).
- Extension functions providing querying operations for collections (eager) and sequences (lazy).
- Various utilities for working with strings and char sequences.
- Extensions for JDK classes making it convenient to work with files, IO, and threading.
kotlin-stdlib
はコア部分、kotlin-stdlib-jre/jdk
はextension的な部分っぽいですね。
似たような議論がこちらに。
変更理由
Java9、特にその中でもモジュールシステムのサポートのためみたいです。
The Kotlin standard library is now fully compatible with the Java 9 module system, which forbids split packages (multiple jar files declaring classes in the same package). In order to support that, new artifacts kotlin-stdlib-jdk7 and kotlin-stdlib-jdk8 are introduced, which replace the old kotlin-stdlib-jre7 and kotlin-stdlib-jre8.
The declarations in the new artifacts are visible under the same package names from the Kotlin point of view, but have different package names for Java. Therefore, switching to the new artifacts will not require any changes to your source code.
モジュールシステムについて
- 別名 Project Jigsaw
- Packageの上位概念としてモジュールを作る
実際にKotlin内のコードでも使われていますね。
目的
- 構成の信頼性の向上
- モジュール間の依存性を明示的に宣言
- コンパイル時、実行時に依存性の認識が可能
- 強力なカプセル化
- モジュールは明示的にエクスポートしない限り外部からのアクセス不可
- エクスポートは全開放するか特定のモジュールにのみ解放するか選べる
- スケーラブルなJavaプラットフォーム
- プラットフォームがモジュールで分割される
- 必要なものだけを集めたカスタムランタイムを作れる
- ランタイムのサイズを削減できる
- プラットフォームの整合性向上
- パフォーマンスの向上
- これちょっとわかりませんでした。。。
http://www.oracle.com/webfolder/technetwork/jp/javamagazine/Java-SO17-Modules.pdf
エクスポートを明示的にできるのは良さそうですね!open
が必要なのはKotlinの影響もあるんでしょうか。
所感
Android-Java-Kotlinと変数が増えると最新版を追っかけるのは大変ですね……。
モジュールシステムに関しては、gradleを使ったモジュールとの今後が気になります。試しにこれ使ってマルチモジュールのアプリ書いてみたいなぁ(伏線)
また、stdlibに関して、ネーミングの理由はよくわからないままでした。何故拡張版をjreなんてネーミングにしたんでしょう。
おまけ
AndroidのJava7サポートって完全じゃなかったようなーと思い調べてみた所、こんな記事が見つかりました。
Androidの開発環境における「Javaのバージョン」はいくつかの考え方があり、自明ではありません。ここでは3つに分けて考えます。
ひとつはAndroid端末にインストールされているAndroid OS、それにバンドルされているDalvik処理系(仮想マシン)がどのJVMバージョンに相当するかというものです。
もうひとつは、Android端末にインストールされているAndroid OS、それにバンドルされているJava標準ライブラリのバージョンです。
あれ、そしたら結局AndroidってどのJava使えるの……?
Java 8 言語機能の使用 | Android Developers
Android Studio では、特定の Java 8 言語機能とそれらを使用するサードパーティ ライブラリのビルトイン サポートを提供しています。
し、知らなかった……。
こちらからは以上です。
公式のcodelabでFlutterに入門してみた
今度Flutter Meetup Tokyo #2に参加するので、事前にちょっと手を動かしてみました。
What’s Flutter
Flutter - Beautiful native apps in record time
Googleが開発しているmobile appのSDKで、Dartという言語を用いて開発します。
本家サイトによると、特徴は以下の5つ。
- Fast development
- ホットリロードが可能
- Expressive and Flexible UI
- Modern, reactive framework
- Access native features and SDKs
- Unified app development
環境構築
環境
- macOS 10.13.4
- Android Studio 3.1.2
- Xcode 9.3
1. install Dart
$ brew tap dart-lang/dart
==> Tapping dart-lang/dart Cloning into '/usr/local/Homebrew/Library/Taps/dart-lang/homebrew-dart'... remote: Counting objects: 5, done. remote: Compressing objects: 100% (5/5), done. remote: Total 5 (delta 0), reused 2 (delta 0), pack-reused 0 Unpacking objects: 100% (5/5), done. Tapped 1 formula (30 files, 26.3KB)
$ brew install dart --with-dartium --with-content-shell
==> Installing dart from dart-lang/dart ==> Downloading https://storage.googleapis.com/dart-archive/channels/stable/release/1.24.3/sdk/dartsdk-macos-x64-release.zip ######################################################################## 100.0% ==> Downloading https://storage.googleapis.com/dart-archive/channels/stable/release/1.24.3/dartium/dartium-macos-x64-release.zip ######################################################################## 100.0% ==> Downloading https://storage.googleapis.com/dart-archive/channels/stable/release/1.24.3/dartium/content_shell-macos-x64-relea ######################################################################## 100.0% ==> Caveats Please note the path to the Dart SDK: /usr/local/opt/dart/libexec ==> Summary 🍺 /usr/local/Cellar/dart/1.24.3: 3,039 files, 657.7MB, built in 21 seconds
2. install Flutter
$ git clone https://github.com/flutter/flutter.git
Cloning into 'flutter'... remote: Counting objects: 120517, done. remote: Total 120517 (delta 0), reused 0 (delta 0), pack-reused 120516 Receiving objects: 100% (120517/120517), 36.60 MiB | 3.11 MiB/s, done. Resolving deltas: 100% (90418/90418), done.
$ git co v0.3.1 # flutter公式ページのzip最新版がv0.3.1だったため(2018/04/29現在)
$ vim ~/.zprofile
export PATH=$HOME/flutter/bin:$PATH
3. Set up environment
$ flutter doctor
Downloading Dart SDK from Flutter engine 09d05a38912a3c1a906e95099cac9a7e14fae85f... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 43.9M 100 43.9M 0 0 9507k 0 0:00:04 0:00:04 --:--:-- 9508k Building flutter tool... ╔════════════════════════════════════════════════════════════════════════════╗ ║ Welcome to Flutter! - https://flutter.io ║ ║ ║ ║ The Flutter tool anonymously reports feature usage statistics and crash ║ ║ reports to Google in order to help Google contribute improvements to ║ ║ Flutter over time. ║ ║ ║ ║ Read about data we send with crash reports: ║ ║ https://github.com/flutter/flutter/wiki/Flutter-CLI-crash-reporting ║ ║ ║ ║ See Google's privacy policy: ║ ║ https://www.google.com/intl/en/policies/privacy/ ║ ║ ║ ║ Use "flutter config --no-analytics" to disable analytics and crash ║ ║ reporting. ║ ╚════════════════════════════════════════════════════════════════════════════╝ Downloading Material fonts... 0.9s Downloading package sky_engine... 0.5s Downloading common tools... 1.1s Downloading darwin-x64 tools... 1.9s Downloading android-arm-profile/darwin-x64 tools... 0.7s Downloading android-arm-release/darwin-x64 tools... 0.8s Downloading android-arm64-profile/darwin-x64 tools... 0.8s Downloading android-arm64-release/darwin-x64 tools... 0.8s Downloading android-x86 tools... 1.8s Downloading android-x64 tools... 1.5s Downloading android-arm tools... 1.6s Downloading android-arm-profile tools... 1.0s Downloading android-arm-release tools... 1.2s Downloading android-arm64 tools... 1.3s Downloading android-arm64-profile tools... 1.1s Downloading android-arm64-release tools... 1.0s Downloading ios tools... 2.2s Downloading ios-profile tools... 2.1s Downloading ios-release tools... 1.9s Downloading Gradle Wrapper... 0.2s Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel unknown, v0.3.1, on Mac OS X 10.13.4 17E199, locale ja-JP) -Error executing simctl: -6 dyld: Symbol not found: _SimDeviceBootKeyDisabledJobs Referenced from: /Applications/Xcode.app/Contents/Developer/usr/bin/simctl Expected in: /Library/Developer/PrivateFrameworks/CoreSimulator.framework/Versions/A/CoreSimulator in /Applications/Xcode.app/Contents/Developer/usr/bin/simctl [!] Android toolchain - develop for Android devices (Android SDK 27.0.3) ! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses [!] iOS toolchain - develop for iOS devices (Xcode 9.3) ✗ Xcode requires additional components to be installed in order to run. Launch Xcode and install additional required components when prompted. ✗ libimobiledevice and ideviceinstaller are not installed. To install, run: brew install --HEAD libimobiledevice brew install ideviceinstaller ✗ ios-deploy not installed. To install: brew install ios-deploy ✗ CocoaPods not installed. CocoaPods is used to retrieve the iOS platform side's plugin code that responds to your plugin usage on the Dart side. Without resolving iOS dependencies with CocoaPods, plugins will not work on iOS. For more info, see https://flutter.io/platform-plugins To install: brew install cocoapods pod setup [✓] Android Studio (version 3.1) ✗ Flutter plugin not installed; this adds Flutter specific functionality. ✗ Dart plugin not installed; this adds Dart specific functionality. [!] IntelliJ IDEA Community Edition (version 2017.2.6) ✗ Flutter plugin not installed; this adds Flutter specific functionality. ✗ Dart plugin not installed; this adds Dart specific functionality. [✓] VS Code (version 1.22.2) [!] Connected devices ! No devices available ! Doctor found issues in 4 categories.
いくつか引っかかりました。
Android license
$ flutter doctor --android-licenses
3 of 6 SDK package licenses not accepted. 100% Computing updates... Review licenses that have not been accepted (y/N)? y 1/3: License android-googletv-license: 2/3: License google-gdk-license: 3/3: License mips-android-sysimage-license:
iOS
CocoaPods入れてなかったのがつらい。。。30分休み。
Android Studio
- Flutter plugin
- Dart plugin
Device
Device繋がってないよ!
はい。
対応し、再挑戦してみます。
$ flutter doctor
master Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel unknown, v0.3.1, on Mac OS X 10.13.4 17E199, locale ja-JP) [✓] Android toolchain - develop for Android devices (Android SDK 27.0.3) [✓] iOS toolchain - develop for iOS devices (Xcode 9.3) [✓] Android Studio (version 3.1) [✓] VS Code (version 1.22.2) [✓] Connected devices (2 available)
いい感じです。これで開発環境が整いました。
codelabについて
スタートアップの会社名候補を生成するシンプルなモバイルアプリ
を作っていきます。お気に入り機能があり、別のページでお気に入りの名前リストを表示することもできます。
開発はAndroidStudioを使いましたが、VScodeなどを使っても良いそうです。
Step 1: Create the starting Flutter app
Flutterのプロジェクトを新規生成します。
Android Studioを使う場合はウィザードから、VScodeを使う場合はコマンドからアプリの雛形を生成できます。
ファイル構成はこんな感じです。
├── README.md ├── android # androidネイティブのコード ├── build ├── ios # iOSネイティブのコード ├── lib │ └── main.dart # 手を加えていくファイル ├── pubspec.lock ├── pubspec.yaml # 依存関係などなどを管理するファイル └── test └── widget_test.dart
終わったら、codelabにあるコードでlib/main.dart
を置き換えましょう。
Step 2: Use an external package
english_wordsというプラグインを入れます。プラグインの追加は、以下の手順で行います。
pubspec.yaml
に追記english_words: ^3.1.0
- Android Studioのタブに表示されている
Packages Get
をクリック- 以下が表示されればOK
${PATH_TO_FLUTTER}/flutter/bin/flutter --no-color packages get Running "flutter packages get" in ${YOUR_APP_NAME}... Process finished with exit code 0
lib/main.dart
にimportを追記import 'package:english_words/english_words.dart';
後はいつも通り使うだけです。特に難しいところはなさそうです。
Step 3: Add a Stateful widget
WidgetにはStatefulとStatelessの2種類があります。画面を表示している間にWidgetを書き換えるかどうかで使い分けます。今回のアプリでは無限スクロールのListを描画するので、StatefulWidgetを拡張したクラスを使用します。
まずは中央に表示されたtextを試しにstatefulにしてみます。
import 'package:flutter/material.dart'; import 'package:english_words/english_words.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Welcome to Flutter', home: new Scaffold( appBar: new AppBar( title: new Text('Welcome to Flutter'), ), body: new Center( child: new RandomWords(), ), ), ); } } class RandomWords extends StatefulWidget { @override createState() => new RandomWordsState(); } class RandomWordsState extends State<RandomWords> { @override Widget build(BuildContext context) { final wordPair = new WordPair.random(); return new Text(wordPair.asPascalCase); } }
Step 4: Create an infinite scrolling ListView
いよいよ無限スクロールのListViewを生成します。ListView.builder()
を返すWidgetを定義し、builderの引数として渡すitemBuilder
プロパティの中でリスト生成のロジックを記述します。
(省略...) class RandomWordsState extends State<RandomWords> { final _suggestions = <WordPair>[]; final _biggerFont = const TextStyle(fontSize: 18.0); @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text('Startup Name Generator'), ), body: _buildSuggestions(), ); } Widget _buildSuggestions() { return new ListView.builder( // ListView.builderを生成 padding: const EdgeInsets.all(16.0), itemBuilder: (context, i){ // itemBuilderのプロパティにリストを描画する無名関数を定義 if (i.isOdd) return new Divider(); final index = i ~/ 2; if (index >= _suggestions.length) { _suggestions.addAll(generateWordPairs().take(10)); } return _buildRow(_suggestions[index]); }, ); } // リスト1行をセットする Widget _buildRow(WordPair pair) { return new ListTile( title: new Text( pair.asPascalCase, style: _biggerFont, ), ); } }
Step 5: Add interactivity
ハート型アイコンをセットして、タップするとお気に入り登録できるようにします。Icons
クラスがすごく充実していて、アイコン追加がかなり楽そうです。
Androidで言うonClick
メソッドは、onTap
プロパティに対応します。
(省略...) Widget _buildRow(WordPair pair) { final alreadySaved = _saved.contains(pair); return new ListTile( title: new Text( pair.asPascalCase, style: _biggerFont, ), trailing: new Icon( alreadySaved ? Icons.favorite : Icons.favorite_border, color: alreadySaved ? Colors.red : null, ), onTap: () { setState(() { if (alreadySaved) { _saved.remove(pair); } else { _saved.add(pair); } }); }, ); }
Step 6: Navigate to a new screen
画面遷移のコードを書いていきます。Flutterでは新しい画面のことをroute
と言うみたいですね。
route
のスタックは、Navigation
クラスを使って管理していきます。
(省略...) void _pushSaved(){ Navigator.of(context).push( new MaterialPageRoute( builder: (context) { // お気に入り登録されたリストを各行にマップ final tiles = _saved.map( (pair) { return new ListTile( title: new Text( pair.asPascalCase, style: _biggerFont, ), ); }, ); // リストを生成 final divided = ListTile .divideTiles( context: context, tiles: tiles, ) .toList(); // appBarと画面のコンテンツ(リスト)をセット return new Scaffold( appBar: new AppBar( title: new Text('Saved Suggestions'), ), body: new ListView(children: divided), ); }, ), ); }
Step 7: Change the UI using Themes
UIの色をいじってみます。今回はひとまず、primaryColorを変更してappBarの色を変えてみます。
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Startup Name Generator', theme: new ThemeData( primaryColor: Colors.white, ), home: new RandomWords(), ); } }
簡単ですね!ただ、結構大胆にカスタマイズをしようとすると大変なことになりそうな。。。
MaterialDesignからどうしても離れなきゃいけないときは厳しそうですね。
作ったもの
まとめ
一通りアプリを完成させるところまでやってみました。かなり簡単にそれなりのUIを作成することができました。
イメージとしてはAnkoのような感じかなーと思っていたのですが、ちょっと違う印象でした。ReactやVueなどのライブラリは使ったことがないのですが、そういったjsのライブラリで培ったノウハウが必要になりそうです。
とはいえ、これでFlutterのことが何となく完全に理解できました。今からmeetupが楽しみです。
今後やりたいこと
- ファイル構成を考える
- ファイルってどうやって分けていくんだろ
- いい加減Reactやらねば。。。
- OS依存の機能を使う
- network通信
- GPS
- camera
- SMS(出来るかな……?)
#技術書典 に行ってきたからまずは買ったものをまとめておく
昨日、技術書典4に行ってきました。前回に引き続き2回目の参加です。
今回は晴れだったこともあり、恐ろしいほどの参加者数に。
twitter.comただいまを持ちまして、 #技術書典4 閉幕しました!今回の総参加者数は6380人でした。
— 技術書典公式アカウント (@techbookfest) 2018年4月22日
皆さんご来場ありがとうございました。
だいぶ気合いの入った入場待ちの列に感化されて、合計12冊の収穫を得ました。熱かったししょうがないです。
もちろんまだ読み終わってないのですが、一旦出会い等をまとめておきます。
目次
- DNSをはじめよう ~基礎からトラブルシューティングまで~
- Now and Future
- SVGで作ろうローディングアニメーション
- こうしてぼくらは、書籍を売るアプリを作った
- こうしてぼくらは、書籍を売るアプリを作った2.0.1
- Gopherの休日
- 大きめのAndroidアプリでの設計を考えてみる〜pocket〜
- 神Excelで働き方改革(仮)
- Vue.jsとFirebaseで作るミニWEBサービス 初めてのサーバーレスシングルアプリケーション
- ざっクリわかる Ubuntu 18.04 LTS
- Nature Design with Houdini 自然界のアルゴリズムを利用したデザインレシピ集
- Android Things with Kotlin Hands-on Book
DNSをはじめよう ~基礎からトラブルシューティングまで~
気になった理由
- 学生時代、wordpressに独自ドメインを当てるときまさに「数時間かかるんだよねー」とか知ったかぶりをしてた
- 会社入れば研修とかでやるじゃろ、って思ってたらあんまやらなかった
- 今後自分でサービスを立ち上げたりするときにアワアワしたくない
- ドリル付き←最高
- 半透明の遊び紙で製本してあり、本自体のクオリティが高い
Now and Future
気になった理由
第1章 Android通知最前線 What's new in Android P
- 普段Androidの開発をする上で、新しいOSの機能ってシェアの都合上すぐに触ることがない(アプリに求められるのはある程度色々なデバイスで動くこと)
- 何だかんだ理由をつけて調べてなかったが、Twitterで情報収集するようにしてから気になって仕方がない
第2章 Fit API for Androidを使ってみよう
第7章 Androidライブラリ実践入門
SVGで作ろうローディングアニメーション
気になった理由
こうしてぼくらは、書籍を売るアプリを作った
気になった理由
- 目当ての章
- 第3章 今日から始めるChatOpsの勘どころ
- 第4章 Debug Menuはじめました。
- 第5章 実践Go言語のインタフェース
- 特に第4章の学びが濃そう
- debugメニュー、「絶対触っちゃいけないゾーン」になってしまったり
- productionを想定してちゃんと管理しているのが流石
こうしてぼくらは、書籍を売るアプリを作った2.0.1
気になった理由
- 目当ての章
- 第2章 Google App EngineでGoのバージョンアップを行う
- 第4章 キンパツは企画を考え、どんなことをしてきたのか
- キンパツこと@operandosさんの話は特にきになる
- ようやくキャリア周りの話に敏感になるようになってきた
Gopherの休日
気になった理由
- お世話になっているgolang.tokyoの頒布物
- 表紙が最高
- @tenntennさんが売り子してるのすごい
大きめのAndroidアプリでの設計を考えてみる〜pocket〜
気になった理由
clean architectureAndroidアプリ設計の記事でお世話になっているkgmyshinさん(思い込みでclean architectureだと決めつけていましたが、記事中に指摘はありませんでした。訂正します。2018/04/23 19:49)- ただ設計だけでなくマルチモジュールでの開発
- 以前yahooのbonfireでもマルチモジュールの話は聞いたけど、実例が読めるのは有難い
- Bonfire Android #3 - connpass
- 取り置き制度は神
神Excelで働き方改革(仮)
気になっていること
- 自動化自動化と叫ばれているだけあって、いわゆる神Excelはほとんど姿を見かけない(見かけたことがない)
- ただ、たまに他の人のスプレッドシートを見ると「オッ」となることが
- 我々は神Excelの悲劇を忘れてはならない
- (正直何が出来て何がつらいのかよくわかってないので、雰囲気でしか喋れない)
Vue.jsとFirebaseで作るミニWEBサービス 初めてのサーバーレスシングルアプリケーション
作者様はこちら nabettu.hatenablog.com
気になった理由
- SPA作ったことない!
- Vue.jsもいい加減触りたい!
- しっかりWebサービス立てるのも大事だけど、Firebase等使ってライトにサクッと立てる技術も大事だと思った
ざっクリわかる Ubuntu 18.04 LTS
気になった理由
- これ学生時代に欲しかったなぁとの思いから
- 大学の授業や先輩の宗教上の理由から、やたらとCentじゃなくてUbuntuを触る機会が多かった
- やっぱり初めに触れるなら、CUIだけのサーバー用途よりもGUIを含めたデスクトップ環境の方が楽でいい
- 「ざっクリ」というだけあってそんなに濃い内容が詰まってるわけではないけれど、聞かれたときにスッと出したい本
Nature Design with Houdini 自然界のアルゴリズムを利用したデザインレシピ集
気になった理由
- これぞ技術書典に来た理由
- webとかAndroidとか、普段の業務や趣味の領域じゃ出会わない分野と出くわすことができる
- アルゴリズミックデザインの本
アルゴリズムを用いて形状のデザインをする」
自分の手入力により形を作り上げていくデータが重くなりがちな作り方ではなく、数理的なルールを記述するだけで少ないデータで複雑な形状を作ることができる
- SVGと繋がる点がありそう
- 対象読者は、アルゴリズミックデザインについて全く知らない人、ではないので、ちょっとずつ勉強しながら読んでいきたい
Android Things with Kotlin Hands-on Book
気になった理由
- 今のところAndroidアプリ開発者としてお仕事をしている
- PWAなどが流行っている中、今やっているネイティブアプリの知識って今後どこまで通用するのかちょっと不安になる
- この本のことを知ってからAndroid Thingsのことも知ったんだけど、こういった道で自分の経験を活かしていけるのであれば……という気持ち。
以上です! (長かった……)
GWに向けて、最高の遊び道具が揃った感じですね!やっていきましょう!
ちなみに
買ったものはTrelloでまとめてみるトライアルをしてみています。