AdSheepは、新米バックエンドエンジニアの技術分野に留まらないライフハック風ブログです

Androidでリソースフォルダに配置したXMLファイルの利用方法

Android

どうも、ShouNです。
Androidのレイアウトは、XML形式で管理されています。また、共通利用する文字列をstring.xmlに設定して適宜取り出すなど、XMLファイルを使う機会が多いです。
今回はAndroidでリソースフォルダ(app/main/src/res)に配置したXMLファイルを読み込む方法や、値を取り出す実装についてです。

resからxmlを取得するには

Context.getResources().getXml()を使います。
Activity内ならContext.getResources()でOKです。リソースへのアクセスについて
getXml()の第一引数にはリソースファイル名R.<ディレクトリ>.<ファイル>を渡します。
戻り値はXmlResourceParser型になります。

// res/xml/settings.xmlを取得する例
// Activity外
XmlResourceParser parser = Context.getResources().getXml(R.xml.settings);

// Activity内
XmlResourceParser parser = Context.getResources(R.xml.settings);

また、XML内でres/のリソースを反映させたい場合、リソースの指定は@<ディレクトリ検索>/<ファイル>で行います。

// layout/activity_main.xmlに別xmlで指定したデザインxmlを反映させたい場合
<button
android:src="@drawable/button">
</button>

XMLの内容をMapに落とし込む

Xmlを読み込んでいくにはXmlPullParserクラスを使います。
XmlResourceParserクラスはXmlPullParserクラスを継承していますから、そのまま呼び出せます。
Xmlを上から順に読み込み、任意のタグ内で値を取り出します。
Xmlの読み込みはXmlPullParser.getEventType()クラスを使います。
ドキュメントを進むにはXmlPullParser.next()です。
例えば以下の寿司ネタと値段をまとめたres/xml/sushineta.xmlを読み込むとします。
タグ内の属性値を取得するにはgetAttribute****()を使いますが、値の型によって使用する関数が異なります。
例えば、真偽値で取得する場合はgetAttributeBooleanValue(),文字列型で取得する場合はgetAttributeValue()を使うなどの違いがあります。
また、タグに挟まれた値を取得するにはgetText()を使います。

<?xml version="1.0" encoding="utf-8">
<map linked="true">
    <sushi neta="maguro">108</sushi>
    <sushi neta="tamago">108</sushi>
</map>

Activity外のファイルからアクセスする前提で実装を進めます。
optionとして<map>linked要素でHashMapか否かを指定できるようにしました。

// xmlのデータを格納するMapと値格納用変数を用意
Map<String, String> map = null;
String neta = null;
String price = null:

// xmlリソースの読み込み(Activity外)
XmlResourceParser parser = context.getResources().getXml(R.xml.sushineta);
// xmlタグのイベントタイプを読み込み
int eventType = parser.getEventType();

// イベントタイプが"ドキュメントの最後"(=1)になるまで続ける
while(eventType = XmlPullParser.END_DOCUMENT){
    switch(eventType)
    // 読み込み直後のイベントタイプはここ。"ドキュメントの最初"(=0)
    case XmlPullParser.START_DOCUMENT:
        break;

    // イベントタイプが開始タグの場合。`<map>`及び`<sishi>`の位置(=2)
    case XmlPullParser.START_TAG:
        // タグの名前を取得し、処理を分岐
        if ( parser.getName().equals("map") ){
            // タグ名がmapの場合、属性"linked"の値を取得
            boolean isLinked = parser.getAttributeBooleanValue(null, "linked", false);

            // "linked"の値が真ならLinkedHashMap、偽ならHashMapを生成。(三項演算子)
            map = isLinked ? new LinkedHashMap<String, String>() : new HashMap<String, String>();
        }else if( parser.getName().equals("sushi") ){
            // タグ名がsushiの場合、属性"neta"の値を取得
            neta = parser.getAttributeValue(null, "neta");

            // "neta"がnullならドキュメントの読み込みを終了してnullを返す
            if (neta == null){
                parser.close();
                return null;
            }
        }
        break;

    // イベントタイプが終了タグの場合。`</map>`及び`</sushi>`の位置(=3)
    case XmlPullParser.END_TAG:
        // </sushi>の場合は取得した属性値とテキスト値をMapに格納
        if(parser.getName().equals("shushi")){
            map.put(neta, price);

            // 変数の初期化
            neta = null;
            price = null;
        }
        break;

    // イベントタイプがテキストの場合。`<sushi>テキスト</sushi>`の位置(=4)
    case XmlPullParser.TEXT:
        // netaの値が確定していた場合のみpriceを取得する
        if ( null != neta ){
            price = parser.getText();
        }
        break;
    }
    // ドキュメントを読み進める
    eventType = parser.next();
}
// ドキュメントを読み込み終わったらMapを返す
return map;

今回は省略していますが、この実装に別途例外処理を追加するのがベターです。
実行結果のLinkedHashMapの中身は実質以下のようになります。

map = {"maguro" = "108", "tamago" = "108"};

以上です。

ShouN
ShouN

Androidでは独自のXMLリソースにいろいろな設定を保持させることも多いかと思います。この実装を拡張して、引数でファイルや属性値に柔軟に対応させれば、開発が捗るかもしれないです。

コメント