2011年12月17日土曜日

Android 4.0.3 Platform

Android 4.0.3 Platformを意訳しました。訳が正しくない可能性もあります。正確な内容を理解するには原文を参照してください。

---
API Level: 15

Android 4.0.3 は Android .4x (Ice Cream Sandwich) プラットフォームファミリーのインクリメンタルリリースです。 このリリースには、ユーザーと開発者むけの新しい機能、API の変更、いくつかのバグフィックスが含まれています。

開発者は Android SDK のコンポーネントとして Android 4.0.3 platform をダウンロードできるようになりました。Android emulator を使って Android 4.0 でのアプリケーションの開発やテストを行うことができます。このプラットフォームには Android library と system image, emulator skin などが含まれていますが、外部のライブラリは含まれていません。

Android 4.0.3 向けの開発とテストを始めるには、Android SDK Manager を使って自分の SDK にプラットフォームをダウンロードします。より詳しい情報は Adding SDK Components を見てください。Android が初めての人は、まず download the SDK Starter Package から始めてください。

ユーザーと開発者向けの新しい機能の概要は Platform Highlights を見てください。

---

API Overview



Social stream API in Contacts Provider

ステータスアップデートやチェックインなどのソーシャルストリームデータを使うアプリケーションは各ユーザーのコンタクトにデータを同期したり、各写真といっしょにストリームのアイテムを提供することができるようになりました。

個々のコンタクトのソーシャルストリームを含むデータベーステーブルは ContactsContract.StreamItems によって定義されています。それぞれの Uri はストリームアイテムが所属する ContactsContract.RawContacts ディレクトリ内に格納されています。各ソーシャルストリームテーブルは、ストリームアイテムのメタデータ用のいくつかのカラムを含みます。例えば情報元(アバター)を表すアイコンやアイテムのラベル、主なテキストコンテンツ、アイテムについてのコメント(他の人からのレスポンスとか)などです。ストリームに関連する写真は ContactsContract.StreamItemPhotos で定義されるテーブルに格納されます。これあh ContactsContract.StreamItems Uri のサブディレクトリとして利用できます。

より詳しい情報は ContactsContract.StreamItemsContactsContract.StreamItemPhotos を見てください。

コンタクトに対してソーシャルストリームアイテムを読み書きするには、アプリケーションはパーミッションをリクエストする必要があります。リクエストするには、マニフェストファイルに <uses-permission android:name="android.permission.READ_SOCIAL_STREAM"> かつ/または <uses-permission android:name="android.permission.WRITE_SOCIAL_STREAM"> を宣言します。


Calendar Provider
  • Calendar Provider でのカラーテーブルを表す新しいクラス CalendarContract.Colors が追加されました。このクラスは与えられたアカウントで利用出来る色にアクセスするためのフィールドを提供します。色は COLOR_KEY によってリファレンスされ、与えられたアカウント名/タイプに対してユニークでなければなりません。これらの値は sync adapter によってのみアップデートできます。
  • exchange/sync サポートのための ALLOWED_AVAILABILITYALLOWED_ATTENDEE_TYPES が追加されました。
  • 参加者用の TYPE_RESOURCE (会議室など)、AVAILABILITY_TENTATIVE、イベント用の EVENT_COLOR_KEY が追加されました。


Home screen widgets

Android 4.0 からホームスクリーンウィジェットはもはや自身のパディングを含みません。代わりに現在の画面の特徴に基づいて、現在のシステムが自動的に各ウィジェットにパディングを追加します。これにより、グリッド上のウィジェットにより均一で統一な表現を与えます。ホームスクリーンウィジェットをホストするアプリケーションをアシストするために、プラットフォームは新しいメソッド getDefaultPaddingForWidget() を提供します。アプリケーションはこのメソッドを呼んでシステムが定義したパディングを取得することができ、ウィジェットに割り当てるセルの数を計算するときにそれを考慮できます。


