rksoftware

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

Android アプリを Xamarin.Android に移植する (4)

前々々回 Android アプリを Xamarin.Android に移植する (1) で UI を移植しました。

今回 2 つ目のクラスの移植を行って行きます。

■ 今回移植するクラス

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

■ MediaCodecWrapper クラスの作成

Mac の場合

・ソリューションエクスプローラーの太字になっているプロジェクト名で右クリック (二本指タップ) し 追加 > 新しいファイル を選択します。
・[新しいファイル]ダイアログで General > 空のクラス を選択し、[名前] に 「MediaCodecWrapper」 を入力します。
・[新規]ボタンをクリックします。

Windows の場合

・ソリューションエクスプローラーの太字になっているプロジェクト名で右クリック (二本指タップ) し 追加 > 新しい項目 を選択します。
・[新しい項目の追加]ダイアログで Visual C# > クラス を選択し、[名前] に 「MediaCodecWrapper」 を入力します。
・[新規]ボタンをクリックします。

■ MediaCodecWrapper クラスの移植

上記で作成したクラスのソースのクラス宣言は次のようになっているはずです。

public class MediaCodecWrapper
{
    public MediaCodecWrapper()
    {
    }
}

ソースコードの コピー & ペースト

Java の MediaCodecWrapper クラスの package、import を除いた

public class MediaCodecWrapper {

...
中略
...

}

で C# の MediaCodecWrapper クラスの using、namespace を除いた

    public class MediaCodecWrapper
    {
        public MediaCodecWrapper()
        {
        }
    }

を上書きします。

using の追加

前回の CameraHelper クラスでは、1 つずつ確認しながら using を追加しました。
今回は必要な using は最初に追加してしまいます。

using System.Linq;
using Android.OS;
using Android.Media;
using System.Collections.Generic;
using Java.Nio;
using Java.Lang;
using Java.Util;
using Android.Views;

を追加します。最初からある using System; と合わせて次のようになります。

using System;
using System.Linq;
using Android.OS;
using Android.Media;
using System.Collections.Generic;
using Java.Nio;
using Java.Lang;
using Java.Util;
using Android.Views;

メソッド名の変更

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

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

