Androidでいい感じにREST APIを扱う(Kotlin Coroutine, OkHttp3, Retrofit2, Moshi) を書いた

Qiitaアドベントカレンダーに参加した。

qiita.com

あんまり有用な記事をかける気がしないのでQiitaに投稿する気はなかった。というのは建前で、多くの人の目に触れる分、下手に間違ったことを書いて過激な人の目に入って攻撃に合うのが恐かった。 しかし書いてみると知識の再確認ができたり、「いいね」がもらえて自己承認欲求が満たされたりと非常に良い経験ができた。

反省点としてはあまり伸びなかったこと。 それなりに関心を引ける分野を選んだつもりだけど、目新しさが足りないのとやはり内容が浅かったのが原因だと思う。

基本はこちらの地下ブログでアウトプットを続けるつもりだけど、来年もアドベントカレンダーだけは参加してもっと面白い記事が書きたいな。

Javaの入力がわかりずらい

こちらで説明した記事だとKotlinの拡張関数を使って簡潔に書いている。 が、それだと処理の流れを追い辛い(かもしれない)のでここではJavaで解説します。

登場人物

・InpurStream ・InputStreamReader ・BufferedReader

InputStreamとは

データ入力に対するストリーム ストリームについてはこちらを参照 ストリーム

InputStreamReader

文字列入力を読み込むためのリーダー 文字列を読み込みたいのでInputStremを文字列情報に変換する。

使い方はコンストラクタにいれるだけ

BufferedReader

InputStreamをBufferingするためのリーダー Buffering ・・・バッファを使い読み書き回数を減らすこと →InputStremReaderのままだと一字ずつしか読むことができないので効率が悪い。 BufferedReaderを使うことで一行ずつ読み込むことが可能になる。

使い方はコンストラクタにいれるだけ

いざ

// 文字列を読むためのReaderを作る
InputStreamReader isr = new InputStreamReader(new InputStream());
// このままでは効率が悪いためBufferedReaderを作る
BufferedReader br = new BufferedReader(isr);
// あとは一行づつ読み込むことができる。
int i = 0;
while(br.ready()){
  String str = br.readLine();
  System.out.println("The " + i++ + "th line : " + str);
}

間違っていましたらコメントください(__)

CustomViewでメーターっぽいViewを作成する

続き?

iseabjapan.hatenablog.com

やる事

①コンストラクタを作成

②Viewクラスのメソッドをオーバーライド

- onMeasure()
- onTouchEvent()
- onDraw()

いざ

①コンストラクタを作成

CustomViewを作成する=Viewクラスを継承する際は4つのセカンダリコンストラクタを書く必要がある。ただし@JvmOverloads のアノテーションをプライマリコンストラクタに追記するとコンストラクタはその一つで済む。

具体的には

class MeterView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = 0
) : View(context, attrs, defStyle) {

で十分となる。

 

②Viewクラスのメソッドをオーバーライド

onMeasure()

ここでやるべきは setMeasureDimension()でViewのサイズを確定させる事。 引数の二つのmeasurespecには親ビューからのサイズ情報が入っており、サイズ指定する場合はMeasureSpec.getSize()で引き出す必要がある。

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        //スクリーンサイズを取得する。
        val displayMetrics = DisplayMetrics()
        (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.getMetrics(
            displayMetrics
        )
        mScreenWidth = displayMetrics.widthPixels
        mScreenHeight = displayMetrics.heightPixels
        //サイズの指定が会った際に大きさを取得する。
        if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
            mScreenWidth = MeasureSpec.getSize(widthMeasureSpec)
        }
        if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
            mScreenHeight = MeasureSpec.getSize(heightMeasureSpec)
        }
        //自分自身のサイズを setMeasuredDimension で確定させる
        setMeasuredDimension(mScreenWidth, mScreenHeight)
        initButtonSize(mScreenWidth, mScreenHeight) ※ 一つ一つのボタンの大きさを確定
    }

