SQLの窓

2013年08月31日


Android : Google Spreadsheets API version 3.0でGoogleスプレッドシートを参照

SkyDrive へ移動



Google のライブラリのダウンロード

gdata-java-client

かなり多くのライブラリが中に入っていますが、実際使うのは 5つです。

1) gdata-client-1.0.jar
2) gdata-core-1.0.jar
3) gdata-spreadsheet-3.0.jar
4) guava-11.0.2.jar
5) jsr305.jar

1〜3 は、lib ディレクトリ内で、4〜5 は、deps 内です。Google 的には、gdata を使うのは spreadsheet のみのようで、他の機能( 例えばカレンダー ) は他の API を使うようになっています。また、認証も本来は OAuth を推奨しているようですが、機能の説明に重点を置くために、簡単にユーザーとパスワードのログインで処理しています。


package com.example.posttest;

import java.net.URL;
import java.util.List;

import com.google.gdata.client.spreadsheet.*;
import com.google.gdata.data.spreadsheet.*;
import com.google.gdata.util.*;

import android.os.AsyncTask;
import android.widget.EditText;

public class Android_Spreadsheet {

	// AsyncTask のインラインで参照する為の final
	private final String _userid;
	private final String _password;

	// **************************************************************
	// コンストラクタ
	// **************************************************************
	public Android_Spreadsheet(String _userid, String _password) {
		this._userid = _userid;
		this._password = _password;
	}

	// **************************************************************
	// AsyncTask の onPostExecute から外部イベントとして呼び出す為のインターフェイス
	// **************************************************************
	public interface SendMailed {
		public void onTweetResult(List<CellEntry> result);
   	}
	
	// **************************************************************
	// 通信処理
	// **************************************************************
	public void GetCells(String FileName, String SheetName,final SendMailed OnTweetResult) {
		
		new AsyncTask<String, Void, List<CellEntry>>() {

			// **************************************************************
			// 非同期処理
			// **************************************************************
			@Override
			protected List<CellEntry> doInBackground(String... params) {
				
				List<CellEntry> cells = null;

				String result_string = "";
				try {
					
					// *************************************************
					// ClientLogin
					// *************************************************
				    String USERNAME = _userid;
				    String PASSWORD = _password;

				    SpreadsheetService service = new SpreadsheetService("MySpreadsheetIntegration-v1");
				    service.setUserCredentials(USERNAME, PASSWORD);
				    
					// *************************************************
				    // バージョン3
					// *************************************************
				    SpreadsheetService sss = new SpreadsheetService("MySpreadsheetIntegration");
				    sss.setProtocolVersion(SpreadsheetService.Versions.V3);
				    
					// *************************************************
				    // 一覧を取得
					// *************************************************
				    URL SPREADSHEET_FEED_URL = new URL(
				        "https://spreadsheets.google.com/feeds/spreadsheets/private/full");
				    SpreadsheetFeed feed = service.getFeed(SPREADSHEET_FEED_URL, SpreadsheetFeed.class);
				    // 一覧用のリスト
				    List<SpreadsheetEntry> spreadsheets = feed.getEntries();
				    
				    // スプレッドシート一覧より目的のインデックスを取得
				    int target = 0;
				    for (SpreadsheetEntry Spreadsheet : spreadsheets) {

				    	System.out.println( Spreadsheet.getTitle().getPlainText() );
				    	
				    	if ( Spreadsheet.getTitle().getPlainText().equals(params[0]) ) {
				    		break; 
				    	}
				    	
				    	target++;
				    	
				    }				    
				    

				    SpreadsheetEntry spreadsheet = spreadsheets.get(target);
				    WorksheetFeed worksheetFeed = service.getFeed(
					        spreadsheet.getWorksheetFeedUrl(), WorksheetFeed.class);
				    List<WorksheetEntry> worksheets = worksheetFeed.getEntries();

				    target = 0;
				    for (WorksheetEntry worksheet : worksheets) {

				    	System.out.println( worksheet.getTitle().getPlainText() );
				    	
				    	if ( worksheet.getTitle().getPlainText().equals(params[1]) ) {
				    		break; 
				    	}
				    	
				    	target++;
				    	
				    }				    
				    
				    WorksheetEntry worksheet = worksheets.get(target);

				    URL cellFeedUrl = worksheet.getCellFeedUrl();
				    CellFeed cellFeed = service.getFeed(cellFeedUrl, CellFeed.class);
				    cells = cellFeed.getEntries();
					
				}
				catch( Exception e ) {
					System.out.println(e.getMessage());
				}
				
				return cells;
			}

			// **************************************************************
			// 非同期処理終了後の処理( 画面へのアクセスが可能 )
			// **************************************************************
			@Override
			protected void onPostExecute(List<CellEntry> result) {
				// 引数のインターフェイス内のメソッドを呼び出す
				OnTweetResult.onTweetResult(result);
			}
			
		}.execute(FileName,SheetName);
	
	}

}

