"Simple" is "Best"

開発時に詰まったところや 調べた結果日本語での情報が無かったり古かったりした場合に自分用のメモとして高校生(?)が更新していくブログ

独自で作ったブランチモデルの一通りのメモ

友達と一緒にHexoを使ったメディアを作ろうと言う話になった

Gitで管理して、リモートはGitHub、デプロイにはNetlifyを使う

迷ったときに見返す、完璧自分用メモ

Media-Flow

今回自分で作ってみたブランチモデル

過去に一度もブランチモデルなんて使ったことないのだが

メディアとして一般公開する予定だし、8ヶ月ほどの運営は確実なので使おうということになった

GitFlowとGitHub-Flowを参考にした

f:id:bigbuddha:20181015214756p:plain

masterブランチ

Netlifyには、このブランチをデプロイするように設定してある

リリース用の、完全品質を保証するブランチ

developpostからのmergeのみで更新される

また、developからmergeするときにTagをつける

Tagをつける時以外、直接操作してはいけない

(Tag付けも自動で行いたい…)

developブランチ

開発用ブランチ

一般公開はまだしてないけど、ほぼ確定している

別途テスト用Netlifyサイトを用意し、そこに繋げる

開発者だけじゃない人たちでも、アクセスして確認ができる

Netlifyにはサイトにパスワードをかける機能があるので、それを使って身内限定にする

どうやら有料プランのようなので、URLを身内限定で共有するということにしておく

topicブランチのmergeのみで更新される

直接操作してはいけない

postブランチ

記事を投稿/変更/削除するためのブランチ

masterから派生している

HexoはMarkdownで記事がかける

そのmdファイルをPushする

Topic ブランチ

機能の追加/修正を行うブランチ

developから派生している

ブランチ名は{issue番号} {内容、スネークケース}

完成したら、developへプルリク

mergeされたdevelopが、masterにmergeされたら

masterにmergeした人が削除する

感想

過去にブランチモデルを利用したことがない&さっき運用を始めた

なので、使い心地がどうとかはわからない

ただ、自分で作って、自分たちで使ったことによって

より一層Gitの使い方がわかった気がする

近いうちに使ってみた感想とか書きたい

参考記事

いまさらだけどGitを基本から分かりやすくまとめてみた

【Git】リモートからの取得とリモートへの反映で行っていること(fetch,pull,push)

Dialogと仲良くなる その1

先日、RecyclerViewと仲良くなった(気がする)

次はDialogと仲良くなってみる

参考サイト

ひたすら公式

Dialogs - Android Developers

Dialogs - Material Design

Android Developersに載っているコードをほぼまんま写した

公式を見る際に、日本語で見るとKotlinのコードが表示されない

なので、英語が苦手な方は英語と日本語交互に見ながらやるとよいだろう

(なんで日本語のはないんだろう・・・)

Fragment Dialog

公式より、Dialogを使うときはFragmentDialogを使いなさいと言われている

Fragmentか〜って感じではあるが、かのGoogle様直々のお願いなので守らなくてはいけない

どうやらFragmentを使うことにより、サイクルが確保される模様

お断り

Dialogには様々な種類がある

この記事では、全てのDialogを扱うわけではない

扱うDialog

Alert dialog

-> Alert dialogs interrupt users with urgent information, details, or actions.

Simple dialog

-> Simple dialogs display a list of items that take immediate effect when selected.

Confirmation dialog

-> Confirmation dialogs require users to confirm a choice before the dialog is dismissed.

扱わないDialog

Full-screen dialog

-> Full-screen dialogs fill the entire screen, containing actions that require a series of tasks to complete.

Date/Time PickerDialog

-> A dialog with a pre-defined UI that allows the user to select a date or time.

Custom Dialog

-> Dialogに表示するレイアウトをカスタマイズしたもの

Custom DialogとPickerDialogの説明以外は全てここから引用

PickerDialogはリファレンスより

また、今回利用するDialogは全てサポートライブラリのDialogである

コピっててなんかエラーが出るってなったらまずはimport文を見よう

下準備

MainActivtyのレイアウトと、Simple,Confirmationで扱うArrayListの定義は特筆することがないのでコードだけ

あとSnackbarを使うのでADSLも忘れずに

Android Design Support Library

これ入れとくだけでナウくなるからいっぱい好き

