2010年11月30日火曜日

Andoid android:scrollbarSize は バーの幅ではありません。

以前スクロールバーをカスタマイズするエントリを書きましたが、

「Android ScrollBar (ScrollView) スクロールバーをカスタマイズ」

 android:scrollbarSize はバーの幅ではなかったのです!

 いや、あの、リファレンス
 http://developer.android.com/reference/android/view/View.html#attr_android:scrollbarSize
 には、

  "Sets the width of vertical scrollbars and
  height of horizontal scrollbars."

 ってあるんですよ。バーの幅だと思うでしょ。。。

 それが、


<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbarSize="100dip"
>


とやっても、見た目に変化なし。





で、結論から言うと、

  android:scrollbarThumbVertical
 


  android:scrollbarTrackVertical

を定義して、かつ

android:scrollbarStyle に

 "insideInset" か "outsideInset"

を設定しないとだめです。

# デフォルトは insideOverlay だから気づきにくかった。。。




<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbarSize="100dip"
android:scrollbarStyle="insideInset"
android:scrollbarThumbVertical="@drawable/scrollbar_thumb"
android:scrollbarTrackVertical="@drawable/scrollbar_track"
>


この場合は、こうなります。




こういうのなんて言うのかな。
inset の幅?


 

2010年11月28日日曜日

Android タブコンテンツの黒線はなんなんだ?!

Tab を使ったレイアウトで タブコンテンツに Activity(Intent) を割り当てる場合、テーマに Theme.Light を指定するとこうなります。



ぶざまなw

この黒線はなんだ!?




結論から言うと、フルスクリーンモード以外の Activity には(多分必ず)この線が入ります。


普通のレイアウトで Theme.Light


普通のレイアウトで Theme.Light.NoTitleBar


普通のレイアウトで Theme.Light.NoTitleBar.Fullscreen


ほら、なくなったでしょ。


なので、試しにフルスクリーンでタブレイアウトにしてみました。

<application;gt タグに指定した場合


お、消えたよ。

なぜフルスクリーンモードでこの線がきえるかというと、

<item name="android:windowContentOverlay">@null</item>

を設定しているからなんです。


http://developer.android.com/reference/android/R.attr.html#windowContentOverlay

This Drawable is overlaid over the foreground of the Window's content area, usually to place a shadow below the title.

タイトルの下の影。まさにそれです。
つまり、この設定をした独自テーマを作れば別にフルスクリーンしなくてもいいんです。


と思って、タブコンテンツの <Activity> タグに上記の設定をした独自テーマを設定したんですけど、だめでした。 ><

それで、もしかして、、、、「タブコンテンツのテーマはタブホストのテーマと同じになって、個別に設定できない?」と思って、試しに TabHost の <Activity> タグに android:theme="@android:style/Theme.Black"、タブコンテンツの <Activity> タグに android:theme="android:style/Theme.Light" としたら、予想通り!タブコンテンツは黒テーマになりました。 orz

ならば、コードから!ということでタブコンテンツの onCreate() で setTheme(android.R.style.Theme_Light) したのですが、あえなく撃沈。。。




結局、TabHost のテーマに <item name="andorid:windowContentOverlay">@null<item /> 入れないとダメってことか。



ちゃんと消えるけど、TabHost のタイトルバーの下の影もきえちゃうから、なんかのっぺり。。。


タブコンテンツの onCreate() で


getWindow().setBackgroundDrawableResource(android.R.color.darker_gray);


とかやればウィンドウの背景を変えられるから、なんとかコードから windowContentOverlay の設定変えられないかなぁ。。。と思ったんだけど、runtime 時に個別の style 属性は設定できないみたい。いつか対応されるのかな。。。 http://www.mail-archive.com/android-developers@googlegroups.com/msg22161.html



のっぺりが嫌な場合は、上記のコードでタブコンテンツの背景を設定してごまかすしかないかも。



 



 

Android selector の item タグの color の注意点

 1. android:textColor に使うときは、こう書きます。



<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true"
android:color="#ff3f94be" />
<item android:state_selected="false"
android:color="#99000000" />
</selector>




 2. 一方、android:background に使うときは、こう書きます。


<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" >
<color android:color="#66000000" />
</item>
<item android:state_selected="false">
<color android:color="#33000000" />
</item>
</selector>



1.の方法を android:background に指定したり、
 2.の方法を android:textColor に設定したりすると、
 コンパイルエラーにならないけど、実行時に落ちます!


 # やっと違いがわかった。。。


 

2010年11月26日金曜日

Android 高さが横と同じになるカスタム ViewGroup を作ってみた。

こんなにまじめに View.java と ViewGroup.java と FrameLayout.java を読んだのは初めてだわ。勉強になった。getSuggestedMinimumHeight () とか、いろいろ試して結局こうなったんだけど。すっごい短いよ。
ほんとこれだけ。





package yanzm.example.squarelayoutsample;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;

public class SquareLayout extends FrameLayout {


public SquareLayout(Context context) {
super(context);
}

public SquareLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}


@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
}



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"
/>
<yanzm.example.squarelayoutsample.SquareLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:background="@android:color/white"
android:layout_weight="1"
>
<Button
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="button"
/>
</yanzm.example.squarelayoutsample.SquareLayout>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"
/>
</LinearLayout>
</LinearLayout>



ちなみに、中のボタンは表示されないけどバックグラウンドはADTのレイアウトエディタでも表示されたよ。




 

2010年11月23日火曜日

Andorid TabWidget をカスタマイズする

Android reference http://developer.android.com/resources/tutorials/views/hello-tabwidget.html では、setIndicator の引数でタイトル用の文字列とアイコンのリソースIDを渡していましたが、View を引数に指定することで、TabWidget をカスタマイズすることが可能です。


TabWidget 用の独自 View を作ります。


