投稿者「zawata」のアーカイブ

【Android】カスタムビューにレイアウトxmlを適用する

Viewを継承した独自のカスタムビュー内のレイアウトをxmlで定義することができる。
レイアウトに関する事はJavaコードで書くよりもxmlで定義しておいたほうがコード量が減るし、後々メンテナンスしやすくなる。
今回はこの方法について見ていく。

まず、サンプルアプリの完成形となる画面を示す。

画面上部に表示された赤色の部分がカスタムビューとなっている。
テキストビューとその下に3つボタンがあって、それぞれのボタンを押すとテキストビューに押したボタンのテキストが表示されるようになっている。

まずは、このカスタムビューに適用するレイアウトxmlを示す。

myview.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#FF8888" >
    <TextView 
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text=""/>
    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <Button
            android:id="@+id/button1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="button1"/>
        <Button
            android:id="@+id/button2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="button2"/>
        <Button
            android:id="@+id/button3"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="button3"/>
    </LinearLayout>
</LinearLayout>

このxmlを見ると分かるとおり、内容は通常ActivityのsetContentView()で渡しているxmlの形式と変わらない。

次に、カスタムビューのJavaコード部分を示す。

MyView.java

package com.example.customviewlayoutlesson;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

// 適用するレイアウトxmlのトップレベルViewがLinearLayoutなので、ここでもLinearLayoutを継承する
public class MyView extends LinearLayout {

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // LayoutInflaterでレイアウトxmlの内容でViewを作る
        // LayoutInflater#inflate()の第2引数ではルートとなるViewとして自分自身を指定する
        View layout = LayoutInflater.from(context).inflate(R.layout.myview, this);
        final TextView textView = (TextView)layout.findViewById(R.id.textView);
        
        final View.OnClickListener onClickListener = new OnClickListener() {
            public void onClick(View v) {
                // 押されたボタンのテキストをTextViewに表示する
                textView.setText( ((Button)v).getText() );
            }
        };
        
        layout.findViewById(R.id.button1).setOnClickListener(onClickListener);
        layout.findViewById(R.id.button2).setOnClickListener(onClickListener);
        layout.findViewById(R.id.button3).setOnClickListener(onClickListener);
    }

}

本来、カスタムビューはViewを継承すれば良いのだが、今回はLinearLayoutを継承している。
これは、適用するxmlのトップレベルのViewがLinearLayoutだからで、その型を合わせる必要があるからである。
サンプルではLinearLayoutを例として取り上げたが、ほかにもViewGroupの子クラスのFrameLayoutやRelativeLayoutなどが使える。
19行目で、LayoutInflator#inflate()メソッドを使ってレイアウトxmlからViewを作っている。このメソッドの第2引数で、ルートとなるViewとして自分自身を指定する。こうすることでレイアウトxmlの内容がカスタムビューに適用される。

その後は、いつものActivity#onCreate()の中で行っている手順と同様、findViewById()メソッドで操作したいViewをレイアウトから取得できる。
今回のサンプルでは28-30行目でレイアウト内の3つのボタンにイベントリスナをセットしている。

最後にMain側のレイアウトxmlとActivityのソースを示す。

layout.xml

<RelativeLayout 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" >

    <com.example.customviewlayoutlesson.MyView
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</RelativeLayout>
MainActivity.java

package com.example.customviewlayoutlesson;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.MenuItem;
import android.support.v4.app.NavUtils;

public class MainActivity extends Activity {

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

主要な処理は全てカスタムビュー側に書いてしまったので、Main側の処理はメインレイアウトxmlでカスタムビューを定義するだけになった。
このようにして、各部分のコンポーネント化を押し進めていくとコードの保守性・可読性が上がっていくだろう。


【JavaScript】クラスの作成

JavaScriptでもオジェクト指向プログラミングは可能だが、クラスの実現方法はJavaやC#とはずいぶんと異なった印象を受ける。
JavaやC#のオブジェクト指向が「クラスベース」と呼ばれるのに対し、
JavaScriptのオブジェクト指向は「プロトタイプベース」と呼ばれる。

JavaScriptではどのようにクラスを作成するのかを見ていこう。

もっとも単純なクラス

以下のように書く。

MyClass = function(){};

このように、JavaScriptにおけるクラスとは関数オブジェクトの定義に他ならないことがわかる。
こうして定義された関数オブジェクトはコンストラクタ関数と呼ばれる。
JavaScriptでは通常、変数名は小文字始まりとするが、コンストラクタ関数の場合は普通の関数との区別のため大文字始まりとするのが慣例のようだ。
新しいクラスのオブジェクトを生成するには、以下のようにnew演算子を使ってコンストラクタ関数を呼び出す。

var myClass = new MyClass();

new演算子を使って関数オブジェクトを呼び出すと、内部では以下の流れで処理が行われている。