implementation 'com.android.support:design:27.1.1'

activity_main.xml

Dialogを呼び出すためのButtonを用意する

今回は5種類ほどDialogを実装する

<LinearLayout
    android:id="@+id/container"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    xmlns:android="http://schemas.android.com/apk/res/android">


    <Button
        android:id="@+id/bt_basic"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Basic Dialog"
        android:layout_marginBottom="8dp"
        android:textAllCaps="false"/>

    <Button
        android:id="@+id/bt_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button Dialog"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:textAllCaps="false"/>


    <Button
        android:id="@+id/bt_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="List Dialog"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:textAllCaps="false"/>

    <Button
        android:id="@+id/bt_check"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="CheckBox Dialog"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:textAllCaps="false"/>

    <Button
        android:id="@+id/bt_radio"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="RadioBox Dialog"
        android:layout_marginTop="8dp"
        android:textAllCaps="false"/>

</LinearLayout>

完成図 f:id:bigbuddha:20181007044439p:plain

values/array.xml

ちゃんとリソースファイルを使う癖を付けなな〜とか思いつついつもサボる

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <array name="items">
        <item>Item1</item>
        <item>Item2</item>
        <item>Item3</item>
        <item>Item4</item>
        <item>Item5</item>
    </array>
</resources>

本題

では、実装していく

今回はDialogsというパッケージを作って、そこにバコバコ入れていく

Basic Dialog

最も基本的なDialog f:id:bigbuddha:20181007044442p:plain

ただ文字を表示するだけ、正直使いどころはない

でも、基本を学ぶにはシンプルでちょうどいい

package net.oldbigbuddha.sample.sampledialog.Dialogs

import android.app.Dialog
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog


class BasicDialogFragment : DialogFragment() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {

            val builder = AlertDialog.Builder(it)
            builder.setTitle("Basic Dialog")
                    .setMessage("This is Basic Alert Dialog")

            builder.create()

        } ?: throw IllegalAccessError("Activity cannot be null")
    }
}

本当に最小限の構成

onCreateDialog()でAlertDialogを生成し、返り値にしてやる

ただそれだけ

Builderを作って、色々設定を加えて、create()してやれば出来上がり

let{}is何ってこは”Kotlin スコープ関数”で検索

MainActivityで呼び出し処理を書く

 bt_basic.setOnClickListener {
            BasicDialogFragment().show(supportFragmentManager, "basic")
}

setTitle()で一番上のTitle部分に文字列をセットし

setMessage()でメイン部分(Supporting text)に文字列をセットする

f:id:bigbuddha:20181007044442p:plain

これはデザイン的な話になるが

GoogleはTitle部分に曖昧な文言を設定することを推奨していない

例えば”Are you sure?”である

なるべく読み取れる意味を絞れってはなしですな

Button Dialog

公式的には”Alert Dialog”らしい

右下にボタンがついてるDialog

f:id:bigbuddha:20181007044435p:plain

ココらへんから使いどころがでてくるかな

PositiveButton(OKやAgreeなど)は必ず右側に置かないといけない(自動で置かれるので気にする必要はない)

package net.oldbigbuddha.sample.sampledialog.Dialogs

import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog

class ButtonDialogFragment: DialogFragment() {

    lateinit var onClickPositive: DialogInterface.OnClickListener
    lateinit var onClickNegative: DialogInterface.OnClickListener

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {

            val builder = AlertDialog.Builder(it)
            builder.setTitle("Button Dialog")
                    .setMessage("This is Button Dialog")
                    .setPositiveButton("OK", onClickPositive)
                    .setNegativeButton("cancel", onClickNegative)


            builder.create()
        } ?: throw IllegalAccessError("Activity cannot be null")
    }
}

Listenerを利用して、タップされた時の処理を外で書くようにする

setPositiveButton()で肯定的なアクションを

setNegativeButton()で否定的なアクションを設定する

第一引数でボタンに表示する文字列を設定する(リソースファイルぅ)

MainActivityで呼び出す

bt_button.setOnClickListener {
    val fragment = ButtonDialogFragment()
   fragment.onClickPositive = DialogInterface.OnClickListener { _, _ ->
      Snackbar.make(container, "OnClick 'OK'", Snackbar.LENGTH_SHORT).show()
   }
   fragment.onClickNegative = DialogInterface.OnClickListener {_, _ ->
       Snackbar.make(container, "OnClick 'cancel'", Snackbar.LENGTH_SHORT).show()
   }
   fragment.show(supportFragmentManager, "button")
}

