Techracho

Android 1.5でTabHostを使った場合、StackOverflowErrorが発生する

このエントリーをはてなブックマーク Share
2010.07.30    android, java, 馬場   タグ: , , , , —    baba   

Androidで便利な、タブ表示。

タブ表示

タブ表示

これは、TabActivity, TabHost, TabSpecを使って簡単に実現できます。

src/MainActivity.java

package jp.bpsinc.android.tabtest;

import android.app.TabActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;

public class MainActivity extends TabActivity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    TabHost tabHost = getTabHost();
    LayoutInflater.from(this).inflate(R.layout.main, tabHost.getTabContentView(), true);

    //タブを作る
    TabSpec tab1 = tabHost.newTabSpec("tab1");
    tab1.setIndicator("tab1");
    tab1.setContent(R.id.tab1);

    TabSpec tab2 = tabHost.newTabSpec("tab2");
    tab2.setIndicator("tab2");
    tab2.setContent(R.id.tab2);

    tabHost.addTab(tab1);
    tabHost.addTab(tab2);
  }
}

res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent" android:layout_height="fill_parent">

  <TextView android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="TAB1" android:id="@+id/tab1" />
  <TextView android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="TAB2" android:id="@+id/tab2" />
</FrameLayout>

しかし、サンプルでなく複雑なアプリを作ると、Android 1.5 (SDK Version 3) の時だけ、StackOverflowErrorが発生するという現象に見舞われることがあります。

body : java.lang.StackOverflowError
at android.text.TextUtils#getChars:69
at android.graphics.Canvas#drawText:1278
at android.text.Layout#draw:337
at android.widget.TextView#onDraw:3921
at android.view.View#draw:5838
at android.view.ViewGroup#drawChild:1486
at android.view.ViewGroup#dispatchDraw:1228
at android.view.ViewGroup#drawChild:1484
at android.view.ViewGroup#dispatchDraw:1228
at android.view.ViewGroup#drawChild:1484
at android.view.ViewGroup#dispatchDraw:1228
at android.view.ViewGroup#drawChild:1484
at android.view.ViewGroup#dispatchDraw:1228
at android.widget.AbsListView#dispatchDraw:1319
at android.widget.ListView#dispatchDraw:2820
at android.view.View#draw:5944
….

こちらのGoogleGroupディスカッションでも触れられているようですが、Android 1.5では、タブの中に多重にネストしたコンテンツがあると、StackOverflowErrorが発生します。

手元のエミュレータで試したところ、12回ネストしたところで、エラーになりました。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent" android:layout_height="fill_parent">

  <TextView android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="TAB1" android:id="@+id/tab1" />

  <FrameLayout android:id="@+id/tab2" android:layout_width="fill_parent" android:layout_height="fill_parent">
    <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
      <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
        <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
          <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
            <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
              <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
                <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
                  <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
                    <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
                      <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
                        <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
                          <TextView android:text="TAB2" android:layout_width="wrap_content" android:layout_height="wrap_content" />
                        </LinearLayout>
                      </LinearLayout>
                    </LinearLayout>
                  </LinearLayout>
                </LinearLayout>
              </LinearLayout>
            </LinearLayout>
          </LinearLayout>
        </LinearLayout>
      </LinearLayout>
    </LinearLayout>
  </FrameLayout>
</FrameLayout>

XMLをパーツに分けて書いていると、うっかり12回を超えてしまうことがあるので、注意が必要です。
解決策としては、ネスト回数を減らすか、Android 1.5を対象から外すという後ろ向きなものが最適ですね。

AndroidのパーミッションWRITE_EXTERNAL_STORAGE

このエントリーをはてなブックマーク Share
    android, 馬場   タグ: , , , —    baba   

MusicFlyなどの弊社Androidアプリで、SDカードを使うようには見えないのに

ストレージ:SDカードのコンテンツを修正・削除する

のパーミッションが付与されていて、不安に思う方がいらっしゃったようです。

技術的な話ですが、該当のWRITE_EXTERNAL_STORAGEは、API Level 4(Android 1.6)以上で追加されたもので、Android 1.5の時には、パーミッション無しでSDカードに書き込めました。

Androidでは後方互換性のために、TargetをAndroid 1.5にしたアプリを1.6以上で動かすと、自動的にWRITE_EXTERNAL_STORAGEパーミッションが付与される、というようになっています。

これが、やたら多くのアプリが「ストレージ」パーミッションを要求する理由ですね。実際には書き込みしていないです。

ちなみに、MusicFlyでそれぞれのパーミッションの利用方法は以下の通りです。
READ_PHONE_STATE : 着信時に音楽を停止する機能で利用
KILL_BACKGROUND_PROCESSES、RESTART_PACKAGES : メニューから「終了」を実施するために利用