  1. 新しいオブジェクトを生成する。
  2. コンストラクタ関数を呼び出す。
    コンストラクタ関数内ではthisキーワードを使って新規作成したオブジェクトにアクセスできる。

この性質を利用して、オジェクトにメンバ関数を追加していくことになる。

メンバ変数を追加する

メンバ変数を追加するにはコンストラクタ関数を以下のようにする。

MyClass = function(nm,ag){
    this.name = nm;
    this.age = ag;
};

このように書くことで、MyClassというコンストラクタ経由で作られたオブジェクトには
nameとageというプロパティが含まれることになる。

メソッド(メンバ関数)を追加する

以下のように書く。

MyClass = function(nm,ag){
    this.name = nm;
    this.age = ag;
};

// メソッドの追加
MyClass.prototype.sayHello = function(){
    alert("Hello MyClass!!"); 
}

メソッドは、関数オブジェクトのprototypeというプロパティに追加している。
このprototypeは、関数オブジェクトが必ず持っているプロパティである。
そして、newによって作成されたオブジェクトは、そのコンストラクタ関数のprototypeへの参照を持っている。
このような性質があるから、インスタンスが異なってもクラスの共通の情報を参照できることになる。
メソッドのように、インスタンス毎に異なる値を持つ必要の無いものに関しては
クラス内で共通に参照されるプロトタイプオブジェクトに追加したほうがメモリの効率が良いので、このようなコードとなる。

複数のメソッドを定義するときは、オブジェクトリテラルを使って以下のように書くこともできる。

MyClass = function(nm,ag){
    this.name = nm;
    this.age = ag;
};

// メソッドの追加
MyClass.prototype = {
    sayHello:function(){
      alert("Hello MyClass!!");  
    },
    doSomething:function(){
        alert("My name is " + this.name + ". I'm " + this.age + " years old.");
    }
};

以上が、JavaScriptにおけるクラスの作成の手順となる。


【JavaScript】コレクションについて (2)連想配列

JavaScriptでコレクションを扱うもうひとつの方法は、連想配列、つまりハッシュテーブルである。
実は、JavaScriptでいう「オブジェクト」は既に連想配列としての機能を持っている。
JavaでいうところのHashMapのようなものである。
それでは、連想配列の使い方について見ていく。

連想配列を宣言して初期化する

以下のような形が基本となる。

// その1 単純にObjectをnewする
var hash1 = new Object();

// その2 オブジェクトリテラルを使う
var hash2 = {"001":"bob", "002":"John", "003:Alice"};

その2で、キーと値の組み合わせの列を{}で括る書き方をオブジェクトリテラルという。
「var hash2 = {}」とした場合は「new Object()」とするのと同じことである。

連想配列に要素を追加する

以下のようにする。

// その1 インデクサに任意のキーを指定して要素を代入する
hash1["key1"] = "value1";

// その2 ドットの後にキー名を指定して要素を代入する
hash1.key2 = "value2";

キーの型は文字列でも数値でも良い。ただし、数値の場合、その2の書き方では上手くいかないようだ。
JavaScriptでは、このようにしてオブジェクトに追加したキーのことを「プロパティ」と呼ぶ。

連想配列から要素を削除する

delete句を使い、以下のように書く。

// その1 インデクサを指定する
delete hash1["key1"];

// その2 プロパティを指定する
delete hash1.key2;

連想配列を列挙する

for-in文を使い、以下のように書く。

for( var key in hash1 ){
   document.write(key + ":" + hash1[key] + "<br>");
}

for-in文はオブジェクトに追加されたキー(プロパティ)の一覧を列挙する。

以上が、JavaScriptにおける連想配列の基本的な使い方となる。


【JavaScript】コレクションについて (1)配列

JavaScriptでコレクションを扱いたい場合は
・配列
・連想配列

のいずれかを使うことになる。
今回は、配列について見てみる。

配列を宣言して初期化する

以下のような形が基本となる。

// その1 new Array()を使う
var array1 = new Array("mike","tome","john");

// その2 []を使う
var array2 = ["one","two","three"];

このようにして作成された配列はArrayオブジェクトとなる。

配列に要素を追加する

Arrayオブジェクトのpush()関数が使える。

array1.push("alice");

配列を列挙する

昔ながらのfor文を使ったやり方は以下のような感じになる。

for(var i=0; i<array1.length; ++i ){
    document.write(i + ":" + array1[i] + "<br>");
}

配列全体の長さを知るにはlengthプロパティを使う。
また、インデクサを使って任意の場所の要素へアクセスできる。

別の方法はArrayオブジェクトのforEach()関数を使うやり方で、以下のような感じになる。

array1.forEach(function( elem, index, ary ){
   document.write(index + ":" + elem + "<br>");
});

forEach()関数の引数にはコールバック関数を渡す。
コールバック関数の引数の形式はfunction(要素の値,要素のインデックス,配列オブジェクト)である。
以上が、JavaScriptにおける基本的な配列の使い方となる。


【Android】GestuerDetectorとScrollerを組み合わせた例

View上において、ジェスチャー操作を扱うためのクラスGestureDetectorと
スクロール処理を扱うためのクラスScrollerを組み合わせた使用例を示す。
アプリの概要は以下の通りである。

・画面をタッチして指を移動したとき、それに追従する図形を描画する。
・そこからさらに、はじくような操作をしたときに図形を慣性スクロールさせて描画する。

最終的なイメージは以下のようになる。

このサンプルのレイアウトxmlは以下のようになっている。

<RelativeLayout 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" >