まぁ、タップしたらSnackbarがでてくるというもの

f:id:bigbuddha:20181007044428p:plain

たまに”OK”、”Cancel”ときて、第三のButton”Learn more”がでてくるDialogがある

material.io曰く、第三のButtonは本来不要で、他の場所で補うべきだと述べている

Rather than adding a third action, an inline expansion can display more information. If more extensive information is needed, provide it prior to entering the dialog.

List Dialog

公式的には”Simple Dialog”

Listを表示するDialog

f:id:bigbuddha:20181007044431p:plain

タップした内容を取得できる

package net.oldbigbuddha.sample.sampledialog.Dialogs

import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import android.util.Log
import net.oldbigbuddha.sample.sampledialog.R

class ListDialogFragment: DialogFragment() {
    lateinit var onSelectedItem: DialogInterface.OnClickListener

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {

            val builder = AlertDialog.Builder(it)
            builder.setTitle("List Dialog")
                    .setItems(R.array.items, onSelectedItem)

            builder.create()
        } ?: throw IllegalAccessError("Activity cannot be null")
    }
}

setItems()で配列とListenerを投げてやる

MainActivityの方で、タップした位置を取得することができる

bt_list.setOnClickListener {
  val fragment = ListDialogFragment()
   fragment.onSelectedItem = DialogInterface.OnClickListener {_, index ->
       resources?.let {
           val item = it.getStringArray(R.array.items)[index]
           Snackbar.make(container, "Selected $item", Snackbar.LENGTH_SHORT).show()
       }
   }
   fragment.show(supportFragmentManager, "list")
}

(仮引数の名前をindexとしているが、positionのほうがいいかもしれない)

特別変なことはしていない

let関数の中で自身を指定する場合はitを使う

それぐらい?

Resource周りは勉強しといたほうが良い(人のこと言えない)

図は、item2を選択した場合のSnackbar

f:id:bigbuddha:20181007044428p:plain

RadioBox Dialog

公式的には”Confirmation Dialog”

リファレンスを見ると、setSingleChoiceItems()を利用しているので

Single Chouce Dialogっていうのが一番正しい名前なのかね

f:id:bigbuddha:20181007044418p:plain

package net.oldbigbuddha.sample.sampledialog.Dialogs

import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import net.oldbigbuddha.sample.sampledialog.R

class RadioBoxDialogFragment: DialogFragment() {

    lateinit var onSelectedItem: DialogInterface.OnClickListener
    lateinit var onClickPositive: DialogInterface.OnClickListener

    var selectedIndex = 0

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {

            val builder = AlertDialog.Builder(it)
            builder.setTitle("RadioBox Dialog")
                    .setSingleChoiceItems(R.array.items, 0/* default selected */,onSelectedItem)
                    .setPositiveButton("OK", onClickPositive)

            builder.create()
        } ?: throw IllegalAccessError("Activity cannot be null")
    }
}

