2013年1月14日月曜日

[Android TIPS] 色々な情報をリストに表示する

色々な情報をリストに表示するというのをやってみました。
表示対象は画像と文字列ですが、予め res/drawable とコード内に用意しました
国旗の画像は ここ から取得し、flag01.png ~ flag08.png とし、res/drawable 配下に配置しています。

サンプル野良apkはこちらからどうぞ。

レイアウトはこんな感じ。

activity_country.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <HorizontalScrollView
        android:id="@+id/scrollView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="#FFFFFF" >

        <LinearLayout
            android:id="@+id/listCountry"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:paddingLeft="4dip"
            android:paddingRight="4dip" />
    </HorizontalScrollView>

</FrameLayout>

info_country.xml

<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >

    <TableRow>
        <ImageView
            android:id="@+id/imgFlag"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_span="2"
            android:scaleType="center" />
    </TableRow>

    <TableRow>
        <TextView
            android:layout_width="38dip"
            android:layout_height="wrap_content"
            android:text="国名:" />

        <TextView
            android:id="@+id/txtTitle"
            android:layout_width="142dip"
            android:layout_height="wrap_content" />
    </TableRow>

    <TableRow>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="首都:" />

        <TextView
            android:id="@+id/txtCapital"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </TableRow>

    <TableRow>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="通貨:" />

        <TextView
            android:id="@+id/txtCurrency"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </TableRow>

</TableLayout>

コードはこんな感じ。

package com.sample.sampleapp;

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

public class CountryActivity extends Activity {

 /** WRAP_CONTENT */
 private static final int WC = LinearLayout.LayoutParams.WRAP_CONTENT;

 /**
  * 国情報保持クラス
  */
 private static class Country {
  /** 国旗画像のリソース ID */
  public int flag;
  /** 国名 */
  public String title;
  /** 首都 */
  public String capital;
  /** 通貨 */
  public String currency;

  public Country(int flag, String title, String capital, String currency) {
   this.flag = flag;
   this.title = title;
   this.capital = capital;
   this.currency = currency;
  }
 }

 /** 国情報リスト */
 private static final Country[] mCountries = {
   new Country(R.drawable.flag01, "バハマ", "ナッソー", "バハマ・ドル"),
   new Country(R.drawable.flag02, "バングラディシュ", "ダッカ", "タカ"),
   new Country(R.drawable.flag03, "ベナン", "ポルトノボ", "CFAフラン"),
   new Country(R.drawable.flag04, "カメルーン", "ヤウンデ", "CFAフラン"),
   new Country(R.drawable.flag05, "コロンビア", "ボゴタ", "コロンビア・ペソ"),
   new Country(R.drawable.flag06, "デンマーク", "コペンハーゲン", "デンマーク・クローネ"),
   new Country(R.drawable.flag07, "エクアドル", "キト", "アメリカ合衆国ドル"),
   new Country(R.drawable.flag08, "エルサルバドル", "サンサルバドル", "アメリカ合衆国ドル"),
 };

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_country);

  // ウィジェットの初期化
  initWidget();
 }

 /**
  * ウィジェットの初期化
  */
 private void initWidget() {
  int len = mCountries.length;
  for (int i = 0; i < len; i++) {
   addCountry(i);
  }
 }

 /**
  * 国情報の追加
  * 
  * @param index
  */
 private void addCountry(int index) {
  // レイアウトをインフレート
  View v = this.getLayoutInflater().inflate(R.layout.info_country, null);

  // 国旗
  ImageView imgFlag = (ImageView) v.findViewById(R.id.imgFlag);
  imgFlag.setImageResource(mCountries[index].flag);

  // 国名
  TextView txtTitle = (TextView) v.findViewById(R.id.txtTitle);
  txtTitle.setText(mCountries[index].title);

  // 首都
  TextView txtCapital = (TextView) v.findViewById(R.id.txtCapital);
  txtCapital.setText(mCountries[index].capital);

  // 通貨
  TextView txtCurrency = (TextView) v.findViewById(R.id.txtCurrency);
  txtCurrency.setText(mCountries[index].currency);

  // レイアウトパラメータの調整
  LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(WC, WC);
  params.setMargins(4, 8, 4, 8);
  v.setLayoutParams(params);

  // リストに追加
  LinearLayout listCountry = (LinearLayout) findViewById(R.id.listCountry);
  listCountry.addView(v);
 }
}

TableLayout で幅を設定するのに、任意業の要素1つ1つに幅を設定しなければいけないものなのかしらん。

[Android TIPS] キーワード履歴に使えそうなボタン配置

キーワード履歴に使えそうなボタン配置を作ってみました。
こういう配置を自動的にやってくれるレイアウトとかはないですよね。たぶん(汗)





サンプル野良apkはこちらからどうぞ。