public class CustomTabContentView extends FrameLayout {
LayoutInflater inflater = (LayoutInflater) getApplicationContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

public CustomTabContentView(Context context) {
super(context);
}

public CustomTabContentView(Context context, String title, int icon) {
this(context);
View childview1 = inflater.inflate(R.layout.tabwidget1, null);
TextView tv1 = (TextView) childview1.findViewById(R.id.textview);
ImageView iv1 = (ImageView) childview1.findViewById(R.id.imageview);
tv1.setText(title);
iv1.setImageResource(icon);
addView(childview1);
}
}


ここでは FrameLayout を継承して、res/layout/tabwidget1.xml で指定されたレイアウトから LinearLayout, TextView ,ImageView を生成し、引数で渡された文字列と画像のリソースIDをそれぞれ TextView と ImageView に設定しています。


setIndicator にカスタムビューを指定します。


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

TabHost tabHost = getTabHost();

tabHost.setCurrentTab(1);

View childview1 = new CustomTabContentView(this, "First", R.drawable.ic_tab_home);

TabSpec firstTab = tabHost.newTabSpec("First");
firstTab.setIndicator(childview1);
firstTab.setContent(R.id.first_content);
tabHost.addTab(firstTab);

View childview2 = new CustomTabContentView(this, "Second", R.drawable.ic_tab_star);

TabSpec secondTab = tabHost.newTabSpec("Second");
secondTab.setIndicator(childview2);
secondTab.setContent(R.id.second_content);
tabHost.addTab(secondTab);

View childview3 = new CustomTabContentView(this, "Third", R.drawable.ic_tab_camera);

TabSpec thirdTab = tabHost.newTabSpec("Third");
thirdTab.setIndicator(childview3);
thirdTab.setContent(R.id.third_content);
tabHost.addTab(thirdTab);
}


tabwidget1.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/tabwidget_bg"
android:gravity="center"
>
<TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<ImageView
android:id="@+id/imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>






 

2010年11月19日金曜日

祝 Simeji 2歳 前編

拝啓 Simeji 様

 2歳の誕生日おめでとうございます。

 事情によりお蔵入りになってしまった某記事を記念に掲載いたしますw

 * 以下の内容は、2010年の、、、たぶん3月時点の内容なので、現状と異なることもあると思いますがご了承くださいませw
 

--------------------------------------------------------------


カフェモクいんたびゅー Vol.1 [前篇]



こんにちは。

あんざい ゆき です。

私はLibraroidというAndroidアプリを開発しているのですが、
知り合ったAndroid開発者さんってみんな、すごく面白い方ばかりです!

ということで、あんざいが開発者目線でAndroid開発者さんと
いろいろお話するコーナーを始めることになりました。
その名も

  カフェモクいんたびゅー

初回はSimejiで有名な開発者adamrockerさんです!!

--------------------------------------------------------------
Simejiって?

Simejiは文字入力のためのアプリです。
なんとHT-03Aの発売前から公開されていたんです。草分け的なアプリの代表格です!
詳しくはこちらSimeji -throw Life-
--------------------------------------------------------------


左上:adamrockerさん


■出だしはvoice recoderの話に…

… (インタビューに使ったボイスレコーダーを見て)…

 あんざい:「でも(ボイスレコーダー)便利だよねぇ。」

adamrocker:「昔はこんな(両手で作る四角くらいの)テープ持ってた。」

 あんざい:「Androidアプリがあるから、HT-03Aでも出来るんですけど、でも今電池がピンチだから。」

… (voice recoderアプリを見てる)…

adamrocker:「おお、きたきた、僕が知ってるやつと違うなぁ。僕が知ってるのは、なんかこの音量の針がぴゅんぴゅんぴゅんってなってるやつ。」

 あんざい:「すごいシンプル。」

adamrocker:「液晶のここの凝り方もいいですね。なんかそれっぽい。」

--------------------------------------------------------------
ここで見ていたのは『Voice Recoder』というアプリです。
Android Market Top Free Applications

あんざいは、このアプリの開発者Tokashikiさんが開発している『MyCloset』というAndroidアプリがお気に入りです。
このアプリは「Android Developer Challenge」というアプリコンテストで一次審査を突破したすごいアプリです。
カスタマイズされたUIはかわいくて女子にはたまりません。女子部のアンケートでも好きなアイコン第一位でした!
(「日本Androidの会」:チョコっとスウィートなAndroidマシンを召し上がれ~女子部イベント「Andronjoナイト!」)
開発者としても見習いたいです。ぜひお試しください!
--------------------------------------------------------------


インタビューのメモを取るあんざい


■そしてガジェットの話に…

adamrocker:「外部バッテリー持たないの?」

 あんざい:「外部バッテリーも(電池が)切れちゃった。」

adamrocker:「え、あ切れちゃったの。eneloop?」

 あんざい:「ううん、太陽電池のやつ。」

adamrocker:「ええぇ、太陽電池?」

 あんざい:「いや普通に充電もできますよ。夜はさすがに光量が足りないけど。」

adamrocker:「そんなに充電できるんだ。たいていeneloop。あぁそれかわいいね(あんざいの充電ケースを見て)。」

 あんざい:「ソーラーとしてはあまり使ってない(笑)」

adamrocker:「あぁ、普通に充電しても使えるってことか。
容量は?これがPANS? あんざいさんはPANSという充電池を常備してるみたいです。」

adamrocker:「パンズ?パンツ? パンツってだれ?」

 あんざい:「パンツくんは、あれですよ。東のエデンですよ。」

…(脱線)…



あんざいの充電器PANS & adamrockerさんがかわいいと言ったケース


adamrocker:「ちなみに、これどのくらい充電できるんですか? 容量とか覚えてます?」

 あんざい:「うーん。書いてない。覚えてない。」

adamrocker:「バッテリーは充電容量見ないと。」

 あんざい:「みんなeneloopなのね。」

adamrocker:「eneloopいいよ。USBの口が2個付いてて。ガジェッターはだいたい何個か持ってるでしょ。これだとiPhoneとAndroidにつなげて両方充電できるし、iPhoneだったら2回分充電できる。」

 あんざい:「これ(PANS)もフルぐらいはいけるよ。」