スプレッドシートの一覧や、シートの一覧等、それぞれで通信処理をするので、Android では AsyncTask を用いて全て一括で処理を行って、最後の一覧のみを終了イベントとして処理するようにしています。

package com.example.posttest;

import java.net.URL;
import java.util.List;

import com.example.posttest.Android_Spreadsheet;
import com.google.gdata.data.spreadsheet.CellEntry;
import com.google.gdata.data.spreadsheet.CellFeed;
import com.google.gdata.data.spreadsheet.SpreadsheetEntry;
import com.google.gdata.data.spreadsheet.WorksheetEntry;
import com.google.gdata.data.spreadsheet.WorksheetFeed;

import android.os.Bundle;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;

public class MainActivity extends Activity {

	private Android_Spreadsheet as = null;
	private ArrayAdapter<String> adapter = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		as = new Android_Spreadsheet(
			"", // アカウント
			"" // パスワード
			);
		
		adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1);
		
	}

	public void postAction(View view) {
		
	    
	    new MessageBox(this,"確認","送信しますか?") {

			@Override
			void onYesClick(DialogInterface dialog, int which) {

				System.out.println("onYesClick");
				
				as.GetCells( "In Googleドライブ", "シート2" ,new Android_Spreadsheet.SendMailed() {
					
						@Override
						public void onTweetResult(List<CellEntry> result) {
							
							ListView listView = (ListView)MainActivity.this.findViewById(R.id.listView1);
							listView.setAdapter(adapter);
							
						    for (CellEntry cell : result) {
						    	
						    	adapter.add( cell.getCell().getValue().toString() );
						    	// System.out.println( cell.getCell().getValue().toString() );
						    	
						    }				    
						    
						}
				});
				
			}
			
		}.show();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		
		// 選択されたメニュー項目の ID
		int menuId = item.getItemId();
		
		switch(menuId){
		case R.id.action_settings:
			System.out.println("action_settings が選択されました");
			break;
		case R.id.menu_action1:
			System.out.println("menu_action1  が選択されました");
			
			Intent intent = new Intent();
			intent.putExtra("data", "メッセージを別のページに引き渡します");
			intent.setClassName("com.example.posttest", "com.example.posttest.NextPage");
			startActivity(intent);		
			
			break;
		}

		return true;
	}		
	
}




posted by lightbox at 2013-08-31 01:10 | Android | このブログの読者になる | 更新情報をチェックする

2013年06月27日


Android 4.2.2(ADT) : class MyDatePicker extends DatePickerDialog

Android : 日付ダイアログをインナーで使う では、その場で処理を記述するのでとりあえずの処理としてならば簡単ですが、きちんと制御したい場合には不向きなので、継承してクラスを作ってみました

ただ、この場合専用のインターフェイスが必要になります。それと、もともとキャンセルした時とそうでない決定ボタンを使った時の処理に汎用性が無かったので、いろいろ面倒な事をする結果となっています。



※ 画面の大きな Nexus 7 でエミュレートしてみました
package com.example.shapetextview;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import org.w3c.dom.Document;

import jp.winofsql.*;

