PreferenceFragmentでクリックイベントを登録したい

PreferenceFragmentでクリックのみの設定項目を追加したい。ということで調べてみた。

以下ソース。

import android.app.Activity;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceFragment;
import android.widget.Toast;

public class Preferences extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getFragmentManager()
        	.beginTransaction()
        	.replace(android.R.id.content, new PrefsFragment())
        	.commit();
    }

    public static class PrefsFragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.preferences);

            Preference preference = (Preference) findPreference("preference_key");
            preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                public boolean onPreferenceClick(Preference preference) {
                	Toast.makeText(getActivity(), "Preferenceクリック", Toast.LENGTH_SHORT).show();
                	return true;
                }
            });
        }
    }
}

以下レイアウトファイル

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
	xmlns:android="http://schemas.android.com/apk/res/android">
	
	<PreferenceCategory android:title="設定">
	    
	    <Preference
    		android:key="preference_key"
    		android:title="Prefereceクリックのみ"
    		android:summary="Preferenceクリックで何かを処理したい" />
	    
	</PreferenceCategory>
	
</PreferenceScreen>

実行するとこんな感じ
device-2014-02-23-2100212

戻るボタンを制御したい

戻るボタンを制御したい。ということで調べてみた。いろいろやり方はあるみたい。

onKeyDownイベント

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
	if (keyCode == KeyEvent.KEYCODE_BACK){
		return;
	}
	
	return super.onKeyDown(keyCode, event);
}

dispatchKeyEventイベント

@Override
public boolean dispatchKeyEvent(KeyEvent e) {
   if (e.getAction() == KeyEvent.ACTION_DOWN) {
       return;
   }

   return super.dispatchKeyEvent(e);
}

onBackPressedイベント

@Override
public void onBackPressed(){
	super.onBackPressed();
}

onBackPressedを使用した方が簡単そうなので、これを使用することにする。

PreferenceFragmentについて

設定画面を作成したい。ということで、PreferenceFragmentについて調べてみた。PreferenceFragmentを作成すれば、設定値を保存してくれる画面が作成できる。いろいろ種類があったけど、よく使いそうな5つについて調べた。調べたのは以下の5種類

  • チェックボックス
  • スイッチ
  • エディットテキスト
  • リスト
  • マルチセレクト

チェックボックス

「true」、「false」の値を保持できる。
preferencefragment_checkbox
以下レイアウトファイル

<CheckBoxPreference
    android:key="checkbox_preference"
    android:title="チェックボックス"
    android:summary="チェックボックスサマリー" />

以下値取得のコード

SharedPreferences spf = PreferenceManager.getDefaultSharedPreferences(this);
boolean checkboxValue = spf.getBoolean("checkbox_preference", false);

スイッチ

チェックボックスと同様に「true」、「false」の値を保持できる。
preferencefragment_switch

以下レイアウトファイル

<SwitchPreference
    android:key="switch_preference"
    android:title="スイッチ"
    android:summary="スイッチサマリー" />

以下値取得のコード

SharedPreferences spf = PreferenceManager.getDefaultSharedPreferences(this);
boolean switchValue = spf.getBoolean("switch_preference", false);

エディットテキスト

エディットテキストはクリックするとエディットテキストのダイアログがでてくる。
preferencefragment_edittext1preferencefragment_edittext2

以下レイアウトファイル

<EditTextPreference
	android:key="edittext_preference"
	android:title="エディットテキスト"
	android:summary="エディットテキスト"
	android:dialogTitle="タイトル" />

以下値取得のコード

SharedPreferences spf = PreferenceManager.getDefaultSharedPreferences(this);
String edittextValue = spf.getString("edittext_preference", "");

リスト

リストはクリックするとリストダイアログが出てくる。リストにセットする表示項目と値はarrays.xmlに設定している。
preferencefragment_listdevice-2014-02-11-115412

以下レイアウトファイル

<ListPreference
	android:key="list_preference"
	android:title="リスト"
	android:summary="リストサマリー"
	android:entries="@array/entries_list_preference"
	android:entryValues="@array/entryvalues_list_preference"
	android:dialogTitle="タイトル" />

以下値取得のコード

SharedPreferences spf = PreferenceManager.getDefaultSharedPreferences(this);
String listValue = spf.getString("list_preference", "0");

マルチセレクト

マルチセレクトはクリックするとチェックボックス付きのリストが出てくる。リストにセットする表示項目と値はarrays.xmlに設定している。
preferencefragment_multiselectdevice-2014-02-11-115926

以下レイアウトファイル