KILL_BACKGROUND_PROCESSESは、自分だけを終了したいのに範囲が広すぎますね。
Froyo以上対応にすればもう少しましな解決ができるんでしょうけど・・・。悩ましいです。

※MusicFlyではXperia miniなどのQVGA端末に対応するため、Targetを1.6にしつつ1.5でも動作するように調整しています。
 そのため、次期バージョンでは、「ストレージ」パーミッションが消えているかもしれません。MusicFly2.3をお楽しみに!

Amazon Kindle 購入しました

このエントリーをはてなブックマーク Share
2010.07.27    馬場   タグ: , , —    baba   

値下げでだいぶ安くなったKindleを購入しました。
Amazon.comのアカウントが必要ですが、2~3日で届くのはすごいですね。

普通の段ボールで到着。

海を渡ってきました

海を渡ってきました

開封すると、電子ペーパーに最初の使い方が表示されています。

開封!

開封!

裏面は銀色です。

裏面 技適マークが付いてます

裏面 技適マークが付いてます

かなり薄くて軽いですね。
また、電子ペーパーは書き換えに1秒ほどかかるものの、本の閲覧は快適だし、マーケットもそれほどストレス無く使えます。
さすがにWebは、最適化されていないものは使いづらいですが。

購入直後に、Dear Takao Babaとか出てきてびっくりしました。
購入者のAmazonアカウントと初めからひもづけてあるので、特に設定せずに本の購入もできる、さすがAmazon。

日本語はそのままだと表示出来ませんが、PDFならある程度は問題ないし、調整すれば色々できそうです。

手元のコンテンツを見やすく表示する調整や、KDKの非公式実装を使った実験などを楽しんでいます。
早くKDKの承認がおりないかな

ブログにブックマークボタンを付けました

このエントリーをはてなブックマーク Share
2010.07.23    馬場   タグ: , , , , , , —    baba   

BPSの開発ブログ(このブログ)や社長ブログに、はてなブックマークやTwitter、Facebookで共有するためのボタンを付けました。

気に入った記事があったら是非クリックして下さい。やるきがでます。

ExcelでCSV保存したときに半角スペースがはてな(?)に文字化けする

このエントリーをはてなブックマーク Share
2010.07.22    馬場   タグ: , , —    baba   

ExcelからCSV形式で保存するには、「名前を付けて保存」ダイアログでCSV(カンマ区切り)を選択すればOKです。

#エクスポートしたいところですが、保存しかできないようですね。連続でやるときにはすごく不便。

ところが、これをやると、文字化けが発生してしまうことがあります。

半角スペースが含まれるExcelファイルで、Excel上で見るぶんには問題ないのですが、これをCSVエクスポートすると、1行目だけ文字化けします。
test

見た目は同じだけど上だけ文字化けする

見た目は同じだけど上だけ文字化けする

これは、同じ半角スペースに見えても「CODE=32(0×20)」の「半角スペース」と、「CODE=160(0xA0)」の「NBSP」があるのが原因になっています。
0xA0のNBSPはShift-JISでは表現できないので、保存時に「?」に文字化けしてしまいます。

これを解消するには、160のNBSPを32の半角スペースに一括置換すれば良いです。

やりかた:

適当なセルに「=CHAR(160)」という数式を入力して、それの結果をコピーします。

charcode=160の文字を作成

charcode=160の文字を作成

その後、Ctrl+Fの「置換」から、検索対象にコピーした内容をペースト、置換後の文字に普通の半角スペースを入力して、「すべて置換」すればOKです。

置換

置換

これで、見た目は変わらないけど文字化けしないExcelファイルができました。

というか、普通に使っていればこんな問題は起きないんですが、なぜか送られてきたExcelがこんなことになっていて・・・
先方はどんな使い方をしていたんでしょうね?

型で象るカメラアプリ「カタぞう」iPhone版リリース!

このエントリーをはてなブックマーク Share
    カタぞう, プレスリリース, 馬場   タグ: , , , —    baba   

お待たせしました!

カタぞうのiPhone版が、本日AppStoreで公開されました。

カタぞうiPhone版
http://itunes.apple.com/jp/app/id382218930

インストールしてみた

インストールしてみた

カタぞうは、見慣れた日常の一コマを、思わず笑みのこぼれる一枚に変えられる簡単・シンプルなカメラアプリです。
自由にテンプレートをアップロードでき、楽しみ方は無限大。

是非カタぞうをお試し下さい!

IEでJavaScriptからButtonのtypeを変更できない

このエントリーをはてなブックマーク Share
2010.07.20    Windows, javascript, 馬場   タグ: , , , —    baba   

JavaScriptで動的にエレメントを作成するときは、

var div = document.createElement('div');

のようにやります。

たとえば、ボタンを作ってそのボタンにクリックイベントを追加、などの良くある処理は、jQueryを使って、