adamrocker:「じゃあそれ2個分くらい。それ2個持ってもいいんだけど(笑)大きさもこのくらいで。あれかなりいいよ。ちょっとどっか行くときは必ず持っていく。」

 あんざい:「adamrockerさんはeneloopおすすめ、と。」

adamrocker:「たしか2400くらいある。多分ね(本当は 5000mAh 3.7V)。iPhone 2.何回分とか書いてあったはず。」

--------------------------------------------------------------
ここでeneloopと言っているのは、KBC-L2ASという商品のことです。
詳しくはSANYOのホームページ SANYO USB出力付リチウムイオンバッテリー KBC-L2ASをご覧ください。

PANSと言っている、あんざい使用の充電器はこちらです。
 モバイル ソーラー充電器 DR. SOLAR CHARGER 【ブラック】PANS SHOP

実はHT-03Aには予備バッテリーがついてきます。わざわざケースまでついてきます。でもあんざいは使ってません。単に後ろのカバーを外すのが面倒なのです。
HT-03Aをガンガン使うなら充電器は必須ですよ~。
--------------------------------------------------------------



adamrockerさん使用のeneloop充電器 (KBC-L2AS)



■携帯電話何台持っているかの話に…

 あんざい:「ちなみに携帯何台持ってますか?」

adamrocker:「iPhoneとNexus OneとHT-03AとGDD PhoneとG1とSmart Q5もってます。ええとね、Smart Q5もらった、G1買った、GDD Phoneもらった。Nexus One買った。iPhone買った。買ってるなぁ。結構。」

 あんざい:「結構買ってるじゃん!」

adamrocker:「おかしいなぁ。もらうためにやってたのになぁ(笑)。開発してるともらえるかなぁって、甘かった。欲しくなっちゃった。」


■ 『iPhone』

 あんざい:「iPhoneってどうですか?」

adamrocker:「最高。」

 あんざい:「(笑)いつ買ったんですか?」

adamrocker:「ちょうど一年前くらい。iPhone使うまえにずっとiPodとWi-Fiでやってたのね。それでフリック入力が大好きで。普通の携帯も持ってたんだけど、もう押すのが嫌で。ずっとメール一行とかで。さすがにこれはまずいと思ってiPhone買ったのね。それでAndroid 1.5でソフトキーボードが出たときに、Androidでもできるんじゃない?と思って。」

 あんざい:「iPhoneはやっぱりタッチの感度がすごいですよね。」

adamrocker:「あとはディスプレイがすごくきれい。特に色の再現性が高くて、そこはジョブスのこだわりがあるんだなと思う。独自のアンチエイリアスとか細かい気遣いがされてるなって。見てて疲れない。」


--------------------------------------------------------------
ちなみに、あんざいはこんなに持ってません。HT-03AとHTC tattooとGDD Phoneの3台です。
残念ながら、あんざいはiPhoneを持っていませんが、触らせてもらったときに、タッチの感度にすごく感動したのを覚えています。
よくiPhoneと比較してAndroidの利点を語るときにマルチタスクが上げられていますが、あんざいはWidgetなどで画面をカスタマイズすることができる点がすごく好きです。
--------------------------------------------------------------


adamrockerさんお気に入りのiPhoneの画面



■ずばり今年Androidは…

 あんざい:「今年たくさん出ますよね。auが6月でしたっけ。ちょっと先だなぁ。」

adamrocker:「Xperiaが4月でしょ。だからネタ的には大丈夫だと思う。5月にGoogle IOでしょ。そこで多分Nexus 2でしょ。6月にauでしょ。だからネタ的にはずっと絶え間なく続くんじゃないかな。えらいこっちゃ(笑)。毎月のようにニュースが流れてくるよ。」

 あんざい:「来年あたり家電でるかなぁ?」

adamrocker:「家電は作るのに時間かかるからね。チャンビーみたいのだったら出てきてもいいと思うんだけど。ディスプレイだけで、コンセントにつながってて目覚ましがついてて。あれ結構かわいい。」

 あんざい:「確かにおもちゃが先に出そうですね。」

--------------------------------------------------------------
ほんとうに、今年は端末がかなりでてくると思います。
また、カーナビなど組み込みへの応用も進むと思います。
何にせよ楽しみでしかたありません!
--------------------------------------------------------------



■カフェモクモク

 あんざい:「開発は主に家ですか?カフェではやらない?」

adamrocker:「うーん。昔はね結構カフェで。でもMacBookの電池がヘタってきてね、出れなくなっちゃって。うん。カフェはいいと思う。モクモクとか楽しいし集中できる。家だとついつい遊んじゃうし。家のいいところは環境がいいところかな。ディスプレイが大きいとか、マシンがいいとか、自分の好きな曲聞けるとか。」

 あんざい:「重たい本を持たなくていいし。でもなぜかカフェとかに行くとはかどるんだよね。」

adamrocker:「隣でカチャカチャしてると燃えてくるというか。負けねぇぞみたいな(笑)」

 あんざい:「モクモクすると時間が鳥のように飛んでいってしまうんだよね~。その分きっと楽しいんだと思う。」


--------------------------------------------------------------
モクモクとは開発者があつまって黙々とひたすら開発をすることです。楽しいです。
--------------------------------------------------------------



おしゃれなレストランにてインタビュー



■いつ開発してる?

 あんざい:「平日って開発します?」

adamrocker:「もちろんします。ぷらーっと帰って、ぷらーっと。趣味だから(笑)」

 あんざい:「でも私もそうなんだ~」

adamrocker:「結構ねぇ。やりたいときにできないと、こうムラムラするというか(笑)。気楽にコーヒー置いてやってます。今のSimejiの前のバージョンを作ってたときはビールを飲みながらやるのがはやってた。個人的に。わかんねーとか言いながら(笑)。一ヶ月くらい。結構楽しかった。ドリンキングコーディングおすすめです。」

 あんざい:「たまにやりますよ。なんかドリンキングコーディングってごろいいですね。(笑)」

