2012年5月24日木曜日

Android Dialog の大きさを自分で設定する

毎回、過去に実装したアプリのコードを見るのがめんどいのでメモしておきます。

例えば、入力欄のあるオリジナルのダイアログを作るとします。

  1. public void showOriginalDialog(Context context) {  
  2.   
  3.     LinearLayout ll = new LinearLayout(context);  
  4.     ll.setOrientation(LinearLayout.VERTICAL);  
  5.   
  6.     TextView tv = new TextView(context);  
  7.     tv.setText("お名前を入力してください。");  
  8.   
  9.     EditText et = new EditText(context);  
  10.   
  11.     ll.addView(tv);  
  12.     ll.addView(et);  
  13.   
  14.     Dialog dialog = new Dialog(context);  
  15.     dialog.setContentView(ll);  
  16.     dialog.setTitle("アカウント作成");  
  17.   
  18.     dialog.show();  
  19. }  
この場合、次のようにダイアログの幅は TextView の幅、つまり「お名前を入力してください。」の幅になります。



3.0 以降は Theme.Holo.Dialog がデフォルトのテーマになり、一定の幅のダイアログになります。また、Theme.Holo.Dialog.MinWidth というテーマだとそれよりもさらに少し幅が広いダイアログになります。

しかし 3.0 以前では、デフォルトでは上記のようにダイアログの幅はコンテンツがちょうど収まる幅になってしまいます。

ダイアログの幅を自分で設定し、例えば画面の幅の8割にするには、次のように WindowManager.LayoutParams で指定します。
  1. public void showOriginalDialog(Context context) {  
  2.   
  3.     DisplayMetrics metrics = getResources().getDisplayMetrics();  
  4.     int dialogWidth = (int) (metrics.widthPixels * 0.8);  
  5.   
  6.     ...  
  7.   
  8.     Dialog dialog = new Dialog(context);  
  9.     dialog.setContentView(ll);  
  10.     dialog.setTitle("アカウント作成");  
  11.   
  12.     WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();  
  13.     lp.width = dialogWidth;  
  14.     dialog.getWindow().setAttributes(lp);  
  15.   
  16.   
  17.     dialog.show();  
  18. }  




また、ダイアログが表示される位置も変えることができます。デフォルトではセンターですが、ダイアログを下部に表示したい場合は
  1. dialog.getWindow().setGravity(Gravity.BOTTOM);  
のように、ダイアログの Window に setGravity() で位置を指定します。






2012年5月23日水曜日

Android Support Package の Fragment から startActivityForResult() を使うときの注意点

今回は Support Package で Fragment を使う場合の注意点です。

まず、FragmentActivity で startActivityForResult() を使う場合、requestCode は 16bit 以下にしなければなりません。

http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/support/v4/java/android/support/v4/app/FragmentActivity.java#654
  1. 654     /** 
  2. 655      * Modifies the standard behavior to allow results to be delivered to fragments. 
  3. 656      * This imposes a restriction that requestCode be <= 0xffff. 
  4. 657      */  
  5. 658     @Override  
  6. 659     public void startActivityForResult(Intent intent, int requestCode) {  
  7. 660         if (requestCode != -1 && (requestCode&0xffff0000) != 0) {  
  8. 661             throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");  
  9. 662         }  
  10. 663         super.startActivityForResult(intent, requestCode);  
  11. 664     }  
このように、16bit 以上の場合は IllegalArgumentException が発行されるようになっています。

なぜこのような処理をしているかというと、requestCode を使って Activity から呼ばれた場合と、Fragment から呼ばれた場合を区別するようにしているからです。

requestCode が 0x0000ffff より小さい場合 → Activity から呼ばれた
requestCode が 0x0000ffff より大きい場合 → Fragment から呼ばれた

http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/support/v4/java/android/support/v4/app/FragmentActivity.java#666
  1. 666     /** 
  2. 667      * Called by Fragment.startActivityForResult() to implement its behavior. 
  3. 668      */  
  4. 669     public void startActivityFromFragment(Fragment fragment, Intent intent,  
  5. 670             int requestCode) {  
  6. 671         if (requestCode == -1) {  
  7. 672             super.startActivityForResult(intent, -1);  
  8. 673             return;  
  9. 674         }  
  10. 675         if ((requestCode&0xffff0000) != 0) {  
  11. 676             throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");  
  12. 677         }  
  13. 678         super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff));  
  14. 679     }  
