20200124のAndroidに関する記事は6件です。

初心者の初心者による初心者のためのAndroidアプリ開発

はじめに

こんにちは。
現在、高専の4年生をしており、最近Androidアプリ開発について勉強しています。きっかけとしては単純なもので、自身の研究内容にAndroidアプリの開発の知識が必要だったというだけです。プログラミング自体に興味を持ってからの日々が浅いので、正直見るもの見るもの新鮮です。

この記事を見られているということは、タイトルにも書かれているようにAndroidアプリ開発の初心者、もしくは、これから学んでいくという方がほとんどだと思います。そのため、『これからAndroidアプリ開発について学んでいきたいと思ったけどプログラミング全然知らないけど大丈夫かな?』って方をメインに、つまずきやすいと感じた部分を初心者なりに精一杯わかりやすく解説して学んでいけたらと思っています。

イベントとイベントハンドラ、リスナ

今回はイベントとイベントハンドラとリスナについて解説していきます。正直、イベントとリスナはまだしも、イベントハンドラなんていう単語聞いたことないと思います。もちろん私も聞いたことなかったです。意味自体は非常に簡単で一度聞いてしまえば難しく考える必要なものではないです。

イベントとは、ボタンのタップ、アイコンをドラッグなど使用者が画面に対して何かの操作を実行することです。いわゆるスマートフォンの基本操作です。そのイベントに対して実行する処理のことをイベントハンドラと呼びます。こちらは、画面の遷移やアプリケーションの起動などといった動作のことです。そして、このイベントの検出を行っているものをリスナと呼びます。
この考え方自体はAndroidに限らず、iOSやデスクトップアプリなどといった使用者の操作に応じて特定の処理を実行するアプリケーションでの共通の考え方でもあります。

Androidでリスナを設定する手順

ここでは実際に簡易的なアプリケーションの作成をしていきます。あらかじめ下記のコードを記述しておくことをお勧めします。このとき、『override』にコンパイルエラーが発生していても問題ありません。

res/value/strings.xml
image.png

res/layout/activity_main.xml
image.png

java/com.websarva.wings.android.ibennto/MainActivity.kt
image.png

1.各イベントに対応したリスなクラスを作成する
2.リスナクラス内の所定のメソッド(つまりイベントハンドラ)に処理を記述する
3.リスナクラスのインスタンスを生産してリスナ設定メソッドを引数(媒介)として渡す

リスナクラスの作成する

MainActivityのonCreate()メソッド以降に次のコードを記述します。ここでは、privateインナークラスにListenerクラスを追記することで、『ボタンのタップ』という操作に対してリスナクラスへ移ります。

java/com.websarva.wings.android.ibennto/MainActivity.kt
image.png

リスナクラスには専用のインターフェースを実装します。タップという操作に対してのリスナクラスを作成するには『View.OnClickListener』インターフェースを実装します。インターフェースを実装した時点でそのインターフェースに定義されているメソッドを記述する必要があります。今回のコードのView.OnClickListenerインターフェースではonClick()メソッドのことです。つまり、これがイベントハンドラです。

リスナクラス内の所定のイベントハンドラに処理を記述する

次に、イベントハンドラであるonClick()メソッドに処理を記述していきます。EditTextから入力された文字を取得し、TextViewに表示させる動作です。

java/com.websarva.wings.android.ibennto/MainActivity.kt
image.png

リスナクラスのインスタンスを生産してリスナ設定メソッドを引数として渡す

最後にリスナを設定します。onCreate()メソッド内に以下のコードを挿入します。

java/com.websarva.wings.android.ibennto/MainActivity.kt
image.png

完成です!

最後に

今回作成したものは単純なアプリケーションですが、今後の必要となる要素が詰まっているので繰り返し学習していきたいですね。

本記事は、『基礎&応用力をしっかり育成! Androidアプリ開発の教科書 Kotlin対応 なんちゃって開発者にならないための実践ハンズオン』を参考にしております。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android 10で、端末からIMEIなどのSIM情報を取得する方法

はじめに