adamrocker:「寝ながらデバッグとか、睡眠デバッグとか言うじゃん。知らない?夢のなかでデバックするの。もうすぐ到達できるようになると思う。寝てるときにデバックして、起きたらできたー!!ってなって、朝起きてばーってやって、ほらね、みたいに解決するの。」

 あんざい:「でもそれってありえますよ。脳みそ動いているわけだから寝てる間に。よく、夜の勉強は中途半端にやれって言いますよね。だから夜のコーディングは中途半端にやれと。」

adamrocker:「朝になったらできてるっていう。確かに、夜はコーディングって感じがするなぁ。朝はプログラミングって感じだけど。」


--------------------------------------------------------------
Android界隈では、こんなアプリがほしいなぁとTwitterでつぶやくと朝にはできている。という現象が起こりました。かっこいいですね。そんな開発者になりたいです。
--------------------------------------------------------------



■Android開発者は面白いよ!
 
 あんざい:「Android開発者って…」

adamrocker:「変な人ばっかでしょ。」

 あんざい:「自分も変な人だから、人のこと言えない。」

adamrocker:「変な人には変な人がくる。だから全員変な人(笑)。Androidのいいところは、ハードとOSとか、アプリとかクラウド、まぁサーバーとか全部入ってくるから。今度Flashも入ってくるでしょ。かなり面白いよ。」

adamrocker:「Androidは技術者にとってパラダイス感があるね。一番最初に面白かったのは、Androidの会が出来る前にみんなで集まろうって飲み会したときにハードの人がきて、その人が飲み会の席で基盤出すのね(笑)。で、「ここの回路が…」とか言い出して。なんだこれ、超面白いって思って。あれね、すごいショックだった。飲み会でハード出すんだーって(笑)。そのときチャンビーがはやってて、みんな分解してるのね。ぱかって開けて「このスイッチめっちゃチープやでー」とか言いながら。うわー、すごいなこの人って思って。超面白い。」

 あんざい:「iPhoneの中にAndroidいれちゃう人とかいますからねー。」

adamrocker:「ほんと勇者だよ。あの人は。」


--------------------------------------------------------------
あんざいが開発しているLibraroidというアプリにもたくさんのアドバイスをいただいています。
みなさん、ありがとうございます!!
開発者はコメントにとても励まされます。もっと良くしよう!と気合が入ります。
気に入ったアプリ、すごいなと思ったアプリには、ぜひぜひコメントしてください。

Androidの開発者の方々は本当に面白い方が多いです。
まさに少年の心を持った大人の方がたくさんいます。
そして、いろんな要素が含まれているAndroidは、自分の専門以外の話が聞ける点が一番楽しかったりします。
--------------------------------------------------------------



adamrockerさんはペン回しが上手



■カフェモクいんたびゅー

これで前篇は終わりです。

このタイトルですが、Androidアプリを開発するようになって、開発者さん達と集まってひたすら開発する『モクモク』がものすごく楽しくて、Android開発 = モクモクくらいの認識があるのです。そこで、ここからタイトルをつけさせていただきました。

後篇では、adamrockerさんの Androidアプリを開発するきっかけやSimejiアプリの開発についてご紹介します!お楽しみに!

-----------------------------------------------------------------



 今読むと、突っ込みどころ満載ですねw
 Google I/O で 2 でなかったしねー。。。

2010年11月18日木曜日

Android カスタムタイトルバーを使う

タイトルバーのレイアウトを XML で定義することができます。

setContentView の前に

 requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);

で宣言し、setContentView のあとにXMLファイルを

 getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);

で指定します。



@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);

setContentView(R.layout.viewer2);

getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);

// Set up the custom title
TextView mTitle;
mTitle = (TextView) findViewById(R.id.title_left_text);
mTitle.setText(R.string.app_name);
mTitle = (TextView) findViewById(R.id.title_right_text);
mTitle.setText(R.string.app_version);
}




<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
<TextView
android:id="@+id/title_left_text"
android:layout_alignParentLeft="true"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:ellipsize="end"
android:singleLine="true"
style="?android:attr/windowTitleStyle"
android:gravity="center_vertical" >
/>
<TextView
android:id="@+id/title_right_text"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#fff"
android:gravity="center_vertical" >
/>
</RelativeLayout>



 android:gravity="center_vertical" > は root view (この場合 RelativeLayout) ではなく、子要素(この場合 TextView)に入れないと、反映されませんでした!

2010年11月11日木曜日

Android Tab Layout

Tab Layout

 タブ UI を実現するには、TabHostTabWidget を使う必要がある

 ・TabHost はレイアウトの root node でなければならない

 ・TabHost は、タブを表示するための TabWidget とタブの内容を
  する Framelayout を含む

 

 タブコンテンツの実装方法には2つある
  
  1. 同じ Activity で、タブごとに View を切り替える

  2. タブごとに別々の Activity を割り当てる


 ■ 同じ Activity で、タブごとに View を切り替える

  Tech Booster さんの説明がわかりやすいです。

  ・タブメニューを利用する - Tech Booster -



 ■ タブごとに別々の Activity を割り当てる

 
  1. 各タブの Activity を用意する


public class Tab1Activity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

TextView textview = new TextView(this);
textview.setText("This is Tab1");
setContentView(textview);
}
}


  AndroidManifest.xml に <activity> を追加するのを忘れずに!


  2. タブ用のアイコンを準備する

    タブが選択されたときと、選択されていないときの2種類用意する
 
    用意した2つのアイコンで state-list drawable を定義する


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- When selected, use grey -->
<item android:drawable="@drawable/ic_tab_home_selected"
android:state_selected="true" />
<!-- When not selected, use white-->
<item android:drawable="@drawable/ic_tab_home_unselected" />
</selector>


    tab 用のアイコンの作成は簡単なのであれば、
    Android Asset Studio が便利!


  3. main.xml を作成


<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp" />
</LinearLayout>
</TabHost>


   TabHost は TabWidget を FrameLayout を含まなければならない
   さらに、それらの ID は tabs と tabcontent でなければならない
   上記のレイアウトでは FrameLayout が空なので、TabHost は
   自動的に Activity を割り当てる


  5. ランチャー Activity が TabActivity を継承するようにする