さらに、どの Fragment から呼ばれたかも requestCode に反映されます。

ポイントは
  1. super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff));  
ですね。

FragmentActivity では、自身が持っている Fragment にそれぞれ index が振られます。それが mIndex です。
つまり、Fragment から Fragment#startActivityForResult() を呼んだ場合、最終的に Activity#startActivityForResult() に渡される requestCode は、上位16ビットがその Fragment の index、下位16ビットが Fragment#startActivityForResult() に渡した requestCode になります。


で、どこではまるかというと、Fragment#startActivityForResult() から呼んだ Intent を FragmentActivity#onActivityResult() で受ける場合です。

http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/support/v4/java/android/support/v4/app/FragmentActivity.java#128
  1. 128     /** 
  2. 129      * Dispatch incoming result to the correct fragment. 
  3. 130      */  
  4. 131     @Override  
  5. 132     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  6. 133         int index = requestCode>>16;  
  7. 134         if (index != 0) {  
  8. 135             index--;  
  9. 136             if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) {  
  10. 137                 Log.w(TAG, "Activity result fragment index out of range: 0x"  
  11. 138                         + Integer.toHexString(requestCode));  
  12. 139                 return;  
  13. 140             }  
  14. 141             Fragment frag = mFragments.mActive.get(index);  
  15. 142             if (frag == null) {  
  16. 143                 Log.w(TAG, "Activity result no fragment exists for index: 0x"  
  17. 144                         + Integer.toHexString(requestCode));  
  18. 145             }  
  19. 146             frag.onActivityResult(requestCode&0xffff, resultCode, data);  
  20. 147             return;  
  21. 148         }  
  22. 149   
  23. 150         super.onActivityResult(requestCode, resultCode, data);  
  24. 151     }  
このように FragmentActivity#onActivityResult() では、requestCode の値を見て、それが Fragment から呼ばれたものなら、Fragment の onActivityResult() を呼んでいます。このときちゃんと requestCode の下位16ビットだけ渡しています。

Fragment#startActivityForResult() で呼んだ Intent を同じ Fragment の onActivityResult() で受け取っている場合には特に問題はないのですが、 Fragment#startActivityForResult() で呼んだ Intent を FragmentActivity の onActivityResult() で処理する場合には、渡された requestCode の下位16ビットだけを比較対象にする必要があります。

具体的に書くと、

  1. public class ActivityA extends FragmentActivity {  
  2.   
  3.     public static final int REQUEST_CODE_3 = 3;  
  4.     FragmentA mFragment;  
  5.       
  6.     @Override  
  7.     protected void onCreate(Bundle savedInstanceState) {  
  8.         super.onCreate(savedInstanceState);  
  9.   
  10.         mFragment = new FragmentA();  
  11.         getSupportFragmentManager().beginTransaction().add(R.id.container, mFragment).commit();  
  12.     }  
  13.       
  14.     @Override  
  15.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  16.         super.onActivityResult(requestCode, resultCode, data);  
  17.           
  18.         // ここでは requestCode は 3 ではないので、下位16ビットを比較  
  19.         if(resultCode == RESULT_OK && (requestCode & 0xffff) == REQUEST_CODE_3) {  
  20.             // do something  
  21.         }  
  22.     }  
  23.   
  24.     class FragmentA extends Fragment {  
  25.         ...  
  26.   
  27.         private void openNewActivity() {  
  28.             Intent intent = new Intent(getActivity(), ActivityB.class);  
  29.             startActivityForResult(intent, ActivityA.REQUEST_CODE_3);  
  30.         }  
  31.   
  32.         @Override  
  33.         public void onActivityResult(int requestCode, int resultCode, Intent intent) {  
  34.             super.onActivityResult(requestCode, resultCode, intent);  
  35.             // ここでの requestCode は 3 = REQUEST_CODE_3  
  36.         }  
  37.     }  
  38. }  


このように、 ActivityA の中の FragmentA から requestCode = 3 で startActivityForResult() を呼んだ場合、FragmentA の onActivityResult() で渡される requestCode は 3 ですが、ActivityA の onActivityResult() で渡される requestCode は例えば 665539 のような値になります。

