2016年04月11日

ArrayAdapterのコンストラクタのResourceIDの役割

こんにちは、Androidエンジニアの外山です。

今回はAndroidのListViewなどにセットするArrayAdapterのコンストラクタで指定するResourceIDの役割について調べてみました。

ArrayAdapterのコンストラクタ

ArrayAdapterのコンストラクタは以下のようになっています。
public ArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId,
            @NonNull List<T> objects) {
        mContext = context;
        mInflater = LayoutInflater.from(context);
        mResource = mDropDownResource = resource;
        mObjects = objects;
        mFieldId = textViewResourceId;
}

ResourceIDの使用箇所

mResourceとmFieldIdはgetViewでViewインスタンスを作成する時に使用されています。

mResourceはViewのレイアウトファイルを参照する際に、mFieldIdはレイアウトファイル内のTextViewを指定する際に使用されているようです(指定しなかった場合は生成したView自身をTextViewとして扱う)。

public View getView(int position, View convertView, ViewGroup parent) {
    return createViewFromResource(mInflater, position, convertView, parent, mResource);
}

private View createViewFromResource(LayoutInflater inflater, int position, View convertView,
        ViewGroup parent, int resource) {
    View view;
    TextView text;
    if (convertView == null) {
        view = inflater.inflate(resource, parent, false);
    } else {
        view = convertView;
    }
    try {
        if (mFieldId == 0) {
            //  If no custom field is assigned, assume the whole resource is a TextView
            text = (TextView) view;
        } else {
            //  Otherwise, find the TextView field within the layout
            text = (TextView) view.findViewById(mFieldId);
        }
    } catch (ClassCastException e) {
        Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
        throw new IllegalStateException(
                "ArrayAdapter requires the resource ID to be a TextView", e);
    }
    T item = getItem(position);
    if (item instanceof CharSequence) {
        text.setText((CharSequence)item);
    } else {
        text.setText(item.toString());
    }
    return view;
}

mDropDownResourceも同じような感じになっています。



まとめ

getViewをオーバーライドしてsuperメソッドを呼ばない場合は、引数で渡したResourceIDが使われることはないので`-1`など適当な値を渡しておけば大丈夫である。

リスト内のアイテムで一つのTextViewにのみ値をセットする場合はResourceIdを指定することでサブクラスを作成せずにAdapterを作成できるようだ。


RecyclerViewの登場によって使う機会が減ってきたListViewやGridViewですが簡単なリストを作成する際には手軽に使えていいですね。

posted by Seesaa京都スタッフ at 18:53| Comment(0) | Android | このブログの読者になる | 更新情報をチェックする

2015年10月30日

[iOS] FastlaneでゴニョゴニョしてからEMLauncherにアップする

iOSアプリ開発担当の加島です。最近のリズムゲームアプリはすごいですね!(3Dアニメとか)ただ、私は現実のコインを使わないことにしています。今のところ。
さて、今回は巷で話題(となってから少し時間が経ちましたがここ最近僕の中で話題)のFastlaneを使ってみました。基本的な使い方は公式ガイドである以下をご参照ください。

iOSアプリの継続的デリバリーに便利なfastlaneのご紹介 - Qiita
http://qiita.com/gin0606/items/162d756dfda7b84e97d4

iOSアプリのリリースフロー自動化ツールfastlaneのmeetupに通訳で参加しました - Mercari Engineering
http://tech.mercari.com/entry/2015/07/13/143000

このFastlaneのビルドツールであるgymですが、直接ビルド設定を指定することができるようになっています。例えばlane内で

gym(
scheme: 'Dev',
configuration: 'Debug',
)

と指定すれば、DevSchemeのDebugConfigurationでビルドしてくれます!

以下、奮闘の記録です。

続きを読む
posted by Seesaa京都スタッフ at 00:00| Comment(0) | iOS | このブログの読者になる | 更新情報をチェックする

2015年10月23日

[Android]Robolectricを使ったテスト用ファイル読み込み

主にAndroidアプリ開発をしている外山です。

Androidのユニットテスト時にテスト用のファイルを読み込む方法について調べてみました。


事前準備

ライブラリ追加

今回はRobolectric 3.0を使用する
testCompile "org.robolectric:robolectric:3.0"

Testファイル

Testを記述するためのクラスを作成
ExampleUnitTest.java
@RunWith(CustomRobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public class ExampleUnitTest {

	// InputStreamをStringへ変換するためのメソッド
	private static String streamToString(InputStream is) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buf = new byte[2048];
        int size;
        try {
            while ((size = is.read(buf)) != -1) {
                baos.write(buf, 0, size);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return new String(baos.toByteArray());
    }
}

assetsディレクトリを使う方法

デフォルトではユニットテスト時にはtest/assetsディレクトリが使われないのでassetsディレクトリのパスを指定することでテスト用のファイルを使用するように変更します

CustomRobolectricGradleTestRunner.javaを作成しgetAppManifest内でassetsのパスを指定する

public class CustomRobolectricGradleTestRunner extends RobolectricGradleTestRunner {
    public CustomRobolectricGradleTestRunner(Class klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected AndroidManifest getAppManifest(Config config) {
        String buildVariant = (BuildConfig.FLAVOR.isEmpty() ? "" : BuildConfig.FLAVOR + "/") 
	        + BuildConfig.BUILD_TYPE;
        return new AndroidManifest(
                Fs.fileFromPath("src/main/AndroidManifest.xml"),
                Fs.fileFromPath("build/intermediates/res/" + buildVariant),
                Fs.fileFromPath("src/test/assets")
        );
    }
}

テスト

ExampleUnitTest.javaに以下を追加して読み込みができているかをチェックすると無事テストが成功しています

@Test
public void readFileFromAssets() throws IOException {
	InputStream is = RuntimeEnvironment.application.getAssets().open("assets_hello.txt");
	String text = streamToString(is);
	assertThat(text, is(equalTo("hello assets")));
}

resourcesディレクトリを使う方法

app/src/test/resources/resources_hello.txtにファイルを作成しておきます

resourcesディレクトリ内に作成したファイルから文字列を取得する場合は以下のようにします

プログラム

ExampleUnitTest.javaに以下を追加して読み込みができているかをチェックすると無事テストが成功しています
@Test 
public void readFileFromResources() {
	InputStream is = getClass().getResourceAsStream("/resources_hello.txt");
	String text = streamToString(is);
	assertThat(text, is(equalTo("hello resources")));
}

getClass().getResourceAsStream(path)を使用することでresourcesディレクトリ内のファイルへアクセスするためのInputStreamを取得することができます


まとめ

APIサーバーからのレスポンスをモックする際などコードに文字列を埋め込むのはつらいものがあるのでファイルから読み込めるというのはとても便利なのでテスト用に長い文字列を使いたい時などは使っていきたいですね。 今回はファイルから文字列を取得しましたが画像の読み込みなどももちろんできるので用途に応じて使い分けましょう。


posted by Seesaa京都スタッフ at 20:50| Comment(0) | Android | このブログの読者になる | 更新情報をチェックする