public class HelloTabWidget extends TabActivity {



  6. onCreate() の中身を実装する


public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// Resource object to get Drawables
Resources res = getResources();

// The activity TabHost
TabHost tabHost = getTabHost();

// Resusable TabSpec for each tab
TabHost.TabSpec spec;

// Reusable Intent for each tab
Intent intent;

// Create an Intent to launch an Activity
// for the tab (to be reused)
intent = new Intent().setClass(this, Tab1Activity.class);

// Initialize a TabSpec for each tab and
// add it to the TabHost
spec = tabHost.newTabSpec("tab1")
.setIndicator("Home",
res.getDrawable(R.drawable.ic_tab_home))
.setContent(intent);
tabHost.addTab(spec);

// Do the same for the other tabs
intent = new Intent().setClass(this, Tab2Activity.class);
spec = tabHost.newTabSpec("tab2")
.setIndicator("Camera",
res.getDrawable(R.drawable.ic_tab_camera))
.setContent(intent);
tabHost.addTab(spec);

intent = new Intent().setClass(this, Tab3Activity.class);
spec = tabHost.newTabSpec("tab3")
.setIndicator("Star",
res.getDrawable(R.drawable.ic_tab_star))
.setContent(intent);
tabHost.addTab(spec);

tabHost.setCurrentTab(2);
}


   getTabHost() で TabHost を取得し、Tab.Spec で生成した
   各タブを TabHost.addTab() で TabHost に追加する
   setCurrentTab() で初期起動時のタブを設定する





 

Android Android Market で Release Note を書けるようになったよ!

Android Developers Blog: Android Market Action

を読んでて、

Fifth, we rolled in a “recent changes” feature, a place for developers to put their release notes. Android Market has a zero-friction process for app update, and the really great apps have followed the “release early, release often” philosophy. As a developer, I like having a place to write down what’s behind an app release, and as a person who downloads lots of apps, I like to know what the goodies are in each new update.

ん?

a “recent changes” feature, a place for developers to put their release notes

リリースノートをかける場所なんてあったっけ?って思って Android developer console を見てみたら、ありました!いつできたんだろう?



Release Changes というフォームが追加されています。



さっそく入力してみました!

マーケット側の表示がこんな感じになりました。





「最近の変更点」という部分が増えてます~。



 

Hudson

http://hudson-ci.org

参考サイト
 ・http://gihyo.jp/dev/feature/01/hudson


「継続的インテグレーション」(Continuous Integration,以下CI)ツールの一つ


■ CI

Extreme Programmingに端を発し、Martin Fowlerによって広められた概念
狭義には、別々に開発された部品を持ち寄ってお互いの動作を検証する「統合テスト」を早い段階から恒常的に行うこと


■ Hudson

 ● 利点

  1.インストール・セットアップが簡単

    ・インストール処理の必要なし
    ・設定は全てウェブ上から可能
    ・入力内容に対してリアルタイムで検査が走ってエラーが表示される

  2.拡張性が高い

    ・プラグインを通じて機能拡張が可能
    ・既に多くのプラグインが開発されいる

 (参考サイト: http://gihyo.jp/dev/feature/01/hudson/0001)



 ● インストール

  http://hudson-ci.org/ にアクセスして、
  Download hudson.war の下のリンクをクリック



  ダウンロードした hudson.war を適当なディレクトリに移動して

  > java -jar hudson.war




  成功すれば、localhost:8080 にアクセスすると、Hudson が起動している




 ● ジョブの作成

   左上の新規ジョブ作成からジョブを作成できる

   ジョブの種類は以下の5つから選択できる
     ・フリースタイル・プロジェクトのビルド
     ・Maven2プロジェクトのビルド
     ・外部ジョブの監視
     ・マルチ構成プロジェクトのビルド
     ・既存ジョブのコピー



 ● HelloHudson を作ってみる

   フリースタイル・プロジェクトのビルドで「HelloHudson」を作成してみる



   OKを押すと、プロジェクトの設定画面に移る

   ビルドを Windowsバッチコマンドの実行 にする



   コマンドの入力フォームがでるので、文字列を出力させるコマンドを指定する



   保存をクリック


  
 ● ビルド実行

   左側のビルド実行をクリック



   
   ダッシュボードからなら、プロジェクトに右側の時計をクリック




 ● 実行結果の確認
 
   左側のビルド履歴をクリック



   クリックするとこんな画面



   左側のコンソール出力をクリックすると、コンソールでの出力結果が確認できる




 ● 実行結果の確認

   ダッシュボードの左側の 「Hudson の管理」をクリック

2010年11月7日日曜日

Android Quick Action の Android ライブラリプロジェクトを作ってみた

Google I/O の UI design pattern で紹介されていた、Quick Action (twitter アプリや Gmail アプリで使われている) は標準の SDK の中にはありません。

# Quick Action については以前のエントリ「Android UI Design Tips」でも簡単にかきました。

leibun さんのブログのエントリがわかりやすいです。
QuickActionの使い方 - leibunのテクニカルブログ -

leibun さんのブログでも紹介されてますが、
 ・http://code.google.com/p/simple-quickactions/
 ・http://www.londatiga.net/it/how-to-create-quickaction-dialog-in-android/

とかでソースが公開されています。

http://www.londatiga.net/it/how-to-create-quickaction-dialog-in-android/ のコードを落としてきてアプリにソースや画像をコピーしてもいいのですが、コピーするの面倒だし、2つのスタイルが別々になっているし、一つのアプリで複数のスタイルの QuickAction を使えないし、Trackアニメーションを設定できないので fork してみました。

 サンプル apk はこちら

 ・スタイルを twitter 公式アプリの QuickAction っぽいの
  or シンプルなポップアップ から選択可能
 ・外枠用の layout リソースIDを指定可能
 ・中の item 用の layout リソースIDを指定可能
 ・Track アニメーションの interpolator の指定が可能












https://github.com/yanzm/QuickActionLib からダウンロードして、Eclipse に Import します。QuickActionLib の Properties で [Android] - [Library] の Is Library をチェックし、Android ライブラリプロジェクトにします。




使う方では、プロジェクトの Properties で [Android] - [Library] - [Add] から QuickActionLib を追加






こんな感じで使う


package yanzm.example.quickactiontest2;

import yanzm.products.quickaction.lib.ActionItem;
import yanzm.products.quickaction.lib.QuickAction;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class QuickActionTest2 extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);