import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.text.format.DateFormat;
import android.view.Menu;
import android.view.View;
import android.widget.DatePicker;
import android.widget.TextView;
import android.widget.TimePicker;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.setContentView(R.layout.activity_main);
	}
	
	private interface MyClick {
		public void onClick(DialogInterface dialog, int which,int yy,int mm, int dd);
		public void onCancel();
	}

	private class MyDatePicker extends DatePickerDialog {

		private int yy = 0;
		private int mm = 0;
		private int dd = 0;

		private int year = 0;
		private int monthOfYear = 0;
		private int dayOfMonth = 0;
		
		private MyClick clickCallBack;

		public MyDatePicker(Activity context, MyClick callBack,
				int year, int monthOfYear, int dayOfMonth) {
			super(context, null, year, monthOfYear, dayOfMonth);
			this.clickCallBack = callBack;
			this.year = year;
			this.monthOfYear = monthOfYear;
			this.dayOfMonth = dayOfMonth;
		}

		@Override
		public void onClick(DialogInterface dialog, int which) {
			if ( yy == 0 ) {
				yy = this.year;
				mm = this.monthOfYear;
				dd = this.dayOfMonth;
			}
			clickCallBack.onClick(dialog, which,yy,mm+1,dd);
		}

		@Override
		public void onDateChanged(DatePicker view, int year, int month, int day) {
			this.yy = year;
			this.mm = month;
			this.dd = day;
		}
		
		@Override
		protected void onStop() {
			super.onStop();
			if ( yy == 0 ) {
				clickCallBack.onCancel();
			}
		}
		
	}	
	
	public void openDialog(View view) {
		
		final Calendar c = Calendar.getInstance();
		int year = c.get(Calendar.YEAR);
		int month = c.get(Calendar.MONTH);
		int day = c.get(Calendar.DAY_OF_MONTH);

		MyDatePicker mdp = 	new MyDatePicker(this,new MyClick(){

			@Override
			public void onClick(DialogInterface dialog, int which, int yy,
					int mm, int dd) {
				TextView textView = (TextView)MainActivity.this.findViewById(R.id.textView1);
				textView.setText(yy + "/" + mm + "/" + dd);
				
			}
			
			@Override
			public void onCancel() {
				System.out.println("キャンセル");
			}

			
		}, year, month, day);
		mdp.show();

	}
}

onStop は、無条件にダイアログが閉じられた時に発生します。
onDateChanged は、日付を UI で変更しないと発生しません



posted by lightbox at 2013-06-27 19:57 | Android | このブログの読者になる | 更新情報をチェックする

2013年06月26日


Android : 日付ダイアログをインナーで使う( 完了ボタンをクリックした場合としない場合の対応 )







DatePickerDialog の 引数に与える callback では、完了ボタンを押さなくても日付が返って来るので、単純に閉じた時の判断をしています。
package com.example.shapetextview;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import org.w3c.dom.Document;

import jp.winofsql.*;

import android.app.Activity;
import android.app.DatePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.DatePicker;
import android.widget.TextView;

public class MainActivity extends Activity {

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

	public void openDialog(View view) {

		final Calendar c = Calendar.getInstance();
		final int year = c.get(Calendar.YEAR);
		final int month = c.get(Calendar.MONTH);
		final int day = c.get(Calendar.DAY_OF_MONTH);

		new DatePickerDialog(this, null, year, month, day) {

			int yy = 0;
			int mm = 0;
			int dd = 0;

			@Override
			public void onDateChanged(DatePicker view, int year, int month,
					int day) {
				this.yy = year;
				this.mm = month;
				this.dd = day;
			}
			
			@Override
			public void onClick(DialogInterface dialog, int which) {

				TextView textView = (TextView)MainActivity.this.findViewById(R.id.textView1);
				if ( this.yy == 0  ) {
					textView.setText(year + "/" + (month+1) + "/" + day);
				}
				else {
					textView.setText(this.yy + "/" + (this.mm+1) + "/" + this.dd);
				}
			}
			
		}.show();
		
	}
}

関連する記事

Android 4.2.2(ADT) : class MyDatePicker extends DatePickerDialog


posted by lightbox at 2013-06-26 21:42 | Android | このブログの読者になる | 更新情報をチェックする

2013年06月20日


Android(4.2.2) ADT から Post や Get をできるかぎり簡単にするサンプル

SkyDrive へ移動