なので、Fragment から startActivityForResult() で呼んだ Intent を FragmentActivity の onActivityResult() で処理する場合は、下位16ビットだけで比較しましょう。




2012年5月16日水曜日

FabLab 鎌倉に行って看板つくってきた!

昔の蔵を改良したような建物でした。すてき!





入り口横の看板



レーザーカッターで作られた作品がいっぱい飾ってありました



二階が作業コーナー。ここにも作品がいっぱい



静岡県のパズル?



工具とか



レーザーカッターを使ったときの手順です。今回は uPhyca の看板を作ってきました。

1. 画像を AI ファイルで用意する

レーザーカッターに使う画像は AI ファイル(.ai)で用意しておきます。レーザーがあたる領域はアートボードの領域になります。つまり、ベニヤ板の左上がアードボードの左上にくる感じ。

これはロゴの周りに余白をいれてなかったので失敗バージョン。



Adobe Illustrator で余白が入るようにアートボードの大きさを修正。


2. AI ファイルを CorelDRAW で開く

CorelDRAW の印刷設定先にレーザーカッターを指定できるようになっていて、このソフトからレーザーカッターにデータを送る。 1.で用意した AI ファイルを CorelDRAW で開き、印刷設定を開く。

今回は、

厚さ6mm くらいの杉(だったはず)のベニヤ板で
スピード 90%
強さ 70%

で彫刻
しました。

もっと色を濃くしたい場合は、スピードを落とすか、強さをあげる。
材質が変わると適切な設定値も変わるので、何回もトライ&エラーで設定値を見つけるそうです。今回はベニヤ板だったので説明してくれた FabLab お兄さんの言う通りの値にしました。


3. ベニヤ板をレーザーカッターにセット

レーザーカッターに蓋を開けてベニヤ板を左上にセット。蓋を開けているときはレーザーがでないようになっているとのこと。どの辺りにレーザーがあたるのか先にチェックできるようになっていて、ポインターをオンにすると赤い光がでるようになります。 この状態でスタートすると、レーザーは出ずに実際のように動くので、正しい位置にレーザーがオフセットがセットされているかみることができます。

レーザーカッターの操作パネル



位置が確認できたら、排気のための装置のスイッチをON!これを入れないとレーザーカッターの中に煙が充満して危険なので必ずいれること! では、蓋を閉めてスタート!

レーザーがでる部分が左右にものすごい速さで動いて彫刻されていくー。



できたー!



遅くなったけど、報告完了!
もう少し細かく写真とればよかったなぁ。。。

2012年5月10日木曜日

「やきはらえ」って言ったら LED が光ってブラウザでアニメーションが走るようにしてみた。

追記: 2012/5/11

「やきはらえ」の気合いが足りないとのご指摘を受けましたので、撮り直しました。
もう少し気合い入れたいんだけど、うまくしゃべらないと「出来映え」とか認識されちゃうのよねー。。。
--------


前に金曜ロードショーでラピュタが放送されるときに
↓なことやって
「Y.A.M の 雑記帳: Android ADKを使ってバルスって言ったら青色LEDが光るようにしてみた。」

まーた、思いついたのでやりました。
(明日の金曜ロードショーはナウシカです)

ナウシカといえば、やっぱ「焼き払え!」「薙ぎ払え!」ですよね。

ということで、Android で音声認識して、「やきはらえ」と言ったら LED 点灯 + ブラウザでアニメーションさせるのを作ってみました。



今回の構成はこんな感じ



ブラジルの dart ハッカソンで Dart + Arduino でクイズをするという作品があって https://plus.google.com/u/0/115028528042654643072/posts/3iXwhtSr9C2 おおお、と思って最近 dart 始めたこともあって、まぁ、やってみたってところです。