// 1st item
final ActionItem chart = new ActionItem();

chart.setTitle("Chart");
chart.setIcon(getResources().getDrawable(R.drawable.chart));
chart.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(QuickActionTest2.this, "Chart selected" , Toast.LENGTH_SHORT).show();
}
});


// 2nd item
final ActionItem production = new ActionItem();

production.setTitle("Products");
production.setIcon(getResources().getDrawable(R.drawable.production));
production.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(QuickActionTest2.this, "Products selected", Toast.LENGTH_SHORT).show();
}
});


// 3rd item
final ActionItem budget = new ActionItem();

budget.setTitle("Budget");
budget.setIcon(getResources().getDrawable(R.drawable.budget));
budget.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(QuickActionTest2.this, "Budget selected", Toast.LENGTH_SHORT).show();
}
});

// 4th item
final ActionItem dashboard = new ActionItem();

dashboard.setIcon(getResources().getDrawable(R.drawable.dashboard));
dashboard.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(QuickActionTest2.this, "dashboard selected" , Toast.LENGTH_SHORT).show();
}
});


// 5th item
final ActionItem users = new ActionItem();

users.setIcon(getResources().getDrawable(R.drawable.users));
users.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(QuickActionTest2.this, "Products selected", Toast.LENGTH_SHORT).show();
}
});


// Default Setting
Button btn1 = (Button) this.findViewById(R.id.btn1);
btn1.setText("Default Setting");
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
QuickAction qa = new QuickAction(v);

qa.addActionItem(chart);
qa.addActionItem(production);

qa.show();
}
});

// ANIM_AUTO
Button btn2 = (Button) this.findViewById(R.id.btn2);
btn2.setText("QuickAction.ANIM_AUTO\nmany Items");
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
QuickAction qa = new QuickAction(v);

qa.addActionItem(chart);
qa.addActionItem(production);
qa.addActionItem(chart);
qa.addActionItem(production);
qa.addActionItem(chart);
qa.addActionItem(production);
qa.addActionItem(chart);
qa.addActionItem(production);
qa.setAnimStyle(QuickAction.ANIM_AUTO);

qa.show();
}
});


// ANIM_GROW_FROM_CENTER
Button btn3 = (Button) this.findViewById(R.id.btn3);
btn3.setText("QuickAction.ANIM_GROW_FROM_CENTER\nItems have no title\nNo Interpolator");
btn3.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
QuickAction qa = new QuickAction(v);

qa.addActionItem(dashboard);
qa.addActionItem(users);
qa.setAnimStyle(QuickAction.ANIM_GROW_FROM_CENTER);
qa.setAnimTrack(R.anim.rail2, null);

qa.show();
}
});

// ANIM_GROW_FROM_CENTER
Button btn4 = (Button) this.findViewById(R.id.btn4);
btn4.setText("QuickAction.ANIM_GROW_FROM_LEFT\nItems have no title\nNo Track Animation");
btn4.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
QuickAction qa = new QuickAction(v);

qa.addActionItem(dashboard);
qa.addActionItem(users);
qa.setAnimStyle(QuickAction.ANIM_GROW_FROM_LEFT);
qa.setAnimTrackEnabled(false);

qa.show();
}
});

// ANIM_GROW_FROM_CENTER
Button btn5 = (Button) this.findViewById(R.id.btn5);
btn5.setText("QuickAction.ANIM_GROW_FROM_RIGHT");
btn5.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
QuickAction qa = new QuickAction(v);

qa.addActionItem(chart);
qa.addActionItem(users);
qa.setAnimStyle(QuickAction.ANIM_GROW_FROM_RIGHT);

qa.show();
}
});

// ANIM_GROW_FROM_CENTER
Button btn6 = (Button) this.findViewById(R.id.btn6);
btn6.setText("QuickAction.ANIM_REFLECT");
btn6.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
QuickAction qa = new QuickAction(v);

qa.addActionItem(dashboard);
qa.addActionItem(users);
qa.setAnimStyle(QuickAction.ANIM_REFLECT);
qa.setLayoutStyle(QuickAction.STYLE_BUTTON);

qa.show();
}
});



//////////////////////////////////////////////////////

// Default Setting + STYLE_LIST
Button btn7 = (Button) this.findViewById(R.id.btn7);
btn7.setText("QuickAction.STYLE_LIST");
btn7.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
QuickAction qa = new QuickAction(v);

qa.addActionItem(chart);
qa.addActionItem(production);
qa.setLayoutStyle(QuickAction.STYLE_LIST);

qa.show();
}
});

// ANIM_AUTO
Button btn8 = (Button) this.findViewById(R.id.btn8);
btn8.setText("QuickAction.ANIM_AUTO\nQuickAction.STYLE_LIST with Constractor");
btn8.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
QuickAction qa = new QuickAction(v, R.layout.popup2, QuickAction.STYLE_LIST);

qa.addActionItem(chart);
qa.addActionItem(production);
qa.setAnimStyle(QuickAction.ANIM_AUTO);

qa.show();
}
});

// ANIM_GROW_FROM_CENTER
Button btn9 = (Button) this.findViewById(R.id.btn9);
btn9.setText("QuickAction.ANIM_GROW_FROM_CENTER\nQuickAction.STYLE_LIST\nsetItemLayoutId()");
btn9.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
QuickAction qa = new QuickAction(v);

qa.addActionItem(dashboard);
qa.addActionItem(users);
qa.setAnimStyle(QuickAction.ANIM_GROW_FROM_CENTER);
qa.setLayoutStyle(QuickAction.STYLE_LIST);
qa.setItemLayoutId(R.layout.action_item);