このサンプルの目的は、サーバにあるデータベースを Android より更新したり読み出したりする事にあります。ここでは、まだ一覧表示である ListView 部分がありませんが、4つのクラスを含むパッケージを用意しています。

1) HttpPostAndXml : Post して結果を XML で取得する
2) HttpGetAndXml : Get して結果を XML で取得する
3) GetRssItemValue : 取得した XML 内の item 要素内の先頭データの指定要素の値を取得する
4) MessageBox : 更新確認の為の AlertDialog を使用する

1) 2) は、非同期処理である必要があるため、AsyncTask を継承しています( AsyncTask<String, Void, Document> )。GetRssItemValue は、RSS 的な単純なフォーマットを想定しており、値を容易に取得可能です。MessageBox は、実際以下のように使っています
package com.example.shapetextview;

import java.util.HashMap;
import java.util.Map;

import org.w3c.dom.Document;

import jp.winofsql.*;

import android.app.Activity;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {

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

	public void postData(View view) {
		
		new MessageBox(this, "確認", "更新しますか?") {
			
			@Override
			public void onYesClick(DialogInterface dialog, int which) {
				
				Map<String,String> map = new HashMap<String,String>();
				map.put("url", "http://10.0.2.2/lightbox/webapi/write_utf8.php");
				map.put("u", "insert into rss (guid,title) values('0001','タイトル')");

				new HttpPostAndXml() {

					@Override
					protected void onPostExecute(Document result) {
						
						GetRssItemValue iv = new GetRssItemValue(result);
						TextView tv = (TextView)MainActivity.this.findViewById(R.id.textView1);
						tv.setText(iv.get("title"));
						
					}
					
				}.execute(map);
				
			}
			
		}.show();
	
	}
	
	public void getData(View view) {
		
		String url = "http://matome.naver.jp/feed/hot";
		url = "http://10.0.2.2/lightbox/webapi/show_utf8.php";
		
		new HttpGetAndXml() {

			@Override
			protected void onPostExecute(Document result) {
				
				GetRssItemValue iv = new GetRssItemValue(result);
				TextView tv = (TextView)MainActivity.this.findViewById(R.id.textView1);
				tv.setText(iv.get("title"));
				
			}
			
		}.execute(url);
	
	}

}

プロジェクトにある wkprefs フォルダにあるデータは、ワークスペースの ADT( Eclipse ) の設定を他のワークスペースに以降する為のセットです(行番号・フォント・テンプレート等)。

※ TextView の Background に、XML で定義したリソースで枠線を引いています

関連する記事

Android : POST 送信。Eclipse(ADT) を使ったコード作成手順 / MessageBox.java と HttpPostAndXml.java



posted by lightbox at 2013-06-20 21:18 | Android | このブログの読者になる | 更新情報をチェックする

2013年06月13日


HashMap の Key と Value をそれぞれソートして、最終的には 配列に変換して テンプレートを使って for ループを簡単に記述する




『囲む』というテンプレートは、『行選択』から実行します。この際、『ソース』メニューからでないと、出来上がったコードのインデントがうまくいかないので注意して下さい。



定義済みの『配列』を自動的に使って for ループを作成してくれます。



for 行の 3つの変数 i は、選択中に変更すると全て内容が変わります。
Map<String,String> hm = new HashMap<String,String>();

hm.put("a", "009");
hm.put("b", "007");
hm.put("c", "008");

// キーのソート
List<String> keys = new ArrayList<String>(hm.keySet());
Collections.sort(keys);
String[] arrkeys = keys.toArray(new String[0]);
for (int i = 0; i < arrkeys.length; i++) {
	System.out.println("key:" + arrkeys[i]);
	System.out.println("hm.get(key):" + hm.get(arrkeys[i]));
}

// 値のソート
List<String> values = new ArrayList<String>(hm.values());
Collections.sort(values);
String[] arr = values.toArray(new String[0]);
for (int i = 0; i < arr.length; i++) {
	System.out.println("value:" + arr[i]);
}




posted by lightbox at 2013-06-13 21:37 | Android | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します
container 終わり



フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

CSS ドロップシャドウの参考デモ
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり