カテゴリー別アーカイブ: Android

作ればわかる!Androidプログラミング Kotlin対応 10の実践サンプルで学ぶAndroidアプリ開発入門

作ればわかる!Androidプログラミング Kotlin対応 10の実践サンプルで学ぶAndroidアプリ開発入門
『作ればわかる!Androidプログラミング Kotlin対応 10の実践サンプルで学ぶAndroidアプリ開発入門』2019年06月19日新発売!
多少タイトルが長い感じがしますが、作ればわかる!Androidプログラミングの第五版にあたります。第五版と付いていないのは、今回プログラミング言語をJavaからKotlinに変えたからです。前書きにも書きましたが、KotlinだとJavaのように同じことをなんども書かなくてもすむのでコードの量が減り、プログラミングが軽快になります。

アクティビティやインテント、イベントリスナー、ConstraintLayout、UI部品の使い方といったAndroidアプリ開発で必要な基礎知識から、SurfaceViewによる描画、プリファレンスによる情報の記録、各種センサーやRealmデータベースといったかなり本格的な内容までが学べます。

目次
第1章 アプリを作るための準備
第2章 はじめてのアプリ作成
第3章 Kotlin入門
第4章 ハイ&ローゲーム
第5章 名刺切らしてまして
第6章 ご飯なんにする?
第7章 和室に入ったのだ~れ?
第8章 盗難防止アラーム
第9章 ブラックホールを避けろ!
第10章 若くても血圧は記録せよ
第11章 メモメモタイム
第12章 わたしはウクレレ
詳細は翔泳社さんのサイトでご確認ください。

自分で言うのもなんですが、今回はわかりやすい本になったと思います。
ぜひ、ご購読ください!

Realmはレアルムではなく、レルム

外国語のカタカナ読みを議論するのは不毛だと思うけど、なんて読んでいいのかわからないのも困るので、Realmはレルムと読むそうです。(not レアルム)
Realmは、SQLiteやCoreDataに置き換わるモバイルデータベース。
SQLiteのORマッパーではないという注意書きを付けたくなるほど使いやすいらしい。
詳しい説明はこちらからどうぞ。

使ってみた感想は「もうSQLiteには戻れない」です。

Java(Android)でRealmを使う簡単な手順を説明します。
Android Studioでプロジェクトを作成したら、build.gradleを編集します。
build.gradle(Project)にはclasspathを追加します。

    dependencies {
        classpath 'com.android.tools.build:gradle:2.1.0'
        classpath "io.realm:realm-gradle-plugin:1.0.0"	// 追記(ついに1.0.0!)

build.gradle(Module)にはpluginを追加します。

apply plugin: 'com.android.application'
apply plugin: 'realm-android'		// 追記

たとえば、何かの会のメンバーを登録して、ある条件で抽出して画面に表示する処理を作成してみます。
まず、Memberクラスを作成します。

package com.example.kanehiro.tryrealm;

import io.realm.RealmObject;

public class Member extends RealmObject {
    private long id;
    private String name;
    private boolean admin;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public boolean getAdmin() {
        return admin;
    }

    public void setAdmin(boolean admin) {
        this.admin = admin;
    }

}

Memberクラスには、idと名前、そしてadmin(管理者)かどうのかブール値(真偽値)を持たせます。
そして、それぞれのプロパティにgetterとsetterを用意します。
次にアクティビティのコードです。

package com.example.kanehiro.tryrealm;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

import io.realm.Realm;
import io.realm.RealmConfiguration;
import io.realm.RealmResults;

public class MainActivity extends AppCompatActivity {

    private Realm realm;
    private RealmConfiguration realmConfig;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Create the Realm configuration
        realmConfig = new RealmConfiguration.Builder(this).build();

        new AsyncTask<Void, Void, String>() {
            @Override
            protected String doInBackground(Void... voids) {
                String info;
                memberWrite();
                info = memberQuery(false);
                memberDelete();
                return info;
            }

            @Override
            protected void onPostExecute(String result) {
                TextView txt01 = (TextView)findViewById(R.id.txt01);
                txt01.setText(result);
            }
        }.execute();

    }
    private void memberWrite() {

        Realm realm = Realm.getInstance(realmConfig);

        realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                for (int i = 0; i < 30; i++) {
                    Member member = realm.createObject(Member.class);
                    member.setId(i);
                    member.setName("Member" + i);
                    if ((i % 3) == 0){
                        member.setAdmin(true);
                    }
                }
            }
        });
        realm.close();
    }
    private String memberQuery(boolean admin) {

        StringBuilder strBuild = new StringBuilder();
        Realm realm = Realm.getInstance(realmConfig);
        RealmResults<Member> results = realm.where(Member.class)
                .equalTo("admin", admin).findAll();
        for (Member m : results) {
            strBuild.append(m.getId());
            strBuild.append(",");
            strBuild.append(m.getName());
            strBuild.append("\n");

        }

        realm.close();
        return strBuild.toString();
    }

    private void memberDelete() {
        Realm realm = Realm.getInstance(realmConfig);
        realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                realm.delete(Member.class);
            }
        });
        realm.close();
    }
}

