« 2011年5月 | トップページ

2011年6月

2011年6月19日 (日)

Blogを移転します。

今まではこのcocologを使っていたのですが、
以前から気になっていた、Bloggerに移転したいと思います。
基本的に新しい記事はBloggerの方に書いていきます。

基本的に技術的なネタを投稿するようにしたいと思っています。

移転先:http://takashi-yokoyama.blogspot.com/

| | コメント (0) | トラックバック (0)

レイアウトのXMLに配置したButtonのOnClickがDialogだと使えない?

最近、ButtonのOnClickListenerを実装するのが面倒なので、レイアウトXMLのonClickを利用しているのですが、Dialogでも同じように使おうとしたところ、
以下のメッセージと共に、java.lang.IllegalStateException例外が発生しました。

「Could not find a method buttonClick(View) in the activity」

その時はOnClickListenerを実装した方が早かったので、深く調べなかったのですが、
たまたま思い出したので調べてみました。
※今回はAndroid2.2のソースコードを追いかけました。1.6でも同様の現象に
ぶち当たるのではないかと思います。

実際、レイアウトXMLのOnClickが設定される場所は、Viewクラスにあります。


 

                case R.styleable.View_onClick:
                    if (context.isRestricted()) {
                        throw new IllegalStateException("The android:onClick attribute cannot " 
                                + "be used within a restricted context");
                    }

                    final String handlerName = a.getString(attr);
                    if (handlerName != null) {
                        setOnClickListener(new OnClickListener() {
                            private Method mHandler;

                            public void onClick(View v) {
                                if (mHandler == null) {
                                    try {
                                        mHandler = getContext().getClass().getMethod(handlerName,
                                                View.class);
                                    } catch (NoSuchMethodException e) {
                                        int id = getId();
                                        String idText = id == NO_ID ? "" : " with id '"
                                                + getContext().getResources().getResourceEntryName(
                                                    id) + "'";
                                        throw new IllegalStateException("Could not find a method " +
                                                handlerName + "(View) in the activity "
                                                + getContext().getClass() + " for onClick handler"
                                                + " on view " + View.this.getClass() + idText, e);
                                    }
                                }

                                try {
                                    mHandler.invoke(getContext(), View.this);
                                } catch (IllegalAccessException e) {
                                    throw new IllegalStateException("Could not execute non "
                                            + "public method of the activity", e);
                                } catch (InvocationTargetException e) {
                                    throw new IllegalStateException("Could not execute "
                                            + "method of the activity", e);
                                }
                            }
                        });
                    }
                    break;

handlerName変数には呼び出されるはずのメソッド名が設定されています。

ポイントは、以下のコードになります。

mHandler = getContext().getClass().getMethod(handlerName,
                                                View.class);

この部分で、クラス内にOnClickで設定したメソッドがあるかどうかを確認しています。

さて、今回の現象はActivityだとメソッドが発見できて、Dialogだと発見出来ないわけですから、getContext().getClass()で返されるクラスのインスタンスが違ってそうな気がします。

ということで、Dialog#onCreate(setContentView()を呼び出した直後)で

Log.d("TEST", "ClassName = " + getContext().getClass().getName());

なんてことをしてみました。

すると、出力されたログをみると、

ClassName = android.view.ContextThemeWrapper

と出力されました。

Activityだと同じ事ができないのですが、恐らく、getContext()がActivityのインスタンスを返すのだろうと思われます。

Activityのインスタンスであれば、自分で実装してあれば、メソッドが発見できますが、DialogでgetContext()するとContextThemeWrapperが返ってくるため、

OnClickのメソッドが発見出来ないわけです。

ちなみに、Dialog#getContext()が返す、Contextは、以下の通りです。

 

        mContext = new ContextThemeWrapper(
            context, theme == 0 ? com.android.internal.R.style.Theme_Dialog : theme);

    public final Context getContext() {
        return mContext;
    }

ということで、結論は、「DialogではレイアウトXMLのOnClickは使えない」と言うことがわかりました。

DialogではおとなしくOnClickListenerを実装しましょう。

 

| | コメント (1) | トラックバック (0)

ソースコードを読む力

