2015年11月20日金曜日

9-patch image Validation

作成された(納品された)素材の 9-Patch PNG は、壊れていることが多い。

壊れているとビルドができません。
-----------------------------------------------------------------------------------------------------
/hogeproject/res/drawable-mdpi/btn_on.9.png
Error:No marked region found along edge.
       Found along left edge.
/hogeproject/res/drawable-xxhdpi/btn_on.9.png
Error:No marked region found along edge.
Error:Error: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/android-sdk/build-tools/21.1.2/aapt'' finished with non-zero exit value 42
:beetv:mergeDmyDebugResources FAILED
Error:Execution failed for task ':hogeproject:mergeDmyDebugResources'.

> /hogeproject/res/drawable-xxhdpi/btn_on.9.png: Error: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/Applications/android-sdk/build-tools/21.1.2/aapt'' finished with non-zero exit value 42
Information:BUILD FAILED
-----------------------------------------------------------------------------------------------------

ビルドができなくなると開発に支障をきたすので、プロジェクトに取り込む前に検査を行い、エラーがある場合は作成元へフィードバックする。

aapt コマンドで精査することができます。

-----------------------------------------------------------------------------------------------------
$ aapt
...
 aapt c[runch] [-v] -S resource-sources ... -C output-folder ...
   Do PNG preprocessing on one or several resource folders
   and store the results in the output folder.

 aapt s[ingleCrunch] [-v] -i input-file -o outputfile
   Do PNG preprocessing on a single file.

...
-----------------------------------------------------------------------------------------------------

$ aapt c -S res -C /var/tmp
Crunching PNG Files in source dir: res
To destination dir: /var/tmp
ERROR: 9-patch image res/drawable-hdpi/btn_off.9.png malformed.
       No marked region found along edge.
       Found along left edge.
ERROR: 9-patch image res/drawable-hdpi/btn_on.9.png malformed.
       No marked region found along edge.
       Found along left edge.

壊れているファイルはERRORが発生します。

このテキストを作成元へ送りつけましょう...

2015年8月20日木曜日

Androidのメソッド数が65kを超える犯人はPlay Services!!

Androidのメソッド数が65kを超える犯人はPlay Servicesであることは周知であるとおもいますが、その対応の方法

Jake Wharton神 がブログに記載してます!!!

Play Services 5.0 Is A Monolith Abomination - Jake Wharton http://jakewharton.com/play-services-is-a-monolith/

Google Play Services に導入手順が記載されてますね。

Setting Up Google Play Services  |  Google APIs for Android  |  Google Developers https://developers.google.com/android/guides/setup

以下のように全て指定してしまうと大変なメソッド数になるので、
compile 'com.google.android.gms:play-services:7.5.0'
必要な機能だけ指定するといいようです。
compile 'com.google.android.gms:play-services-cast:7.8.0'
compile 'com.google.android.gms:play-services-gcm:7.8.0'
わたしのアプリでは、CastとGCMだけにしています。

2015年7月31日金曜日

FragmentのからActivity/ParentFragmentへのEvent通知