Spell-checking
  • spell-checker サービスにアクセスできるアプリでは、新しい cancel() メソッドを使ってセッション中で保留されていたり実行されている spell-checker タスクをキャンセルすることができます。
  • spell-checker サービスでは、新しい候補フラグ RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS によってサービスは低信頼度の候補から高信頼度の候補を区別することができます。例えば、spell-checker はユーザーの辞書にはないが、よく候補が現れる入力ワードにフラグをセットしたり、ユーザーの辞書にないがあまり役立たない候補がでる入力ワードのフラグを外したりできます。
    spell-checker に接続したアプリは getSuggestionsAttributes()getSuggestionsCount() などの他の候補属性との組み合わせで RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS フラグを使うことができます。これにより入力ワードをタイポとしてマークし他の候補を出すかどうか決めることができます。
  • text span 用の新しい FLAG_AUTO_CORRECTION スタイルは、ユーザーがタイピング/編集した文字/テキストに自動修正が適用されたことを表します。このタイプの候補は異なったレンダリングがされ、自動修正が起こったことを示します。


Bluetooth

新しい public メソッド fetchUuidsWithSdp()getUuids() によってリモートデバイスによってサポートされている機能 (UUIDs) を決めることができます。fetchUuidsWithSdp() の場合、システムはサポートされている UUIDs を取得するためにリモートデバイス上でサービスディスカバリを実行し、結果を ACTION_UUID intent として発行します。


UI toolkit

新しいメソッド setUserVisibleHint()getUserVisibleHint() によってフラグメントは現在ユーザーに見えているかどうかのヒントをセットすることができます。システムは見えているフラグメントの Loader が実行されるまで見えていないフラグメントの開始を遅らせます。見えているかどうかのヒントはデフォルトでは "true" です。


Graphics

Accessibility
  • RemoteViews のクライアントは setContentDescription() を使えるようになりました。これにより、インフレートされたレイアウト上の View に content description をセットすることができます。
  • 新しいメソッド getMaxScrollX()getMaxScrollY()setMaxScrollX()setMaxScrollY()を使って、AccessobilityRecord オブジェクトに対する最大のスクロールオフセットを設定したり取得したりできます。
  • touch-exploration モードが有効のとき、新しいセキュア設定 ACCESSIBILITY_SPEAK_PASSWORD は、ハンドセットが使われていなくてもパスワードフィールドに入力されたテキストを IME が読み上げることをユーザーがリクエストしているかどうかを示しています。デフォルトでは、ハンドセットが使われていない場合パスワードテキストは読まれません。


Text-to-speech
  • ネットワークの TTS サポートを調べたり有効にしたりするための新しいメソッド getFeatures() が追加されました。
  • 音声合成エラーの通知を受取るためにエンジンが登録する新しいリスナークラス UtteranceProgressListener が追加されました。


Database
  • Content Provider が cross-process なクエリの結果をより効果的に返せるようにする新しいクラス CrossProcessCursorWrapper が追加されました。この新しいクラスはリモートでプロセスに送られる cursor をラップしたブロックを作成するときに便利です。また通常の Cursor オブジェクトを CrossProcessCursor オブジェクトに透過的に変換することもできます。
    CrossProcessCursorWrappter クラスは Content Provider を実装するときにアプリケーションがよく遭遇する一般的なパフォーマンス問題とバグを解決します。
  • 入力として名前文字列を受取るコンストラクタ CursorWindow(String) が追加されました。システムはもはやローカルとリモートの cursor window を区別しません。そのため CursorWindow(boolean) は無効になりました。


Intents

デバイス上のアプリケーションのタイプを指定するための新しいカテゴリ(CATEGORY_APP_BROWSERCATEGORY_APP_CALENDARCATEGORY_APP_MAPS など)が追加されました。


Camera

Permissions 新しいパーミッションが追加されました。
  • READ_SOCIAL_STREAMWRITE_SOCIAL_STREAM : 共有されている Contacts Provider 内の連絡先にソーシャルストリームデータを読み書きするための sync adapter を許可します。