今、専門学校の授業の一環で、私が担当するクラスの生徒たちが
ModxというPHPのCMSのソースコードを
読み、その結果から資料を作るという事をしています。
もうすぐ、Modxのカスタマイズもやるそうです。

4月から始まって、2か月半が経過しました。
聞こえてくる言葉は「ソースコードが汚い。」「何をしているかわからない」
と言った、マイナスな言葉が多く聞こえてきていますが、
私がたまに質問を受けて、ソースコードを見てみると、そうでもなく、
処理を追いかける事ができます。
(実際読んで頂ければわかりますが、汚いとは思えません。
プログラムの設計思想などを議論するつもりもないです。それは、
人それぞれだと思いますので。)

最初は、「そんなに汚いコードでもない」と思っていたのですが、
冷静に分析すると、これまでプログラムと格闘してきた経験の差と
言うことなのかもしれません。

ソースコードの読み方を教える事はできないかもしれませんが、
ソースコードを読むためのヒントは教える事ができるのかもしれないなと
思いました。

一般的(IT企業とか)にはどういう風に教えているのでしょうね。

| | コメント (1) | トラックバック (0)

2011年6月14日 (火)

第7回中国GTUG勉強会@広島を開催しました。

先週末の事ですが、第7回中国GTUG勉強会@広島を開催しました。
今までは、岡山での開催ばかりで、以前から「広島開催」の希望が
ありました。

これまで、場所を確保することが問題となっていました。
(私が岡山在住であるので、広島で借りられる会議室がどこにあるのか。
会議室を県外の人が借りられるのかという問題があるため、
私が直接行動に移すことが難しかった)

しかし、3月の日本Androidの会中国支部勉強会で名刺交換をさせて
いただいた、広島国際学院大学の先生にメールでお願いをしてみたところ、
快諾していただきました。
ということで、開催にこぎつける事ができました。

今回は、ひとまず知っている人に講師をお願いしました。
キャンセルの想定をしていたので多めにお願いをした所、
お願いした全ての方にOKの回答を頂くことができました。

ということで、後は、当日参加していただいた方には
楽しんでいただいたような状況となりました。

第7回はスタッフ込みで40名弱の参加をしていただきました。
たくさんの希望を頂いたので、タイミングが合えばまた
開催をしたいと思いますのでよろしくお願いします。

広島で場所を確保してもらえるようにスタッフもそろそろ
お願いしようと思っています。(何人かの方には立候補していただいたので
その中から1人にお願いしようと思っています)

| | コメント (0) | トラックバック (0)

2011年6月 8日 (水)

XAMPP on WindowsでMySQLの文字化け対策

専門学校の授業中にXAMPP Ver1.7.4をインストールして、PHPとMySQLを使って
授業をしています。
そろそろPHPの授業でデータベース連携の内容に入ろうとした所で
事件が起こりました。

「MySQLに登録したデータ(日本語)がすべて文字化けする。」

ということで、調べてみました。XAMPP Ver1.7.4の時点ではMySQLの標準文字コードはlatin1のようです。(?)

PHPの方は文字コードをUTF-8に変更しているので、PHPMyAdminから
追加、更新すると文字化けした状態で登録されてしまいます。
(mysqlクライアントからも同様の結果になりました。
set names sjisしても文字化けが直りませんでした。。これだと無事更新できそうだけど…?)

結局、MySQLの設定ファイルmy.iniを修正して、デフォルトの文字コードを
変更すれば修正できそうだという情報を見つけたので、以下の記述を追加しました。

[XAMPPインストールフォルダ]¥mysql¥bin¥my.ini
[mysqld]
character-set-server = utf8
skip-character-set-client-handshake

[mysqldump]
default-character-set=utf8

[mysql]
default-character-set=utf8

character-set-serverの設定名は、
古いバージョンだと、default-character-setになるようです。

サーバを再起動すると、無事更新できるようになるかと思ったのですが、
文字コードを指定する前に作成したデータベースに関しては、文字コードが
設定されているようで、この設定では反映されませんでした。
なので、以下のクエリを実行して、デフォルトの文字コードを変更します。

ALTER DATABASE sampledb default character set=utf8;

これでも、すでに登録されたデータに関しては復活しないようなので、
テストデータに関しては諦めるしか無いのかもしれません。。。

| | コメント (1) | トラックバック (0)