onCreateメソッドから見ていきましょう。RealmConfiguration.BuilderでRealm configurationオブジェクトを生成します。
追加するレコードの件数は少ないけど、AsyncTaskクラスを使って非同期に書き込み、読み込み、削除を実行します。
抽出結果はテキストビューに表示します。
memberWriteメソッドでMemberを書込みます。まず、Realm.getInstanceでRealmを取得します。
書き込み(追加、変更、削除)処理は、トランザクションの中で行わなければなりません。
ここでは、トランザクションブロックを使っています。realm.executeTransactionの部分です。
トランザクションはexecuteTransactionメソッドを抜けたタイミングでコミットされます。
memberQuery()で、クエリーを発行しています。whereにクラスを指定して、.equalToなどで検索条件を指定します。
ここではadminかどうかが検索条件です。findAll()で取得します。
次に取得した結果セットをループしてgetterで値を取得し、文字列として返します。
memberDelete()では、realm.delete(Member.class)で全オブジェクトを削除しています。
adminじゃないMemberを抽出した結果が次の画像です。
tryrealm実行結果

Xamarin、ザマリン、青空、晴れた空 -加速度センサーを使う-

 こちらのサイトのメロディーとともにお楽しみください。
正式なタイトルはVisual StudioのXamarinでAndroidアプリを作る(#5) 加速度センサーを使うです。
 5回目となる今回は、加速度センサーを使ってみます。developer.xamarin.comのレシピを参考にします。
今回はMainActivityの全コードを掲載します。
MainActivity

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Hardware;

namespace AcceleGet
{
    [Activity(Label = "AcceleGet", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity, ISensorEventListener
    {
        static readonly object _syncLock = new object();
        SensorManager _sensorManager;
        TextView _sensorTextView;

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);
            _sensorManager = (SensorManager)GetSystemService(Context.SensorService);
            _sensorTextView = FindViewById<TextView>(Resource.Id.accelerometer_text);
        }
        public void OnAccuracyChanged(Sensor sensor, [GeneratedEnum] SensorStatus accuracy)
        {
            //throw new NotImplementedException();
        }
        public void OnSensorChanged(SensorEvent e)
        {
            lock (_syncLock)
            {
                _sensorTextView.Text = string.Format("x={0:f}, y={1:f}, z={2:f}", e.Values[0], e.Values[1], e.Values[2]);
            }
        }
        protected override void OnResume()
        {
            base.OnResume();
            _sensorManager.RegisterListener(this,
                                            _sensorManager.GetDefaultSensor(SensorType.Accelerometer),
                                            SensorDelay.Ui);
        }
        protected override void OnPause()
        {
            base.OnPause();
            _sensorManager.UnregisterListener(this);
        }
    }
}

 センサーを使うにはISensorEventListenerインターフェースをインプリメントします。
ISensorEventListenerインターフェースをインプリメントする場合は、OnAccuracyChangedメソッドとOnSensorChangedを実装する必要があります。
 加速度センサーを使うには、OnResumeメソッドで、SensorType.Accelerometerを指定して、SensorManagerのRegisterListenerでリスナーを登録します。SensorDelay.Uiはセンサーの値を取得する間隔の指定です。
 OnPauseメソッドではUnregisterListenerでリスナーの登録を解除します。
 OnSensorChangedメソッドで加速度センサーの値を取得して画面に表示します。lockで他のスレッドから更新されないように排他制御しています。

実行画面

実行画面

Xamarin、ザマリン、青空、晴れた空

 今回からタイトルを変えました。こちらのサイトのメロディーとともにお楽しみください。
正式なタイトルはVisual StudioのXamarinでAndroidアプリを作る(#4) TwoTouchMailを作るです。
 4回目となる今回は、「作ればわかる!Androidプログラミング」の第二章のサンプルアプリTwoTouchMailをXamarinで作ってみます。とは言っても、インテントでデータを渡す方法やアクティビティを指定しない暗黙的なインテントについてはこれまでの3回ですでに説明しました。
 今回説明するのは、dimens.xml、RadioGroupの使い方、そしてStrings.xmlに登録した文字列をC#のコードで取得する方法です。
dimens.xmlにはpaddingで使うマージンのサイズを登録します。dimens.xmlは新規作成するXMLファイルです。
dimen.xml

<?xml version="1.0" encoding="utf-8" ?>
<resources>
  <!-- Default screen margins, per the Android Design guidelines. -->
  <dimen name="activity_horizontal_margin">16dp</dimen>
  <dimen name="activity_vertical_margin">16dp</dimen>
</resources>

 そして、レイアウトファイルにマージンを指定します。下記のサンプルはお迎えを頼むPickUp.axmlです。
RelativeLayoutの内側に余白を作るためにdimens.xmlの縦横marginをpaddingXXXXXXに指定しています。
PickUp.axml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">
    <TextView
        android:text="@string/subject"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:id="@+id/textView" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editText"
        android:layout_below="@+id/textView"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:text="@string/button_pickup" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="@string/place"
        android:id="@+id/textView3"
        android:layout_below="@+id/editText"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginTop="16dp" />
    <RadioGroup
        android:id="@+id/rg_place"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView3"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true">
        <RadioButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/station"
            android:checked="true"
            android:id="@+id/radioButton1" />
        <RadioButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/school"
            android:id="@+id/radioButton2" />
        <RadioButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/always"
            android:id="@+id/radioButton3" />
    </RadioGroup>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button_send"
        android:id="@+id/button"
        android:layout_below="@+id/rg_place"
        android:layout_centerHorizontal="true" />
</RelativeLayout>

PickUp2
これで、TextViewやRadioGroupの周囲に空白ができます。

 次にRadioGroupの使い方に進みます。PickUp.axmlにあるようにRadioGroupは複数のRadioButtonをグループ化して、択一にするために使います。この例の場合、一番上のラジオボタンのandroid:checkedが”true”になっています。
 RadioGroupを使うPickUpActivityクラスのコードをみていきましょう。
PickUpActivityクラス

   public class PickUpActivity : Activity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Create your application here
            SetContentView(Resource.Layout.PickUp);

            var btnSend = FindViewById<Button>(Resource.Id.button);

            btnSend.Click += delegate {
                RadioGroup rgPlace = (RadioGroup)FindViewById(Resource.Id.rg_place);
                int checkedId = rgPlace.CheckedRadioButtonId;
                String strPlace = ((RadioButton)FindViewById(checkedId)).Text;
                EditText edit01 = (EditText)FindViewById(Resource.Id.editText);
                String title = edit01.Text;

                Context context = this;
                Android.Content.Res.Resources res = context.Resources;

                var uri = Android.Net.Uri.Parse("mailto:" + res.GetString(Resource.String.mail_to));

                Intent intent = new Intent(Intent.ActionSendto, uri);
                intent.PutExtra(Intent.ExtraSubject, title);
                intent.PutExtra(Intent.ExtraText, strPlace + "に迎えにきて");
                StartActivity(intent);

            };

        }
    }

 
 btnSendがクリックされたら、選択されているRadioButtonのTextをメール本文に含めるようにしたいのですが、まず選択されているラジオボタンのIDを次のコードで取得します。