qa.show();
}
});

// ANIM_GROW_FROM_CENTER
Button btn10 = (Button) this.findViewById(R.id.btn10);
btn10.setText("QuickAction.ANIM_GROW_FROM_LEFT");
btn10.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
QuickAction qa = new QuickAction(v);

qa.addActionItem(budget);
qa.addActionItem(production);
qa.setAnimStyle(QuickAction.ANIM_GROW_FROM_LEFT);
qa.setLayoutStyle(QuickAction.STYLE_LIST);
qa.setItemLayoutId(R.layout.action_item2);

qa.show();
}
});

// ANIM_GROW_FROM_CENTER
Button btn11 = (Button) this.findViewById(R.id.btn11);
btn11.setText("QuickAction.ANIM_GROW_FROM_RIGHT\ncustom layout");
btn11.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
QuickAction qa = new QuickAction(v, R.layout.popup2, QuickAction.STYLE_LIST);

qa.addActionItem(budget);
qa.addActionItem(production);
qa.setAnimStyle(QuickAction.ANIM_GROW_FROM_RIGHT);
qa.setItemLayoutId(R.layout.action_item3);

qa.show();
}
});

// ANIM_GROW_FROM_CENTER
Button btn12 = (Button) this.findViewById(R.id.btn12);
btn12.setText("QuickAction.ANIM_REFLECT");
btn12.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
QuickAction qa = new QuickAction(v);

qa.addActionItem(dashboard);
qa.addActionItem(users);
qa.setAnimStyle(QuickAction.ANIM_REFLECT);
qa.setLayoutStyle(QuickAction.STYLE_LIST);

qa.show();
}
});
}
}

 

 

2010年11月5日金曜日

Android 普通の LinearLayout で ExpandableListView っぽいのをつくる

前回のエントリ「Android ExpandableListView の子要素に EditText を入れるといろいろめんどい」で、ExpandableListView の中に EditText を入れるのは大変だと書きました。実は普通にやったほうが簡単だったりします。

キモは <include> タグ です。

こんなふうに同じレイアウトを何個も使う場合、<include> タグを使うとすっきりします。
<include> タグについては BootCamp のときにも話しました。
BootCamp を開催したよ。

サンプルでは、デフォルトのボタンを使ったのでダサいですが、そこはうまくカスタマイズしてください。




package yanzm.example.fakeexpandablelisttest;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;

public class FakeExpadableListTest extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

int groupIds[] = {R.id.group1, R.id.group2, R.id.group3, R.id.group4, R.id.group5, R.id.group6};

for(int i = 0 ; i < groupIds.length; i++) {
LinearLayout ll = (LinearLayout) findViewById(groupIds[i]);
Button button = (Button)ll.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LinearLayout ll = (LinearLayout)v.getParent();
LinearLayout groupll = (LinearLayout)ll.findViewById(R.id.inputgroup);
switch(groupll.getVisibility()) {
case View.GONE:
groupll.setVisibility(View.VISIBLE);
break;
case View.VISIBLE:
groupll.setVisibility(View.GONE);
break;
}

((Button)v).setText("Hidden Group");
}
});
}
}
}



main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<include android:id="@+id/group1" layout="@layout/item" />
<include android:id="@+id/group2" layout="@layout/item" />
<include android:id="@+id/group3" layout="@layout/item" />
<include android:id="@+id/group4" layout="@layout/item" />
<include android:id="@+id/group5" layout="@layout/item" />
<include android:id="@+id/group6" layout="@layout/item" />
</LinearLayout>


item.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
<
<Button
android:id="@+id/button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Show Group"
/>

<LinearLayout
android:id="@+id/inputgroup"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="32dip"
android:visibility="gone"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<TextView
android:layout_width="30dip"
android:layout_height="wrap_content"
android:text="住所"
android:layout_marginLeft="5dip"
/>
<EditText
android:id="@+id/edittext1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="text"
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<TextView
android:layout_width="30dip"
android:layout_height="wrap_content"
android:text="氏名"
android:layout_marginLeft="5dip"
/>
<EditText
android:id="@+id/edittext2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="text"
/>
</LinearLayout>
<RadioGroup
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<RadioButton
android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="男性"
/>
<RadioButton
android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="女性"
/>
</RadioGroup>
</LinearLayout>
</LinearLayout>

2010年11月4日木曜日

Android ExpandableListView の子要素に EditText を入れるといろいろめんどい

ML で、ExpandableListView の子要素に EditText を入れたい、というのがあったので試したのですが、まぁめんどいです。

まず、IMEで変換候補がポップアップされると、ListView が再描画されてしまいます。つまり、BaseAdapter だと getView() が、ExpandableListView だと getGroupView() や getChildView() がそのつど呼ばれます。そうすると、EditText にあてていた Focus が外れてしまうため、変換候補がでた瞬間に変換候補のポップアップが消えるというやっかいな現象が起こってしまうわけです。

対処方法(というか、とりあえずの解決策)は、requestFocus() することです。子要素が EditText だったり、 EditText が一つだけ含まれる LinearLayout だったりする場合は、convertView.requestFocus() でいいのですが、2個以上の EditText が含まれる場合は、どの EditText が requestFocus するかを保持しておかないとうまくいきません。

さらにさらに、EditText に入力された内容は、adapter で管理して、group を展開した場合の初期値に入れないと、1つの子要素で入力したものが、すべての子要素に伝搬します。これを制御するのはとても面倒です。なぜなら、最初にいったように毎回 ListView が再描画されてしまうので、へたに getView で adapter のデータを editText に setText すると、入力してもしても入力されない、という状況になるからです。

というわけで、サンプルです。
# 正直使えませんw




package yanzm.example.expandablelisttest3;

import java.util.ArrayList;
import java.util.List;

import android.app.ExpandableListActivity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.EditText;
import android.widget.TextView;

public class ExpandableListTest3 extends ExpandableListActivity {

MyExpandableListAdapter mAdapter;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setList();
}