<MultiSelectListPreference
    android:key="multi_preference"
    android:title="マルチセレクト"
    android:summary="マルチセレクトサマリー"
    android:entries="@array/entries_list_preference"
    android:entryValues="@array/entryvalues_list_preference"
    android:dialogTitle="タイトル" />

以下値取得のコード

SharedPreferences spf = PreferenceManager.getDefaultSharedPreferences(this);
Set<String> multiValues = spf.getStringSet("multi_preference", null);
String multiValue = "";
if (multiValues != null) {
	String[] multiValueArray = multiValues.toArray(new String[] {});
	for (int i = 0; i < multiValueArray.length; i++) {
		multiValue += multiValueArray[i];
	}
}

サンプルプロジェクト

今回作成したサンプルプロジェクト、メイン画面はPrerefeceFragmentで設定されている値を表示、設定画面はPreferenceFragmentを使用して作成。以下作成したファイル。

  • MainActivity.java
  • Preferences.java
  • activity_main.xml
  • preferences.xml(res/xmlフォルダに作成)
  • arrays.xml(valuesフォルダに作成)

MainActivity.java

import java.util.Set;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// 設定画面の表示
		Button settings = (Button)this.findViewById(R.id.settings);
		settings.setOnClickListener(new OnClickListener() {
        	public void onClick(View v) {
        		Intent intent = new Intent(MainActivity.this, Preferences.class);
        		startActivityForResult(intent, 0);
        	}
        });

		setPreferenceValues();
	}

	private void setPreferenceValues() {
		SharedPreferences spf = PreferenceManager.getDefaultSharedPreferences(this);

		// チェックボックス
		TextView tvCheckBoxValue = (TextView)this.findViewById(R.id.checkbox_value);
		boolean checkboxValue = spf.getBoolean("checkbox_preference", false);
		tvCheckBoxValue.setText(String.valueOf(checkboxValue));

		// スイッチ
		TextView tvSwitchValue = (TextView)this.findViewById(R.id.switch_value);
		boolean switchValue = spf.getBoolean("switch_preference", false);
		tvSwitchValue.setText(String.valueOf(switchValue));

		// エディットテキスト
		TextView tvEditTextValue = (TextView)this.findViewById(R.id.edittext_value);
		String edittextValue = spf.getString("edittext_preference", "");
		tvEditTextValue.setText(edittextValue);

		// リスト
		TextView tvListValue = (TextView)this.findViewById(R.id.list_value);
		String listValue = spf.getString("list_preference", "0");
		tvListValue.setText(listValue);

		// マルチセレクト
		TextView tvMultiValue = (TextView)this.findViewById(R.id.multi_value);
		Set<String> multiValues = spf.getStringSet("multi_preference", null);
		String multiValue = "";
		if (multiValues != null) {
			String[] multiValueArray = multiValues.toArray(new String[] {});
			for (int i = 0; i < multiValueArray.length; i++) {
				multiValue += multiValueArray[i];
			}
		}
		tvMultiValue.setText(multiValue);
	}

	/*
	 * Activityの戻り処理
	 */
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		switch (requestCode) {
		case 0:
			setPreferenceValues();
			break;
		}
	}
}

Preferences.java

import android.app.Activity;
import android.os.Bundle;
import android.preference.PreferenceFragment;