レイアウトはこんな感じ。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <EditText
            android:id="@+id/editInput"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1" />

        <Button
            android:id="@+id/btnAdd"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Add" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/layoutTag1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:id="@+id/layoutTag2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:id="@+id/layoutTag3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

コードはこんな感じ。

package com.sample.sampleapp;

import java.util.ArrayList;

import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.app.Activity;
import android.graphics.Paint;

public class TagActivity extends Activity {

 /** WRAP_CONTENT */
 private final int WC = ViewGroup.LayoutParams.WRAP_CONTENT;

 /** タグのラベル最大長 */
 private final int MAX_LABEL_LEN = 8;

 /** ボタンのパディング */
 private final int BTN_PADDING = 8;

 /** 横幅に占めるボタンに必要な最低幅の割合 */
 private final float RATIO_BUTTONS = 0.8f;

 /** タグを表示するリニアレイアウト */
 private final int[] mListTagLayout = { R.id.layoutTag1, R.id.layoutTag2, R.id.layoutTag3 };

 /** タグボタンのリスト */
 private final ArrayList<Button> mListTagButton = new ArrayList<Button>();

 /** 画面幅 */
 private int mScreenWidth = 0;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_tag);

  // 画面幅の取得
  WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
  mScreenWidth = wm.getDefaultDisplay().getWidth();

  // ウィジェットの初期化
  initWidget();
 }

 /**
  * ウィジェットの初期化
  */
 private void initWidget() {
  // 「add」ボタンを非活性化
  final Button btnAdd = (Button) findViewById(R.id.btnAdd);
  btnAdd.setEnabled(false);

  // 文字が入力されている場合に「add」ボタンを活性化
  final EditText editInput = (EditText) findViewById(R.id.editInput);
  editInput.addTextChangedListener(new TextWatcher() {
   @Override
   public void onTextChanged(CharSequence s, int start, int count, int after) {
   }

   @Override
   public void beforeTextChanged(CharSequence s, int start, int before, int count) {
   }

   @Override
   public void afterTextChanged(Editable s) {
    btnAdd.setEnabled(0 < s.length());
   }
  });

  // 「add」ボタン押下時に入力された文字列を元にタグを生成
  btnAdd.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    // ボタンを生成
    final Button btn = new Button(TagActivity.this);
    btn.setText(getInputText());
    btn.setPadding(BTN_PADDING, BTN_PADDING, BTN_PADDING, BTN_PADDING);
    btn.setOnClickListener(new OnClickListener() {
     @Override
     public void onClick(View v) {
      editInput.setText(btn.getText());
     }
    });
    // 同一の内容があれば削除
    for (int i = mListTagButton.size() - 1; 0 <= i; i--) {
     if (mListTagButton.get(i).getText().equals(btn.getText())) {
      mListTagButton.remove(i);
     }
    }
    mListTagButton.add(btn);

    // ボタンを配置
    int index = mListTagButton.size() - 1;
    for (int id : mListTagLayout) {
     LinearLayout targetLayout = (LinearLayout) findViewById(id);
     targetLayout.removeAllViews();

     // リストの末尾 (直近に生成されたボタン) から配置
     while (0 <= index) {
      Button target = mListTagButton.get(index);

      // 同一行のボタンに必要なボタン幅を算出
      int totalWidth = 0;
      for (int i = 0; i < targetLayout.getChildCount(); i++) {
       Button child = (Button) targetLayout.getChildAt(i);
       totalWidth += getVirtualButtonWidth(child);
      }
      // 同一行のボタン幅と次に追加するボタンの幅が規定値より大きければ次の行へ
      if (mScreenWidth * RATIO_BUTTONS < totalWidth + getVirtualButtonWidth(target)) {
       break;
      }

      // 対象行へボタンを配置
      LayoutParams params = new LinearLayout.LayoutParams(0, WC);
      params.weight = getVirtualButtonWidth(target);
      targetLayout.addView(target, params);

      index--;
     }
    }

    editInput.setText("");
   }
  });
 }

 /**
  * 入力文字列の取得 (規定文字数以上は省略)
  * 
  * @return
  */
 private String getInputText() {
  final EditText editInput = (EditText) findViewById(R.id.editInput);
  String input = editInput.getText().toString();

  if (MAX_LABEL_LEN < input.length()) {
   input = input.substring(0, MAX_LABEL_LEN) + "...";
  }

  return input;
 }

 /**
  * ボタンの仮想幅を取得
  * 
  * @param button
  * @return
  */
 private int getVirtualButtonWidth(Button button) {
  Paint p = new Paint();
  p.setTextSize(button.getTextSize());
  int labelWidth = (int) p.measureText(button.getText().toString());
  return (labelWidth + button.getTotalPaddingLeft() + button.getTotalPaddingRight());
 }
}

「ボタンの仮想幅」はそのボタンを表示した時にラベルがきちんと見える最小の幅を算出したかったので書いてみましたが、これでよいかしらん。でも「i」とか表示した際に潰れることはないから、これでもよいかしらん。