ADK を使って LED を点灯させるところは前の(「Y.A.M の 雑記帳: Android ADKを使ってバルスって言ったら青色LEDが光るようにしてみた。」 )とほぼ一緒です(ADK のファームウェアは変えてません。

Android アプリ側は、getLastNonConfigurationInstance() が現在 deprecated になっていることもあって、Fragment を使うように変えました。ビューを持たない ADK と通信するようの Fragment を用意したので、前よりもさらに使い回ししやすくなってると思います。

Android で音声認識すると、LED 側と dart 側の2系列にメッセージが飛ぶような感じにしてます。



ブラウザ側のアニメーションは HTML5 の Canvas 機能を dart から使ってます。
うん、結構大変だったw

いちを github にあげてありますが、ちょっと Android 側にバグあるようでたまに固まります。 ちょっと直す時間なかった。。。

yanzm/Yakiharae - Github -



2012年5月5日土曜日

Android アプリからスクリーンキャプチャを無効にする方法

Android 4.0 からデバイスのハードキー(Volume down + Power)でスクリーンキャプチャが取れるようになりましたが、実はアプリからこの機能を無効にすることができます。

それが、WindowManager.LayoutParams.FLAG_SECURE です。

  1. public class MainActivity extends Activity {  
  2.       
  3.     @Override  
  4.     public void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.           
  7.         Window window = getWindow();  
  8.         window.addFlags(WindowManager.LayoutParams.FLAG_SECURE);  
  9.           
  10.         setContentView(R.layout.main);  
  11.     }  
  12. }  


このように FLAG_SECURE を Window にセットすると、キャプチャーを取ろうとしてもエラーになります。



また、DDMS の Devices の Device Screen Capture も真っ黒の画面になります。

2012年5月2日水曜日

Android スタイルメモ

■ TabWidget
  1. <item name="android:tabWidgetStyle">@android:style/Widget.TabWidget</item>  


■ AbsListView
  1. <item name="android:absListViewStyle">@android:style/Widget.AbsListView</item>  


■ ListView
  1. <item name="android:listViewStyle">@android:style/Widget.ListView</item>  
  2. <item name="android:listViewWhiteStyle">@android:style/Widget.ListView.White</item>  


■ ExpandableListView
  1. <item name="android:expandableListViewStyle">@android:style/Widget.ExpandableListView</item>  
  2. <item name="android:expandableListViewWhiteStyle">@android:style/Widget.ExpandableListView.White</item>  


■ ScrollView
  1. <item name="android:scrollViewStyle">@android:style/Widget.ScrollView</item>  


■ HorizontalScrollView
  1. <item name="android:horizontalScrollViewStyle">@android:style/Widget.HorizontalScrollView</item>  


■ GridView
  1. <item name="android:gridViewStyle">@android:style/Widget.GridView</item>  


■ WebView
  1. <item name="android:webTextViewStyle">@android:style/Widget.WebTextView</item>  
  2. <item name="android:webViewStyle">@android:style/Widget.WebView</item>  


■ DropDownListView
  1. <item name="android:dropDownListViewStyle">@android:style/Widget.ListView.DropDown</item>  
  2. <item name="android:dropDownItemStyle">@android:style/Widget.DropDownItem</item>  
  3. <item name="android:dropDownHintAppearance">@android:style/TextAppearance.Widget.DropDownHint</item>  


■ AutoCompleteTextBox
  1. <item name="android:autoCompleteTextViewStyle">@android:style/Widget.AutoCompleteTextView</item>          


■ TextView
  1. <item name="android:textViewStyle">@android:style/Widget.TextView</item>  


■ Button
  1. <item name="android:buttonStyle">@android:style/Widget.Button</item>  
  2. <item name="android:buttonStyleSmall">@android:style/Widget.Button.Small</item>  
  3. <item name="android:buttonStyleInset">@android:style/Widget.Button.Inset</item>  


■ ToggleButton
  1. <item name="android:buttonStyleToggle">@android:style/Widget.Button.Toggle</item>  


■ ImageButton
  1. <item name="android:imageButtonStyle">@android:style/Widget.ImageButton</item>  


■ CheckBox
  1. <item name="android:checkboxStyle">@android:style/Widget.CompoundButton.CheckBox</item>  


■ RadioButton
  1. <item name="android:radioButtonStyle">@android:style/Widget.CompoundButton.RadioButton</item>  


■ EditText
  1. <item name="android:editTextStyle">@android:style/Widget.EditText</item>  


■ Gallery
  1. <item name="android:galleryStyle">@android:style/Widget.Gallery</item>  