Android 10から、端末からIMEIなどのSIM情報を取得するためには、「READ_PRIVILEGED_PHONE_STATE」という権限をAndroidManifest.xmlに追加する必要が出てきました。

Android 10 以降で IMEI とシリアル番号の両方を含む再設定不可能なデバイス ID にアクセスするには、アプリに READ_PRIVILEGED_PHONE_STATE 特権が必要です。
注意: Google Play ストアからインストールされたサードパーティ製アプリでは特権を宣言できません。
Privacy changes in Android 10  |  Android デベロッパー  |  Android Developersより引用

追加するにあたって、以下のようにtools:ignore="ProtectedPermissions"を追加することで、Android Studioから警告されることなく追加できます。(tools:ignore="ProtectedPermissions"を追加しなくても一応ビルドはできます)

AndroidManifest.xml
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
        tools:ignore="ProtectedPermissions" />

しかし、サードパーティ製アプリ(Google Playに公開されているアプリ)では宣言していても、特権を利用することはできません。該当メソッド呼び出し時にSecurityExceptionがスローされます。
上記の宣言に加えて、主に以下の対応が必要になります。

  1. プリインアプリ化する
  2. 「READ_PRIVILEGED_PHONE_STATE」をホワイトリストに追加する

プリインアプリ化する

「READ_PRIVILEGED_PHONE_STATE」を有効にするには、まずアプリをプリインアプリ化する必要があります。
プリインアプリとは、主に/system/app/system/priv-app配下にあるアプリのことです。出荷時にあらかじめインストールされているアプリのことですね。
(端末によっては/product/priv-app配下にもあることもあるようです。)
その中でも、/system/priv-app配下にある、「システム権限を必要とする特権アプリ」である必要があります。

「READ_PRIVILEGED_PHONE_STATE」をホワイトリストに追加する

「READ_PRIVILEGED_PHONE_STATE」をAndroidManifest.xmlに追加し、プリインアプリ化したのち、さらに以下のような感じで権限をホワイトリストに追加する必要があります。
ファイル名に関しては、厳密にこの命名じゃないとダメというのはないらしいです。

/etc/permissions/privapp-permissions-OEM_NAME.xml
<permissions>
  <privapp-permissions package="アプリのパッケージ名">
    <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
  </privapp-permissions>
</permissions>

この辺りはおそらく手動で追加するものではなく、端末によっても格納先が異なると思われるので、あくまで参考程度にしてください。当然ながら、追加するには端末をroot化しないとできません。

注意

  • プリインアプリがショートカットアプリ(起動するとGoogle Playのアプリページに飛ばすだけのもの)の場合、ショートカットアプリに対しても「READ_PRIVILEGED_PHONE_STATE」を宣言しないと無効になってしまうので注意!
  • プリインアプリ更新時、APKファイルの格納先が移動されるようですが、どうやら仕様のようです。
  • アプリ更新時にAndroidManifestがまるまる上書きされるものかと思っていたのですが、まるまる上書きはされず、以前に宣言した内容は残るらしいです。AndroidManifestの仕組みについて勉強しておかないとまずいと実感しました。
  • 複数のマニフェスト ファイルをマージする  |  Android デベロッパー  |  Android Developersの内容を理解すると良いのかもしれないと感じました。(まだちゃんと読めてませんが)

コマンドメモ

この対応を行うにあたって使用したコマンドを下記にまとめます。

対象のアプリ(APKファイル)が端末内のどこにあるのかを調べる

adb shell pm list packages -f | grep -i "アプリのパッケージ名"

プリインアプリの上書き(root権限必須)

adb root
adb remount
adb push "APKファイルのパス" /system/app/
adb shell chmod 644 /system/app/"APKファイル名"
adb reboot

端末のシャットダウン(再起動を繰り返すようになってしまった場合に使用)

adb shell reboot -p

ホワイトリストを端末内にプッシュ

adb push "ホワイトリストのパス" /etc/permissions/

参考URL

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidで特定部分だけ角丸にする