initButtonSize()のなかで各ボタンのサイズを確定させてこんなデータクラスのリストに突っ込んでいる。

    data class ExtractLevel(
        var levelIndex: Int = 0,
        var position: RectF
    )
onTouchEvent()

ここでやるべきはタッチされた際の挙動を制御する事。ここのロジック部分のコードは省略するが やってる事は移動(MotionEvent.ACTION_MOVE)が呼ばれた際に、方向(上下)を判別しinvalidate()による"onDraw()""の呼び出しである。

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        val currentTouchId = getTouchedLevelIndex(x, y) //現在タップされたレベル位置
            MotionEvent.ACTION_MOVE -> {
    〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
                if (mCurrentLevel != currentTouchId && currentTouchId != -1) {
                    updateLevelState(currentTouchId)  //現在のタッチされているボタンのレベルを更新
                    invalidate() //再描画処理
                }
    〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
            }
    }

※厳密には移動距離、移動方向の判別も行っているが気になる人はgitで確認して欲しい。

onDraw()

ここでやるべきはViewの描画処理。今回はonMeasure()内で確定させた各ボタンをCanvasクラスdrawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint)で描画している。

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        drawLevels(canvas)
    }

    private fun drawLevels(canvas: Canvas) { 
        mExtractLevels.forEach { 
            val paint = Paint()
            paint.style = Paint.Style.FILL
            paint.color = ContextCompat.getColor(
                context,
                if (isLevelSelected(it.levelIndex)) R.color.colorLevel else R.color.emptyColorLevel
            )
            canvas.drawRoundRect(
                it.position,
                connerRadius.toFloat(),
                connerRadius.toFloat(),
                paint
            )
        }
    }

完成

 f:id:iseAB:20191029221133g:plain

全部で3回位を想定していたが実質1回で終了してしまった。。。

https://developer.android.com/reference/android/view/View.html#public-constructors_1 http://tiro105.hateblo.jp/entry/2014/06/19/154405

AndroidでPopupWindowを使う(Kotlin) No.1

なにげにはじめて触ったので備忘として。

やる事

KotlinでPopupWindowを使ってみる。

レイアウト工夫なしの実装だけであれば

  1. popupのレイアウト作成
  2. popupwindow インスタンスを作成してshowする

で終わりです。今回ここまでやるのでとりあえず作りたい方はこの記事だけで大丈夫です。

いざ

①popupwindowのレイアウト作成

・お好みのレイアウトでok

②popupwindow インスタンスを作成してshowする。

・画面外タッチでポップアップを消すためには以下を設定

isOutsideTouchable = true
isFocusable = true

・表示メソッドには位置を指定できるshowAtLocation()、引数のviewの下に配置する

showAsDropDown()がある(後者も引数で位置は調整できる。)

 

AndroidPopupWindow

結果

f:id:iseAB:20190806211218p:plain

めっさ雑なので次回行こう調整していきます。

memo

複数ファイルを含むgistはそれぞれのリンクを指定出来ないらしい。

プロジェクト系のコードは別のやり方をした方が良さげ。

 

よくわからんけどKotlin コルーチンを使いたいって人

が読むべきページ

 

これを読んでもおそらく理解はできない。というか自分が出来ていない。ただとりあえず複雑すぎない処理で使う分には十分なのではなかろうか。

AndroidStudioのSelect Deployment Target にデバイスが表示されないバグの解消

事象

「▶︎」ボタンを押してもエミュレーターも実機も表示されない。

新しいエミュレーターを作成してもどこかへ消えてしまう。

f:id:iseAB:20190712002524p:plain

 

解決策

mac再起動

 

 

...ちなみにごちゃごちゃと調べて下をやってみたものの効果はなく

ツールバーの「RUN」->「edit configuration」のTargetをいじる。

f:id:iseAB:20190712002805p:plain

・AVDManagerを開きのエミュレーター右の「▶︎」ボタンタップ

・adb kill-server -> adb start-sever

・AndroidStudio再起動(Invalidate caches/Restart)

 

安心

f:id:iseAB:20190712003257p:plain