FragmentのからActivity/ParentFragmentへのCallbackにInterfaceの定義とアタッチが面倒くさい!

  • 専用Callback
    • Activity/ParentFragmentでCallback Interface実装
    • FragmentでCallbackの登録
      • onAttachでActivityをCallback Interfaceにキャストして登録
      • getParentFragmenでCallback Interfaceにキャストして登録
        • Fragment側で親を意識しなければならない!!
    • 個々のCallback Interfaceの設計と実装
      • 複数のFragmentの数だけCallbackが必要!!
      • タイプセーフティ?
  • 汎用Callback
    • Activity/ParentFragmentでCallback Interface実装
    • FragmentでCallbackの登録
      • onAttachでActivityをCallback Interfaceにキャストして登録
      • getParentFragmenでCallback Interfaceにキャストして登録
        • Fragment側で親を意識しなければならない!!
    • Callback Interfaceの実装は一つ
      • 汎用化の為、Bundleにオブジェクト格納
        • どのFragmentからのCallbackであるか識別が必要!!
        • Bundleからキー指定してイベント通知内容を取得
  • LocalBroadcastManager
    • Activity/ParentFragmentでIntentFilterを指定してレシーバーを登録
    • FragmentからIntentをブロードキャスト
    • 汎用化の為、IntentのBundleにオブジェクト格納
      • どのFragmentからのCallbackであるか識別が必要!!
      • Bundleからキー指定してイベント通知内容を取得
    • 必ずMainスレッドメッセージの送受信
  • Event Bus (Otto)
    • Activity/ParentFragmentでBusを登録
    • FragmentでBusを登録
    • Activity/ParentFragmentでSubscribeメソッド実装
    • FragmentでProduceメソッドを実装
    • ProduceとSubscribeのインターフェースはPOJOのEventオブジェクト
    • Eventオブジェクトなのでタイプセーフティ
    • デフォルトではMainスレッド以外ではメッセージ送信(Produce)は出来ない。
      • Busインスタンス作成時にThreadEnforcer.ANYを指定すれば、他のスレッドからメッセージ送信できる。
      • しかし、Subscribeメソッドがメッセージ送信時スレッドで実行されてしまいスレッド管理が煩雑
      • runOnUiThread でMainスレッドで実行させる仕組みが必要
    • 何処のクラスからでもメッセージ送信(Produce)で来てしまうので、煩雑化する。
      • DialogFragmentのAlertDialogのOnClickCallbackからActivityに通知できる...
      • 意図しない箇所でSubscribeされる。


    android - LocalBroadcastManager vs using callbacks - Stack Overflow http://stackoverflow.com/questions/13948720/localbroadcastmanager-vs-using-callbacks

    Otto square.github.io/otto/
    Does Otto use weak references to store objects passed to register()?
    https://plus.google.com/101050435280754106754/posts/PGygiXURaTW

    EventBus - torutkの日記 http://d.hatena.ne.jp/torutk/20110407/p1

    2014年10月14日火曜日

    Android Gradle で Nativeライブラリ(.so) を含める方法

    Android Gradle Plugin 0.7.2 からサポートされてた O(≧▽≦)O

    StackOverFlowには、自前タスクを作成する回答ばかりヒットしますが、0.7.2 からサポートされてました!!

    New Build System - Android Tools Project Site http://tools.android.com/tech-docs/new-build-system
    0.7.2

    Note: 0.7.2 requires Java7. This is a mistake. Use 0.7.3 instead.
    • Fix issue with Proguard.
    • Add packagingOptions support in Library projects.
    • Solve issue with local jar when testing library projects.
    • Fix bug with variant.addJavaSourceFoldersToModel
    • Add jniLibs folder to source sets for prebuilt .so files.
    • Lint fixes:
      • fix RTL detector
      • fix HTML report to have valid HTML
    New Samples: ndkJniLib, genFolderApi2

    src/main/jniLibs が標準の配置ディレクトリです。
    変更する場合は、

    sourceSets {
        main {
            jniLibs.srcDirs = ['jniLibs']
        }
    }

    とします。


    以上、覚え書き!

    2013年12月26日木曜日

    ActionBarActivityで finalになったonMenuItemSelectedのソースを追ってみた

    ActionBarActivity#onOptionsItemSelectedのソースを追ってみた。

    android.support.v7.app.ActionBarActivity ActionBarActivityDelegate mImpl; @Override public final boolean onMenuItemSelected(int featureId, android.view.MenuItem item) { if (mImpl.onMenuItemSelected(featureId, item)) { return true; } final ActionBar ab = getSupportActionBar(); if (item.getItemId() == android.R.id.home && ab != null && (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) { return onSupportNavigateUp(); } return false; } 5行目で、ActionBarActivityDelegateのonMenuItemSelectedを呼び出している。
    ActionBarActivityDelegateを実装は幾つかのOSレベル毎に分かれているので、 その1つActionBarActivityDelegateBaseを見てみる。

    android.support.v7.app.ActionBarActivityDelegateBase @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { if (featureId == Window.FEATURE_OPTIONS_PANEL) { item = MenuWrapperFactory.createMenuItemWrapper(item); } return mActivity.superOnMenuItemSelected(featureId, item); } ActionBarActivityのsuperOnMenuItemSelectedを呼んでいる。

    android.support.v7.app.ActionBarActivity boolean superOnMenuItemSelected(int featureId, MenuItem menuItem) { return super.onMenuItemSelected(featureId, menuItem); } ActionBarActivityの親クラスFragmentActivityのonMenuItemSelectedを呼んでいる。

    android.support.v4.app.FragmentActivity /** * Dispatch context and options menu to fragments. */ @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { if (super.onMenuItemSelected(featureId, item)) { return true; } switch (featureId) { case Window.FEATURE_OPTIONS_PANEL: return mFragments.dispatchOptionsItemSelected(item); case Window.FEATURE_CONTEXT_MENU: return mFragments.dispatchContextItemSelected(item); default: return false; } } 6行目の親クラスActivityActivityのonMenuItemSelectedを呼んでいる。
    メニューが処理されなかったら、Fragmentのメニューを呼び出している。
    • Window.FEATURE_OPTIONS_PANEL - ActionBarや昔のメニューパネル
    • Window.FEATURE_CONTEXT_MENU - ロングタップで表示されるコンテキストメニュー


    android.app.Activity public boolean onMenuItemSelected(int featureId, MenuItem item) { CharSequence titleCondensed = item.getTitleCondensed(); switch (featureId) { case Window.FEATURE_OPTIONS_PANEL: // Put event logging here so it gets called even if subclass // doesn't call through to superclass's implmeentation of each // of these methods below if(titleCondensed != null) { EventLog.writeEvent(50000, 0, titleCondensed.toString()); } if (onOptionsItemSelected(item)) { return true; } if (mFragments.dispatchOptionsItemSelected(item)) { return true; } if (item.getItemId() == android.R.id.home && mActionBar != null && (mActionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) { if (mParent == null) { return onNavigateUp(); } else { return mParent.onNavigateUpFromChild(this); } } return false; case Window.FEATURE_CONTEXT_MENU: if(titleCondensed != null) { EventLog.writeEvent(50000, 1, titleCondensed.toString()); } if (onContextItemSelected(item)) { return true; } return mFragments.dispatchContextItemSelected(item); default: return false; } } メニューは、12行目でonOptionsItemSelectedを呼び出し。 コンテキストメニューは、32行目でonContextItemSelectedを呼び出し。


    リファレンスのOnMenuItemSelectedの記述
    Default implementation of onMenuItemSelected(int, MenuItem) for activities. This calls through to the new onOptionsItemSelected(MenuItem) method for the FEATURE_OPTIONS_PANEL panel, so that subclasses of Activity don't need to deal with feature codes.

    この通り、onOptionsItemSelectedをオーバーライドしてメニューの処理を実装を行なうのが正しいようだ。

    コンテキストメニューはonContextItemSelectedをオーバーライドして処理を実装を行なうのが正しいようだ。


    Honycombから、Activity#onMenuItemSelectedにはFragmentやActionBarのメニュー処理が行なわれてるためオーバーライドは注意が必要。

    2013年12月25日水曜日

    ActionBarActivityのOnMenuItemSelectedはオーバーライドできない。

    Supoport Libraryに追加されたandroid-support-v7-appcompatのActionBarActivityのOnMenuItemSelectedはオーバーライドできない。



    OnMenuItemSelectedはfinalメソッドとなっている。
    代わりに、
    onOptionsItemSelectedをオーバーライドして使用します。

    リファレンスのOnMenuItemSelectedの記述は以下の通りです。
    Default implementation of onMenuItemSelected(int, MenuItem) for activities. This calls through to the new onOptionsItemSelected(MenuItem) method for the FEATURE_OPTIONS_PANEL panel, so that subclasses of Activity don't need to deal with feature codes.
     そもそも、OnMenuItemSelectedを実装しているのが間違っていた?

    2013年5月16日木曜日

    Android SDK 22 では、Build Toolsが必要になったようです。

    今朝リリースされた Android SDK 22について注意

    このバージョンより Android SDK Build-toolsが必要になりました。 

    インストールしていないと、
    ant debug/release 実行に、以下のようなメッセージが出て、ビルドできません。 {android-sdk}/tools/ant/build.xml:479: SDK does not have any Build Tools installed.

     Eclipseでも、
    Android Tools > Export Signed/Unsigned Application Package... を実行すると同じようなエラーダイアログが表示されました。

     

    SDK 22からToolsに Android SDK Build-tools が増えてるのでインストールしておくように!

    2013年4月1日月曜日

    うるさいLint先生にXMLの警告を無視させる方法

    xmlns:tools="http://schemas.android.com/tools"
    を定義して、
    tools:ignore="ContentDescription,PxUsage"
    無効にしたいチェックを記述します。


    ImageView / ImageButton の contentDescriptionとか記述めんどくさいし。
    セパレータに使用している ImageViewの heightは ピクセル(px)で定義したいし。

    2012年12月23日日曜日

    InputFilterとValidator

    ValidatorじゃなくてAndroidには InputFilterがあるじゃないか?って話がでたので...

    フィルタとは (filter): - IT用語辞典バイナリ
    フィルターは、受け取ったデータに対して何らかの処理や加工を行った上で出力すること

    Androidでは以下のInputFilterが提供されています。
    InputFilter | Android Developers

    public interface

    InputFilter (view source)

    android.text.InputFilter
    Known Indirect Subclasses
    DateKeyListenerFor entering dates in a text field. 
    DateTimeKeyListenerFor entering dates and times in the same text field. 
    DialerKeyListenerFor dialing-only text entry
    As for all implementations of KeyListener, this class is only concerned with hardware keyboards. 
    DigitsKeyListenerFor digits-only text entry
    As for all implementations of KeyListener, this class is only concerned with hardware keyboards. 
    InputFilter.AllCapsThis filter will capitalize all the lower case letters that are added through edits. 
    InputFilter.LengthFilterThis filter will constrain edits not to make the length of the text greater than the specified length. 
    LoginFilterAbstract class for filtering login-related text (user names and passwords)  
    LoginFilter.PasswordFilterGMailThis filter is compatible with GMail passwords which restricts characters to the Latin-1 (ISO8859-1) char set. 
    LoginFilter.UsernameFilterGMailThis filter rejects characters in the user name that are not compatible with GMail account creation. 
    LoginFilter.UsernameFilterGenericThis filter rejects characters in the user name that are not compatible with Google login. 
    NumberKeyListenerFor numeric text entry
    As for all implementations of KeyListener, this class is only concerned with hardware keyboards. 
    TimeKeyListenerFor entering times in a text field. 


    これら以外のフィルターを行いたい場合はカスタムフィルタを作成する必要があります。
    作成の方法はこちらが参考になると思います。
    EditTextにカスタムInputFilterを適用して入力値の制限を行う | MoaiApps Labo 
    Y.A.M の 雑記帳: Android Filterを使ってみた

    フィルタは、特定の文字列以外は認めない機能です。
    入力された文字列を検査する機能では有りません。
    それがバリデータになります。

    バリデータとは 「バリデーター」 (validator): - IT用語辞典バイナリ
    入力されたデータが仕様にそって適切に記述されているかを判断し、不適切な箇所があった場合にはエラーとして通知する。


    特定の文字以外を入力させなくしているから、バリデータは不要でしょうか?

    そんなことは無いと思います。
    • ユーザーID/パスワードなどの必須項目は入力されているか? 
    • パスワードはある桁以上であるか?特定の文字列を含んでいるか?
    • パスワードの確認入力などの二つの項目が同じであるか?
    • 電話番号や郵便番号などの"-"区切りのフォーマットが正しいか?
    など、入力確定後に検査をしなければならないと思います。
    ※フォーマット付き項目については、項目を分けたらとか、フォーマット無しで入力させたらいいじゃないかとかありますけどね。鶴様が既存PCのフォームがフォーマット付きだから踏襲し...(ry

    検査のタイミングですが、
    • フォーカスが外れたタイミング
    • ボタン押下などアクションのタイミング
    となると思います。
    ※必須項目の検査は、全ての入力項目を確定させたアクションでしか行えないと思います。

    AndroidのEditViewの場合、

    1. android:inputType 入力制御 (IMEのパネルを制御)
    2. InputFilter 入力制御 入力されたタイミングでフィルタ
    3. Formatter 文字列入力後にフォーマット
    4. Validator 最終的な文字列の検査

    の順に処理をすればいいんじゃないかと思います。

    バリデータなんて古いぜこっちの方がクールだぜ。ってのあったら教えてほしいな〜。

    でわでわ

    2012年12月20日木曜日

    ViewHolderとValidatorForAndroid

    Android Advent Calendar 2012 #androidadvent2012
    12/20(裏) 担当の @katsummy です。表担当は@stachibanaさんとなります。

    よろしくお願いします〜。

    初心者向けなニッチな内容です。
    シニアの方にはこんなの自分で実装してるよ〜な内容になるかもです。

    ■ViewHolder

    XMLでレイアウトを作成した後、ActivityまたはFragmentの中で扱う為に、inflateしてViewをfindViewByIdして、メンバー変数に格納しますよね?

    結構面倒くさくないですか?
    • レイアウトファイルからidを抜き出し
    • メンバー変数にViewのクラスを記述して、mから始める名前で定義
    • mXxxXxx = (View) findViewById(R.id.xxxx); を記述

    Viewの項目が増えるとコピペの作業と文字列の置き換えが大変。
      android:id="@+id/first_name"

    EditText mFirstName;
    mFirstName = (EditText) findViewById(R.id.first_name);

    また、Activity/Fragmentにメンバー変数が多くなり見難くなると思います。

    そこで、まずは、これらViewの項目の定義を外だしにしてしまいましょう。

    1つのレイアウトに対してViewHolderクラスを作成します。
    ViewHolderってのはListAdapterのテクニックでも使用しているあれです。

    ViewHolderには、基本的にプロパティ値としてViewも持ちます。


    使用前

    public class EditActivity extends Activity implements OnClickListener { public EditText mFirstName; public EditText mLastName; public TextView mLabelLastName; public TextView mLabelFirstName; public Spinner mSex; public FrameLayout mFrameSex; public TextView mLabelSex; public TextView mLabelBirthday; public DatePicker mBirthday; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mFirstName = findViewById(R.id.first_name); mLastName = findViewById(R.id.last_name); mLabelLastName = findViewById(R.id.label_last_name); mLabelFirstName = findViewById(R.id.label_first_name); mSex = findViewById(R.id.sex); mFrameSex = findViewById(R.id.frame_sex); mLabelSex = findViewById(R.id.label_sex); mLabelBirthday = findViewById(R.id.label_birthday); mBirthday = findViewById(R.id.birthday); } }
    使用後
    public class EditActivity extends Activity implements OnClickListener { ViewHolder mViewHolder; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mViewHolder = new ViewHolder(getWindow().getDecorView()); } } public class ViewHolder { public EditText mFirstName; public EditText mLastName; public TextView mLabelLastName; public TextView mLabelFirstName; public Spinner mSex; public FrameLayout mFrameSex; public TextView mLabelSex; public TextView mLabelBirthday; public DatePicker mBirthday; public ViewHolder(View v) { mFirstName = findViewById(v, R.id.first_name); mLastName = findViewById(v, R.id.last_name); mLabelLastName = findViewById(v, R.id.label_last_name); mLabelFirstName = findViewById(v, R.id.label_first_name); mSex = findViewById(v, R.id.sex); mFrameSex = findViewById(v, R.id.frame_sex); mLabelSex = findViewById(v, R.id.label_sex); mLabelBirthday = findViewById(v, R.id.label_birthday); mBirthday = findViewById(v, R.id.birthday); } @SuppressWarnings("unchecked") private T findViewById(View v, int id) { return (T) v.findViewById(id); } }


    これで、Activity/Fragmentから大量のメンバー変数が消えて、コードを追いやすくなるのではないでしょうか?

    でも、ViewHolderにいろいろ書くのは変ってないし、やっぱり面倒くさいよね?

    なので、レイアウトからViewHolderを作成するスクリプトを作成しました。


    レイアウトからViewHolderクラスを作成

    なお、指定するレイアウトは予めフォーマーッター(Ctrl+Shift+F)でフォーマットしておく必要があります。要は、Viewタグの後にIDアトリビュートが定義されてる必要があります。


    ValidatorForAndroid


    社員管理・顧客管理などの編集画面で、一度に多くの項目を入力を行わせるアプリを作成することもあります。

    ※PCアプリ/Webアプリの機能をそのままAndroid側でも実現させようとする要件ですね。

    入力項目で使用するEditViewには、InputTypeでIMEを制御はありますがValidatorは備わってません。エラーメッセージを表示する機能はあるけど、バリデーションは自分でやってねって感じでしょうか。

    StrutsみたいなValidator欲しいなっと思ったので作ってみました。

    granoeste/ValidatorForAndroid · GitHub 


    StrutsみたいなValidator欲しいなっと思ったので作ってみました。
    apache commons validatorを参考にしています。

    サンプル1 EditViewとValidatorの組み合わせ
    // Validators Validators mValidators = new Validators(); EditText mTextPersonName; EditText mTextPassword; Button mButton; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextPersonName = (EditText) findViewById(R.id.textPersonName); mTextPassword = (EditText) findViewById(R.id.textPassword); mButton = findViewByIdAndCast(v, R.id.button); // Define validators for views mValidators.put(mTextPersonName, new Validator[] { new RequiredValidator("Be sure to input. "), new MaxLengthValidator(30, "Being allowed is to 30 characters. "), }); mValidators.put(mTextPassword, new Validator[] { new RequiredValidator("Be sure to input. "), new RangeValidator(8, 16, "Being allowed is from 8 to 16 characters. "), }); mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // Execute Validators mValidators.clearError(); if (mValidators.isValid()) { Toast.makeText(getApplicationContext(), "It has some errors. ", Toast.LENGTH_SHORT).show(); } } }); }

    サンプル2 バリデータ付きEditView
    // Validators Validators mValidators = new Validators(); net.granoeste.validator.ValidatableEditText mTextPersonName; net.granoeste.validator.ValidatableEditText mTextPassword; Button mButton; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextPersonName = (net.granoeste.validator.ValidatableEditText) findViewById(R.id.textPersonName); mTextPassword = (net.granoeste.validator.ValidatableEditText) findViewById(R.id.textPassword); mButton = findViewByIdAndCast(v, R.id.button); // add ValidatableEditText to Validators mValidators.put(mTextPersonName); mValidators.put(mTextEmailAddress); mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // Execute Validators mValidators.clearError(); if (mValidators.isValid()) { Toast.makeText(getApplicationContext(), "It has some errors. ", Toast.LENGTH_SHORT).show(); } } }); } 

    こんな感じで使用します。

    詳しくは、GitHubに上げてるのでソース見てね。
    解んなかったら聞いて下さい。
    ※バグがあったらIssue書いてくれても、ForkしてPull Requesteしてくれてもいいんだよ。

    以上になります。

    明日は、@out_of_kayaさん と @tarotaro4さん が担当されます。



    でわでわ。
    メリークリスマス&よいおとしを。

    2012年12月4日火曜日

    Google Maps Android API v2 がリリースされたようですね。


    Google Maps Android API v2 がリリースされたようですね。


    とりあえず、サンプル動かしてみる。
    Developer Guide 通りにやるだけだけどw

    1. Sample Codeをインポート
    1. まずは、Google Play Services のライブラリをインポート
      1. Select File > Import > Android > Existing Android Code Into Workspace
      2. /extras/google/google_play_services/libproject/google-play-services_lib
    2. 続いて、サンプルアプリケーションのインポート
      1. Select File > Import > Android > Existing Android Code Into Workspace
      2. /extras/google/google_play_services/samples/maps
    3. ライブラリーの追加
      1. Context Menu > Android Tools > Add Support Library...
    2. API Keyの取得
    1. Google APIs Console にアクセス
    2. Services 選択
    3. Google Maps Android API v2 をONにする。
      • MapsのAPIがいくつかあるけど、Androidってついてるやつです。
    4. API Access 選択
    5. [Create new Android key...] 選択
    6. テキストボックスに、証明書のSHA1 fingerprintとパッケージをセミコロンで区切って入力
      • 証明書のSHA1 fingerprintは以下のコマンドで確認出来る。 
      • keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
    7. 登録したら、API keyを確認

    3. AndroidManifest.xmlにAPI Keyを記述

    4. ビルドして実機で確認


    Google Play 開発者サービスがインストールされてないと、以下のようにインストールするように言われます。
    ※しかし、開発者サービスって表示どうにかなりませんかね。理解してないユーザのGoogle Playのコメントが酷い。。。

    サンプルソースチラ見

    • MapはFragmentで提供されてます!
    • ようやく、FragmentにMAPが!
    • しかも、SupportMapFragment!
    • 2.xでも動くようですな
    • Galaxy S (2.3.?)でも動いてました。
    • AndroidManifestのには、OpenGL ES 2.0. の定義があるので、2.2(Lv8)以上でないとだめなようです。

    くわしくは、Google Maps Android API v2 — Google Developers を見る必要がありますね。


    以上 


    2012年9月10日月曜日

    4.0(ICS)でCamera.takePicture呼び出し時にシャッター音が鳴らない?

    4.0(ICS)で、Camera シャッター音が鳴らなくなりました。???

    よくある実装サンプル。 mCamera.autoFocus(new AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { camera.takePicture(null, null, pictureCallback); } }); private Camera.PictureCallback pictureCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { // save picture } }; takePictureの第1引数のShutterCallbackをnull で呼び出してます。
    4.0(ICS)以降では、nullにしているとシャッター音が鳴らなくなっているようです。

    空実装のShutterCallbackを指定すると鳴るようになりました。 mCamera.autoFocus(new AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { camera.takePicture(shutterCallback, null, pictureCallback); } }); private Camera.PictureCallback pictureCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { // save picture } }; private Camera.ShutterCallback shutterCallback = new Camera.ShutterCallback() { @Override public void onShutter() { // NOP } };
    GALAXY NEXUS SC-04D で試しましたが、端末依存とかあるのでしょうか。。。

    2012年8月17日金曜日

    FragmentからstartActivityForResult

    FragmentからstartActivityForResultすると、戻ってきた時にActivityのonActivityResultとFragmentのonActivityResultが呼ばれるようなので。。。覚え書き

    ソースコードを見るとよくわかるね。

    startActivityForResult の動作

    Fragment
    ActivityのstartActivityFromFragmentを呼んでます。 /** * Call {@link Activity#startActivityForResult(Intent, int)} on the fragment's * containing Activity. */ public void startActivityForResult(Intent intent, int requestCode) { if (mActivity == null) { throw new IllegalStateException("Fragment " + this + " not attached to Activity"); } mActivity.startActivityFromFragment(this, intent, requestCode); }
    FragmentActivity
    requestCodeをビット演算させてActivityのsuper.startActivityForResultを呼んでます。 /** * Called by Fragment.startActivityForResult() to implement its behavior. */ public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode) { if (requestCode == -1) { super.startActivityForResult(intent, -1); return; } if ((requestCode&0xffff0000) != 0) { throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); } super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff)); }

    onActivityResult の動作

    FragmentActivity
    requestCodeをビット演算でFragmentからstartActivityForResultActivityが実行されてるかチェックして、 FragmentのonActivityResultの呼び出しを制御しています。 /** * Dispatch incoming result to the correct fragment. */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { int index = requestCode>>16; if (index != 0) { index--; if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) { Log.w(TAG, "Activity result fragment index out of range: 0x" + Integer.toHexString(requestCode)); return; } Fragment frag = mFragments.mActive.get(index); if (frag == null) { Log.w(TAG, "Activity result no fragment exists for index: 0x" + Integer.toHexString(requestCode)); } else { frag.onActivityResult(requestCode&0xffff, resultCode, data); } return; } super.onActivityResult(requestCode, resultCode, data); }
    Fragment
    /** * Receive the result from a previous call to * {@link #startActivityForResult(Intent, int)}. This follows the * related Activity API as described there in * {@link Activity#onActivityResult(int, int, Intent)}. * * @param requestCode The integer request code originally supplied to * startActivityForResult(), allowing you to identify who this * result came from. * @param resultCode The integer result code returned by the child activity * through its setResult(). * @param data An Intent, which can return result data to the caller * (various data can be attached to Intent "extras"). */ public void onActivityResult(int requestCode, int resultCode, Intent data) { }

    Y.A.M の 雑記帳: Android Support Package の Fragment から startActivityForResult() を使うときの注意点

    2012年8月2日木曜日

    XOOM (海外ROM版) JellyBean (4.1.1) アップデート

    XOOM (海外ROM版)に、JellyBean (4.1.1)のOTAが来たのでアップデートしてみた。

    UI
    Nexus7のNavigationBarのPhone UIとは異なり、CombindBarのTablet UIになってます。


    ホームボタン長押しで、Google Nowが表示されます。

    残念な事に Portrait にしか対応してませんでした。。。



    システム情報
    Android Version 4.1.1
    Kernel Version 2.xのまま (Nexus 7は3.x)

    Nortification
    4.1から追加された BigStyleにも対応してます。
    通知の拡張は、下から上に2タッチでスワイプさせます。




    こんな感じ〜。


    2012年7月18日水曜日

    ライブラリにソースとJavadocをアタッチ

    ようやく、ADT r20で、libsのライブラリのソースとJavadocのjar/zipを添付出来るようになりました。


    その手順メモです。

    元ネタ
    Issue 28658 - android - Attaching source code for jar files in Android dependencies


    1.  libsのライブラリと同じ名前でpropertiesファイルを作成します。


    2. propertiesファイルに、srcとdocのパスorファイルを記述します。
    android-support-v4.jar.properties
    src=/Applications/android-sdk/extras/android/support/v4/src/
    doc=
    gson-2.2.1.jar.properties
    src=../libsrc/gson-2.2.1-sources.jar
    doc=../libsrc/gson-2.2.1-javadoc.jar
     ※ファイルはzipでもOKでした。

    3. プロジェクトのAndroid Dependencies のjarアイコンに ドキュメントマークが付きます。

    4. プロジェクト -> Properties -> Java Build Path -> Libraries の Android Dependencies の各ライブラリにソースとJavaDocがアタッチされています。


    設定が反映されない場合、プロジェクトを閉じて開き直すと良いかもしれません。

    一応 githubにプロジェクトを置いておきます。
    https://github.com/granoeste/UseThirdPartyLib

    2012年6月15日金曜日

    PNGから不要なメタデータの削除しよう。

    Icon Design Guidelines に記載されてるように PNGの圧縮を行う。

    When saving image assets, remove unnecessary metadata

    Although the Android SDK tools will automatically compress PNGs when packaging application resources into the application binary, a good practice is to remove unnecessary headers and metadata from your PNG assets. Tools such as OptiPNG or Pngcrush can ensure that this metadata is removed and that your image asset file sizes are optimized.


    PNGから不要なメタデータの削除




    参考
      png圧縮をして容量、メモリにやさしいアプリ制作 - 素人のアンドロイドアプリ開発日記


    ツール
      PNGGauntlet - PNG Compression Software
      PNGOUTWin - A Batch Mode PNG Optimizer and Converter for Windows.
      OptiPNG
      ImageOptim — make websites and apps load faster (Mac app)




    Mac環境なので、ImageOptimを使用しました。


    メタ情報は次のコマンドで確認することができます。
     /Applications/ImageOptim.app/Contents/MacOS/pngcrush -n -v xxxx.png




    5% ~ 10% ものによっては 30% ファイルサイズが削減されてました。




    画像が劣化しているかどうか確認したかったのですが、いいツールが有りませんでした。。。

    2012年5月24日木曜日

    2012 夏モデル端末の ナビゲーションバー 対応状況

    端末下部のハードウェアキーが、4.0から標準のタッチパネル上のソフトウェアキー置き換わった端末のチェックとメモ

    注)
     ホームページ上の写真から判断しているので間違えがあるかもしれません。
     スペック一覧は他のサイトでまとめられているのでそっちを確認してください。