■ Gesture
  1. <item name="android:gestureOverlayViewStyle">@android:style/Widget.GestureOverlayView</item>  


■ PopupWindow
  1. <item name="android:popupWindowStyle">@android:style/Widget.PopupWindow</item>  


■ ProgressBar
  1. <item name="android:progressBarStyle">@android:style/Widget.ProgressBar</item>  
  2. <item name="android:progressBarStyleHorizontal">@android:style/Widget.ProgressBar.Horizontal</item>  
  3. <item name="android:progressBarStyleSmall">@android:style/Widget.ProgressBar.Small</item>  
  4. <item name="android:progressBarStyleSmallTitle">@android:style/Widget.ProgressBar.Small.Title</item>  
  5. <item name="android:progressBarStyleLarge">@android:style/Widget.ProgressBar.Large</item>  
  6. <item name="android:progressBarStyleInverse">@android:style/Widget.ProgressBar.Inverse</item>  
  7. <item name="android:progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item>  
  8. <item name="android:progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item>   


■ SeekBar
  1. <item name="android:seekBarStyle">@android:style/Widget.SeekBar</item>  


■ RatingBar
  1. <item name="android:ratingBarStyle">@android:style/Widget.RatingBar</item>  
  2. <item name="android:ratingBarStyleIndicator">@android:style/Widget.RatingBar.Indicator</item>  
  3. <item name="android:ratingBarStyleSmall">@android:style/Widget.RatingBar.Small</item>  


■ Spinner
  1. <item name="android:spinnerStyle">@android:style/Widget.Spinner</item>  
  2. <item name="android:spinnerDropDownItemStyle">@android:style/Widget.DropDownItem.Spinner</item>  
  3. <item name="android:spinnerItemStyle">@android:style/Widget.TextView.SpinnerItem</item>  


■ Star
  1. <item name="android:starStyle">@android:style/Widget.CompoundButton.Star</item>  


■ Preference
  1. <item name="android:preferenceScreenStyle">@android:style/Preference.PreferenceScreen</item>  
  2. <item name="android:preferenceCategoryStyle">@android:style/Preference.Category</item>  
  3. <item name="android:preferenceStyle">@android:style/Preference</item>  
  4. <item name="android:preferenceInformationStyle">@android:style/Preference.Information</item>  
  5. <item name="android:checkBoxPreferenceStyle">@android:style/Preference.CheckBoxPreference</item>  
  6. <item name="android:yesNoPreferenceStyle">@android:style/Preference.DialogPreference.YesNoPreference</item>  
  7. <item name="android:dialogPreferenceStyle">@android:style/Preference.DialogPreference</item>  
  8. <item name="android:editTextPreferenceStyle">@android:style/Preference.DialogPreference.EditTextPreference</item>  
  9. <item name="android:ringtonePreferenceStyle">@android:style/Preference.RingtonePreference</item>  
  10. <item name="android:preferenceLayoutChild">@android:layout/preference_child</item>  


■ Search widget
  1. <item name="android:searchWidgetCorpusItemBackground">@android:color/search_widget_corpus_item_background</item>  


■ ImageWell
  1. <item name="android:imageWellStyle">@android:style/Widget.ImageWell</item>  


■ KeyboardView
  1. <item name="android:keyboardViewStyle">@android:style/Widget.KeyboardView</item>  


■ QuickContact
  1. <item name="android:quickContactBadgeStyleWindowSmall">@android:style/Widget.QuickContactBadge.WindowSmall</item>  
  2. <item name="android:quickContactBadgeStyleWindowMedium">@android:style/Widget.QuickContactBadge.WindowMedium</item>  
  3. <item name="android:quickContactBadgeStyleWindowLarge">@android:style/Widget.QuickContactBadge.WindowLarge</item>  
  4. <item name="android:quickContactBadgeStyleSmallWindowSmall">@android:style/Widget.QuickContactBadgeSmall.WindowSmall</item>  
  5. <item name="android:quickContactBadgeStyleSmallWindowMedium">@android:style/Widget.QuickContactBadgeSmall.WindowMedium</item>  
  6. <item name="android:quickContactBadgeStyleSmallWindowLarge">@android:style/Widget.QuickContactBadgeSmall.WindowLarge</item>