少し個人的には納得していないコード setSingleChoiceItems()で設定したListener内で、選択されたItemのindexが取れる(fragment.selectedIndex = index`)

それをフィールドで保存してやり、MainActivityで呼び出すことで選択されたItemを取れるようにしている

bt_radio.setOnClickListener {
    val fragment = RadioBoxDialogFragment()
    fragment.onSelectedItem = DialogInterface.OnClickListener { _, index ->
        resources?.let {
            fragment.selectedIndex = index
        }
    }

    fragment.onClickPositive = DialogInterface.OnClickListener{ _, _ ->
        resources?.let {
            val item = it.getStringArray(R.array.items)[fragment.selectedIndex]
            Snackbar.make(container, "Selected $item", Snackbar.LENGTH_SHORT).show()
        }
    }
    fragment.show(supportFragmentManager, "radio")
}

ちなみにsetSingleChoiceItems()の第二引数に-1を設定すると標準で無選択になる

f:id:bigbuddha:20181007044414p:plain

CheckBox Dialog

本記事最後のDialog、公式的には”Confirmation Dialog”

さっきのがsetSingleChoiceItems()だったのに対し、これはsetMultiChoiceItems()を使う

う〜ん、わかりやすい

f:id:bigbuddha:20181007044425p:plain

package net.oldbigbuddha.sample.sampledialog.Dialogs

import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import net.oldbigbuddha.sample.sampledialog.R

class CheckBoxDialogFragment: DialogFragment() {

    lateinit var onClickPositive: DialogInterface.OnClickListener
    lateinit var onClickNegative: DialogInterface.OnClickListener
    lateinit var onMultiSelected: DialogInterface.OnMultiChoiceClickListener

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            val builder = AlertDialog.Builder(it)
            builder.setTitle("CheckBox Dialog")
                    .setMultiChoiceItems(R.array.items, null/* default selected */,onMultiSelected)
                    .setPositiveButton("OK", onClickPositive)
                    .setNegativeButton("cancel", onClickNegative)

            builder.create()
        } ?: throw IllegalAccessError("Activity cannot be null")
    }

}

さっきのSingle同様リスナーを渡します

setMultiChoiceItems()の第二引数はbooleanの配列です

trueのところを選択状態にしてくれます

bt_check.setOnClickListener {
    val fragment = CheckBoxDialogFragment()
    val mSelectedItems = ArrayList<Int>() // Where we track the selected items
    fragment.onMultiSelected = DialogInterface.OnMultiChoiceClickListener { _, index, isChecked ->
        if (isChecked) {
            mSelectedItems.add(index)
        } else if (mSelectedItems.contains(index)) {
            mSelectedItems.remove( Integer.valueOf(index) )
        }
    }
    fragment.onClickPositive = DialogInterface.OnClickListener { _, _ ->
        resources?.apply {
            val mSelectedItemContents = ArrayList<String>()
            mSelectedItems.forEach {
                mSelectedItemContents.add(this.getStringArray(R.array.items)[it])
            }
            Snackbar.make(container, "Selected $mSelectedItemContents", Snackbar.LENGTH_SHORT).show()
        }
    }
    fragment.show(supportFragmentManager, "checkbox")
}

う〜ん、長い

そしてキモい(forEach in スコープ関数ということでthisが使えるapplyで対処した)

まぁ、大体CheckBoxを実装するときと同じですな

タップされたものがFalseならTrueに、TrueならFalseへ

OnMultiChoiceClickListener()で取れたTrace配列はInt型ですので

中の数値ををIndexとしてItem配列から選択されたItemをとりだします

まさかこんなところでforEach()に出会うとは、、、(やぎにぃに感謝)

f:id:bigbuddha:20181007044422p:plain

さぁ、これで全てが出揃った

個人的にはDialogととりあえずは仲良くなれたように思える

あとはFull-ScreenとCustomとPicker、、、

ま、のんびりやっていきます

今回のコードのまとめはこちらから見ることができます

意味わかんねぇよとか、ここエラーでたんですけどとかあったらTwitterでメンション投げてください

@OJI_1941です(ごめんなさいDM閉じてるんで最初はメンションでお願いします)

ありがとうございました

RecyclerViewと仲良くなる

いや今更かよっていう話ではある

しかしながら、私はRecylerViewといつまで経っても仲良くなれず

非常に悶々とした日々をすごした過去を持つ

たまたま久々にAndroidを触り、RecyclerViewと戯れる機会を得たので

真摯に向き合ってみると案外難しくはなかった

てことでちょこっとだけ説明を加えたSample

ゴール

今回はTODOリストもどきを作っていく

f:id:bigbuddha:20181004210318j:plain

アプリを閉じると内容は削除されるし、ソートとかもないので

本当にガワだけである

EdiTextに入力された内容がRecyclerViewに表示される流れに重きを置く

右側のボタンを押すとEditTextの内容が下のRecyclerViewに表示される

Item(一行)の右側のボタンを押すとそのItemが削除される

下準備

とりあえずプロジェクトを新規作成

minSdkは23、targetSdkは27

1.Android Support Design Library

そしてbuild.gradleにADSLを追加

RecyclerView単体もあるが、TextInputLayoutだったりSnackbarを使いたかったので

implementation 'com.android.support:design:27.1.1'

2.ボタン素材(Googleに感謝)

続いて素材を追加

AndroidStudioからGoogleが公開しているMatrial Iconが利用できるので

+-Vectorで取ってくる

ファイル名はic_add_black_24dp.xmlic_remove_black_24dp.xml

(File -> New -> Vector Assetで追加できる)

3.Itemのレイアウトを作成

続いて一行一行をレイアウトするためにファイルを作成する

res > layoutitem.xmlを作成

レイアウトを組んでいく

今回必要なのは

  1. EditTextに入力された内容を表示するTextView
  2. 作成日時を表示するTextView
  3. RemoveをするImageButton

以上3つ

以下そのレイアウト

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="60dp">

    <TextView
        android:id="@+id/tv_name"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:layout_weight="4"
        android:textSize="18sp"
        android:textColor="#000"
        android:gravity="center|start"
        android:paddingStart="4dp" />

    <TextView
        android:id="@+id/tv_created_at"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_weight="3"
        android:text="XXXX/XX/XX"
        android:textSize="12sp"
        android:gravity="bottom|end"
        android:paddingStart="8dp"
        android:paddingEnd="24dp"
        android:paddingBottom="16dp"/>

    <ImageButton
        android:id="@+id/bt_remove_item"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:src="@drawable/ic_remove_black_24dp"
        android:background="#000f"
        android:layout_gravity="center" />

</LinearLayout>

各値はお好みで

ここで特徴なのが親のLinearLayoutの高さが固定であるということ

wrap_contentでも良いのだが、Buttonが小さくなって誤タップが生じてしまうため

ある程度の高さを用意してあげる

ImageButtonは背景をRGBAで透明にしてあげる

本番

Adapter

RecyclerViewAdapterにかかっていると言っても過言ではない

RecyclerViewに関する操作は全てAdapterで行う

某所でAdapterRecyclerViewにデータを渡す橋渡し役だと習った

そんな橋渡し役のコードがこちら

package net.oldbigbuddha.sample.sample_recyclerview

import android.content.Context
import android.support.v7.widget.RecyclerView
import android.text.format.DateFormat
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.TextView
import kotlinx.android.synthetic.main.item.view.*

class ItemAdapter(
        private val mItems:   ArrayList<Item>,
        private val mContext: Context
): RecyclerView.Adapter<ItemAdapter.ViewHolder>() {


    override fun getItemCount(): Int = mItems.size

    // Itemを追加する
    fun addItem(item: Item) {
        mItems.add(item)
        notifyDataSetChanged() // これを忘れるとRecyclerViewにItemが反映されない
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item, parent, false))

    // Itemを削除する
    private fun removeItem(position: Int) {
        mItems.removeAt(position)
        notifyItemRemoved(position)
        notifyDataSetChanged() // これを忘れるとRecyclerViewにItemが反映されない
    }


    override fun onBindViewHolder(holder: ViewHolder, position: Int) {

        holder.tvName.text      = mItems[position].mName

        // reference: Y.A.M の 雑記帳: SimpleDateFormat ではなく android.text.format.DateFormat を使おう - http://bit.ly/2OybKLu
        holder.tvCreatedAt.text = DateFormat.format("yyyy/MM/dd kk:mm:ss", mItems[position].mCreatedAt).toString()

        holder.btRemoveItem.setOnClickListener {
            removeItem(position)
        }

    }

    class ViewHolder(view: View): RecyclerView.ViewHolder(view) {
        val tvName:       TextView    = view.tv_name
        val tvCreatedAt:  TextView    = view.tv_created_at
        val btRemoveItem: ImageButton = view.bt_remove_item
    }
}

RecyclerViewの特徴の1つに必ずViewHolderを実装しないといけないというものがある

ViewHolder is 何 という方はここでは詳しく説明しないのでググっろう

findViewById()は結構実行コストが高いので、一度findしてきたものを保持しておく

だからHolderにViewを入れておく

そんな感じ

私がRecyclerViewと仲良くなれなかった一番最初の原因は

クラスを書いてる間最初しばらくはエラーが出ること

以下の順番で書くとよい

  1. class ~~: RecyclerView.Adapter<~~.ViewHolder>() {}を書く
  2. ViewHolderをぱぱっと書く(kotlin-android-extensionsのお陰で簡単に書けるようになった)
  3. onCreateViewHolderonBindViewHolderをoverrideする
  4. あとは適当に書いていく

Ctrl-OでOverrideできるメソッド一覧がでるので、活用すると良い

notifyDataSetChanged()を忘れると更新されないので注意(これ結構やられる)

MainActivity

あとはひょろひょろ〜と書いていく

  • ちゃんとToolbarを設定すること
  • RecyclerViewにAdapterを設定すること

この2つを忘れなければハマることはない(はず)

 package net.oldbigbuddha.sample.sample_recyclerview

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.design.widget.Snackbar
import android.support.v7.widget.LinearLayoutManager
import android.text.TextUtils
import kotlinx.android.synthetic.main.activity_main.*
import java.util.ArrayList

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Set Toolbar
        setSupportActionBar(my_toolbar)
//        my_toolbar.inflateMenu(R.menu.menu_main)

        // Initialize RecyclerView
        recycler_items.layoutManager = LinearLayoutManager(this)

        // Initialize Adapter
        val adapter = ItemAdapter(ArrayList(), this)

        bt_add_item.setOnClickListener {
            if ( !TextUtils.isEmpty( et_item.text.toString() ) ) {

                val task = Item( mName = et_item.text.toString() )
                adapter.addItem(task)

            } else {

                Snackbar.make(container, "Task is empty", Snackbar.LENGTH_SHORT).show();

            }

            et_item.setText("")

        }

        recycler_items.adapter = adapter

    }
}

Toolbarに色々仕込みたい場合はsrc > menuを作って

よしなに書いてinflateしてあげれば良い

ちゃんとEditTextの中身は検査しよう

これで無事動く

最後に

今回のサンプルコードはここからまとめて見れます

たまに更新するかもしれない

もしわからなかったらTwitter@OJI_1941にメンション投げてください

すぐ食いつきます(すみませんDMは基本閉じてます)

aptでDebianにYarnをインストールした

少し前にDebianにNode.jsを突っ込む記事を書いた

その後しばらくnpmを使っていたのだが

どうやらYarnという素晴らしいものがあるらしい

友人が使え使えと言ってくるので、とりあえずインストールしてみた

その時のメモ

 

実は上記の記事通りcURLスクリプトを取ってきて実行すると

最後にこんな記述がある

## Run `sudo apt-get install -y nodejs` to install Node.js 8.x LTS Carbon and npm
## You may also need development tools to build native addons:
sudo apt-get install gcc g++ make
## To install the Yarn package manager, run:
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn

 記事を書いた当時はYarnなんて一切興味がなかったので

スルーしていたのだが、たまたまNode.jsを別機にいれる時に発見

早速コピペしてみる

 

curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg |\
    sudo apt-key add
echo "deb https://dl.yarnpkg.com/debian/ stable main" | \
    sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn

入ったかを確認

yarn --version
1.10.1

無事インストールできた

AndroidのStatusbarの背景色を変更する

TwitterとかSlackのアプリを開いてみると

上のステータスバー(時間とか電力が表示いされてる所)の色が

ちゃんとActivityに合っている

でも、いざ自分で作ってみると当然自動的に合うわけがない

ステータスバーの色をこちら側で固定するメモ

 

 思ったより簡単

以下のコードで無事好きな色を設定することができる

// Hide Statusbar
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
        window.statusBarColor = ContextCompat.getColor(this, R.color.theme)

ContextCompat.getColor()を使うことがコツ

これを使えばcolor.xmlで定義してるコードを簡単に使うことができる

確かAPI21以降対応してたはず

今回はこれまで

Debian9にNode.jsのLTS版を入れる

何も考えずに

sudo apt install nodejs

なんてすると悲しい結果になる

(実行コマンドがnodejsになり、かつバージョンが古い)

 

てことで2018/04/12時点の最新LTS及びnpmを導入する手順をメモ

参考サイトは以下

linuxconfig.orgココに書いてある内容が8系になっただけ

 

インストール

以下のコマンドをコピって貼っつける

curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -

 

後は普通にaptでインストールするだけ

まぁ簡単

sudo apt install nodejs build-essential libssl-dev

最後にバージョンを確認して終わり

node -v
 -> v8.11.1
npm -v
 -> 5.6.0

Bot職人にでもなりましょうかね〜

 

おまけ

curlで引っ張ってきたスクリプトで何をやっていたのか気になったので覗いてみる

OSの確認とかははすっ飛ばして重要そうな部分を引っ張ってみる

原本はココから見れる

 

export DEBIAN_FRONTEND=noninteractive
SCRSUFFIX="_8.x"
NODENAME="Node.js v8.x LTS Carbon"
NODEREPO="node_8.x"
NODEPKG="nodejs

print_status() {
    echo
    echo "## $1"
    echo
}

bail() {
    echo 'Error executing command, exiting'
    exit 1
}

exec_cmd_nobail() {
    echo "+ $1"
    bash -c "$1"
}

exec_cmd() {
    exec_cmd_nobail "$1" || bail
}

node_deprecation_warning() {
    if [[ "X${NODENAME}" == "Xio.js v1.x" ||
          "X${NODENAME}" == "Xio.js v2.x" ||
          "X${NODENAME}" == "Xio.js v3.x" ||
          "X${NODENAME}" == "XNode.js v5.x" ]]; then

        print_bold 【省略】
        echo
        echo "Continuing in 10 seconds ..."
        echo
        sleep 10

    elif [ "X${NODENAME}" == "XNode.js v0.10" ]; then

        print_bold 【省略】

        echo
        echo "Continuing in 5 seconds ..."
        echo
        sleep 5

    elif [ "X${NODENAME}" == "XNode.js v0.12" ]; then

        print_bold 【省略】
        echo
        echo "Continuing in 3 seconds ..."
        echo
        sleep 3

    fi
}

print_status "Installing the NodeSource ${NODENAME} repo..."

if $(uname -m | grep -Eq ^armv6); then
    print_status "You appear to be running on ARMv6 hardware. Unfortunately this is not currently supported by the NodeSource Linux distributions. Please use the 'linux-armv6l' binary tarballs available directly from nodejs.org for Node.js v4 and later."
    exit 1
fi

PRE_INSTALL_PKGS=""

# Check that HTTPS transport is available to APT
# (Check snaked from: https://get.docker.io/ubuntu/)

if [ ! -e /usr/lib/apt/methods/https ]; then
    PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} apt-transport-https"
fi

if [ ! -x /usr/bin/lsb_release ]; then
    PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} lsb-release"
fi

if [ ! -x /usr/bin/curl ] && [ ! -x /usr/bin/wget ]; then
    PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} curl"
fi

# Populating Cache
print_status "Populating apt-get cache..."
exec_cmd 'apt-get update'

if [ "X${PRE_INSTALL_PKGS}" != "X" ]; then
    print_status "Installing packages required for setup:${PRE_INSTALL_PKGS}..."
    # This next command needs to be redirected to /dev/null or the script will bork
    # in some environments
    exec_cmd "apt-get install -y${PRE_INSTALL_PKGS} > /dev/null 2>&1"
fi

IS_PRERELEASE=$(lsb_release -d | grep 'Ubuntu .*development' >& /dev/null; echo $?)
if [[ $IS_PRERELEASE -eq 0 ]]; then
    print_status "Your distribution, identified as \"$(lsb_release -d -s)\", is a pre-release version of Ubuntu. NodeSource does not maintain official support for Ubuntu versions until they are formally released. You can try using the manual installation instructions available at https://github.com/nodesource/distributions and use the latest supported Ubuntu version name as the distribution identifier, although this is not guaranteed to work."
    exit 1
fi

DISTRO=$(lsb_release -c -s)

check_alt() {
    if [ "X${DISTRO}" == "X${2}" ]; then
        echo
        echo "## You seem to be using ${1} version ${DISTRO}."
        echo "## This maps to ${3} \"${4}\"... Adjusting for you..."
        DISTRO="${4}"
    fi
}

check_alt 【省略】

if [ "X${DISTRO}" == "Xdebian" ]; then
  print_status "Unknown Debian-based distribution, checking /etc/debian_version..."
  NEWDISTRO=$([ -e /etc/debian_version ] && cut -d/ -f1 < /etc/debian_version)
  if [ "X${NEWDISTRO}" == "X" ]; then
    print_status "Could not determine distribution from /etc/debian_version..."
  else
    DISTRO=$NEWDISTRO
    print_status "Found \"${DISTRO}\" in /etc/debian_version..."
  fi
fi

print_status "Confirming \"${DISTRO}\" is supported..."

if [ -x /usr/bin/curl ]; then
    exec_cmd_nobail "curl -sLf -o /dev/null 'https://deb.nodesource.com/${NODEREPO}/dists/${DISTRO}/Release'"
    RC=$?
else
    exec_cmd_nobail "wget -qO /dev/null -o /dev/null 'https://deb.nodesource.com/${NODEREPO}/dists/${DISTRO}/Release'"
    RC=$?
fi

if [[ $RC != 0 ]]; then
    print_status "Your distribution, identified as \"${DISTRO}\", is not currently supported, please contact NodeSource at https://github.com/nodesource/distributions/issues if you think this is incorrect or would like your distribution to be considered for support"
    exit 1
fi

if [ -f "/etc/apt/sources.list.d/chris-lea-node_js-$DISTRO.list" ]; then
    print_status 'Removing Launchpad PPA Repository for NodeJS...'

    exec_cmd_nobail 'add-apt-repository -y -r ppa:chris-lea/node.js'
    exec_cmd "rm -f /etc/apt/sources.list.d/chris-lea-node_js-${DISTRO}.list"
fi

print_status 'Adding the NodeSource signing key to your keyring...'

if [ -x /usr/bin/curl ]; then
    exec_cmd 'curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -'
else
    exec_cmd 'wget -qO- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -'
fi

print_status "Creating apt sources list file for the NodeSource ${NODENAME} repo..."

exec_cmd "echo 'deb https://deb.nodesource.com/${NODEREPO} ${DISTRO} main' > /etc/apt/sources.list.d/nodesource.list"
exec_cmd "echo 'deb-src https://deb.nodesource.com/${NODEREPO} ${DISTRO} main' >> /etc/apt/sources.list.d/nodesource.list"

print_status 'Running `apt-get update` for you...'

exec_cmd 'apt-get update'

node_deprecation_warning

print_status "Run \`apt-get install ${NODEPKG}\` (as root) to install ${NODENAME} and npm"

なるほどわからん

日頃Scriptを組まんのでなおさらわからないが

色々と勉強にはなった(気がする)

Debian9のキーボード配列を変える

状況

  1. その昔、ThinkPad E440(JIS配列)を購入
  2. Winを捨ててLinux
  3. Debian9を入れた
  4. キーボードをUSへ変更した
  5. キーボードの設定がなんかへん ←ここのお話

キーボード入力ではFcitxを使用して管理をしている

USに組み替えた後にしっかり変更したはずなのになんか一部ずれた挙動をする

どうすればいいのだろうと調べてたら以下の記事を見つけたのでDebianでやってみる

qiita.com

大体UbuntuでできることはDebianでできる

 

やってみる

sudo dpkg-reconfigure keyboard-configuration

これでコンフィグ画面が現れるのだが

言語設定がおかしいのか、めでたく文字化け

f:id:bigbuddha:20180224114035p:plain

こういうミスるとヤバそうな設定画面がしっかり読めないのは痛い

先述の記事のスクショを信用してことを運ぶ

文字数的に選択している行は「標準 102 キー (国際) PC」

これを選択して次へ

 

f:id:bigbuddha:20180224114402p:plain

ここまで化けられると何もわからなくて心配になる

先頭の「英語 (US)」を選択

 

f:id:bigbuddha:20180224114601p:plain

AltGrってなんぞやってことでググってみる

どうやら装飾キーの一種で、欧州のほうの文字を入力する際に用いるらしい

AltGr key - Wikipedia

もし今後欧州の文字を入力する機会がある場合はここをいじれば良いのか

(果たして機会は・・・)

今は縁がないのでデフォルトでスルー

わからないときはデフォルトですな

 

f:id:bigbuddha:20180224115118p:plain

Compose Keyってなんぞやってことで再度ググる

さっきと一緒で装飾キーの一種

二打鍵で一つの文字を入力する時に使うらしい

欧州の方は大変ですな

 コンポーズキー (Compose, Comp)

後続のアルファベットとアクセント記号(と似た形の記号)を合成するよう指示するためのキー。たとえば、「Compose」「a」「'」の順に入力すると「á」が入力される。

from 修飾キー - Wikipedia

  

f:id:bigbuddha:20180224115629p:plain

最後にX Serverが死んだ時に強制終了するショートカットを追加するかどうか

まぁ、なしでいいでしょう(適当)

 

以上で設定は終了

無事正しい配列認識になりました

これでようやくまともにコーディングができる