rksoftware

Visual Studio とか C# とかが好きです

Android アプリを Xamarin.Android に移植する (5) [完成]

前々々々回 Android アプリを Xamarin.Android に移植する (1) で UI を移植しました。
出来上がった画面。

今回 3 つ目のクラスの移植を行って行きます。一番のメイン、Activity クラスです。

■ 今回移植するクラス

今回は
com.example.android.mediarecorder.MainActivity クラス を C# で書き換えます。

■ MainActivity クラスの編集

MainActivity クラス

MainActivity クラスは既に生成されているクラスを書き換えて行きます。

using の追加

今回も必要な using は最初に追加してしまいます。ファイルの先頭の using の部分が次のようになるようコピー&ペーストしてください。

using System;
using Android.App;
using Android.Content.PM;
using Android.Hardware;
using Android.OS;
using Android.Views;
using Java.IO;
using Android.Media;
using Android.Widget;
using Java.Lang;
using Android.Util;
using System.Collections.Generic;
using String = System.String;

データメンバー の移植

Java のクラスの先頭で定義されているデータメンバーを移植します。
MainActivity.java の 46 行目からの次のコードを、C# の MainActivity.cs の同じ位置 (クラスの宣言と OnCreate メソッドの宣言の間) に追加します。
下記の C# のコードをコピー&ペーストしてください。
・Java のコード

private Camera mCamera;
private TextureView mPreview;
private MediaRecorder mMediaRecorder;
private File mOutputFile;

private boolean isRecording = false;
private static final String TAG = "Recorder";
private Button captureButton;

・C# のコード

private Camera mCamera;
private TextureView mPreview;
private Android.Media.MediaRecorder mMediaRecorder;
private File mOutputFile;

private bool isRecording = false;
private static readonly string TAG = "Recorder";
private Button captureButton;

コントロールのインスタンス取得の追加

OnCreate での処理を追加します。OnCreate は画面生成時の処理で、今回は画面上のコントロールの実態をデータメンバーに保持します。

SetContentView(Resource.Layout.sample_main);

の下に下記の C# コードをコピー&ペーストします。これまで同様、Java では小文字始まりだったメソッド名が、大文字始まりになります。 ・Java のコード

mPreview = (TextureView) findViewById(R.id.surface_view);
captureButton = (Button) findViewById(R.id.button_capture);

・C# のコード

mPreview = (TextureView)FindViewById(Resource.Id.surface_view);
captureButton = (Button)FindViewById(Resource.Id.button_capture);

OnCreate は次のようになります。

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);
 
    SetContentView(Resource.Layout.sample_main);
 
    mPreview = (TextureView)FindViewById(Resource.Id.surface_view);
    captureButton = (Button)FindViewById(Resource.Id.button_capture);
}

OnCaptureClick メソッドのコピー&ペースト

既に作成済みの OnCaptureClick メソッドに実装を追加します。作成済みの OnCaptureClick メソッドは次のようになっているはずです。

[Java.Interop.Export("onCaptureClick")]
public void OnCaptureClick(View view)
{
}

このメソッドの中に、MainActivity.java の 71 行目 onCaptureClick メソッドの内容をコピー&ペーストします。
いくらかのエラーが出ますが、構わず先へ進みます。

その他のメソッド等のコピー&ペースト

OnCaptureClick の下に、MainActivity.java の 105 行目 setCaptureButtonText メソッド以降のコードを最後の } を除いてコピー&ペーストします。

メソッド名の変更

Java ではメソッド名は小文字で始まりますが、C# では大文字で始まります。
小文字で始まっているメソッド名を、大文字始まりに一気に変更して行きます。

置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