var button = $(document.createElement('button'))
.attr('type', 'button').text('ぼたん');
button.click(function() {
    alert('hello');
});
$('#hoge').next(button);

のようにやると思います。typeを設定しているのは、デフォルトだとsubmit扱いになってしまうからです。

しかしこれ、IEだとエラーになります。

type property can’t be changed

buttonのtypeはなぜか読み取り専用プロパティらしい・・・

ということで、ちょっとダサイですが、以下のようにやって解決。

var button = $('<button type="button">ぼたん</button>');

以上、ちょっとした注意点でした。

PythonでGoogle Language APIを使うときに、&#39;がデコードされなくて困った

このエントリーをはてなブックマーク Share
2010.07.19    Python, 馬場   タグ: , , , , —    baba   

Google Language APIは素晴らしくて、AjaxのAPIがしっかり用意されているほか、RESTfulなAPIとして以下のようなものも使えます。

http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q={文字列}&langpair={変換前言語}|{変換後言語}

さらに、珍しく日本語ガイドがあるのも良いですね。
http://code.google.com/intl/ja/apis/ajaxlanguage/documentation/

さて、このRESTfulAPIで取得する結果はJSONになっているのですが、シングルコーテーション(アポストロフィ)は&#39; に文字参照されて帰ってきます。

Pythonは特に関係ないんですが、文字参照されたままだと色々困ります。
標準モジュールで簡単にデコードするものが見つからなかったので、http://d.hatena.ne.jp/MOOK/20100407/1270601539を使わせて頂くことにしました。

トータルでは、以下のようなコードになります。

import urllib
import json

#参照URLの実体参照解除コードを、entity_referenceモジュールにした
import entity_reference 

text = urllib.quote("私はピーターです")
url = 'http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=%s&langpair=ja|en' % text
result = json.loads(urllib.urlopen(url).read())

print entity_reference.encode(result['responseData']['translatedText'])
#I'm peter

Twitter APIなども、実体参照で返ってきたはずなので、注意が必要ですね。

PythonのSQLiteで検索しようとしたらIncorrect number of bindings supplied. The current statement uses 1, 10 supplied.とか言われた

このエントリーをはてなブックマーク Share
2010.07.16    Python, 馬場   タグ: , , , , —    baba   

最近の言語はSQLite3のライブラリが入っていることが多いですね。

Python 2.6でも簡単で、

import sqlite3
db = sqlite3.connect('data.db')
db.execute('create table users (id integer primary key, name text, age integer)')
db.execute('insert into users (name, age) values (?, ?)', ('yamada', 21))

のように簡単に扱えます。

ところが、使い方を間違えると

Incorrect number of bindings supplied. The current statement uses 1, 10 supplied.

のようなエラーが出てしまうことがあります。

原因は単純。
executeの第2引数はタプルにしないといけません。

項目が1個のとき、()で囲んだだけだとダメですね・・・

#ダメな例
db.execute('select * from users where name = ?', ('yamada'))  

#良い例
db.execute('select * from users where name = ?', ('yamada',))

基本的ですが、普段別の言語を書いていると気づくのが遅れます。

Twitter OAuthのrequest_tokenするところで、CallbackURLを指定したときだけ401 Unauthorizedになる場合

このエントリーをはてなブックマーク Share
2010.07.15    Python, 馬場   タグ: , , , , , , , —    baba   

Twitter APIで認証ページに遷移させるときは、http://twitter.com/oauth/request_token にリクエストトークンを発行依頼します。
しかし、設定を間違えるとここで401 Unauthorizedが発生してしまうことがあります。

Tweepyの場合、以下のようなコードになると思います。

consumer_token = "xxxxxxxxxx"
consumer_secret = "xxxxxxxxxxx"
callback = "http://www.bpsinc.jp/"
auth = tweepy.OAuthHandler(consumer_token, consumer_secret, callback)
redirect_url = auth.get_authorization_url()

今回は、auth.get_authorization_url()のところで、401 Unauthorizedが発生しました。

OAuthHandlerの第3引数、callbackを無しにすると、うまく動きます。

なぜかと思ったら・・・

ここが空だった

ここが空だった

Twitterのページでアプリを登録する際に、「ブラウザアプリケーション」を選んで、Callback URLを入力し忘れると、自動的にクライアントアプリケーションになります

クライアントアプリケーションでCallbackは使えないため、401エラーが返っていたようです(400 Bad Requestを返して欲しかったな・・・)。

また、登録したアプリがBANされた場合にも、同じ現象が発生するようなのでご注意下さい。

« 新しい投稿古い投稿 »

COPYRIGHT [C] 2009 BEYOND PERSPECTIVE SOLUTIONS LTD. ALL RIGHTS RESERVED.