int checkedId = rgPlace.CheckedRadioButtonId;
Android SDKをJavaで使っている場合、選択されているボタンのIDはメソッドで取得します。Xamarinの場合はプロパティとして取得できますので、若干お得感があります。
 次にStrings.xmlに登録した文字列を取得する方法ですが、Android.Content.ResのResourcesクラスのGetStringメソッドで取得できます。Resource.String.mail_toはStrings.xmlに登録した送信先のメールアドレスです。
 そして、ActionSendtoを指定してIntentを生成し、PutExtraメソッドでタイトルや本文を付与し、StartActivityメソッドで暗黙的インテント(明示的に遷移先を指定しないインテント)を発行します。
 すると、メールアプリが起動します。
mailapp2
 これで簡単にメールを送るアプリができました。

Visual StudioのXamarinでAndroidアプリを作る(#3) 暗黙的なインテント

暗黙的なインテントとは遷移先のアクティビティを指定しないインテントのこと
以下の例はIntentにurlとそれを見たいのだということを引数で指定して、
StartActivityメソッドを実行する。
button.Click += delegate {
  var uri = Android.Net.Uri.Parse(“http://www.bing.co.jp”);
  Intent intent = new Intent(Intent.ActionView, uri);
  StartActivity(intent);
};
implicit2
実行すると、ブラウザが起動してwww.bing.co.jpが表示される。

Visual StudioのXamarinでAndroidアプリを作る(#2) Activity間のデータ渡し

Activity間でデータを渡す
遷移元ActivityではIntentを生成し、PutExtraメソッドで渡したいデータを積む。
button.Click += delegate {
  var secondActivity = new Intent(this, typeof(SecondActivity));
  secondActivity.PutExtra(“MyData”, “FirstActivityの方から来ました”);
  StartActivity(secondActivity);
};
遷移先Activityではデータ型に応じたGetXXXXXExtraメソッドでデータを取得する。
var txtView1 = FindViewById(Resource.Id.textView1);
txtView1.Text = Intent.GetStringExtra(“MyData”) ?? “Data not available”;

first
遷移元
second
遷移先

Visual StudioのXamarinでAndroidアプリを作る(#1)

Activityの遷移をするコードは以下のようになる。Intentはどこにも出てこない。
button.Click += delegate {
  StartActivity(typeof(SecondActivity));
};
Activityとそのレイアウトファイルはそれぞれ作成する必要がある。
Android Studioのようにレイアウトファイルを自動作成してくれない。
追加したActivityをAndroidManifest.xmlに手動で追加する必要はない。
Monitor_err
現在の問題点はAndroid Device Monitorが起動しないこと。

Android Tips

1.android.content.res.resources$notfoundexceptionの例外が出るとき
 たとえば、Google developer consoleでMapを使おうとプロジェクトを作っておきながら、
Google Maps Android APIを有効にしていないとき(実は最近やりました)。

2.Activity間やActivityとService間で変数を共有したいとき
Applicationを継承するクラスを作成する。
【定義】
import android.app.Application;

public class AppGlobal extends Application {
// グローバルに扱いたい変数
public double Lat = 0.0;
public double Lon = 0.0;
}
【使うとき】
private AppGlobal appglobal;

if (appglobal.Lat == 0)
などと。