    <com.example.scrollerlesson.FlingLessonView
        android:id="@+id/myView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />

</RelativeLayout>

このように、FlingLessonViewという独自のViewを画面全体に表示している。
それではこの独自のViewのソースを見てみよう。

FlingLessonView.java

package com.example.scrollerlesson;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Scroller;

public class FlingLessonView extends View {

	private static final int ANIMATION_INTERVAL = 10;
	private final Handler handler = new Handler();
	private Scroller scroller;
	private GestureDetector gestureDetector;
	
	private float currX;
	private float currY;
	
	/**
	 * @param context
	 */
	public FlingLessonView(Context context) {
		super(context);
		init(context);
	}
	
	/**
	 * @param context
	 * @param attrs
	 */
	public FlingLessonView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}
	
	/**
	 * ビューの初期化
	 * @param context
	 */
	private void init(Context context){
		
		setLongClickable(true);
		scroller = new Scroller(context);
		gestureDetector = new GestureDetector(context,new OnGestureListener() {
			
			@Override
			public boolean onSingleTapUp(MotionEvent e) {
				// TODO Auto-generated method stub
				return false;
			}
			
			@Override
			public void onShowPress(MotionEvent e) {
				// TODO Auto-generated method stub
			}
			
			@Override
			public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
					float distanceY) {

				if(!scroller.isFinished())
					scroller.abortAnimation();
				
				currX = e2.getX();
				currY = e2.getY();
				
				invalidate();
				
				return true;
			}
			
			@Override
			public void onLongPress(MotionEvent e) {
				// TODO Auto-generated method stub
			}
			
			@Override
			public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
					float velocityY) {
				
				scroller.fling(
					(int)e2.getX(),
					(int)e2.getY(),
					(int)velocityX,
					(int)velocityY,
					0, getWidth(), 0, getHeight());

				handler.post(new Runnable() {
					
					@Override
					public void run() {
						scroller.computeScrollOffset();
						currX = scroller.getCurrX();
						currY = scroller.getCurrY();
						
						invalidate();
						
						if(!scroller.isFinished()){
							handler.postDelayed(this, ANIMATION_INTERVAL);
						}
					}
				});
				
				return true;
			}
			
			@Override
			public boolean onDown(MotionEvent e) {
				// TODO Auto-generated method stub
				return false;
			}
		});
	}
	
	private final Paint paint = new Paint();
	{
		paint.setStyle(Style.FILL);
		paint.setColor(Color.GREEN);
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		canvas.drawColor(Color.BLACK);
		canvas.drawCircle(currX, currY, 60.f, paint);
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		return 
				gestureDetector.onTouchEvent(event) ||
				super.onTouchEvent(event);
	}
}

47行目からのinit()メソッドがこのViewの初期化処理となる。
まずはsetLongClickable()をtrueで呼んでいる。
これをしておかないとOnGestureListenerのonScroll()、onFling()が呼ばれないようである。