public class Preferences extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getFragmentManager().beginTransaction().replace(android.R.id.content, new PrefsFragment()).commit();
    }

    public static class PrefsFragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.preferences);
        }
    }
}

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="10dp"
   	android:orientation="vertical"
    tools:context=".MainActivity" >

	<Button
        android:id="@+id/settings"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="PreferenceFragment画面" />

	<TextView
	    android:id="@+id/textView5"
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    android:layout_marginTop="20dp"
	    android:text="インライン"
	    android:textAppearance="?android:attr/textAppearanceMedium" />

	<TableLayout
		android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp" >

            <TextView
		        android:id="@+id/textView1"
		        android:layout_width="wrap_content"
		        android:layout_height="wrap_content"
		        android:text="チェックボックスの値" />

		    <TextView
		        android:id="@+id/checkbox_value"
		        android:layout_width="0dp"
		        android:layout_height="wrap_content"
		        android:layout_weight="1"
		        android:text=""
		        android:gravity="right" />"

        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp" >

            <TextView
		        android:id="@+id/textView3"
		        android:layout_width="wrap_content"
		        android:layout_height="wrap_content"
		        android:text="スイッチの値" />

		    <TextView
		        android:id="@+id/switch_value"
		        android:layout_width="0dp"
		        android:layout_height="wrap_content"
		        android:layout_weight="1"
		        android:gravity="right"
		        android:text="" />

        </TableRow>

    </TableLayout>

	<TextView
	    android:id="@+id/textView5"
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    android:layout_marginTop="10dp"
	    android:text="ダイアログベース"
	    android:textAppearance="?android:attr/textAppearanceMedium" />

	<TableLayout
		android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp" >

            <TextView
		        android:id="@+id/textView1"
		        android:layout_width="wrap_content"
		        android:layout_height="wrap_content"
		        android:text="エディットテキストの値" />

		    <TextView
		        android:id="@+id/edittext_value"
		        android:layout_width="0dp"
		        android:layout_height="wrap_content"
		        android:layout_weight="1"
		        android:text=""
		        android:gravity="right" />

        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp" >

            <TextView
		        android:id="@+id/textView3"
		        android:layout_width="wrap_content"
		        android:layout_height="wrap_content"
		        android:text="リストの値" />

		    <TextView
		        android:id="@+id/list_value"
		        android:layout_width="0dp"
		        android:layout_height="wrap_content"
		        android:layout_weight="1"
		        android:gravity="right"
		        android:text="" />

        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp" >

            <TextView
		        android:id="@+id/textView3"
		        android:layout_width="wrap_content"
		        android:layout_height="wrap_content"
		        android:text="マルチセレクトの値" />

		    <TextView
		        android:id="@+id/multi_value"
		        android:layout_width="0dp"
		        android:layout_height="wrap_content"
		        android:layout_weight="1"
		        android:gravity="right"
		        android:text="" />

        </TableRow>

    </TableLayout>
</LinearLayout>

preferences.xml

<PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android">

    <PreferenceCategory
            android:title="インラインプレファレンス">

        <CheckBoxPreference
                android:key="checkbox_preference"
                android:title="チェックボックス"
                android:summary="チェックボックスサマリー" />

        <SwitchPreference
			    android:key="switch_preference"
			    android:title="スイッチ"
			    android:summary="スイッチサマリー" />

    </PreferenceCategory>

    <PreferenceCategory
            android:title="ダイアログベースプレファレンス">

        <EditTextPreference
                android:key="edittext_preference"
                android:title="エディットテキスト"
                android:summary="エディットテキスト"
                android:dialogTitle="タイトル" />

        <ListPreference
                android:key="list_preference"
                android:title="リスト"
                android:summary="リストサマリー"
                android:entries="@array/entries_list_preference"
                android:entryValues="@array/entryvalues_list_preference"
                android:dialogTitle="タイトル" />

        <MultiSelectListPreference
			    android:key="multi_preference"
			    android:title="マルチセレクト"
			    android:summary="マルチセレクトサマリー"
			    android:entries="@array/entries_list_preference"
			    android:entryValues="@array/entryvalues_list_preference"
			    android:dialogTitle="タイトル" />

    </PreferenceCategory>

</PreferenceScreen>

arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

	<string-array name="entries_list_preference">
		<item>アイテム1</item>
		<item>アイテム2</item>
		<item>アイテム3</item>
		<item>アイテム4</item>
		<item>アイテム5</item>
	</string-array>

	<string-array name="entryvalues_list_preference">
		<item>1</item>
		<item>2</item>
		<item>3</item>
		<item>4</item>
		<item>5</item>
	</string-array>

</resources>

実行

こんな感じ。最初の画面で設定値を表示している。
device-2014-02-11-121146device-2014-02-11-111235

作成したサンプルプロジェクト
PreferenceFragmentSample

apkファイル作成時に「is not translated in ja」のエラー

apkファイル作成時に「is not translated in ja」のエラーがでた。valuesフォルダのstring.xmlを各国ように作れ?みたいなエラーだった。Lintのエラーチェックを緩めればいいらしい。
設定方法は2種類あって、Eclipseの「Windows」-「Preferences」からやるのと、プロジェクトの「Properties」からやるやり方。
おそらく全者はEclipseにあるプロジェクト全部対象、後者は指定プロジェクトのみなんだと思う。

メニューの「Window」から「Preferences」を開き、「Android」-「Lint Error Checking」を選択
image004
「MissingTranslation」で検索し、Severityを「Warning」にしておく。

プロジェクトのプロパティからやる場合、対象プロジェクトを右クリックして「Property」を開く、
image009
「Android Lint Preferences」を選択し、以下上記と同じ。

これでapkファイルが作成できた。

アプリにAdmobの広告を組み込む

