2015年8月3日月曜日

Android JUnit 4 形式のテストにする

InstrumentationTestRunner は JUnit 3 しかサポートしていませんが、Android Testing Support Library に含まれる AndroidJUnitRunner を使うと JUnit 4-compatible なテストを実行することができます。
(JUnit 3 と JUnit 4.10 までの JUnit 4 互換)

Android 2.2 (API Level 8) 以上が必要です。

▪️ Setup dependencies { androidTestCompile 'com.android.support.test:runner:0.3' // to use JUnit 4 rules androidTestCompile 'com.android.support.test:rules:0.3' } android { defaultConfig { testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } }

▪️ Usage

- JUnit 4 で実装されたテストクラスには @RunWith(AndroidJUnit4.class) をつける
- テスト対象メソッドには @Test をつける
- setUp() を Override し、public に変更し @Before をつける
- tearDown() を Override し、public に変更し @After をつける
- 必要に応じて AndroidTestCase.setContext(InstrumentationRegistry.getContext()) や InstrumentationTestCase.injectInstrumentation(InstrumentationRegistry.getInstrumentation()) を setUp() で行う

@RunWith(AndroidJUnit4.class) public class SimpleTest extends AndroidTestCase { @Before public void setUp() throws Exception { super.setUp(); setContext(InstrumentationRegistry.getContext()); } @Test public void testSample() { assertNotNull(getContext()); } @After public void tearDown() throws Exception { super.tearDown(); } } - InstrumentationRegistryでテスト実行時の情報を取得できる
- InstrumentationRegistry.getContext() : instrumentation のパッケージContext
- InstrumentationRegistry.getTargetContext() : テスト対象のアプリケーションのContext
- InstrumentationRegistry.getInstrumentation() : 実行中の instrumentation


▪️ Test filtering

- @RequiresDevice : 実機でのみ実行(エミュレータでは実行しない)
- @SdkSuppress : 指定された API Level より低い場合実行しない、@SdkSuppress(minSdkVersion=21) など
- @SmallTest, @MediumTest, @LargeTest : テストにどのくらいかかるかのクラス分け


▪️ 参考

- http://developer.android.com/tools/testing-support-library/index.html


2015年6月23日火曜日

AppCompat を継承したテーマで EditText のデフォルトスタイルを上書きするときは android:editTextStyle ではなく editTextStyle を使う

注意: appcompat-v7:22.2.0 での話です。将来 fix される可能性もあります。

AppCompat を継承したテーマで EditText のデフォルトスタイルを上書きしようとして android:editTextStyle を使うと、5系以降しか適用されないという落とし穴があります。

結論

android: をつけずに editTextStyle で指定すると4系にも適用されます。ただし、parent が Widget.AppCompat.EditText の場合、5系でカーソルが白になります(解説参照のこと)。 <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="editTextStyle">@style/EditTextStyle</item> </style> <style name="EditTextStyle" parent="Widget.AppCompat.EditText"> <item name="android:background">#ccccff</item> </style> </resources>
背景を変えたいだけなら android:editTextBackground と editTextBackground 両方を指定するほうが、カーソルの色を維持できます。 <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="android:editTextBackground">@drawable/blue</item> <item name="editTextBackground">@drawable/blue</item> </style> <drawable name="blue">#ccccff</drawable> </resources>


解説

1. デフォルトの状態 <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> </style> </resources>


2. android:editTextStyle を指定した状態 <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="android:editTextStyle">@style/EditTextStyle</item> </style> <style name="EditTextStyle" parent="android:Widget.EditText"> <item name="android:background">#ffffff</item> </style> </resources>


3. editTextStyle を指定した状態 <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="editTextStyle">@style/EditTextStyle</item> </style> <style name="EditTextStyle" parent="android:Widget.EditText"> <item name="android:background">#ffcccc</item> </style> </resources>

EditTextStyle の parent が android:Widget.EditText なので、4.4.2 のカーソルの色が黒になってしまっています。


4. parent が Widget.AppCompat.EditText なスタイルを editTextStyle を指定した状態 <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="editTextStyle">@style/EditTextStyle</item> </style> <style name="EditTextStyle" parent="Widget.AppCompat.EditText"> <item name="android:background">#ccccff</item> </style> </resources>

今度は 5.0.0 のカーソルの色が白になった...

この白が何かというと、v12/values-v12.xml で android:textCursorDrawable に指定されている @drawable/abc_text_cursor_mtrl_alpha です。 これに accentColor で tint するのが適用されず白くなっているようです。たぶん。 <style name="Base.V12.Widget.AppCompat.EditText" parent="Base.V7.Widget.AppCompat.EditText"> <item name="android:textCursorDrawable">@drawable/abc_text_cursor_mtrl_alpha</item> </style> そこで、android:editTextBackground です。


android:editTextBackground

背景を変えるだけなら android:editTextBackground を指定するという方法もあります。 ただ、こちらも落とし穴があり、
- android:editTextBackground // 5系にしか適用されない
- editTextBackground // 4系にしか適用されない
という状態なので、両方指定する必要があります。


5. android:editTextBackground を指定した状態 <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="android:editTextBackground">@drawable/yellow</item> </style> <drawable name="yellow">#ffff00</drawable> </resources>


6. editTextBackground を指定した状態 <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="editTextBackground">@drawable/cyan</item> </style> <drawable name="cyan">#00ffff</drawable> </resources>


7. android:editTextBackground と editTextBackground を両方指定した状態 <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="android:editTextBackground">@drawable/blue</item> <item name="editTextBackground">@drawable/blue</item> </style> <drawable name="blue">#ccccff</drawable> </resources>


2015年6月14日日曜日

android.support.annotation を活用する

利用するには dependencies に追加します。 dependencies { compile "com.android.support:support-annotations:22.0.2" } 参考 上記のドキュメントに書いてありますが、例えば

@Nullable を指定している変数に対して NPE になるようなコードを書くと警告してくれます。



alt + enter で修正候補がでます。



2番目の Replace with 'd != null ?:' を選択するとこんな感じになります。



@NonNull が指定された変数に null を渡そうとすると警告してくれます。



リソースIDはどれも int なので、Drawable のリソースIDを意図しているところに別のリソースIDを渡すことができてしまいます。



そこで Drawable のリソースIDを意図しているところに @DrawableRes を付けると、別のリソースIDを渡そうとしたところがエラーになります。



@StringRes や @LayoutRes など主要なものはそろっています。なにがあるかは package summary で確認してください。

@IntDef や @StringDef では、int や String に対して取りうる値のセットが定義されたアノテーションを作ることができます。
public static final int SIZE_L = 0; public static final int SIZE_M = 1; public static final int SIZE_S = 2; @Retention(RetentionPolicy.SOURCE) @IntDef({SIZE_L, SIZE_M, SIZE_S}) public @interface Size { } @Size public abstract int getSize(); public abstract void setSize(@Size int size); public static final String FORMAT_XML = "xml"; public static final String FORMAT_JSON = "json"; @Retention(RetentionPolicy.SOURCE) @StringDef({FORMAT_XML, FORMAT_JSON}) public @interface FORMAT { } public abstract void fetchData(@FORMAT String format); @IntDef は enum だけでなく flag としても使うことができます。 public static final int SHOW_TITLE = 0x1; public static final int SHOW_SUB_TITLE = 0x2; public static final int SHOW_ICON = 0x4; @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, value = { SHOW_TITLE, SHOW_SUB_TITLE, SHOW_ICON, }) public @interface TitleOptions { } 詳しくは Creating Enumerated Annotations を見るとよいです。