51行目でGestureDetectorのインスタンスを生成している。
そのコンストラクタ引数のひとつであるOnGestureListenerの宣言が少し長くなってしまったが、
今回の例ではonScroll()とonFling()の2つだけを使用している。

65行目のonScroll()メソッドは、View上でタッチし、そのまま指を移動したときに呼び出される。
現在の位置は2番目の引数のMotionEventから取得できるので、
Viewのインスタンス変数に保持しておき、invalidate()を呼び出してonDraw()のタイミングで
新しい座標で図形が描画されるようにしておく。

85行目のonFling()メソッドは、View上で指をはじくような動作をすると呼び出される。
ここでScrollerのfling()メソッドに渡ってきた引数をそのまま渡している。
fling()メソッドでは移動後の最小座標と最大座標を指定できるので、画面内で収まる範囲とした。
この後、定期的にScrollerのgetCurrX(),getCurrY()メソッドで得られる座標を監視すると
Scrollerが内部で計算した値を取得できる。
今回の例ではスクロールが起きている間は10msごとにその座標を監視し、画面を更新するようにしてみた。

129行目からのonDraw()メソッドでは、単に現時点での座標に青い円を描画しているだけである。

135行目のonTouch()メソッドではGestureDetectorにタッチイベントを委譲している。

このようにして、フリック操作と慣性スクロールを連動させることができた。
続きに、エミュレーターで録画した動画を掲載する。
※実機では、もう少しスムーズに描画される。
Continue reading


【Android】ListViewを使う(基本)

ListViewはリスト状に項目を表示できるViewである。
今回はListViewの基本的な使い方を見ていくことにする。

まず、サンプルアプリの画面の配置は以下のようにした。

上にあるEditTextに何か文字を入力してボタンを押すと、ListViewに次々と追加されていくようにする。
この画面にxmlは以下のようになる。

main.xml

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

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <EditText 
            android:id="@+id/txtName"
            android:layout_height="fill_parent"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:inputType="text"
            />
        <Button 
            android:id="@+id/btnInsert"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:text="Insert"
            />
    </LinearLayout>
    <ListView
        android:id="@+id/listView"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

</LinearLayout>

次にActivityのプログラムを示す。

AndroidListViewLessonActivity.java

package com.lesson.lilstview;

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

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class AndroidListViewLessonActivity extends Activity {
	
	private ArrayAdapter<String> adapter;
	private List<String> list = new ArrayList<String>();
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
        ((ListView)findViewById(R.id.listView)).setAdapter(adapter);
        
        findViewById(R.id.btnInsert).setOnClickListener(new OnClickListener() {
			
        	private final TextView textView = (TextView)findViewById(R.id.txtName);
        	
			@Override
			public void onClick(View v) {
				list.add(textView.getText().toString());
				adapter.notifyDataSetChanged();
			}
		});
    }
}

ListViewと、その中のデータを関連付けるためにArrayAdapterというクラスを使っている。
16行目でArrayAdapterの参照をメンバ変数として持っておく。
17行目で生成しているString型のArrayListがListViewのデータとなる。
ListAdapterのインスタンスは25行目のonCreate()メソッド内で生成している。
いくつかあるコンストラクタのうち、このサンプルでは引数が

ArrayAdapter(Context context, int textViewResourceId, List<T> objects)

となっているものを使っている。
2番目の引数で指定しているリソースID
「android.R.layout.simple_list_item_1」
はAndroidに標準で含まれているリソースである。
ソースを引用すると以下のような内容となっているようだ。

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2006 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
          http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceLarge"
    android:gravity="center_vertical"
    android:paddingLeft="6dip"
    android:minHeight="?android:attr/listPreferredItemHeight"
/>

この内容によれば名前のとおりシンプルなTextViewが一つ定義されているだけである。
これがListViewの1項目に相当するようになる。

33行目からがボタンが押されたときの処理となる。
まず34行目で、TextViewの内容をリストに追加している。
注意が必要なのは、ListViewのデータ元となるリストに変更を加えただけでは
表示に反映されないことである。
次の行で、ListView側にデータ元の内容が変更されたことを通知するため、
ArrayAdapterのnotifyDataSetChanged()メソッドを呼んでいる。

アプリを実行すると、以下のようになる。


【Android】SQLiteを使う DBの作成と基本的なSelect