2011年12月9日金曜日

Android ADKを使ってバルスって言ったら青色LEDが光るようにしてみた。

今日の21時から「天空の城ラピュタ」が放送されるので、TLがバルスで盛り上がって楽しかったので作ってみましたw

RecognizerIntent を使って認識した文字がバルスだったら ADK で LED をつけてるだけです。
RecognizerIntent については adakoda 先生のサイト 音声認識(RecognizerIntent)を使用するには - 逆引きAndroid入門 -を参考にしました。
いつもありがとうございます。

Android アプリから LED の ON/OFF を切り替える方法は前回のエントリ(Android Hello ADK をつくった!)をみてください。



やってみたい人は GitHub においたのでどうぞ。
yanzm/SayBalus - GitHub -



2011年12月8日木曜日

Android Hello ADK つくった!

DemoKit を動かしてみたよエントリや、Arduino で ADK 自作したよエントリはあるのに、Android ADK 用の Android アプリのプログラミングについて初心者向けに書かれたエントリがぜーんぜんなかったので書くことにしました。

ちなみにわたくし、電子工作ブランク10年くらいです。 10年前にブレッドボード上にケーブルが空中回廊みたいになった電卓を作って以来です。

今回は USB Accessory モードの方についてです。
公式ドキュメントはこちら
http://developer.android.com/guide/topics/usb/accessory.html
私のブログでの和訳はこちら
http://y-anz-m.blogspot.com/2011/05/usb-accessory.html

さて、とりあえず開発環境の準備。
DemoKit 動かしてみたよエントリがたくさんあるのでその辺りみればだいたい書いてると思う。
ちなみに公式ドキュメントはこちら。
Android Open Accessory Development Kit | Android Developers

これの Getting Started with the ADK を見ながら(私のブログでの和訳はこちら http://y-anz-m.blogspot.com/2011/05/android-open-accessory-development-kit.html

  • Arduino Software : ADK は Arduino がベースになっているので、ADK で動かすプログラムは Arduino で書く。そのための IDE。コーディングは持ちろん、デバッグやファームインストールもこれで行う。
  • CapSence library : 人間の静電容量を感知するためのライブラリ。DemoKit の Android シールドにあるタッチセンサーの為に必要。これいれないとタッチセンサーを触っても DemoKit アプリ側で反応しない。
  • The ADK package : ADK board 用のファームウェアおよび ADK board とシールド用の hardware design files。

をまずはとってきて、ドキュメントの手順通りにセットアップする。

で、DemoKit を動かす。


さて、ここまではできるよ。手順通りにやればいいだけだからね。
でもさ、DemoKit 取り外して、アクセサリに LED 1個つなげて光らせるだけでも自分だけでやれって言われると、
はて、どうしたら、、、?ってなるのですよ。

とりあえず DemoKit のコード見るよねー。
アクセサリ側はまだいいんだけど、Android アプリ側がすごく複雑。。。
とりあえず LED を on/off するだけの最小構成が知りたいのに操作できる項目が多い(DemoKit では入力としてリレー、3色LEDx3。出力としてジョイスティック、ボタン、タッチセンサー、温度、湿度がある)のでレイアウトも複雑だし、クラスも多い。。。。

やはりここは HelloWorld ならぬ HelloADK が必要なのでは。

ということでつくりましたよっと。



1. Android app

まずは Android アプリ側から。

注意点: Build Target は 2.3.3 (API Level 10) の Google APIs にする。

USB Accessory モードはもともと USB Host と同じく Android 3.0 からサポートされたものなのですが、Android 2.3.4 にバックポートされています。このバックポートを使うには 2.3.3 の Google APIs を指定する必要があります!



LED を on / off するだけなんでレイアウトはトグルボタン1個でいいよね。
ADK に接続してるかどうかのステータスも出すようにしよう。

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:orientation="vertical" > <TextView android:id="@+id/status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dip" /> <ToggleButton android:id="@+id/toggleBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dip" /> <TextView android:id="@+id/ledState" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dip" /> </LinearLayout>

では肝心の Activity 側。まずは UsbManager のインスタンスを取得します。
Add-on library (つまり 2.3.4 にバックポートされたライブラリ)を使っている場合は
UsbManager manager = UsbManager.getInstance(this);
3.0 以降のプラットフォーム標準の API を使う場合は
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);