22.2.0で追加されたアノテーション

22.2.0 で新しく 13個のアノテーションが追加されました。上記のドキュメント及び android.support.annotation のパッケージサマリーにはまだないですが、そのうち更新されるでしょう。
Android Studio を 1.3 にしないと警告やエラーがでてくれないので注意です。
  • BinderThread
  • CallSuper
  • CheckResult
  • ColorInt
  • FloatRange
  • IntRange
  • Keep
  • MainThread
  • RequiresPermission
  • Size
  • TransitionRes
  • UiThread
  • WorkerThread
MainThread, WorkerThread, (BinderThread, UiThread), FloatRange, IntRange, Size, CallSuper, CheckResult, ColorInt, RequiresPermission は Google I/O 2015 のセッション「What's New in Android Development Tools」で紹介されていました。



実行スレッド系
- BinderThread
- MainThread
- UiThread
- WorkerThread

メソッドやクラスの実行スレッドを明記する。クラスにつけた場合はそのクラスのメソッド全てが対象になります。
MainThread と UiThread の違いがわからない...

↓バックグラウンドで @MainThread のメソッドを呼ぶと怒られます。



↓@MainThread がついてるメソッドで @WorkerThread のメソッドを呼ぶと怒られます。





値指定系
- FloatRange
- IntRange
- Size

@FloatRange

↓@FloatRangeの範囲外の値を指定すると怒られます。



Float とついているが double にも使えます。



@IntRange

↓@IntRangeの範囲外の値を指定すると怒られます。



Int とついているが long にも使えます。



@Size

@Size は size や length の指定ができます。文字列の長さや array, collection のサイズに使います。





multiple で約数を指定することもできます。



その他
- CallSuper
- CheckResult
- ColorInt
- Keep
- RequiresPermission

@CallSuper

↓@CallSuper を指定したメソッドをOverrideしてsuperを呼ばないと怒られます。





@CheckResult

↓@CheckResult を指定したメソッドを呼び出して戻り値を利用しないと怒られます。





@ColorInt

↓@ColorInt に color リソースID を渡すと怒られます。





@Keep

@Keep はビルド時に minified されたコードから外さないでほしいことを明示するためのアノテーションです。



@RequiresPermission

@RequiresPermission は必要なパーミッションを明示するためのアノテーションです。 @RequiresPermission(Manifest.permission.NFC) public abstract void scanNfc(); ↓パーミッションが無い状態で、それを必要とするAPIを呼ぶと怒ってくれるようになりました。