Androidには標準でSQLiteが組み込まれている。
その基本的な使い方を見てみよう。

SQLiteOpenHelperを使うと、DBのオープンやバージョンのチェックなどをやってくれるので便利である。
そこで、SQLiteOpenHelperを継承したクラスを作ることから始める。

LessonDb.java

package com.lesson.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class LessonDb extends SQLiteOpenHelper {

	private static int DB_VERSION = 1;
	private static String DB_FILENAME= "lesson.db";
	
	public LessonDb(Context context){
		super(context,DB_FILENAME,null,DB_VERSION);
	}
	
	@Override
	public void onCreate(SQLiteDatabase db) {
		
		Log.d("LessonDb", "onCreate");
		
		// ここでDBの作成、初期データの投入を行う
		db.execSQL("CREATE TABLE Persons (" +
				"Id INTEGER PRIMARY KEY AUTOINCREMENT," +
				"Name TEXT )");
		
		db.execSQL("INSERT INTO Persons(Name) VALUES('John');");
		db.execSQL("INSERT INTO Persons(Name) VALUES('Tom');");
		db.execSQL("INSERT INTO Persons(Name) VALUES('Taro');");
		db.execSQL("INSERT INTO Persons(Name) VALUES('Mike');");
		db.execSQL("INSERT INTO Persons(Name) VALUES('Bill');");
		db.execSQL("INSERT INTO Persons(Name) VALUES('Bob');");
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// アップグレード時に呼ばれる
	}
}

オーバーライドしたonCreate()メソッドはDBの新規作成時に呼ばれるメソッドで、
ここにDBの作成処理と初期データの投入処理を書く。
ここでは

Persons

Id INTEGER型、プライマリーキー、オートインクリメント
Name TEXT型

という構造のテーブルを作成し、初期データの投入を行っている。

それでは、このテーブルのデータをSelectしてみよう。
メインのソースは以下のようになる。

MainActicity.java

package com.lesson.db;

import android.app.Activity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        SQLiteDatabase db = null;
        
        try{
        	db = new LessonDb(this).getReadableDatabase();
	        
	        Cursor c = null;
	        try{
	        	c = db.query(
		        		"Persons", 
		        		new String[]{"Id","Name"}, 
		        		null, 
		        		null, 
		        		null, 
		        		null, 
		        		"Id");
		        
		        while( c.moveToNext() ){
		        	Log.d("result","" + c.getInt(0) + " " + c.getString(1));
		        }
	        }catch(Exception e){ 
	        }finally{
	        	if( c != null ){
	        		c.close();
	        		c = null;
	        	}
	        }
        }
        catch(Exception e){
        }finally{
        	if(db != null){
        		db.close();
        		db = null;
        	}
        }
    }
}

19行目のgetReadableDatabase()でSQLiteDatabaseを取得している。
ここで、DBがまだ存在しないときはSQLiteHelperのonCreate()が呼ばれてDBが作成される。
DBが既に存在しているときは、そのDBを返す。
23行目のSQLiteDataBase#query()メソッドでDBのデータをSelectしている。
ここでは、SQLiteDataBase#rawQuery()メソッドを使って

c = db.rawQuery("SELECT Id,Name FROM Persons ORDER BY Id", null);

のように書いても良い。
実行すると、ログに

05-12 05:13:43.075: D/result(666): 1 John
05-12 05:13:43.075: D/result(666): 2 Tom
05-12 05:13:43.094: D/result(666): 3 Taro
05-12 05:13:43.094: D/result(666): 4 Mike
05-12 05:13:43.094: D/result(666): 5 Bill
05-12 05:13:43.094: D/result(666): 6 Bob

のように出力される。

WHERE句を使った条件指定を行う場合についても見ておこう。
例えば、以下の例では先頭の名前が”T”のデータを抽出している。

c = db.query(
	"Persons", 
	new String[]{"Id","Name"}, 
	"Name LIKE ?", 
	new String[]{"T%"}, 
	null, 
	null, 
	"Id");

rawQuery()メソッドを使った場合は以下のようになる。

c = db.rawQuery(
	"SELECT Id,Name FROM Persons WHERE Name LIKE ? ORDER BY Id", 
	new String[]{"T%"});

実行すると、ログに

05-12 05:27:26.665: D/result(782): 2 Tom
05-12 05:27:26.665: D/result(782): 3 Taro

のように出力されるだろう。