shapecornertopLeftRadiusみたいなパラメータがあるので、角丸にしたい部分だけ指定してあげれば角丸になる。以下は上だけ角丸にした場合。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/white" />
    <corners
        android:topLeftRadius="4dp"
        android:topRightRadius="4dp" />
</shape>

全体角丸はradiusで指定したほうがわかりやすい

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/white" />
    <corners android:radius="4dp" />
</shape>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android] コールバック

作るもの

以下をコールバックを使って実装する。
・ボタンを押すと非同期処理を実行する
・非同期処理が終わったら画面に「終了」を表示する
スクリーンショット (4).png

用意するクラス

・AsyncTaskCallback.java  コールバックインターフェース
・MainActivity.java     コールバックインターフェースを実装する
・HttpAsync.java      非同期処理クラス

使い方

AsyncTaskCallback.java

public interface AsyncTaskCallbacks {
    public void onTaskFinished();    //終了
    public void onTaskCancelled();   //キャンセル
}

MainActivity.java

public class MainActivity extends AppCompatActivity implements AsyncTaskCallbacks {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setAsyncButton();
    }

    private void setAsyncButton(){
        Button button = findViewById(R.id.button);
        button.setOnClickListener((View v)->{
            HttpAsync httpAsync = new HttpAsync(this);
            httpAsync.execute();  //非同期処理呼び出し
        });
    }

    @Override
    public void onTaskFinished() {
        TextView textView = findViewById(R.id.text);
        textView.setText("終了");
    }

    @Override
    public void onTaskCancelled() {
        TextView textView = findViewById(R.id.text);
        textView.setText("キャンセル");
    }
}

HttpAsync.java

public class HttpAsync extends AsyncTask<Integer, Void, Integer> {

    private AsyncTaskCallbacks callbacks_ = null;

    //コールバック登録用コンストラクタ
    public HttpAsync(AsyncTaskCallbacks _callBacks){
        this.callbacks_ = _callBacks; //コールバック登録
    }

    @Override
    protected Integer doInBackground(Integer... params) {
        //何かの処理
        return null;
    }

    @Override
    protected void onPostExecute(Integer result) {
        callbacks_.onTaskFinished(); //非同期処理が終了したらonTaskFinished()を呼ぶ
    }

    @Override
    protected void onCancelled() {
        callbacks_.onTaskCancelled(); //非同期処理がキャンセルされたらonTaskCancelled()を呼ぶ
    }
}

感想

参考URLのサイトがかなりわかりやすくて簡単に実装できた。

参考URL

https://seesaawiki.jp/w/moonlight_aska/d/Activity%a4%cb%a5%b3%a1%bc%a5%eb%a5%d0%a5%c3%a5%af%a4%b9%a4%eb

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

libGDXでandroidアプリのチューニングをした話

はじめに

もう9年前。iOSアプリが日本ランキングの1位になった。
iOSソリティアV
さりげなく自慢:relaxed:

調子に乗ってandroidアプリも作った。nativeで作った。
nativeで当時android2.xなので、SurfaceViewを使った。
androidソリティアV

アニメがいまいちだったけど:sob:、ちゃんとできた。
フレームレートがちょっと低かった。

android/iOS共に現在10周年記念中。

数年前、チューニングしたいな、と思った。
iOSとソース一本化
したいけど、一本化するにはやることが多すぎる・・・。

うーん、GLSerfaceViewとかか。めんどくさそうだぞ。しかもよくなるか保証がない。

そんなとき、こんな記事が
Android StudioでlibGDX入門

ああlibGDX・・・web化しようとして、前にやったなあ。GWTがいまいちで途中で止めたんだった。
一部の画面のみlibGDXとか、できるのか。

そんなわけで一部の機能のみlibGDX、問題ない機能はそのままnative。な構成のandroidアプリとしてチューニングしてみることになったのであった。

結果

良かった点

  • ちゃんとチューニングになった。開発も結構サクサクいけた。
  • 前に捨てたlibGDXコードもちゃんと再利用できた。
  • Javaソース追えるので、いろいろ助かった。
  • android nativeのレイアウトをlibGDX画面の上に出したりもできた。ほんとに最低限のみ、libGDX化で行けた。楽だった。
  • 使い慣れたテクスチャパッカーも使える。