流れ的には

onCreate()
 ・UsbManager の取得
 ・オレオレIntent action でパーミッション依頼用の
  PendingIntent 作成
 ・オレオレIntent action とアクセサリが外されたときの
  BroadcastReceiver 登録
 ・画面のセットアップ

onResume()
 ・USBAccessory の一覧を取得して、権限チェック。
  あればアクセサリとの接続を開く
  (UsbAccessory からファイルデスクリプタを取得し
   ファイルデスクリプタから入出力用の Stream を取得。
   入力を受け取るためのスレッドを開始)
  、なければパーミッションを依頼

onPause()
 ・アクセサリとの接続閉じる

onDestroy()
 ・onCreate() で登録した BroadcastReceiver 解除

・View が操作されたら出力用ストリームにバイト列書き込む。
・入力受け取り用スレッドでバイト列受け取ったらUIスレッドに渡してViewに反映

こんな感じかな。 あとは、コメントみてくれ。

public class MainActivity extends Activity implements Runnable { private static final String TAG = "HelloLED"; private static final String ACTION_USB_PERMISSION = "com.uphyca.android.app.helloled.action.USB_PERMISSION"; private PendingIntent mPermissionIntent; private boolean mPermissionRequestPending; private UsbManager mUsbManager; private UsbAccessory mAccessory; ParcelFileDescriptor mFileDescriptor; FileInputStream mInputStream; FileOutputStream mOutputStream; private ToggleButton mToggleButton; private TextView mLedStatusView; private TextView mStatusView; private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_USB_PERMISSION.equals(action)) { synchronized (this) { // Intent からアクセサリを取得 UsbAccessory accessory = UsbManager.getAccessory(intent); // パーミッションがあるかチェック if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { // 接続を開く openAccessory(accessory); } else { Log.d(TAG, "permission denied for accessory " + accessory); } mPermissionRequestPending = false; } } else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) { // Intent からアクセサリを取得 UsbAccessory accessory = UsbManager.getAccessory(intent); if (accessory != null && accessory.equals(mAccessory)) { // 接続を閉じる closeAccessory(); } } } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // UsbManager のインスタンスを取得 mUsbManager = UsbManager.getInstance(this); // オレオレパーミッション用 Broadcast Intent mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0); // オレオレパーミッション Intent とアクセサリが取り外されたときの Intent を登録 IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_USB_PERMISSION); filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED); registerReceiver(mUsbReceiver, filter); setContentView(R.layout.main); mToggleButton = (ToggleButton) findViewById(R.id.toggleBtn); mLedStatusView = (TextView) findViewById(R.id.ledState); mStatusView = (TextView) findViewById(R.id.status); mToggleButton.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { byte command = 0x1; byte value = (byte) (isChecked ? 0x1 : 0x0); sendCommand(command, value); } }); enableControls(false); } @Override public void onResume() { super.onResume(); if (mInputStream != null && mOutputStream != null) { return; } // USB Accessory の一覧を取得 UsbAccessory[] accessories = mUsbManager.getAccessoryList(); UsbAccessory accessory = (accessories == null ? null : accessories[0]); if (accessory != null) { // Accessory にアクセスする権限があるかチェック if (mUsbManager.hasPermission(accessory)) { // 接続を開く openAccessory(accessory); } else { synchronized (mUsbReceiver) { if (!mPermissionRequestPending) { // パーミッションを依頼 mUsbManager.requestPermission(accessory, mPermissionIntent); mPermissionRequestPending = true; } } } } else { Log.d(TAG, "mAccessory is null"); } } @Override public void onPause() { super.onPause(); closeAccessory(); } @Override public void onDestroy() { unregisterReceiver(mUsbReceiver); super.onDestroy(); } private void openAccessory(UsbAccessory accessory) { // アクセサリにアクセスするためのファイルディスクリプタを取得 mFileDescriptor = mUsbManager.openAccessory(accessory); if (mFileDescriptor != null) { mAccessory = accessory; FileDescriptor fd = mFileDescriptor.getFileDescriptor(); // 入出力用のストリームを確保 mInputStream = new FileInputStream(fd); mOutputStream = new FileOutputStream(fd); // この中でアクセサリとやりとりする Thread thread = new Thread(null, this, "DemoKit"); thread.start(); Log.d(TAG, "accessory opened"); enableControls(true); } else { Log.d(TAG, "accessory open fail"); } } private void closeAccessory() { enableControls(false); try { if (mFileDescriptor != null) { mFileDescriptor.close(); } } catch (IOException e) { } finally { mFileDescriptor = null; mAccessory = null; } } private void enableControls(boolean enable) { if (enable) { mStatusView.setText("connected"); } else { mStatusView.setText("not connected"); } mToggleButton.setEnabled(enable); } private static final int MESSAGE_LED = 1; private class LedMsg { private byte on; public LedMsg(byte on) { this.on = on; } public boolean isOn() { if(on == 0x1) return true; else return false; } } // ここでアクセサリと通信する @Override public void run() { int ret = 0; byte[] buffer = new byte[16384]; int i; // アクセサリ -> アプリ while (ret >= 0) { try { ret = mInputStream.read(buffer); } catch (IOException e) { break; } i = 0; while (i < ret) { int len = ret - i; switch (buffer[i]) { case 0x1: // 2byte のオレオレプロトコル // 0x1 0x0 や 0x1 0x1 など if (len >= 2) { Message m = Message.obtain(mHandler, MESSAGE_LED); m.obj = new LedMsg(buffer[i + 1]); mHandler.sendMessage(m); } i += 2; break; default: Log.d(TAG, "unknown msg: " + buffer[i]); i = len; break; } } } } // UI スレッドで画面上の表示を変更 private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_LED: LedMsg o = (LedMsg) msg.obj; handleLedMessage(o); break; } } }; private void handleLedMessage(LedMsg l) { if(l.isOn()) { mLedStatusView.setText("ON"); } else { mLedStatusView.setText("OFF"); } } // アプリ -> アクセサリ public void sendCommand(byte command, byte value) { byte[] buffer = new byte[2]; if(value != 0x1 && value != 0x0) value = 0x0; // 2byte のオレオレプロトコル // 0x1 0x0 や 0x1 0x1 buffer[0] = command; buffer[1] = value; if (mOutputStream != null) { try { mOutputStream.write(buffer); } catch (IOException e) { Log.e(TAG, "write failed", e); } } } }

つぎにアクセサリをフィルターするための XML ファイルを作成
manufacturer と model と version はアクセサリ側のコードの aac() の引数をあわせる。

res/xml/accessory_filter.xml


最後の AndroidManifest.xml にいろいろ宣言
・<uses-library android:name="com.android.future.usb.accessory" />
・android.hardware.usb.action.USB_ACCESSORY_ATTACHED の intentfilter
・android.hardware.usb.action.USB_ACCESSORY_ATTACHED の meta-data

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.uphyca.android.helloadk" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="10" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <uses-library android:name="com.android.future.usb.accessory" /> <activity android:label="@string/app_name" android:launchMode="singleInstance" android:name=".MainActivity" android:taskAffinity="" > <intent-filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter > <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" android:resource="@xml/accessory_filter" /> </activity> </application> </manifest>

これでアプリ側は完了。



2. アクセサリ側

アプリ側で定義したオレオレプロトコルに沿って LED 用のデジタル出力ピンのレベルの HIGH/LOW を切り替えるだけ。

#include <Wire.h> #include <Max3421e.h> #include <Usb.h> #include <AndroidAccessory.h> #define LED 24 AndroidAccessory acc("uPhyca", "HelloADK", "DemoKit Arduino Board", "1.0", "http://www.android.com", "0000000012345678"); void setup(); void loop(); void init_leds() { // 24 の DIGITAL を LED 用の出力にする pinMode(LED, OUTPUT); } void setup() { Serial.begin(115200); Serial.print("\r\nStart"); init_leds(); acc.powerOn(); } void loop() { byte msg[2]; byte led; if (acc.isConnected()) { int len = acc.read(msg, sizeof(msg), 1); if (len > 0) { if (msg[0] == 0x1) { if(msg[1] == 0x1) { digitalWrite(LED, HIGH); msg[0] = 0x1; msg[1] = 0x1; acc.write(msg, 2); } else { digitalWrite(LED, LOW); msg[0] = 0x1; msg[1] = 0x2; acc.write(msg, 2); } } } } else { digitalWrite(LED, LOW); } delay(10); }

DIGITAL の 24 を LED のアノードにつないで、カソードを抵抗につないで、抵抗を GND につないでと。



最初に接続を許可するか聞かれるので OK する。



トグルボタンで LED の ON /OFF できたー。





GitHub にもおいといたよ!
yanzm/HelloADK - GitHub - http://goo.gl/bhG1t



2011年12月4日日曜日

Android DialogFragmentを使うときの注意点。

4日目〜。

---

DialogFragment で表示される Dialog を指定するための主なメソッドは次の2つ。 onCreateView() と onCreateDialog()。

Dialog のテーマと ContentView の中身だけ指定すればいい場合は onCreateView() の方だけを Override すればいいのですが、例えば OK と Cancel のある AlertDialog を作ろうとしたときは onCreateDialog() を Override して、表示したい AlertDialog のインスタンスを返すようにします。

このときに、onCreateView() で null 以外を返していると落ちます。

public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FragmentManager manager = getFragmentManager(); MainFragmentDialog dialog = new MainFragmentDialog(); dialog.show(manager, "dialog"); } public class MainFragmentDialog extends DialogFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.main, null, false); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle("タイトル"); builder.setPositiveButton("OK", null); builder.setNegativeButton("Cancel", null); return builder.create(); } } }