【Android】android-query(AQuery)を使ってみる

AQueryというものを見つけた。
jQuery風にAndroidのプログラミングができるものらしい。

さっそく試してみることにした。
プロジェクトページから必要なjarファイルをダウンロードしてくる。今回は「android-query-0.21.7.jar」にした。
Eclipseで新規のAndroidプロジェクトを作成し、先ほどダウンロードしたjarを参照ライブラリに追加しておく。

このページを参考にしつつ、コードを書いていく。

レイアウトxmlは以下のように書いた。

main.xml

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

    <TextView
            android:id="@+id/text"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"            
            android:text="Text" />    
    <Button 
            android:id="@+id/button" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:text="Button" />

</LinearLayout>

メインのActivityは以下のようにしてみた。

MainActivity.java

package com.lesson.aq;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;

import com.androidquery.AQuery;

public class MainActivity extends Activity {
	private AQuery aq;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        aq = new AQuery(this);
        aq.id(R.id.text).text("Hello");
        aq.id(R.id.button).text("Click Me").clicked(this, "buttonClicked");
    }
    
    // ボタンが押されたときの処理
    public void buttonClicked(View button){
        aq.id(R.id.text).text("Hello World!!");
    }
}

実行すると以下のようになる。

初期画面
ボタンクリック

確かに、OnClickListener等を書かなくて良い分、コードの見通しも良くなっている気がする。
なかなか面白いプロジェクトだと思う。

その他参考記事:
http://www.infoq.com/jp/news/2012/04/AQuery


【C#/LINQ】GroupJoinメソッドを使って外部結合を行う

LINQのGroupJoinメソッドを使って、SQLの外部結合に相当するデータ操作を行ってみよう。
以下のようなテーブルデータがあるとして、それをLINQで結合してみることにする。

Persons

Id Name Age JobId
1 John 56 1
2 Mike 23 NULL
3 Ken 64 2
4 Alice 41 4
5 Tom 22 NULL
Jobs

Id Name
1 Programmer
2 Engineer
3 Sportman
4 King
5 Slave

前回の内部結合の例と違うのは、
PersonsテーブルのJobIdがNULLの場合があることである。

ちなみに、この例を Persons.JobId と Jobs.Id で内部結合した場合は以下のようになるだろう。

PersonId:1 Name:John Age:56 Job:Programmer
PersonId:3 Name:Ken Age:64 Job:Engineer
PersonId:4 Name:Alice Age:41 Job:King

このように、内部結合では結合条件に合致しないデータは結果から除かれる。
左側のデータを残しつつ結合した結果を得たいときは外部結合を使う。

それでは、プログラムを見ていこう。

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LinqJoinLessonOuter
{
    class Program
    {
        class Person
        {
            public int Id { get; set; }
            public String Name { get; set; }
            public int Age { get; set; }
            public int? JobId { get; set; }
        }

        class Job
        {
            public int Id { get; set; }
            public String Name { get; set; }
        }

        static void Main(string[] args)
        {
            var persons = new Person[]{
                new Person{ Id = 1,Name = "John",  Age = 56, JobId = 1},
                new Person{ Id = 2,Name = "Mike",  Age = 23, JobId = null},
                new Person{ Id = 3,Name = "Ken",   Age = 64, JobId = 2},
                new Person{ Id = 4,Name = "Alice", Age = 41, JobId = 4},
                new Person{ Id = 5,Name = "Tom",   Age = 22, JobId = null},
            };

            var jobs = new Job[] { 
                new Job{ Id = 1, Name = "Programmer" },
                new Job{ Id = 2, Name = "Engineer" },
                new Job{ Id = 3, Name = "Sportman" },
                new Job{ Id = 4, Name = "King" },
                new Job{ Id = 5, Name = "Slave" }
            };

            // 外部結合を行うメソッド式
            var outerJoin =
                persons.GroupJoin(jobs, p => p.JobId, j => j.Id, (p, j) => new
                {
                    PersonId = p.Id,
                    Name = p.Name,
                    Age = p.Age,
                    Jobs = j.DefaultIfEmpty()
                })
                .SelectMany(x => x.Jobs, (x, j) => new
                {
                    PersonId = x.PersonId,
                    Name = x.Name,
                    Age = x.Age,
                    JobName = j != null ? j.Name : "<unemployed>"
                });

            foreach (var j in outerJoin)
            {
                Console.WriteLine(
                   "PersonId:{0}\tName:{1}\tAge:{2}\tJob:{3}", j.PersonId, j.Name, j.Age, j.JobName);
            }
        }
    }
}