public void setList() {
final int LIST_SIZE = 10;

List<String> groupList = new ArrayList<String>();
List<List<String>> childList1 = new ArrayList<List<String>>();
List<List<String>> childList2 = new ArrayList<List<String>>();

List<String> childData = new ArrayList<String>();
childData.add("");

for (int i = 0; i < LIST_SIZE; i++) {
groupList.add("Title: " + i);
childList1.add(childData);
childList2.add(childData);
}

mAdapter = new MyExpandableListAdapter(groupList, childList1, childList2);
setListAdapter(mAdapter);
}

public class MyExpandableListAdapter extends BaseExpandableListAdapter {
private List<String> groups;
private List<List<String>> childs1;
private List<List<String>> childs2;

private boolean isFocusEditText1 = true;

private LayoutInflater inflater;

public MyExpandableListAdapter(List<String> groups, List<List<String>> childs1, List<List<String>> childs2) {
this.groups = groups;
this.childs1 = childs1;
this.childs2 = childs2;

this.inflater = (LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

public int getRowId(int groupPosition) {
return groupPosition;
}

public Object getChild(int groupPosition, int childPosition) {
return childs1.get(groupPosition).get(childPosition);
}

public Object getChild2(int groupPosition, int childPosition) {
return childs2.get(groupPosition).get(childPosition);
}

public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}

public int getChildrenCount(int groupPosition) {
return childs1.get(groupPosition).size();
}

public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
Log.d("getChildView", "groupPosition = " + groupPosition + ", childPosition = " + childPosition + ", isLastChild = " + isLastChild + ", convertView = " + convertView + ", ViewGroup = " + parent);

if(convertView == null) {
convertView = inflater.inflate(R.layout.child_item, parent, false);
}
EditText editText1 = (EditText)convertView.findViewById(R.id.edittext1);
editText1.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
Log.d("onFocusChanged1", "View : " + v + ", hasFocus : " + hasFocus);
if(hasFocus)
isFocusEditText1 = true;
}
});
EditText editText2 = (EditText)convertView.findViewById(R.id.edittext2);
editText2.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
Log.d("onFocusChanged2", "View : " + v + ", hasFocus : " + hasFocus);
if(hasFocus)
isFocusEditText1 = false;
}
});
if(isFocusEditText1)
editText1.requestFocus();
else
editText2.requestFocus();

return convertView;
}

public Object getGroup(int groupPosition) {
return groups.get(groupPosition);
}

public int getGroupCount() {
return groups.size();
}

public long getGroupId(int groupPosition) {
return groupPosition;
}

public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
Log.d("getGroupView", "groupPosition = " + groupPosition + ", isExpanded = " + isExpanded + ", convertView = " + convertView + ", ViewGroup = " + parent);

if(convertView == null) {
convertView = inflater.inflate(R.layout.group_item, parent, false);
}
if(convertView instanceof TextView) {
((TextView)convertView).setText(getGroup(groupPosition).toString());
}
return convertView;
}

public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}

public boolean hasStableIds() {
return true;
}
}
}


group_item.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingLeft="48dip"
android:minHeight="?android:attr/listPreferredItemHeight"
/>


child_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingLeft="64dip"
android:paddingRight="12dip"
android:minHeight="?android:attr/listPreferredItemHeight"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<TextView
android:layout_width="30dip"
android:layout_height="wrap_content"
android:text="住所"
android:layout_marginLeft="5dip"
/>
<EditText
android:id="@+id/edittext1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="text"
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<TextView
android:layout_width="30dip"
android:layout_height="wrap_content"
android:text="氏名"
android:layout_marginLeft="5dip"
/>
<EditText
android:id="@+id/edittext2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="text"
/>
</LinearLayout>
<RadioGroup
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<RadioButton
android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="男性"
/>
<RadioButton
android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="女性"
/>
</RadioGroup>
</LinearLayout>


個人的には、可変長でない場合は素直に LinearLayout でレイアウトして、Visibility で管理したほうがいいのでは、と思いました。 → 次のエントリ??


# それにしても、 RadioGroup の挙動も微妙だった。あんまり RadioGroup 使わないから、もしかして使い方まちがってるのかもw

Android Adapter & ListView & ExpandableListView

第3回デザイン部勉強会で Adapter (と関連する ListView ,ExpandableListView)について話しました。

資料です。



これようのサンプルアプリも作ったので、
こちらからどうぞ

https://github.com/yanzm/AdapterSamples



 

2010年11月1日月曜日

Android LevelListDrawable

LevelListDrawable は Level(整数値)と画像リソースをひも付けて、1つの drawable で複数の画像リソースを切り替えられるようにしたもの

各 Level と 画像リソースの紐付けは XML ファイルで定義することができる

LevelList

setLevel() で drawable に level を設定すると、level list 内を上から走査していって、設定値より大きい or 等しい android:maxLevel が定義されたリソースを読み込む

XMLファイルの保存場所 : res/drawable/filename.xml 
(ファイル名はリースID になる)

リソースの参照
  Java: R.drawable.filename
  XML: @[package:]drawable/filename

syntax:

<?xml version="1.0" encoding="utf-8"?>
<level-list
xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:drawable="@drawable/drawable_resource"
android:maxLevel="integer"
android:minLevel="integer" />
</level-list>


elements:

 <level-list>

  root element でなければならない
  1つ以上の <item> element を含む

 <item>
 
  level にひも付ける画像リソースを定義する

  attributes:

    android:drawable
   
      設定する画像リソースの ID、必須

    android:maxLevel

      整数値、この item が許可する最大 level

    android:minLevel
  
      整数値、この item が許可する最小 level

example:

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:drawable="@drawable/status_off"
android:maxLevel="0" />
<item
android:drawable="@drawable/status_on"
android:maxLevel="1" />
</level-list>


View に割り当てた後は、level は setLevel() もしくは setImageLevel() で変更可能


# 電池の残量はこれが使われている
# パラパラアニメーションとかつくれるはず