2010年8月9日月曜日

AdapterViewでデータバインド

 AdapterView ← Adapter ← データソース

AdapterView
Adapterを用いてViewを生成し、表示する。

Adapter
データソースからデータを読み取る。Viewを生成し、データをViewに設定する。Viewの生成には、layout.xmlによるテンプレートを使用可能。
  • ArrayAdapter:配列をデータソースとして、各要素をそれぞれTextViewにして返す。
  • SimpleCursorAdapterCursorをデータソースとする。返す要素はTextView or ImageView。Cursorの列ごと対応するViewIDを指定することで、使用するテンプレートを変更可能。
データソース
Adapterにデータを提供する。
  • Cursor:行・列を持つデータ構造。move~で行移動、get~(colIndex)で現在の行のデータ取得。

[memo]
・CursorAdapter.newViewで新しいViewを作った場合、1行分一気にできる?
→指定したlayout.xmlをinflateしてるだけぽい。

・行移動はAdapterView任せ?
→newView・bindViewともに行移動はしない。

・newViewでViewを生成→bindViewでデータセットの流れが正しい?
→正しい。View.findViewByIDで列に対応するViewを取得し、データをセットしている。

参考:SimpleCursorAdapter.bindView, ResourceCursorAdapter.newView

実行時に生成したViewにstyleを適用

簡単な方法は発見できず。
代替案は、生成したいView用のlayout.xmlを用意し、LayoutInflaterでxmlからViewを生成する。

res/values/styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Cell">
        <item name="android:background">#CDBA96</item>
        <item name="android:textColor">#FFFAF0</item>
        <item name="android:textSize">15dip</item>
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:gravity">center</item>
    </style>
</resources>
res/layout/row.xml
<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView style="@style/Cell" android:text="Cell1" />
    <TextView style="@style/Cell" android:text="Cell2" />
</TableRow>
Activity.java
LayoutInflater li = getLayoutInflater();
li.inflate(R.layout.row, (ViewGroup) findViewById(R.id.parent), true);

2010年8月4日水曜日

layoutXMLファイルでViewのカスタム属性を定義

1. カスタム属性を定義する
res/values/attr.xmlで属性を定義する。
  <xml version="1.0" encoding="utf-8"?>
  <resources>
    <attr name="textValue1" format="string" />
    <attr name="textColor" format="color" />
  </resources>
attr.xmlを編集すると、R.attrクラス内に属性定数が自動的に作成される。
  public final class R {
      public static final class attr {
          public static final int textValue1=0x7f010000;
          public static final int textColor=0x7f010001;
      }
  }
定義した属性名は、AndroidManifest.xmlで設定されている名前空間 (=Rオブジェクトの名前空間) に属する。今回は com.testapp を想定。
ただ、組み込みで大量の属性が用意されている (R.attr) ため、大抵の場合は自前で用意しなくてもいい気がする。

2. 属性を設定する
res/layout/main.xmlでViewに属性を設定する。TestView要素の属性のうち、tes名前空間のものがユーザー定義、android名前空間のものが組み込み。
  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tes="http://schemas.android.com/apk/res/com.testapp"
      android:orientation="vertical"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      >
  <com.testapp.view.TestView
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
     
      android:text="aaa"
      android:textSize="15pt"
     
      tes:textValue1="test1"
      tes:textColor="#FFFFFF"
    />
  <com.testapp.view.TestView
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
     
      android:text="aaa"
      android:textSize="4pt"
     
      tes:textValue1="test2"
      tes:textColor="#FFFFFF"
    />
  </LinearLayout>

3. 属性を読み込む
res/values/attr.xmlで、属性の配列を定義する。
属性の配列は、複数の属性を一括して読み込むため?に、複数の属性をまとめたもの。