mMediaRecorder.stop()mMediaRecorder.Stop()
mOutputFile.delete()mOutputFile.Delete()
mCamera.lockmCamera.Lock
mMediaRecorder.reset()mMediaRecorder.Reset()
mMediaRecorder.release()mMediaRecorder.Release()
mCamera.release()mCamera.Release()
mCamera.getParameters()mCamera.GetParameters()
CamcorderProfile.get(CamcorderProfile.Get(
parameters.setPreviewSize(parameters.SetPreviewSize(
mCamera.setParameters(mCamera.SetParameters(
mCamera.setPreviewTexture(mCamera.SetPreviewTexture(
mCamera.unlock()mCamera.Unlock()
mMediaRecorder.setmMediaRecorder.Set
mMediaRecorder.prepare()mMediaRecorder.Prepare()
mMediaRecorder.start()mMediaRecorder.Start()

string クラスへの変更

Java の String クラスは C# の string を使用するよう変更します。

置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

Stringstring

プロパティへの変更 (setter)

Java の setter メソッドはなるべく、プロパティに変更されています。

置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

captureButton.setText(title)captureButton.Text = title

メソッドの orverride の変更

Java ではスーパークラスのメソッドをサブクラスで orverride する場合はアノテーションを設定しました。C# では return 値の型の前に orverride キーワードを記述します。
スーパークラスは C# に移植されたクラスのため、メソッド名の先頭が大文字になる点に注意してください。
・Java のコード

@Override
protected void onPause()

・C# のコード

protected override void OnPause()

base への書き換え

Java ではスーパークラスのメソッドを呼び出す場合、super キーワードを使用します。C# では同様の機能は base になります。
スーパークラスは C# に移植されたクラスのため、メソッド名の先頭が大文字になる点に注意してください。

置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

super.onPause()base.OnPause()

bool への変更

Java では真偽値型は boolean キーワードですが、C# では bool になります。

置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

booleanbool
Booleanbool

IList への書き換え

List<> は Java のインタフェースですが、C# ではインタフェースは頭に I が付いた IList<> という名前になります。

置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

List<IList<

プロパティへの変更

Java の getter メソッドはなるべく、プロパティに変更されています。

置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

parameters.getSupportedPreviewSizes()parameters.SupportedPreviewSizes
parameters.getSupportedVideoSizes()parameters.SupportedVideoSizes
mPreview.getWidth()mPreview.Width
mPreview.getHeight()mPreview.Height
mPreview.getSurfaceTexture()mPreview.SurfaceTexture
e.getMessage()e.Message
mOutputFile.getPath()mOutputFile.Path

プロパティ名の変更

Java では公開データメンバは小文字で始まりますが、C# ではプロパティになり、大文字始まりになります。

置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

profile.videoFrameWidthprofile.VideoFrameWidth
profile.videoFrameHeightprofile.VideoFrameHeight
optimalSize.widthoptimalSize.Width
optimalSize.heightoptimalSize.Height

列挙体への変更

Java では公開フォールドはなるべく列挙体に変更されています。

置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

CamcorderProfile.QUALITY_HIGHCamcorderQuality.High
MediaRecorder.AudioSource.DEFAULTAudioSource.Default
MediaRecorder.VideoSource.CAMERAVideoSource.Camera

メソッド定義の変更

MainActivity.cs の 133 行目あたりの次の文でエラーが発生しているはずです。
これは、CameraHelper クラス移植の際に手数を減らすために、本来行うべき Java の List<> から C# の IList<T> への置き換えを行っていないために発生しました。
※まずは、CameraHelper を一度ビルドできる状態にするため省略しました。

Camera.Size optimalSize = CameraHelper.getOptimalVideoSize(mSupportedVideoSizes,
    mSupportedPreviewSizes, mPreview.Width, mPreview.Height);

メソッド名 getOptimalVideoSize にカーソルをあわせて F12 キーを押し、メソッドの定義へ移動します。
移動した先のメソッド定義を次のように変更します。

public static Camera.Size getOptimalVideoSize(IList<Camera.Size> supportedVideoSizes,
    IList<Camera.Size> previewSizes, int w, int h)

この書き換えにより、10 行程下の行でエラーが発生します。このエラーを正すために、エラー行の 3 つ上の行の型定義も IList<Camera.Size> に変更します。
・変更前

List<Camera.Size> videoSizes;

・変更後

IList<Camera.Size> videoSizes;

CameraHelper.cs にエラーがないことを確認し、MainActivity.cs に戻ります。

デバッグ出力メソッドの変更

デバッグ出力メソッドのメソッド名 dDebug になっているので変更します。
また、エラー出力のメソッド名 eError になります。

置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

Log.d(Log.Debug(
Log.e(Log.Error(

クラス継承のキーワードの変更

Java ではクラスの継承は extends キーワードですが、C# では : になります。

置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

extends:

型引数 Void の変更

AsyncTask<> の型引数 VoidJava.Lang.Void であることを明示します。

置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

AsyncTask<Void, Void, bool>AsyncTask<Java.Lang.Void, Java.Lang.Void, bool>

MediaPrepareTask クラスの変更

MediaPrepareTask クラスは Android API のクラスで、非同期処理を行う AsyncTask クラスのサブクラスです。
この MediaPrepareTask クラス内の変更も基本はこれまでのパターンで良いのですが、このクラスがインナークラスとして実装されているという大きな違いがあります。
Java のインナークラスは親クラスのインスタンスを参照できますが、C# ではそのようなことができません。
C# へ置き換える際に、MediaPrepareTask クラスが親クラスである MainActivity クラスのインスタンスを明示的に持つよう変更します。

少し複雑な書き換えを行うため MediaPrepareTask クラスは完成コードをまるごとコピー&ペーストします。
次のコードへコピー&ペーストで置き換えてください。

class MediaPrepareTask : AsyncTask<Java.Lang.Void, Java.Lang.Void, bool>
{
    private readonly WeakReference<MainActivity> _activity;
    public MediaPrepareTask(MainActivity activity)
    {
        _activity = new WeakReference<MainActivity>(activity);
    }
    protected override Java.Lang.Object DoInBackground(params Java.Lang.Object[] voids)
    {
        MainActivity activity;
        _activity.TryGetTarget(out activity);
        if (activity.prepareVideoRecorder())
        {
            activity.mMediaRecorder.Start();
            activity.isRecording = true;
        }
        else
        {
            activity.releaseMediaRecorder();
            return false;
        }
        return true;
    }
    protected override void OnPostExecute(bool result)
    {
        MainActivity activity;
        _activity.TryGetTarget(out activity);
        if (!result)
        {
            activity.Finish();
        }
        activity.setCaptureButtonText("Stop");
    }
    protected override bool RunInBackground(params Java.Lang.Void[] @params)
    {
        throw new NotImplementedException();
    }
}

MediaPrepareTask クラス使用の変更

MediaPrepareTask クラスを MainActivity クラスのインスタンスを保持するよう変更しました。
そのため、MediaPrepareTask クラスの使用されている場所で、MainActivity クラスのインスタンスを設定するよう変更します。

置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

new MediaPrepareTask().execute(null, null, null);new MediaPrepareTask(this).Execute(null, null, null);

■ MainActivity クラスの完成

これで MainActivity クラスは完成です。
ビルドを行いエラーが出ないことを確認してください。(警告は大量に出ますが、今回はそのままにします)
エラーが出た場合は、エラーの内容と上記の書き換えを見比べて頑張ってエラーに対処してください。

■ 実行

ここまでで、一度アプリを実行してみましょう。
Visual Studio (for Mac) のツールバーの 右三角 アイコンをクリックします。
次の画面が表示されるはずです。

前回出来上がった画面

ここで、[capture] ボタンをタップするとエラーが発生します。
これはまだこのアプリがカメラ等を使うためのパーミッションを設定していないためです。
パーミッションを正しく設定していない場合、該当する機能を使用しようとするとエラーが発生するのがスマートデバイスの特徴です。

■ 設定ファイルの設定

編集する設定ファイルは、Properties フォルダー内の AndroidManifest.xml ファイルです。
・ソリューションエクスプローラー上で AndroidManifest.xml をダブルクリックします。
・エディターで XML ファイルが開くので次のように編集します。ソースが開かない場合、[ソース]タブを選択してください。
<manifest> タグの中に次の要素を追加します。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_VIDEO" />
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
AndroidManifest.xml 全体は例えば次のようになります。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.example.android.XamMediaRecorder">
    <uses-sdk android:minSdkVersion="16" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECORD_VIDEO" />
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />
    <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name"/>
</manifest>

■ 実行

これでアプリが正しく動作するはずです。
もし、activity.mMediaRecorder.Start(); で例外が発生する場合は、AndroidManifest.xml<uses-sdk の設定値を確認してみてください。

お疲れ様でした! これで Android アプリの Xamarin.Android への移植ができました。
この経験をもとにぜひ Xamarin で Android アプリを開発してください!