12-04 22:51:37.099: E/AndroidRuntime(586): Caused by: android.util.AndroidRuntimeException: requestFeature() must be called before adding content

onCreateView() を Override せずに、onCreateDialog() で返す AlertDialog に対して setView() するのはOK

public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FragmentManager manager = getFragmentManager(); MainFragmentDialog dialog = new MainFragmentDialog(); dialog.show(manager, "dialog"); } public class MainFragmentDialog extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { LayoutInflater inflater = getActivity().getLayoutInflater(); View view = inflater.inflate(R.layout.main, null, false); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle("タイトル"); builder.setPositiveButton("OK", null); builder.setNegativeButton("Cancel", null); builder.setView(view); return builder.create(); } } } これは落ちない。

onCreateDialog() を Override しないとほんとに単純なダイアログしかだせないよねー。。。



2011年12月3日土曜日

Android 4.0 で TextView のレイアウトが変わる?

3日目〜

---


TextView を継承したオリジナル CheckedTextView を作って使っているのですが、
コードは全く変えてないのにプレビューレベルで 3.2 以前と 4.0 で
それのレイアウトが変わってしまうのです。。。

これは注意ポイントかも。

うーん。。。
まだ解決法わかってないので、調査結果はあしたにでも!






2011年12月2日金曜日