悪かった点

  • 日本語の突っ込んだ情報が無い・・・

最後に

結構良かった。androidアプリとして、間違った選択ではなかった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidアプリでソフトウェアキーボードの入力を1文字ずつ検出する

概要

Androidで1文字ずつ文字の検出をしようと思ってonKey~をつかってもできませんでした.
ちょっと調べたところ,ソフトウェアキーボードのキーボードイベントを拾ってくれないらしいです.
https://developer.android.com/training/keyboard-input/commands

ので,1文字ずつ文字の検出する機能を実装しました.

EditTextの場合,TextChangedListenerを使えば一応できますが,入力ではなくてEditTextのテキストが変わったら反応するため処理がめんどくさいので使いません.

ソースコード

https://github.com/yokoro13/Get-Software-KeyBoard-Input

実装

今回はEditTextの場合で実装していきます.
この実装方法だと(たぶん)ほかのViewでもつかえます.WebViewでは使えました.

やることは簡単で,もとになるViewを継承してonCreateInputConnectiondispatchKeyEventをオーバーライトするだけです.

dispatchKeyEventKeyEventを拾えるので,そこで入力時にしたいことを記述します.

MyEditText.kt
package com.usr.myedittexttest

import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.KeyEvent
import android.view.inputmethod.BaseInputConnection
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
import android.widget.EditText

class MyEditText : EditText {

    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)

    override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection {
        return BaseInputConnection(this, false)
    }

    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        val dispatchFirst = super.dispatchKeyEvent(event)

        if (event.action == KeyEvent.ACTION_UP){
            val charCode = event.unicodeChar
            // 入力時の処理
            Log.d("MyEditText", "onKey keyCode=$charCode")
            Log.d("MyEditText", "onKey input=${charCode.toChar()}")
        }
        return dispatchFirst
    }
}

新しいViewはもとのViewと同じように使用できます.

MainActivity
package com.usr.myedittexttest

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    lateinit var myEditText: MyEditText

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

        myEditText = findViewById(R.id.my_edit)
    }
}

activity_main.xmlは使うViewを作成したものに変更します.

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".MainActivity">

    <com.usr.myedittexttest.MyEditText
        android:id="@+id/my_edit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

入力を通知するListenerを作成する場合

入力したことを通知するListenerを使ったほうが便利なのでListenerを作成します.

まずListenerを作成

InputListener
package com.usr.myedittexttest

import java.util.*

interface InputListener: EventListener {
    fun onKey(text: Char)
}

次にMyEditTextを少し改造

MyEditText
package com.usr.myedittexttest

import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.KeyEvent
import android.view.inputmethod.BaseInputConnection
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
import android.widget.EditText

class MyEditText : EditText {

    private var listener: InputListener? = null

    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)

    override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection {
        return BaseInputConnection(this, false)
    }

    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        val dispatchFirst = super.dispatchKeyEvent(event)

        if (event.action == KeyEvent.ACTION_UP){
            val charCode = event.unicodeChar
            // 入力時の処理
            Log.d("MyEditText", "onKey keyCode=$charCode")
            Log.d("MyEditText", "onKey input=${charCode.toChar()}")
            // 入力を通知
            listener?.onKey(charCode.toChar())
        }
        return dispatchFirst
    }

    fun setListener(listener: InputListener){
        this.listener = listener
    }
}

MainActivityでは入力の通知を受け取った時の処理を記述します.
そのためにInputListenerを実装します.
通知を受け取るとonKeyが呼ばれるので,そこに処理を記述してください.

MainActivity.kt
package com.usr.myedittexttest

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

class MainActivity : AppCompatActivity(), InputListener{
    lateinit var myEditText: MyEditText

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

        myEditText = findViewById(R.id.my_edit)
        myEditText.setListener(this)
    }

    override fun onKey(text: Char) {
        Log.d("MainActivity", "text=$text")
        // TODO something
    }
}

終わりに

勉強し始めたばかりですので,より良い方法があるかと思います.よろしければお教えくださいますと幸いです.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む