codec.start()codec.Start()
mDecoder.stop()mDecoder.Stop()
mDecoder.release()mDecoder.Release()
codec.getInputBuffers()codec.GetInputBuffers()
codec.getOutputBuffers()codec.GetOutputBuffers()
Looper.myLooper()Looper.MyLooper()
trackFormat.getString(trackFormat.getString(
mimeType.contains(mimeType.Contains(
MediaCodec.createDecoderByType(MediaCodec.CreateDecoderByType(
videoCodec.configure(videoCodec.Configure(
input.remaining()input.Remaining()
buffer.capacity()buffer.Capacity()
buffer.clear()buffer.Clear()
buffer.put(buffer.Put(
mDecoder.queueInputBuffer(mDecoder.QueueInputBuffer(
mDecoder.queueSecureInputBuffer(mDecoder.QueueSecureInputBuffer(
extractor.readSampleData(extractor.ReadSampleData(
extractor.getSampleCryptoInfo(extractor.GetSampleCryptoInfo(
out_bufferInfo.set(out_bufferInfo.Set(
mDecoder.releaseOutputBuffer(mDecoder.ReleaseOutputBuffer(
mDecoder.dequeueInputBuffer(mDecoder.DequeueInputBuffer(
mDecoder.dequeueOutputBuffer(mDecoder.DequeueOutputBuffer(
mDecoder.getOutputBuffers()mDecoder.GetOutputBuffers()
mHandler.post(mHandler.Post(

プロパティ名の変更

Java では公開データメンバは小文字で始まりますが、C# ではプロパティになり、大文字始まりになります。
小文字で始まっているプロパティ名を、大文字始まりに一気に変更して行きます。

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

mOutputBuffers.lengthmOutputBuffers.Length
mInputBuffers.lengthmInputBuffers.Length)
info.offsetinfo.Offset
info.sizeinfo.Size
info.presentationTimeUsinfo.PresentationTimeUs
info.flagsinfo.Flags
Locale.USLocale.Us

定数名の変更

Java では公開フォールドはすべて大文字・_区切りですが、C# では大文字始まり大文字区切りになります。

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

MediaFormat.KEY_MIMEMediaFormat.KeyMime

列挙体への変更

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

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

MediaCodec.BUFFER_FLAG_END_OF_STREAM(int)MediaCodec.BufferFlagEndOfStream
, flags), (MediaCodecBufferFlags)flags)
MediaCodec.INFO_TRY_AGAIN_LATER(int)MediaCodec.InfoTryAgainLater
MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED(int)MediaCodec.InfoOutputBuffersChanged
MediaCodec.INFO_OUTPUT_FORMAT_CHANGED(int)MediaCodec.InfoOutputFormatChanged

final ローカル変数の変更

Java では変更不可のローカル変数の定義に final キーワードを使用しますが、C# では const になります。
さらに C# では文字列がリテラルでなければなりません。そのため、ここでは const が使用できません。final の指定は削除します。

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

final String mimeType =string mimeType =

final 引数の変更

Java ではメソッドの引数にはなるべく final を付けますが、C# では同等の機能はありません。
引数の final を削除します。

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

final → ``````

Queue への変更

Java の ArrayDeque<> クラスは Xamarin に移植されていません。代わりに C# の Queue<T> クラスを使用します。

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

Queue<Integer>Queue<int>
ArrayDeque<>Queue<int>

また、クラスが変わりのでメソッドも変わります。Queue<T> の同等のメソッドを使用するよう変更します。

!mAvailableInputBuffers.isEmpty()mAvailableInputBuffers.Any()
mAvailableInputBuffers.remove()mAvailableInputBuffers.Dequeue()
!mAvailableOutputBuffers.isEmpty()mAvailableOutputBuffers.Any()
mAvailableOutputBuffers.peek()mAvailableOutputBuffers.Peek()
mAvailableOutputBuffers.remove()mAvailableOutputBuffers.Dequeue()
mAvailableInputBuffers.add(mAvailableInputBuffers.Enqueue(
mAvailableOutputBuffers.clear()mAvailableOutputBuffers.Clear()
mAvailableOutputBuffers.add(mAvailableOutputBuffers.Enqueue(

throws の削除

Java のメソッドが返す場合、返す例外の種類を throws で宣言しますが、C# では同等の機能はありません。
throws の宣言を削除します。

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

throws IOException
throws MediaCodec.CryptoException, WriteException

bool への変更

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

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

booleanbool

Java.Lang.String の使用

Xamarin にする場合、文字列は C# の string を使用するべきですが、Java の String も使用できます。
今回は Java の String のメソッドが使用されている箇所は、手数を少なく書き換えのミスを減らすために、Java から移植されたクラスを使用します。

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

String.format(Java.Lang.String.Format(

プロパティへの変更

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

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

mDecoder.getOutputFormat()mDecoder.OutputFormat

ラムダ式への変更

Java でのイベントリスナーの匿名クラスは、C# ではラムダ式で実装します。
ラムダ式を使用すると匿名メソッドを短い手数で記述することができます。

Java の実装

mHandler.post(new Runnable() {
    @Override
    public void run()
    {
        mOutputFormatChangedListener
        .outputFormatChanged(MediaCodecWrapper.this,
            mDecoder.getOutputFormat());

    }
});

C# での実装

mHandler.Post(new Runnable(() =>
{
    mOutputFormatChangedListener
    .outputFormatChanged(this,
        mDecoder.OutputFormat);

}));

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

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

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

extends:

コンストラクタの変更

Java ではコンストラクタ内でスーパークラスのコンストラクタを呼ぶキーワードは super ですが、C# では base になります。
また、記述する場所を変更になります。

Java の実装

private WriteException(String detailMessage)
{
    super(detailMessage);
}

C# での実装

public WriteException(string detailMessage) : base(detailMessage)
{
}

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

■ 次回予定

ここまでで二つ目のクラスの移植は完了しました。
次回で最後のクラスである Activity の移植を行って行きます。

Android アプリを Xamarin.Android に移植する (3)

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

前回出来上がった画面

今回からロジックの移植を行って行きます。

■ Android サンプルの構成

サンプルアプリは次の3つのクラスで構成されています。
com.example.android.common.media.CameraHelper クラス
com.example.android.common.media.MediaCodecWrapper クラス
com.example.android.mediarecorder.MainActivity クラス
この3クラスを C# で書き換えます。

C# の クラスと Java のクラス

C# のクラスと Java のクラスは記述方法、機能ともに概ね変わりません。
そのため、今回は C# でも3クラスを作成します。

ファイルの作成場所

Java ではクラスのソースファイルは、そのクラスのパッケージに合わせたフォルダーに作成しなければなりません。
C# でも Java のパッケージに当たる名前空間という機能がありますが、この名前空間とフォルダーは合わせる必要はありません。
合わせる必要はありませんが、一般的には名前空間とフォルダーは合わせて作成します。
ただし、今回は手順の簡略化のため、ファイルはすべてプロジェクトの一番浅いフォルダーに置いてしまいます。

■ CameraHelper クラスの作成

Mac の場合

・ソリューションエクスプローラーの太字になっているプロジェクト名で右クリック (二本指タップ) し 追加 > 新しいファイル を選択します。
・[新しいファイル]ダイアログで General > 空のクラス を選択し、[名前] に 「CameraHelper」 を入力します。
・[新規]ボタンをクリックします。

Windows の場合

・ソリューションエクスプローラーの太字になっているプロジェクト名で右クリック (二本指タップ) し 追加 > 新しい項目 を選択します。
・[新しい項目の追加]ダイアログで Visual C# > クラス を選択し、[名前] に 「CameraHelper」 を入力します。
・[新規]ボタンをクリックします。

■ CameraHelper クラスの移植

上記で作成したクラスのソースのクラス宣言は次のようになっているはずです。

public class CameraHelper
{
    public CameraHelper()
    {
    }
}
クラスの宣言は Java と大きく変わらないことがわかります。
このように C# と Java の構文はかなり似た部分が多くなっています。

ソースコードの コピー & ペースト

C# と Java の構文は似ているので、まずは Java のソースコードを C# へコピー & ペーストしてしまいます。
Java の CameraHelper クラスの package、import を除いた

public class CameraHelper {
...
中略
...
}
で C# の CameraHelper クラスの using、namespace を除いた
public class CameraHelper
{
    public CameraHelper()
    {
    }
}
を上書きします。
大量のエラーが出ますが、これはコードの大枠が C# として認識できるものということでもあります。
このエラーを潰して行くだけで、Xamarin.Android として成立するコードになる簡単な作業です。

static final の置き換え

大量のエラーの最初は

public static final int MEDIA_TYPE_IMAGE = 1;
という一文のエラーです。これは Java で定数を定義している文ですが、C# では finalreadonly になります。
public static readonly int MEDIA_TYPE_IMAGE = 1;
すぐ下の MEDIA_TYPE_VIDEO の定義も同様に書き換えます。

using の追加

次のエラーは

public static Camera.Size getOptimalVideoSize(List<Camera.Size> supportedVideoSizes,
で、Camera が見つからないというエラーです。これは Camera クラスを使用するための、Java では import に当たる宣言を行っていないために発生しています。
C# では using というキーワードになります。
using はエラーの発生している Camera にカーソルを合わせて、Mac の場合は option+enter、Windows の場合は ctrl+. から自動で追加することもできます。
using Android.Hardware; を追加します。

List への書き換え

次のエラーは

public static Camera.Size getOptimalVideoSize(List<Camera.Size> supportedVideoSizes,
で、List<> が見つからないというエラーです。List<> は Java のインタフェースですが、C# ではインタフェースは頭に I が付いた IList<> という名前になります。
正しく置き換えるのであれば IList<T> に置き換えるべきですが、今回は書き換えの手数を省略するために C# での実装クラスである List<T> に置き換えます。 Camera クラスと同様に、using を追加します。
using は Mac の場合は option+enter、Windows の場合は ctrl+. から自動で追加することもできます。
using System.Collections.Generic; を追加します。

const ローカル変数への書き換え

次のエラーは

final double ASPECT_TOLERANCE = 0.1;
という一文で、これはローカル定数を宣言しています。C# では finalconst になります。
const double ASPECT_TOLERANCE = 0.1;

Double.MaxValues への書き換え

次のエラーは

double minDiff = Double.MAX_VALUE;
doubleMAX_VALUE が見つからないというエラーです。Java では定数はすべて大文字・_区切りで定義されていますが、C# では定数は大文字始まり・大文字区切りで定義されています。
double minDiff = Double.MaxValue;
C# では Doubledouble は別名の関係でどちらで書いても同じです。
この書き換えは何度か出てくるので、覚えておくかここで一括置換してしまいましょう。
置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

foreach への書き換え

次のエラーは

for (Camera.Size size : videoSizes)
で、: がおかしいですというエラーです。これは Java でコレクションの要素全てに対する繰り返しを行う記述です。 C# では foreach (... in ...) になります。
foreach (Camera.Size size in videoSizes)
この書き換えは何度か出てくるので、覚えておくかここで一括置換してしまいましょう。
置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

size.Width、size.Height への書き換え

次のエラーは

double ratio = (double)size.width / size.height;
width が見つからないというエラーです。Java の public データメンバーは C# ではプロパティになります。public データメンバーとプロパティは利用する側のコードは同じですが、C# の流儀では、頭が大文字になります。
double ratio = (double)size.Width / size.height;
同様に同じ文の size.heightsize.Height に変更します。

これら書き換えは何度か出てくるので、覚えておくかここで一括置換してしまいましょう。
置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

Math.Abs への書き換え

次のエラーは

if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
`abs が見つからないというエラーです。Java のメソッド名の流儀は頭を小文字で始めますが、C# では頭が大文字で始まります。
if (Math.Abs(ratio - targetRatio) > ASPECT_TOLERANCE)
ここで、Math クラスは Java の標準ライブラリのクラスですが、今回書使用しているのは C# の標準ライブラリの Math クラスです。
このように基本的なクラスには Java と C# で近いものがあることが珍しくありません。

この書き換えは何度か出てくるので、覚えておくかここで一括置換してしまいましょう。
Math.absMath.Abs と置換します。
置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

previewSizes.Contains への書き換え

次のエラーは

if (Math.Abs(size.Height - targetHeight) < minDiff && previewSizes.contains(size))
contains が見つからないというエラーです。 Java のメソッド名の流儀は頭を小文字で始めますが、C# では頭が大文字で始まります。
if (Math.Abs(size.Height - targetHeight) < minDiff && previewSizes.contains(size))

この書き換えは何度か出てくるので、覚えておくかここで一括置換してしまいましょう。
previewSizes.containspreviewSizes.Contains と置換します。
置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

Camera.Open() への書き換え

次のエラーは

return Camera.open();
open が見つからないというエラーです。Java のメソッド名の流儀は頭を小文字で始めますが、C# では頭が大文字で始まります。
return Camera.Open();

この書き換えは何度か出てくるので、覚えておくかここで一括置換してしまいましょう。
Camera.openCamera.Open と置換します。
置換は Mac の場合は option+command+F、Windows の場合は ctrl+H で行えます。

(int)Camera.CameraInfo.CameraFacingBack への書き換え

次のエラーは

return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
CAMERA_FACING_BACK が見つからないというエラーです。これは Java の定数ですが、C# では列挙体になります。
Xamarin(C#) ではこのような種類を表す定数はなるべく列挙体にする方針になっています。
また、列挙体のメンバーも定数なので、C# では大文字始まりの大文字区切りのスタイルになります。
return getDefaultCamera(Camera.CameraInfo.CameraFacingBack);
ただし、ここでこの書き換えを行うと、またこの一文がエラーになります。
getDefaultCamera メソッドは今回作成している CameraHelper クラスで宣言しているメソッドで、引数が int になっています。
正しく書き換えるのであればメソッドの定義を変えるべきですが、ここでは手数を少なく書き換えのミスを減らすために、列挙体メンバーを int にキャストします。
return getDefaultCamera((int)Camera.CameraInfo.CameraFacingBack);

同様に 9 行程下の

return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_FRONT);
return getDefaultCamera((int)Camera.CameraInfo.CameraFacingFront);

属性への書き換え

次は

@TargetApi(Build.VERSION_CODES.GINGERBREAD)
のエラーです。これは Java のアノテーションの宣言ですが、C# では属性になります。次のように書き換えます。
[Android.Annotation.TargetApi(Value = (int)Android.OS.Build.VERSION_CODES.Gingerbread)]

Camera.NumberOfCameras への書き換え

次は

int mNumberOfCameras = Camera.getNumberOfCameras();
のエラーです。Xamarin(C#) では決まった値を取得するメソッドはプロパティになっています。
また、プロパティは大文字で始まります。
int mNumberOfCameras = Camera.NumberOfCameras;

Camera.GetCameraInfo への書き換え

次は

Camera.getCameraInfo(i, cameraInfo);
のエラーです。C# ではメソッド名は大文字始まりになります。
Camera.GetCameraInfo(i, cameraInfo);

cameraInfo.facing への書き換え

次は

if (cameraInfo.facing == position)
のエラーです。C# 列挙体になっています。また、変数 position を int で宣言しているのでキャストします。
本来であれば変数の型を変更するべきですが、ここでは手数を少なく書き換えのミスを減らすために、列挙体メンバーを int にキャストします。
if ((int)cameraInfo.Facing == position)

using Java.IO の追加

次は

public static File getOutputMediaFile(int type)
File が見つからないというエラーです。
ファイルの先頭に
using Java.IO;
を追加します。 using は Mac の場合は option+enter、Windows の場合は ctrl+. から自動で追加することもできます。
この時、using System.IOusing Java.IO が選択肢に出てくるので注意してください。
System.IO 名前空間は .NET(C#) のクラスライブラリーのもので、Xamarin にするならば本来こちらを使用するべきですが、ここでは手数とミスを減らすために、Java から移植されたクラスを使用します。

Environment の using エイリアスの追加

次は

if (!Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED))
getExternalStorageState が見つからないというエラーです。これは、C# と Android API に同名の Environment クラスが存在し、現在は C# のクラスを参照しています。これを Android API のクラスを参照するように、using エイリアスという機能で EnvironmentAndroid.OS.Environment の別名として定義します。
ファイルの先頭に
using Environment = Android.OS.Environment;
を追加します。また、C# では getter メソッドはプロパティになるので、get と () を削除します。 また、Environment.MEDIA_MOUNTED 定数も先頭大文字・大文字区切りに変更します。
if (!Environment.ExternalStorageState.equalsIgnoreCase(Environment.MediaMounted))
これでも、まだ equalsIgnoreCase が見つからないというエラーになっています。
equalsIgnoreCase メソッドに相当する機能は、C# ではオプション引数付きの Equals メソッドになります。
if (!Environment.ExternalStorageState.Equals(Environment.MediaMounted, StringComparison.OrdinalIgnoreCase))

Environment.GetExternalStoragePublicDirectory への書き換え

次は

File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
    Environment.DIRECTORY_PICTURES), "CameraSample");
getExternalStoragePublicDirectory が見つからないというエラーです。メソッド名を大文字始まりに変更します。
また、Environment.DIRECTORY_PICTURES 定数も先頭大文字・大文字区切りに変更します。
File mediaStorageDir = new File(Environment.GetExternalStoragePublicDirectory(
    Environment.DirectoryPictures), "CameraSample");

mediaStorageDir.Exists()、mediaStorageDir.Mkdirs() への書き換え

次は

if (!mediaStorageDir.exists())
{
    if (!mediaStorageDir.mkdirs())
exists が見つからないというエラーです。メソッド名を大文字始まりに変更します。
mkdirs も同じように変更します。
if (!mediaStorageDir.Exists())
{
    if (!mediaStorageDir.Mkdirs())

using Android.Util の追加

次は

Log.d("CameraSample", "failed to create directory");
Log が見つからないというエラーです。
ファイルの先頭に
using Android.Util;
を追加します。 using は Mac の場合は option+enter、Windows の場合は ctrl+. から自動で追加することもできます。
また、d メソッドは Xamarin では Debug になっているので変更します。
Log.Debug("CameraSample", "failed to create directory");

using Java.Text、using Java.Util の追加

次は

String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
SimpleDateFormatLocaleDate が見つからないというエラーです。
ファイルの先頭に
using Java.Text;
using Java.Util;
を追加します。 using は Mac の場合は option+enter、Windows の場合は ctrl+. から自動で追加することもできます。
本来であればここは C# の DateTime 構造体など変更するべきですが、ここでは手数を少なく書き換えのミスを減らすために、Java から移植されたクラスを使用します。
また、US 定数は Xamarin では Usformat メソッドは先頭が大文字になっているので変更します。
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.Us).Format(new Date());

mediaStorageDir.Path、File.Separator への書き換え

次は

mediaFile = new File(mediaStorageDir.getPath() + File.separator +
getPathseparator が見つからないというエラーです。これまでのように、プロパティへの変更と、先頭大文字への変更を行います。
mediaFile = new File(mediaStorageDir.Path + File.Separator +

同じようにすぐ下の

mediaFile = new File(mediaStorageDir.getPath() + File.separator +
mediaFile = new File(mediaStorageDir.Path + File.Separator +
と変更します。

■ CameraHelper クラスの完成

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

■ 次回予定

ここまでで一つ目のクラスの移植は完了しました。
次回以降でさらに別のクラスの移植を行って行きます。

Android アプリを Xamarin.Android に移植する (2)

前回 Android アプリを Xamarin.Android に移植する (1) で UI を移植しました。

前回出来上がった画面

アプリが起動している間の画面は出来上がりましたが、ホームやマルチタスクボタンを押すした際のアイコンが Xamarin のデフォルトになっています。
今回はまずはこのアイコンを設定します。

■ アプリケーションのアイコンの設定

前回、UI を含みリソースは Android のプロジェクトから Xamarin.Android へ移植しています。
アイコンはこのリソースの中に含まれている画像ファイルです。
今はまだ、ファイルを追加しただけでアイコンとして設定されていないのでデフォルトの画像になってしまっています。

アプリケーションのアイコンはアプリケーションのプロパティから設定できますが、今回は設定のファイルを直接編集していきます。
GUI は分かりやすいですが、IDE によって変わってしまうという面があります。
今回編集する設定ファイルは、内容が簡単である点とサンプルアプリというお手本があるため直接編集してしまう方が間違いがないです。
お手本がない場合は、GUI から設定すると良いでしょう。

設定ファイルの設定

編集する設定ファイルは、Properties フォルダー内の AndroidManifest.xml ファイルです。
・ソリューションエクスプローラー上で AndroidManifest.xml をダブルクリックします。
・エディターで XML ファイルが開くので次のように編集します。
<application> タグを中に android:icon 属性がある場合は次のように編集します。ない場合は書き足します。

<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name">
</application>
これで、Resources フォルダー内の drawable フォルダー内の ic_launcher.png ファイルがアイコンとして使用されます。

アイコン確認

これだけで、アイコンの移植が完了しました。
F5 キーで実行し、画面を確認しましょう。
アプリケーションのアイコンがビデオカメラ?のような画像になっていれば成功です。

■ 次回予定

ここまでで UI の移植は完了しました。
次回以降でロジックの移植を行って行きます。

Android アプリを Xamarin.Android に移植する (1)

Android アプリを Xamarin.Android に書き換えるシリーズ(1) です。
※Android サンプルアプリ MediaRecorder ( https://github.com/googlesamples/android-MediaRecorder )

■ プロジェクトの新規作成

Xamarin.Android のアプリを作るので最初はプロジェクト(ソリューション)の新規作成からです。

Mac の場合

・Visual Studio for Mac を起動します。
・メニューの ファイル > 新しいソリューション を選択します。
・[新しいプロジェクト用のテンプレートを選択する] で Android > アプリ > (全般) の Android アプリ (C#) を選択し、[次へ] をクリックします。
・[アプリ名] に適当な名前を入力し、[次へ] をクリックします。
・[プロジェクト名] [ソリューション名] は特に変える必要はありません。[場所] は好みの場所があれば変更してください。
・[作成] をクリックします。

Windows の場合

・Visual Studio (2017) を起動します。
・メニューの ファイル > 新規作成 > プロジェクト を選択します。
・[新しいプロジェクト] で Visual C# > Android > からのアプリ(Android) を選択します。
・[名前] に適当なアプリケーション名を入力します。[場所] は好みの場所があれば変更してください。
・[OK] をクリックします。

■ UI の移植

Xamarin はネイティブの UI 定義が使えることが特徴です。
ネイティブの UI 定義が使えるとは、Android で言えば、Android(Java) のプロジェクトで res フォルダー内にある .axml や .xml、画像ファイル などをそのまま使うということです。

Mac の場合

res フォルダーは Xamrin では Resources フォルダになります。
・Android(Java) プロジェクトの res フォルダーの中身(フォルダー、ファイル)を、Xamarin プロジェクトの Resources フォルダーにコピーします。
・Visual Studio for Mac の [ソリューション] の Resources を右クリック(二本指タップ)します。
追加 > フォルダーからファイルを追加 を選択します。
Resources フォルダーを選択し、[開く] をクリックします。
・[Resources から追加するファイルを選ぶ] で [すべて含める] をクリックします。
・全てチェック ON になっていることを確認し、[OK] をクリックします。

Windows の場合

res フォルダーは Xamrin では Resources フォルダーになります。
・Android(Java) プロジェクトの res フォルダーの中身(フォルダー、ファイル)を、Xamarin プロジェクトの Resources フォルダーにコピーします。
・(Win+e などで)エクスプローラーを開き、Resources フォルダー内のフォルダーをすべて選択しドラッグ、Visual Studio のソリューションエクスプローラー上の Resources フォルダーにドロップします。
・タイトル「ファイルを新しい場所に移動しますか?」メッセージ「1つ以上のファイルを "XXXXXXX\Resources" に移動します。この操作は長時間かかることがあります。」のダイアログで [OK] をクリックします。

共通

・「ファイルが見つかりません」というエラーが出て、Resources\layout\Main.axml 無いとされる場合、ソリューションエクスプローラーから Main.axml ファイルを削除します。

■ UI の適用

Xamarin はネイティブの UI 定義が使えることが特徴です。
axml などのファイルを使えるばかりでなく、Activity も Activity クラスで定義します。
また、先程コピーしたリソースファイルも、自動生成される Resource クラスに定数として定義されます。
(Android(Java) では R でしたが Xamarin では Resource になります)
また、Activity クラスのメソッドなども基本、Java と同じものが定義されており、同じ使い方になります。
ただし、メソッド名などは C# の流儀に変わっているので少し名前が変更されています。
例えば Java では onCreate メソッドでしたが、C# では OnCreate メソッドになっています。

Activity の書き換え

・ソリューションエクスプローラー上で、MainActivisy.cs をバブルクリックして開きます。
・7 行目あたりに

[Activity(Label = "XamMediaRecorder", MainLauncher = true, ...

という記述があります。
これが Activity に対する設定になります。Android では AndroidManifest.xml 内に記述していましたが、Xamarin では Activity クラスの属性として設定します。
属性とは、Java で言うところのアノテーションに当たる機能です。
この属性を先程追加したリソースを参照するよう、次のように変更します。

[Activity(Label = "@string/app_name", MainLauncher = true, ScreenOrientation = ScreenOrientation.Landscape)]

・ここでビルドを行うと「ScreenOrientation が見つからない」という様なエラーになります。
ScreenOrientation クラスの存在する名前空間(Java のパッケージに当たる機能)を using (Java でいう import に当たる機能) で宣言します。
ファイルの先頭の using の塊に次を追加します。

using Android.Content.PM;

この追加はエラーの発生している View にカーソルを合わせて、Mac の場合は alt+enter > クイック修正、Windows の場合は ctrl+. から自動で追加することもできます。
・また、Export 属性を使用する場合、dll の参照を追加する必要があります。
Mac の場合は、ソリューション上の [参照] を右クリック(二本指タップ) > [参照の編集] で開くダイアログで。
Windows の場合は、[参照] を右クリック > [参照の追加] で開くダイアログで [アセンブリ] を選択し。
Mono.Android.Export にチェックをつけて [OK] をクリックします。
・画面レイアウトの定義も、先程追加したリソースを参照するよう、次のように変更します。
変更箇所は15〜17行目あたりにあります。
変更前

SetContentView(Resource.Layout.Main);

変更後

SetContentView(Resource.Layout.sample_main);

・Mac の場合、OnCreate メソッド内に次のようなコードが書かれています。

Button button = FindViewById<Button(Resource.Id.button_capture);

button.Click += delegate {button.Text = $"{count++} clicks!";

このコードは、不要なので削除します。

イベントハンドラメソッドの作成

サンプルの画面定義にはボタンクリックのイベントが設定されています。
このイベントのハンドラとなるメソッドを MainActivity に作成します。
・MainActivity クラスに次のメソッド定義を追加します。
Java.Interop.Export 属性で定義に設定されているメソッド名を設定することがポイントです。

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

・ここでビルドを行うと「View が見つからない」という様なエラーになります。
View クラスの存在する名前空間(Java のパッケージに当たる機能)を using (Java でいう import に当たる機能) で宣言します。
ファイルの先頭の using の塊に次を追加します。

using Android.Views;

この追加はエラーの発生している View にカーソルを合わせて、Mac の場合は alt+enter > クイック修正、Windows の場合は ctrl+. から自動で追加することもできます。
・また、Export 属性を使用する場合、dll の参照を追加する必要があります。
Mac の場合は、ソリューション上の [参照] を右クリック(二本指タップ) > [参照の編集] で開くダイアログで。
Windows の場合は、[参照] を右クリック > [参照の追加] で開くダイアログで [アセンブリ] を選択し。
Mono.Android.Export にチェックをつけて [OK] をクリックします。

ここまでで、MainActivity.cs は次の様になります。

using Android.App;
using Android.Widget;
using Android.OS;
using Android.Content.PM;
using Android.Views;

namespace XamMediaRecorder
{
    [Activity(Label = "@string/app_name", MainLauncher = true, ScreenOrientation = ScreenOrientation.Landscape)]
    public class MainActivity : Activity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            SetContentView(Resource.Layout.sample_main);
        }

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

■ UI の確認

これだけで、UI の移植が完了しました。
F5 キーで実行し画面を確認しましょう。
次の様な画面が表示されていれば成功です。

■ 次回予定

ここまでで UI の移植は完了しました。
次回以降でロジックの移植を行って行きます。

Android アプリ開発の MediaRecorder サンプルを Xamarin.Android に書き換えてみました

Xamarin がどれほど AndroidAPI をそのまま使えるか試すために、アプリ開発のサンプルを Xamarin.Android に書き換えてみました。

Android のサンプル

Android のカメラ、マイクを使って動画を撮る単純なアプリです。
 

Xamarin.Android に書き換えたプロジェクト

■ 書き換えの概要

Xamarin.Android の空のプロジェクトを作る
Android のサンプルからソースをまるごと持ってくる
C# (Xamarin.Android) に書き換える
という簡単な作業です。

■ 書き換えポイント

ロジック的には全く書き換えるポイントはありません。
if 文の一つすら追加も削除もなく書き換えることができました。

書き換えた点は概ね次の点です。
Android(Java) の世界ではメソッドの頭は小文字ですが、Xamarin(C#) の世界では大文字なので書き換えます。
Android(Java) では getter メソッドだったものが Xamarin(C#) ではプロパティになっていることがあるので書き換えます。
・一部の Java のクラスを .NET のライブラリのクラスに書き換えます。
・その他、言語仕様の差などを少々書き換えます。

■ 注意点

・最低限の書き換えで済ますために、変更しなくて済むメソッド名は変更していません(小文字始まりになっています)。
・最低限の書き換えで済ますために、型エイリアスを使っています。
・元のコードの雰囲気を残すために型推論を使っていません。

■ 書き換えの詳細

後日書きます。

Android の Sample が NullPointerException で終了する (part2)

次の Android のアプリ開発の Sample を実行すると、みんな大好き NullPointerException が発生することがあります。
・MediaRecorder
https://developer.android.com/samples/index.html

Android のエミュレーターでは、カメラの有無も設定できます。
カメラが無い設定でエミュレーターを作っている場合、カメラ機能を使おうとするとエラーになります。
このカメラの有無の確認が実装されていないため、存在しないカメラを使おうとして NullPointerException が発生します。

確認方法

・Android Virtual Device Manager(AVD マネージャー)を開き、使用しているデバイスの Edit this AVD をクリック。

・左下の方にある Show Advanced Setting ボタンをクリック。

Camera が None になっていたらカメラがありません。

対策

AVD を新しく作ってください。

Android の Sample が NullPointerException で終了する

次の Android のアプリ開発の Sample を実行すると、みんな大好き NullPointerException が発生します。

・MediaRecorder
https://developer.android.com/samples/index.html から検索

正確には、発生する場合がある、でしょうか。

Android ではアプリがカメラなどを使う際、ユーザーが許可をする必要があります。
ユーザーの許可はインストール時、不許可になっています。 このユーザーによる許可を確認するチェックが実装されていないため、不許可の機能を使おうとして NullPointerException が発生します。

対策

端末またはエミュレーター上で MediaRecorder アプリに対して機能の許可をします。

・設定から

・アプリの設定

・MediaRecorder

・Permissions

・全てを許可(ON)します