第3回名古屋android勉強会 Fragment基礎講座/ハンズオン を行ったよ。

さぁ、2日目です。
いつまで続けられるかな。


---

11月19日に名古屋で行った Fragment のハンズオンの資料を載せました。
タイポとかなおってる。。。はず!




2011年12月1日木曜日

Android AlertDialog の背景を変更する for 4.x

どうも、お久しぶりです。
あんざいゆきです。
世の中の Advent Calendar 祭りに乗り遅れたので、一人 Advent Calendar やることにしました。


---

前に Android AlertDialog の背景を変更する というエントリを書いたのですが、どうも 3.0 以降だとこのハックがうまくいかないという報告があり確かめてみました。

このエントリの通りにやると


のようなダイアログになるはずなのですが、悲しいかな、Android 4.0 のエミュレータで実行したら、こんな感じになってしまいました。





orz

外枠はでてるけど、AlertDialog の内部の色が変わってないので、android:alertDialogStyle が効かなくなってるっぽいです。

AlertDialog の内側の属性値の android:topBright などを参照しているのは AlertController です。
この AlertController に android:topBright などが設定されたテーマを渡さなければいけないのですが、このテーマを渡すためのメソッドである AlertController#AlertParams() が呼ばれているのが AlertDialog.Builder 内の以下の部分だけなのです。