PersonsクラスのJobIdは、nullを使うためnull許容型とした。
プログラムの実行結果は以下のようになった。

PersonId:1 Name:John Age:56 Job:Programmer
PersonId:2 Name:Mike Age:23 Job:<unemployed>
PersonId:3 Name:Ken Age:64 Job:Engineer
PersonId:4 Name:Alice Age:41 Job:King
PersonId:5 Name:Tom Age:22 Job:<unemployed>
続行するには何かキーを押してください . . .

参考に、以上の操作をクエリ式で書いた場合とSQLで書いた場合について示す。

クエリ式

var outerJoin =
    from p in persons
    join j in jobs on p.JobId equals j.Id into gs
    from g in gs.DefaultIfEmpty()
    select new { PersonId = p.Id, Name = p.Name, Age = p.Age, JobName = g != null ? g.Name : "<unemployed>" };
SQL

SELECT
	p.Id AS PersonID
	p.Name AS Name
	p.Age AS Age
	j.Name As JobName
FROM
	Persons AS p
LEFT OUTER JOIN
	Jobs AS j
ON p.JobId = j.Id

【C#/LINQ】Joinメソッドを使って内部結合を行う

LINQのJoinメソッドを使って、SQLの内部結合に相当するデータ操作を行ってみよう。
以下のようなテーブルデータがあるとして、それをLINQで結合してみることにする。

Persons

Id Name Age JobId
1 John 56 1
2 Mike 23 1
3 Ken 64 2
4 Alice 41 4
5 Tom 22 3
Jobs

Id Name
1 Programmer
2 Engineer
3 Sportman
4 King
5 Slave

C#のプログラムは以下のようになる。

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Person
    {
        public int Id { get; set; }
        public String Name{ get; set; }
        public int Age { get; set; }
        public int JobId { get; set; }
    }

    class Job
    {
        public int Id { get; set; }
        public String Name { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var persons = new Person[]{ 
                new Person{ Id = 1,Name = "John",  Age = 56, JobId = 1},
                new Person{ Id = 2,Name = "Mike",  Age = 23, JobId = 1},
                new Person{ Id = 3,Name = "Ken",   Age = 64, JobId = 2},
                new Person{ Id = 4,Name = "Alice", Age = 41, JobId = 4},
                new Person{ Id = 5,Name = "Tom",   Age = 22, JobId = 3},
            };

            var jobs = new Job[] { 
                new Job{ Id = 1, Name = "Programmer" },
                new Job{ Id = 2, Name = "Engineer" },
                new Job{ Id = 3, Name = "Sportman" },
                new Job{ Id = 4, Name = "King" },
                new Job{ Id = 5, Name = "Slave" }
            };

            var query =
                persons.Join(jobs, p => p.JobId, j => j.Id, (p, j) => new {
                    PersonId = p.Id,
                    Name = p.Name,
                    Age = p.Age,
                    JobName = j.Name
                });

            foreach (var j in query)
            {
                Console.WriteLine("PersonId:{0}\tName:{1}\tAge:{2}\tJob:{3}", j.PersonId, j.Name, j.Age, j.JobName);
            }
        }
    }
}

プログラムの実行結果は以下のようになった。

PersonId:1 Name:John Age:56 Job:Programmer
PersonId:2 Name:Mike Age:23 Job:Programmer
PersonId:3 Name:Ken Age:64 Job:Engineer
PersonId:4 Name:Alice Age:41 Job:King
PersonId:5 Name:Tom Age:22 Job:Sportman
続行するには何かキーを押してください . . .

参考に、以上の操作をクエリ式で書いた場合とSQLで書いた場合について示す。

クエリ式

var query =
	from p in persons
	join j in jobs on p.JobId equals j.Id
	select new{ PersonId=p.Id, Name=p.Name, Age = p.Age, JobName = j.Name };
SQL

SELECT
	Persons.Id AS PersonId,
	Persons.Name AS Name,
	Persons.Age AS Age,
	Jobs.Name AS JobName
FROM
	Persons
INNER JOIN
	Jobs ON Persons.JobId = Jobs.Id

クエリ式は、タイプ数は少なく済むのだが、今ひとつ可読性に優れないこともあって、
最近は全く使わなくなってしまった…。