Activity#finishActivityを解析した

今日、ある方がTwitter上で起動元のActivityを閉じるにはどうするか?
みたいな事をつぶやかれていたので、色々試したことを記述しておくことにする。

で、ドキュメント的にはActivity#finishActivity()を呼び出せば、呼び出し元の
Activityをfinish()する事ができるようだが、実際に1.6 on HT-03Aで動かしてみると
想定した動きにならなかった。

で、finishActivity()のソースコード(v1.6_r2)を見てみると、以下のソースコードだった。

    /**
     * Force finish another activity that you had previously started with
     * {@link #startActivityForResult}.
     * 
     * @param requestCode The request code of the activity that you had
     *                    given to startActivityForResult().  If there are multiple
     *                    activities started with this request code, they
     *                    will all be finished.
     */
    public void finishActivity(int requestCode) {
        if (mParent == null) {
            try {
                ActivityManagerNative.getDefault()
                    .finishSubActivity(mToken, mEmbeddedID, requestCode);
            } catch (RemoteException e) {
                // Empty
            }
        } else {
            mParent.finishActivityFromChild(this, requestCode);
        }
    }

mParentという変数は、呼び出し元Activityと思われるが、この変数に

Activityのインスタンスがセットされていれば、呼び出し元Activityが

無事finishできるようです。

しかし、mParentにインスタンスがセットされるような箇所が存在しませんでした。バグなんですかね…?

ついでに、ドキュメント上、呼び出せと言われているstartActivityForResult()の

ソースコードは以下です。

   /**
     * Launch an activity for which you would like a result when it finished.
     * When this activity exits, your
     * onActivityResult() method will be called with the given requestCode. 
     * Using a negative requestCode is the same as calling 
     * {@link #startActivity} (the activity is not launched as a sub-activity).
     * 
     * 

Note that this method should only be used with Intent protocols      * that are defined to return a result.  In other protocols (such as      * {@link Intent#ACTION_MAIN} or {@link Intent#ACTION_VIEW}), you may      * not get the result when you expect.  For example, if the activity you      * are launching uses the singleTask launch mode, it will not run in your      * task and thus you will immediately receive a cancel result.      *      *

As a special case, if you call startActivityForResult() with a requestCode      * >= 0 during the initial onCreate(Bundle savedInstanceState)/onResume() of your      * activity, then your window will not be displayed until a result is      * returned back from the started activity.  This is to avoid visible      * flickering when redirecting to another activity.      *      *

This method throws {@link android.content.ActivityNotFoundException}      * if there was no Activity found to run the given Intent.      *      * @param intent The intent to start.      * @param requestCode If >= 0, this code will be returned in      *                    onActivityResult() when the activity exits.      *      * @throws android.content.ActivityNotFoundException      *      * @see #startActivity      */     public void startActivityForResult(Intent intent, int requestCode) {         if (mParent == null) {             Instrumentation.ActivityResult ar =                 mInstrumentation.execStartActivity(                     this, mMainThread.getApplicationThread(), mToken, this,                     intent, requestCode);             if (ar != null) {                 mMainThread.sendActivityResult(                     mToken, mEmbeddedID, requestCode, ar.getResultCode(),                     ar.getResultData());             }             if (requestCode >= 0) {                 // If this start is requesting a result, we can avoid making                 // the activity visible until the result is received.  Setting                 // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the                 // activity hidden during this time, to avoid flickering.                 // This can only be done when a result is requested because                 // that guarantees we will get information back when the                 // activity is finished, no matter what happens to it.                 mStartedActivity = true;             }         } else {             mParent.startActivityFromChild(this, intent, requestCode);         }     }

すでにmParent変数を参照して動作を振り分けるようになっています。

しかも、mParent変数に値をセットしている場所もない…。

これはどういう事なんだろうか…。

最後にmParent変数へのsetterを見つけたので転載しておきます。

    // ------------------ Internal API ------------------
    
    final void setParent(Activity parent) {
        mParent = parent;
    }

どうやら、setterは非公開APIのようですので、普通にアプリケーションを

作成するためにはfinishActivity()を使って呼び出し元Activityを閉じるという

事はできなさそうです。

| | コメント (0) | トラックバック (0)

« 2011年5月 | トップページ