Android 4.0
358 public Builder(Context context) { 359 this(context, resolveDialogTheme(context, 0)); 360 } 373 public Builder(Context context, int theme) { 374 P = new AlertController.AlertParams(new ContextThemeWrapper( 375 context, resolveDialogTheme(context, theme))); 376 mTheme = theme; 377 }

つまり、AlertDialog.Builder(context) ではなく、AlertDialog.Builder(context, theme) で android:alertDialogStyle が定義されているテーマを渡さなければなりません。なぜなら、Builder(context) の方では、resolveDialogTheme() という内部メソッドに対して第2引数に 0、つまり android:alertDialogStyle が定義されていないものを渡しているからです。

一方、Android 2.3 では第2引数にテーマを渡せるコンストラクタが AlertDialog.Builder にありません

Android 2.3
272 public Builder(Context context) { 273 P = new AlertController.AlertParams(context); 274 }

しかし、AlertParams() には Builder の引数の Context をそのまま渡しているため、アプリのテーマとして設定されているものが AlertController にわたり、アプリのテーマとして android:alertDialogStyle を指定しておけば反映されるという状態でした。

で、結論としては、こうすればOKです。

// 独自ダイアログをつかってテーマを指定 public void showMyDialog(View v) { MyAlertDialog myDialog = new MyAlertDialog(this, R.style.MyDialog); myDialog.setTitle("dialog"); myDialog.setMessage("test!"); myDialog.setButton("OK", (OnClickListener) null); myDialog.show(); }
↑これは上記のエントリと同じ

<?xml version="1.0" encoding="utf-8"?> <resources> ... <style name="MyDialog" parent="@android:style/Theme.Dialog"> <item name="android:windowBackground">@drawable/rect</item> <item name="android:buttonStyle">@style/CustomButton</item> <item name="android:alertDialogStyle">@style/AlertDialog</item> </style> ... </resources>

この MyDialog の方にも @style/AlertDialog をいれます!
前はアプリケーション、もしくはアクティビティのテーマに設定していましたが、ダイアログのテーマとして指定する方にも入れると Android 4.0 でも適用されます!

ちなみに、Android 4.0 から Theme の style item の android:alertDialogTheme が追加されていました。
なので、上記のエントリで AlertDialog を継承した独自 Dialog クラスを作成していますが、これをせずに

<?xml version="1.0" encoding="utf-8"?> <resources> <style name="MyTheme" parent="@android:style/Theme.Black"> <item name="android:alertDialogStyle">@style/AlertDialog</item> <item name="android:alertDialogTheme">@style/MyDialog</item> </style> ... <style name="MyDialog" parent="@android:style/Theme.Dialog"> <item name="android:windowBackground">@drawable/rect</item> <item name="android:buttonStyle">@style/CustomButton</item> </style> ... </resources>

のようにすれば外枠やボタンを反映できるようになりました。