android:textおよびandroid:textSizeは、Viewクラスでは読み込めない属性なので、TestView側で読み込み処理を書く必要がある。よって、styleableオブジェクトに含める。
  <?xml version="1.0" encoding="utf-8"?>
  <resources>

    <attr name="textValue1" format="string" />
    <attr name="textColor" format="color" />

    <!--属性の配列の定義-->
    <declare-styleable name="TestViewSet">
   
      <!--組み込みの属性-->
      <attr name="android:text" />
      <attr name="android:textSize" />
     
      <!--ユーザー定義の属性-->
      <attr name="textValue1" />
      <attr name="textColor" />
     
    </declare-styleable>

    <!--複数定義可能なことを例示するために記載-->
    <declare-styleable name="TestViewSet2">
      <attr name="android:text" />
      <attr name="textColor" />
    </declare-styleable>
   
  </resources>
R.styleable内に定義した情報が反映されていることを確認する。
  public final class R {
    public static final class styleable {
       
      //context.obtainStyledAttributes用のattr値配列
      public static final int[] TestViewSet = {
        0x01010095, 0x0101014f, 0x7f010000, 0x7f010001
      };
     
      //obtainStyledAttributesで取得したTypedArray内での、各属性のindex値
      public static final int TestView_android_text = 1;
      public static final int TestView_android_textSize = 0;
      public static final int TestView_textColor = 3;
      public static final int TestView_textValue1 = 2;
     
      //複数定義すれば、複数作成される
      public static final int[] TestViewSet2 = {
        0x0101014f, 0x7f010001
      };
      public static final int ViewBase_android_text = 0;
      public static final int ViewBase_textColor = 1;
    };
  }
TestViewクラスで読み込む
  /**
   * コンストラクタ
   */
  public TestView(Context context, AttributeSet attrs, int defStyle)
  {
    super(context, attrs, defStyle);

    //このオブジェクト用の属性セットを取得
    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TestViewSet, defStyle, defStyle);

    //android:text(string)を取得
    ta.getString(R.styleable.TestViewSet_android_text);
   
    //android:textSize(dimension)を取得
    ta.getDimension(R.styleable.TestViewSet_android_textSize, 15f);
   
    //textVaelue1(string)を取得
    ta.getString(R.styleable.TestViewSet_textVaelue1);

    //textColor(color)を取得
    ta.getColor(R.styleable.TestViewSet_textColor, Color.BLUE);
  }

2010年7月31日土曜日

XperiaでGmailアプリケーションを起動するとYouTubeが開く

再現手順は以下の通り。
  1. GmailやEMailの本文に記載されたURLから、YouTubeを起動する。
  2. Homeボタンでデスクトップに戻る
  3. Gmailを起動する
このとき、1. で中断したYouTubeが起動される。YouTube画面で「戻る」ボタンを押すと、Gmailの画面に戻る。

Androidアプリケーションはブラウザのタブで、インテントは同一タブで別アプリケーションを起動するイメージってことかな。最初は本気でバグったのかと思ったけど。

それにしても、Gmailボタンを押してYouTubeが起動されるのはおかしくない? Gmailボタンを押してるんだから、メールが読みたいに決まってる。そこでYouTube画面が出てきたら、バグったと思われても仕方ないような気がする。

仮に上記動作を良しとしても、YouTubeからGmailに戻る方法が、「戻る」ボタン連打か端末再起動しか無いのは不便すぎる。例えば、1. でYouTube起動後、複数の動画を巡回した場合、「戻る」ボタンを連打して最初の動画まで戻らないと、Gmailに戻れない。自分は「戻る」ボタン1回でGmailに戻れたから気づけたけど、動画巡回してたら今でもバグだと思ってたかもしれない。

現状のままだと、直観的じゃ無さ過ぎると思うので、改善案を3つほど考えてみた。どれか実装してくれないかなー。
  • Home画面からのアプリケーション起動は、アプリケーションごとに決められたエントリポイントから開始する。
  • インテントもHome画面からの起動と同様に、別プロセスとして起動する。
  • アプリケーションの任意終了機能を付ける。

テスト

テスト書き込み