OPPO Reno Aを購入したので開封!
OPPO Reno A購入の背景
楽天モバイルの無料サポータープログラムに当選したためです。 無料サポータープログラムでは対象の機種を購入する必要があり、今回はOPPO Reno Aを購入したので開封していきます。
当選のメールが到着してから、申込みのメールがなかなかこなかったのでソワソワしていましたが、OPPO Reno Aの在庫があるタイミングだったのでそのまま購入しました。
※ブルーが良かったなと思いつつ、ブルーは在庫がなかったのでブラックを購入しています。
実際に注文してからは3日後に届きました。
開封
早速開封していきます。
ダンボールの中身は、OPPO Reno Aの箱と、SIMカード、スタートガイドでした。
写真だとわかりにくいですが、箱のサイズはかなり大きいです。
iPhone 11と比較してみると、かなり大きいことが分かります。
早速開封していきます。
蓋を開けた直後は、本体とはご対面できず・・
説明書の下に本体が入っていました。
ぱっとみこれは裏面か?と思いましたが、フロントパネル側でした。
さらにその下には透明なケースと、USB-Type Cのケーブル。
このケースは柔らかい素材のものでした。
ケースの下に充電器と、イヤホンが付属しています。
OPPO Reno Aには最近のスマホには珍しくイヤホンジャックがあります。
iPhone11との画面サイズ比較です。
OPPO Reno Aの方が若干ですがサイズが大きいことが分かります。
数日使ってみての感想
メインで使っている端末はPixel3なので、そちらとの比較になります。
電波状況
※10月に開始された楽天モバイルのMNO回線です
- 家で使っていると窓から離れると圏外になってしまう
- 都心にあるオフィスでは基本的には繋がる
- 地下鉄ではau回線に繋がるため快適
- 利用者がまだ少ないからか、MVNO回線に比べると昼や夜は繋がりさえすれば快適
OPPO Reno A
- Pixel3と比較すると、画面がかなり大きく動画やKindleを見るのも快適
- Pixel3では漫画を読む場合はたまに拡大が必要でした。
- バッテリー持ちは良好
- Pixel3と比べると安心感がかなりあります。 ※Pixel3のバッテリー持ちが良くないからというのもありますが・・
- ゲームをするには若干スペック不足
- ドラゴンクエストウォークをプレイしてみた感じでは、標準画質ではモタつきが発生しストレスフルでした。
- プレイを再開しようとしても起動画面からになる (メモリは6GBあるのになぜ・・・
- Pixel3に比べてモタつきが発生する
といった感じです。
基本的にはゲームをプレイしなければ普段使いには全問題ない印象でした。
また、若干のもたつきに関しては、以下の 高パフォーマンスモード に設定することでかなりマシになりました。
バッテリーの持ちがどうなるか若干気がかりですが、それでもPixel3よりは持つ印象です。
まとめ
Pixel3 aが発売されたこのタイミングでPixel3を購入・・!
タイトルの通りですが、Pixel3aが発表されたにもかかわらず、Pixel3を購入しました。
前々からPixel3は気になっていましたが、以下の記事を見て雑誌を購入して、
「Pixel3すごい(語彙力・・)」
「このカメラ欲しい」
という感じで、ずっと悶々としていました。 しかし、定価9万5千円はちょっと高いのでバレンタインセールのようにディスカウントが発生するタイミングを虎視眈々と狙っていました。
Googleストアで「Pixel 3」のNot Pinkが2万円オフのセール、Pixel 3 XLなら2万5000円オフ - Engadget 日本版
全く安くなる気配は無く、月日が流れ。。
Pixel3aという廉価版が出るらしいというので、常に情報をウォッチしていたところ、 5月7日のGoogle I/Oでついに発表されました。(眠い目を擦りながらリアルタイムで視聴
値段も5万円以下ということで、速攻でポチり、17日発売を待つのみという状態に。
その後もソワソワしながら色々調べていると、Pixel3aには「Pixel VisualCore」が載っていないという情報が。
カメラに関しては素人なため、正直気にならないだろうと思っていましたが、 ラクマを眺めていたところ、
が58,000円で販売されていました。
ラクマで3,000円引きのクーポンを配布していたため、55,000円で購入できることに。
Pixel3aの定価が48,600円なので、Pixel3a + 6,000円ほどでPixel3のSIMフリーが手に入ることになります。しかもUSB Type-Cのイヤホン付き。
個人間取引ということで赤ロムやSIMロック未解除といった不安がありましたが、
IMEI番号やSIMロック解除画面の画像も載せてくれていたので信用して購入し、Pixel3aはキャンセルしました。
※本記事を参考にフリマアプリなどで購入し、不利益を被られても一切の責任を負いかねます。
購入したPixel3は当日中に発送していただき、翌日には到着。
早速開封していきます。
丁寧にプチプチで梱包されていました。
Pixel3の箱が登場
角が少し潰れていましたが特に問題なし。
蓋を開けると、フィルムに包まれたままのPixe3が登場。
イヤホンや充電ケーブルも未開封のまま。
SIMもアンロックされており、IIJmioのdocomo SIMでも問題なく通信することができました。
早速色々撮影していきます。
まずは東京駅
気持ちキレイに撮れている気がします。
夜バージョン。
近未来感があります。
※Night Sightは未使用です
続いて食べ物系。
ラーメンや
お肉もお手のもの
鮮やかな肉の塊
まだこれくらいしかPixel3で写真の撮影をできていませんが、現時点では非常に満足です。
iPhone Xで撮影した写真と比較しても鮮やかに撮れるなーという印象です。
とはいえiPhoneのエコシステムからはなかなか抜け出せないので、しばらくはiPhone Xをメイン端末として引き続き使用しつつ、Pixel3はカメラ中心で0simで運用していこうと思います。
CodelabsのAndroid Room with a ViewをKotlinでやってみた
Codelabsについて
Googleが提供している、様々なチュートリアルやハンズオンのコースです。 https://codelabs.developers.google.com/
Android Room with a View
AndroidのRoom
ViewModel
LiveData
について学ぶことができるコースです。
GoogleがKotlinで実装した良いサンプルとして、こちらも公開されています。 https://github.com/googlesamples/android-sunflower
motivation
Android開発の学習をする上で、LiveData
について押さえておきたかったので、Room with a Viewのcodelabsを見つけました。
手を動かしながら学べるのでちょうど良いと思いましたが、実装がJavaだったので置き換えつつ進めてみることにしました。
ゴールはCodelabのアプリがKotlinコードで動作することです。 Javaの元コードを見て、Kotlinだとこんな感じかな〜という風に書き換えて実装していったものになるので、正しい書き方とは異なる可能性もあります。
Environment
- Android Studio 3.2
各セクション毎の変更点
2. Create your app
特になし。 Kotlinでアプリを作成します。
3. Update gradle files
kotlin-kapt
プラグインを有効にし、annotationProcessorの定義をkaptに変更しています。
apply plugin: 'kotlin-kapt' ・ ・ ・ // Room components implementation "android.arch.persistence.room:runtime:$rootProject.roomVersion" // annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion" kapt "android.arch.persistence.room:compiler:$rootProject.roomVersion" androidTestImplementation "android.arch.persistence.room:testing:$rootProject.roomVersion" // Lifecycle components implementation "android.arch.lifecycle:extensions:$rootProject.archLifecycleVersion" // annotationProcessor "android.arch.lifecycle:compiler:$rootProject.archLifecycleVersion" kapt "android.arch.lifecycle:compiler:$rootProject.archLifecycleVersion"
4. Create the entity
Entityの定義方法自体がJavaとKotlinで大きく異なり、かなり楽に記述できます。
import android.arch.persistence.room.ColumnInfo import android.arch.persistence.room.Entity import android.arch.persistence.room.PrimaryKey @Entity(tableName = "word_table") class Word(@PrimaryKey @ColumnInfo(name = "word") var mWord: String)
5. Create the DAO / 6. The LiveData class
- では
getAllWords()
の戻り値はList<Word>
ですが、6.でLiveData<List<Word>>
に修正する
import android.arch.lifecycle.LiveData import android.arch.persistence.room.Dao import android.arch.persistence.room.Insert import android.arch.persistence.room.Query import com.example.hoge.roomwithaview.entity.Word @Dao interface WordDao { @Insert fun insert(word: Word) @Query("DELETE from word_table") fun deleteAll() @Query("SELECT * from word_table ORDER BY word ASC") fun getAllWords(): LiveData<List<Word>> }
7. Add a Room database / 12. Populate the database
getDatabase
メソッドはcompanion object
で定義していますが、package-level function
で定義するのが正しいかも。
※sunflowerサンプルアプリケーションでも実装はcompanion object
で実装されていました。
PopulateDbAsync
クラスは12. Populate the databaseでの実装になります。
ここはCallbackの書き方で少しハマりました。
import android.arch.persistence.db.SupportSQLiteDatabase import android.arch.persistence.room.Database import android.arch.persistence.room.Room import android.arch.persistence.room.RoomDatabase import android.content.Context import android.os.AsyncTask import com.example.hoge.roomwithaview.dao.WordDao import com.example.hoge.roomwithaview.entity.Word @Database(entities = [Word::class], version = 1) abstract class WordRoomDatabase: RoomDatabase() { abstract fun wordDao(): WordDao companion object { @Volatile private var instance: WordRoomDatabase? = null fun getDatabase(context: Context): WordRoomDatabase { if (instance == null) { synchronized(WordRoomDatabase::class.java) { if (instance == null) { instance = Room.databaseBuilder(context.applicationContext, WordRoomDatabase::class.java, "word_database") .addCallback(object : RoomDatabase.Callback() { override fun onOpen(db: SupportSQLiteDatabase) { PopulateDbAsync(instance!!).execute() } }) .build() } } } return instance!! } } } class PopulateDbAsync(db: WordRoomDatabase): AsyncTask<Void, Void, Void>() { private val mDao: WordDao = db.wordDao() override fun doInBackground(vararg params: Void?): Void? { mDao.deleteAll() var word = Word("Hello") mDao.insert(word) word = Word("World") mDao.insert(word) return null } }
8. Create the Repository
import android.app.Application import android.arch.lifecycle.LiveData import android.os.AsyncTask import com.example.hoge.roomwithaview.dao.WordDao import com.example.hoge.roomwithaview.db.WordRoomDatabase import com.example.hoge.roomwithaview.entity.Word class WordRepository(application: Application) { private val mWordDao: WordDao private var mAllWords: LiveData<List<Word>> init { val db = WordRoomDatabase.getDatabase(application) mWordDao = db.wordDao() mAllWords = mWordDao.getAllWords() } fun getAllWords(): LiveData<List<Word>> { return mAllWords } fun insert(word: Word) { InsertAsyncTask(mWordDao).execute(word) } } class InsertAsyncTask(wordDao: WordDao): AsyncTask<Word, Void, Void>() { private val mAsyncTaskDao: WordDao = wordDao override fun doInBackground(vararg params: Word?): Void? { mAsyncTaskDao.insert(params[0]!!) return null } }
9. Create the ViewModel
import android.app.Application import android.arch.lifecycle.AndroidViewModel import android.arch.lifecycle.LiveData import com.example.hoge.roomwithaview.entity.Word import com.example.hoge.roomwithaview.repository.WordRepository class WordViewModel(application: Application) : AndroidViewModel(application) { private val mRepository: WordRepository = WordRepository(application) var mAllWords: LiveData<List<Word>> init { mAllWords = mRepository.getAllWords() } fun insert(word: Word) { mRepository.insert(word) } }
10. Add XML layout
ここはcodelabの通りに実装します。
layout/activity_main.xml
の変更部分で +
ボタンが、ぱっとは見つからなかったので、@drawable/ic_android_black_24dp
で代用
11. Add a RecyclerView
import android.content.Context import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import com.example.hoge.roomwithaview.R import com.example.hoge.roomwithaview.entity.Word class WordListAdapter(context: Context): RecyclerView.Adapter<WordListAdapter.WordViewHolder>() { class WordViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { val wordItemView: TextView = itemView.findViewById(R.id.textView) } private val mInflater: LayoutInflater = LayoutInflater.from(context) private var mWords: List<Word>? = null override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WordViewHolder { val itemView = mInflater.inflate(R.layout.recyclerview_item, parent, false) return WordViewHolder(itemView) } override fun onBindViewHolder(holder: WordViewHolder, position: Int) { if (mWords != null) { val current = mWords!![position] holder.wordItemView.text = current.mWord } else { holder.wordItemView.text = "No Word" } } fun setWords(words: List<Word>) { mWords = words notifyDataSetChanged() } override fun getItemCount(): Int { return if (mWords != null) { mWords!!.size } else { 0 } } }
13. Add NewWordActivity
レイアウトファイルについては、codelabの通りに実装します。
import android.app.Activity import android.content.Intent import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.text.TextUtils import android.widget.Button import android.widget.EditText class NewWordActivity : AppCompatActivity() { companion object { const val EXTRA_REPLY: String = "com.example.hoge.roomwithaview.REPLY" } private var mEditWordView: EditText? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_new_word) mEditWordView = findViewById(R.id.edit_word) val wordText = mEditWordView!!.text val button = findViewById<Button>(R.id.button_save) button.setOnClickListener { val replyIntent = Intent() if (TextUtils.isEmpty(wordText)) { setResult(Activity.RESULT_CANCELED, replyIntent) } else { val word = wordText.toString() replyIntent.putExtra(EXTRA_REPLY, word) setResult(Activity.RESULT_OK, replyIntent) } finish() } } }
14. Connect with the data
import android.app.Activity import android.arch.lifecycle.Observer import android.arch.lifecycle.ViewModelProviders import android.content.Intent import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView import android.util.Log import android.view.Menu import android.view.MenuItem import android.widget.Toast import com.example.hoge.roomwithaview.adapter.WordListAdapter import com.example.hoge.roomwithaview.entity.Word import com.example.hoge.roomwithaview.model.WordViewModel import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { companion object { private const val NEW_WORD_ACTIVITY_REQUEST_CODE = 1 } private var mWordViewModel: WordViewModel? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) val recyclerView = findViewById<RecyclerView>(R.id.recyclerview) val adapter = WordListAdapter(this) recyclerView.adapter = adapter recyclerView.layoutManager = LinearLayoutManager(this) mWordViewModel = ViewModelProviders.of(this).get(WordViewModel::class.java) mWordViewModel!!.mAllWords.observe(this, Observer { adapter.setWords(it!!) }) fab.setOnClickListener { val intent = Intent(this, NewWordActivity::class.java) startActivityForResult(intent, NEW_WORD_ACTIVITY_REQUEST_CODE) } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == NEW_WORD_ACTIVITY_REQUEST_CODE && resultCode == Activity.RESULT_OK) { val word = Word(data!!.getStringExtra(NewWordActivity.EXTRA_REPLY)) mWordViewModel!!.insert(word) mWordViewModel!!.mAllWords.value!!.forEach { Log.i("hoge", it.mWord) } } else { Toast.makeText( applicationContext, R.string.empty_not_saved, Toast.LENGTH_LONG).show() } } override fun onCreateOptionsMenu(menu: Menu): Boolean { // Inflate the menu; this adds items to the action bar if it is present. menuInflater.inflate(R.menu.menu_main, menu) return true } override fun onOptionsItemSelected(item: MenuItem): Boolean { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. return when (item.itemId) { R.id.action_settings -> true else -> super.onOptionsItemSelected(item) } } }
以上で一通り手順に沿っての実装が完了しました。 早速動作確認してみます。
トップページが表示され、PopulateDbAsync
クラスのdoInBackground
メソッドによって、HelloとWorld という文字が表示されています。
Droid君のアイコンをタップすると次の画面に遷移し、文字を入力できます。
testと入力し、SAVEボタンをタップするとTOPページに戻り、testの項目が追加されています。
一通りの動作確認ができました。 引き続きAndroid開発に携われるように色々学んでいこうと思います。
Android 9 Pieを試してみたくてEssential Phoneを購入
タイトルの通りなのですが、私の保有しているAndroid端末はNexus6が最後に購入したもので、Androidバージョンは7.1.1で止まっていました。 主な原因はPixelシリーズが日本で販売されないためです。 ただ、Pixelシリーズは結構高価なスマートフォンなので、販売されていても購入していたかは微妙・・・
そんな中、Android 9 Pieが突如リリースされ、なんとPixelだけではなくEssential Phoneでも利用可能という事で勢いに任せて購入しました。
技適もあるし。
(どうせPixel 3は日本では販売されないだろうと踏んで・・
今回私が購入したのは、Halo Grayです。
公式サイト経由で購入するより、Amazon.comで購入する方が安く・早く届きそうだったので、今回はAmazon.comで購入しました。
$373.44に諸々がプラスされ、$424.97でした。
8/20(月) 22時の段階では$223.99まで値下がりしていました。
1週間の差で$150も安くなるとは・・・くやしい!!
とはいえ悔やんでも仕方がないので、このスペックでAndroid Pieが使えて$373というだけでも充分安いと自分に言い聞かせ、早速開封していきます。
到着
注文が8/13(月)のお昼頃で到着予定は8/16(木)でしたが、週末しか受け取れなかったので実際に受け取ったのは8/18(土)になります。
海外からの注文だと思えば充分に早いですね。
箱の中には本当に簡単な衝撃吸収材と
本体の箱が入っているだけでした。
箱自体はとてもシンプルです。
裏面にピリピリと剥がしていく所がありました。
箱自体は横にスライド式です。
- 本体
- 充電器
- USB Type-Cケーブル
- イヤホンジャック(USB Type-C)変換ケーブル
- SIM用のピン等
が入っていました。
iPhone X(左)との比較
どちらも黒い上にサイズもほとんど同じで、正面からではぱっと見では違いがわかりません。
裏返すと、カメラの方向・指紋リーダー・ブランドロゴなどで違いがはっきりとわかります。
設定画面はこんな感じです。
初期のAndroidバージョンは 7.1.1でした。
プリインストールアプリも殆ど入っておらず、清々しいです。
また、Android 9 Pieはソフトウェアアップデート画面に進むとすぐに降ってきました。
1GBほど容量がありましたが、無事にインストール完了。
それ以降、少し触った限りではキビキビ動作し、全く不満などはありません。 これから少しづつ触っていき、色々と試してみたいと思います。
Oculus GoをGET!
先日発売となった Oculus GoをようやくGETしました。
発売が発表された直後は少し気になりつつも一旦保留にしていました。
というのも以前購入した、スマホをセットするタイプのVRゴーグルを使って、まだこれからだな~と思っていたためです。
しかし、各種レビューを読んだりTwitterを見ているうちに、スタンドアローン型だし試してみようという気になったので、購入に踏み切りました。
購入から到着まで
Oculus Goのサイト上で購入処理を行ったのが 5/7(月)の夜で、
FedExから到着予定日は5/11(金) の夕方になっていました。
結構早いなと思っていましたが、不在票が5/10(木)に入っていました・・
現在はわからないですが、注文から到着まで4日ほどで到着するのは結構早いな~というイメージです。
また、日本語で宛先を入力すると問題がある場合があるとの事だったので、今回は英語で宛先を入力しています。
Expansysでの購入でも何度かお世話になったことがある、こちらのサイトを使って簡単に英語住所に変換できます。
開封
ダンボール内もダンボールを折りたたんだもので埋められており、輸送中に動かないようにこていされています。
箱を取り出したところ。
今回購入したのは32GBです。
蓋を開けると、本体、リモコン、黒い箱が入っています。
黒い箱の中にはメガネ用のパーツが入っていました。
メガネ用のパーツはこの動画を参考にセットすることができます。
さらに黒い箱の奥には、電池、USBケーブル、リモコン用の紐、説明書などが入っています。
Oculus Go 本体の液晶側にはOculusアプリをダウンロードしてセットアップを始めようといった文字がプリントされた紙が入っています。
Oculusアプリの指示に従うことで簡単にOculusのセットアップができました。
感想
思っていたよりは画質がまだ荒いなと感じる部分もありましたが、なかなかの没入感があります。
また、操作には慣れが必用そう。
しかし、これ単体で動作するというのはなかなか快適で、ベッドで仰向けになりながらNetflixを見たりすることもできました。
これから色々なアプリを試していきたいと思います。
【Android学習】FragmentをKotlinでやってみる
今更感がありますが先日、Udemyを眺めていたところ、以下のコースが目に留まったので、Android開発の学習を進めています。 ※セクション3:Java Deep Diveなど、自分には必用なさそうな部分は省いています。
レッスン自体はAndroid N向けの内容のため、最新のAndroid Studioなどではlayoutを作るときに若干勝手が違って苦労する部分もありますが、コース自体はよくできているなという印象です。
しかし、Fragmentを使いたくなったのですがコース内を選択してもヒットしなかったので、こちらの記事を参考に試してみました。
また、Javaでそのまま写経するのもな・・という感じだったので、Kotlinで書いてみました。
レイアウトファイルの作成
まずはFragmentを作成します。
HogeFragment
とかだと味気ないのでにQiitaの例にmaker
、brand
という項目を追加して、CarDetailFragment
という名前にしてみました。
Fragment作成はapp -> New -> Fragment -> Fragment(Blank)
で空のFragmentを作成しました。
レイアウト fragment_car_detail.xml
はこんな感じになります。
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".CarDetailFragment"> <!-- TODO: Update blank fragment layout --> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/helloText" android:layout_width="match_parent" android:layout_height="50dp" android:text="Hello Fragment" android:textAlignment="center" android:textSize="24sp" /> <Button android:id="@+id/numberButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="1" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/makerText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="maker" /> <TextView android:id="@+id/brandText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="brand" /> </LinearLayout> </LinearLayout> </FrameLayout>
Fragmentのコード作成
CarDetailFragment.kt
になります。
デフォルトでいろいろメソッドが宣言されているのですが、一旦消して書き直しています。
Kotlinならではのイディオムがあったりしますが、難しいことはないと思います。
断片的に書いていって、最後に全体を書きます。
まずは変数宣言です。
lateinit
で後で初期化するよと明示しています。
private lateinit var mTextView: TextView private lateinit var maker: String private lateinit var brand: String
続いてcompanion object。
定数やJavaでいうところのstaticメソッドはここに定義しました。
createInstance
メソッドでは、インスタンス生成時にパラメーターを受け取って、Bundleに渡してFragmentのargumentsにセットしています。
companion object { private const val KEY_MAKER = "maker_name" private const val KEY_BRAND = "brand_name" fun createInstance(maker:String, brand:String): CarDetailFragment { val carDetailFragment = CarDetailFragment() val args = Bundle() args.putString(KEY_MAKER, maker) args.putString(KEY_BRAND, brand) carDetailFragment.arguments = args return carDetailFragment } }
あとはFragmentのライフサイクルメソッドを実装していきます。
ライフサイクルは公式の図がわかりやすいと思います。
今回は、
- onCreate ・・・ パラメータを受け取ってメンバ変数にセット
- onCreateView ・・・ Viewの生成
- onViewCreated ・・・ View生成後の操作
を実装しています。
全体は↓こんな感じになりました。
// packageは省略 import android.os.Bundle import android.support.v4.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Button import android.widget.TextView class CarDetailFragment : Fragment() { private lateinit var mTextView: TextView private lateinit var maker: String private lateinit var brand: String companion object { private const val KEY_MAKER = "maker_name" private const val KEY_BRAND = "brand_name" fun createInstance(maker:String, brand:String): CarDetailFragment { val carDetailFragment = CarDetailFragment() val args = Bundle() args.putString(KEY_MAKER, maker) args.putString(KEY_BRAND, brand) carDetailFragment.arguments = args return carDetailFragment } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val args = arguments if (args == null) { maker = "" brand = "" } else { maker = args.getString(KEY_MAKER) brand = args.getString(KEY_BRAND) } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { super.onCreateView(inflater, container, savedInstanceState) return inflater.inflate(R.layout.fragment_car_detail, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) mTextView = view.findViewById(R.id.helloText) view.findViewById<Button>(R.id.numberButton).setOnClickListener { mTextView.text = "${mTextView.text}!" } val makerText = view.findViewById<TextView>(R.id.makerText) makerText.text = maker val brandView = view.findViewById<TextView>(R.id.brandText) brandView.text = brand } }
Fragmentを呼び出すActivityの作成
こちらもFragmentと同じように app -> New -> Activity -> Blank Activity
で作成します。
LinearLayoutだけ定義し、中身は空っぽにしておきます。
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout 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" tools:context=".CarDetailActivity"> <LinearLayout android:id="@+id/carDetailContainer" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"></LinearLayout> </android.support.constraint.ConstraintLayout>
CarDetailActivity.kt
は以下のような感じになりました。
パラメーターとして
という文字をFragmentに対して渡しています。
// packageは省略 import android.support.v7.app.AppCompatActivity import android.os.Bundle class CarDetailActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_car_detail) // コードからフラグメントを追加 if (savedInstanceState == null) { val transaction = supportFragmentManager.beginTransaction() transaction.add(R.id.carDetailContainer, CarDetailFragment.createInstance("TOYOTA", "プリウス")) transaction.commit() } } }
実際に実行して、CarDetailActivity
を開くと、以下のようにFragmentの内容が表示され、パラメータの受け渡しが行われていることが確認できました。
最初はなかなかとっつきにくいなーと思っていましたが、実際に触ってコードを書いていくと、「あーなるほど」という感じで少しづつ理解ができてきました。
引き続き時間を見つけて色々実装を試してみたいと思います。
Air Podsを買ったら最高だった
前々から欲しいなとは思っていましたが値段が値段なのでなかなか踏ん切りがつかず、Ear Podsをずっと使っていました。
何度かBluetoothイヤホンを使ったことがある身としては、iPhoneから伸びるEar Podsのケーブルは非常に邪魔で、しょっちゅうイライラしていました。
また、先端がLightningケーブルになっているため、Macには刺せないのも不便だなと感じていました。
そんな中、AppleStoreギフト券を貰ったのでせっかくのチャンスなのでAir Podsの購入に踏み切ることに。 「2018年には進化したAir Podsが出る」といった噂や、「ケースがAir Powerに対応して発売される」といった噂があり正直迷いましたが、Air Power充電は別にいいかなと思い、購入しました。
早速Apple Storeを見ていると、ネット注文では7営業日・・・
しかし、店舗だと在庫があるようで、直接店舗に行って購入することに。
無事にゲット。
アクセサリでもAppleの商品を開封するときのワクワク感はたまりません。
蓋を開けると恒例の「Designed by Apple in California」
その下にはLightningケーブルが入っていました。
ついにケースとご対面。
早速ケースを開けると・・・
iPhoneが反応し、Air Podsとの接続確認ダイアログが出現。
接続ボタンを押すとすぐにiPhoneとAir Podsの接続が完了しました。
このユーザー体験はAppleならではと関心します。
最後に本体をケースから取り出してみました。
若干マグネットになっており、蓋を空けてもケースから落としにくい親切設計。
同じApple IDの端末にはシームレスに切り替わるはずですが、複数のデバイスが近くにある場合には、iPhoneやMacからAirPodsに接続してあげる必要がありました。
iPhoneの場合、「コントロールパネル?」からミュージックパネルの右上のアイコンを選択すると
音を出すデバイスが選べるので、Air Podsを選択するとAid Podsと接続ができます。
また、Bluetoothの接続画面から、Air Podsのダブルタップ時の動作も設定できます。
Siriを割り当てることもできますが、「再生/一時停止」を割り当てておくと、iPhoneに触ること無くAir Podsを耳にセットしてAir Podsをダブルタップすると音楽が再生されるのでかなり便利です。
「耳からうどん」などと言われることもありますが、最近では街中でAir Podsを耳に着けているユーザーもちらほら見かけるようになってきたので、あまり目立つこともなくて良いかも。
まだまだ使い倒せていないですが、これからどんどん使い倒していきたいと思います。