作成してたアプリが完成して広告を組み込んだ。前にもやってことあるけど、組み込み方法が変わってたので手順をメモっておこうと思う。いくつかはまってしまった箇所があって結構時間がかかってしまった。Googleの手順をこちら

1.広告ユニットIDの取得

まずはAdmobで「広告ユニット ID」を取得しておく。

2.google-play-services_libの追加

場所はここ
[android-sdk-windows/extras/google/google_play_services/libproject/google-play-services_lib]
これをEclipseにプロジェクトをインポートする。対象プロジェクトに追加するので、対象プロジェクトを右クリックで、プロパティを開き「google-play-services_lib」を追加。

3.AndroidManifest.xmlの編集

3つ追加項目がある。
以下のタグをapplicationタグの中に追加する。

<meta-data
    android:name="com.google.android.gms.version"
    android:value="@integer/google_play_services_version"/>

以下のタグをapplicationタグの中に追加する。

<activity
    android:name="com.google.android.gms.ads.AdActivity"
    android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>

以下のパーミッションを追加する。

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

設定したAndroidManifest.xmlファイル

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.company"
          android:versionCode="1" android:versionName="1.0">
  <application android:icon="@drawable/icon" android:label="@string/app_name"
               android:debuggable="true">
    <meta-data android:name="com.google.android.gms.version"
               android:value="@integer/google_play_services_version"/>
    <activity android:label="@string/app_name" android:name="BannerExample">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>
    <activity android:name="com.google.android.gms.ads.AdActivity"
              android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>
  </application>
  <uses-permission android:name="android.permission.INTERNET"/>
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
</manifest>

4.広告の組み込み(javaファイルのみで組み込む場合)

xmlレイアウトを使用せず。組み込む場合はこっち。5は実行しなくてよい。

メインのレイアウトにidを振る。

android:id="@+id/mainLayout"

以下のコードを組み込むActivityのjavaソースに追加。

adView = new AdView(this);
adView.setAdUnitId("Admobで取得した広告ユニットID");
adView.setAdSize(AdSize.BANNER);

LinearLayout layout = (LinearLayout)findViewById(R.id.mainLayout);
layout.addView(adView);

AdRequest adRequest = new AdRequest.Builder()
.addTestDevice(AdRequest.DEVICE_ID_EMULATOR)		// エミュレータ
.addTestDevice("テストデバイスID")	// テストデバイス
.build();
adView.loadAd(adRequest);

5.広告の組み込み(xmlレイアウトファイルを使用する場合)

レイアウトファイルに組み込む場合はこっち。4は実行しなくてよい。
広告の場所を細かく指定したい場合はこっちの方がいいかと。

広告を組み込むメインのレイアウトに以下の文を追加

xmlns:ads="http://schemas.android.com/apk/res-auto"

広告を組み込むレイアウトファイルに以下を追加

<com.google.android.gms.ads.AdView
	android:id="@+id/adView"
 	android:layout_width="wrap_content"
  	android:layout_height="wrap_content"
	ads:adUnitId="Admobで取得した広告ユニットID"
	ads:adSize="BANNER" />

作成したレイアウトファイル

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:ads="http://schemas.android.com/apk/res-auto"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
  <com.google.android.gms.ads.AdView android:id="@+id/adView"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         ads:adUnitId="MY_AD_UNIT_ID"
                         ads:adSize="BANNER"/>
</LinearLayout>

以下のコードを組み込むActivityのソースに追加。

AdView adView = (AdView)this.findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice(AdRequest.DEVICE_ID_EMULATOR)	// エミュレータ
.addTestDevice("テストデバイスID")	// テストデバイス
.build();
adView.loadAd(adRequest);

6.はまったこと

まず、「google-play-services_lib」をインポートしたところ、プロジェクトが実行できなくなった。

Unable to execute dex: GC overhead limit exceeded
Conversion to Dalvik format failed: Unable to execute dex: GC overhead limit exceeded

ここに解決方法が書いてあった。「eclipse.ini」を編集すれば直る。

「AndroidManifest.xml」の追加のところで、以下の部分でエラーになった。

android:value="@integer/google_play_services_version"

SDKを新しくして「google-play-services_lib」インポートし直したら直った。

その他、デバイスIDは、プロジェクトを実行するとログに出てきます。tagを「Ads」に設定すると

02-01 15:43:39.628: I/Ads(8197): Use AdRequest.Builder.addTestDevice("xxxxxxxxxxxxxxxx") to get test ads on this device.

とあるので”..xxxx..”の部分がデバイスIDです。