SQLの窓

2017年10月26日


別に納品するわけでは無いので、Android の ListView のカスタマイズなんてこれで十分でしょ / TestArrayAdapter バージョン2

前のバージョンはこちらになりますが、世の中に腐るほどある getView 内の記述を外に出しただけのものでした。しかし、よくよく考えてみると、Adapter 内で セットされているデータを取り出して、そのクラスで引数に渡したらもっと楽だし、View のセットなんかも、Adapter 内ですればいい事に気が付きました。

これで、いろんなテストを気楽に出来る人が増えると思うわけです。

TestArrayAdapter
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;

public class TestArrayAdapter<T> extends ArrayAdapter<T> {

	public interface OnGetViewListener<T> {
		abstract public void onGetViewListener(int position, View convertView, ViewGroup parent, T item);
	}

	private OnGetViewListener listener = null;
	private int resource;
	private LayoutInflater inflater;

	public TestArrayAdapter(Context context, int resource, OnGetViewListener listener) {
		super(context, resource);
		this.listener = listener;
		this.resource = resource;
		this.inflater = (LayoutInflater)context.getSystemService
				(Context.LAYOUT_INFLATER_SERVICE);
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		if ( listener != null ) {

			if (convertView == null) {
				convertView = inflater.inflate(resource, null);
			}

			T item = this.getItem(position);

			listener.onGetViewListener(position, convertView, parent, item);
			return convertView;
		}
		return super.getView(position, convertView, parent);
	}
}

呼び出し側は、正式には以下のようにインターフェイスにも<WebData> を指定する事で引数の値を WebData として取得できます。しかし、面倒な場合は <WebData> を指定せずに引数を Object でもらってキャストして使えばいいはずです。
		// *******************************
		// ListView で一覧
		// *******************************
		listView = (ListView) MainControl.this.findViewById(R.id.listView);
		arrayAdapter = new TestArrayAdapter<WebData>(MainControl.this, R.layout.item, new TestArrayAdapter.OnGetViewListener<WebData>() {
			@Override
			public void onGetViewListener(int position, View convertView, ViewGroup parent, WebData item) {

				TextView textView;

				textView = convertView.findViewById(R.id.textCode);
				textView.setText(item.社員コード);

				textView = convertView.findViewById(R.id.textName);
				textView.setText(item.氏名);

				textView = convertView.findViewById(R.id.textType);
				textView.setText(item.所属名);
			}
		});
		listView.setAdapter(arrayAdapter);


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


    <TextView
        android:id="@+id/textCode"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"/>

    <TextView
        android:id="@+id/textName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/black"
        android:textSize="23sp"/>

    <TextView
        android:id="@+id/textType"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="6dp"
        android:textSize="18sp"/>


</LinearLayout>




WebData
public class WebData {

	String 社員コード;
	String 氏名;
	String フリガナ;
	String 所属;
	String 性別;
	int 給与;
	String 手当;
	String 管理者;
	String 生年月日;
	String 生年月日2;
	String 所属名;

	@Override
	public String toString() {
		return 氏名;
	}
}


WebData は 『Java : Class 構造より、update 文を作成する』でも使用しています



タグ:android
posted by lightbox at 2017-10-26 20:00 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年10月21日


Java : Class 構造より、update 文を作成する

作ったのは Android Studio です。

以下のクラスですが、一目して ListView 用のクラスである事が解ると思います( toString が表示用 )。また、JSON を Google Gson でデシリアライズする事を想定しているので、C 言語の構造体のような単純な定義です。

WebData クラス
public class WebData {

	String 社員コード;
	String 氏名;
	String フリガナ;
	String 所属;
	String 性別;
	int 給与;
	String 手当;
	String 管理者;
	String 生年月日;
	String 生年月日2;
	String 所属名;

	@Override
	public String toString() {
		return 氏名;
	}
}


この Class のインスタンスに、画面から入力データをセットし、Class 構造を取得して Update 文の where 以降以外を作成するコードが以下です。

buildUpdate メソッド
	public String buildUpdate(String table, Object obj, Class<?> cls) {
		String result = "";

		Field[] flds = cls.getDeclaredFields();
		String type = "";
		String col = "";
		String val = "";
		for( Field fld : flds ) {

			// null 値の場合無視
			try {
				if ( fld.get( obj ) == null ) continue;
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}

			// フィールド名( 列名に一致させた Class 定義 )
			col = fld.getName();
			try {
				// 値( 文字列 )
				val = fld.get( obj ).toString();

			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}

			// データ型 : この場合、int かそれ以外
			type = fld.getType().getName();

			if (!result.equals("")) {
				result += ",";
			}
			if (type.equals("int")) {
				if ( val.equals("") ) {
					result += String.format(" %s = null ", col);
				}
				else {
					result += String.format(" %s = %s ", col, val);
				}
			}
			else {
				if ( val.equals("") ) {
					result += String.format(" %s = null ", col);
				}
				else {
					result += String.format(" %s = '%s' ", col, val);
				}
			}

		}

		result = String.format("update %s set %s", table, result );

		return result;
	}

※ 本来、StringBuilder 等を使用するところを += 演算子を使用しているのはコードを見やすくする為です

エラー処理は最低限で作られていますが、重要なのは Field オブジェクトなので、ご理解下さい。

冒頭に、cls.getDeclaredFields() で、定義されている Field オブジェクトの配列を取得して、ループ処理しているだけです。

引数より、Object として、WebData のインスタンスが渡されて、fld.get( obj ).toString() でデータを取得し、fld.getName() として取得された文字列は列名と一致する事を想定しています。

あとは、fld.getType().getName() で、int かそれ以外でシングルクォートの使い方を決めるわけです。

以下は、その呼び出しコードです。
		WebData curInput = new WebData();

		String code = helper.getTextViewString(R.id.textViewCode);

		// 使用しないフィールドには null をセットする
		curInput.社員コード = null;
		curInput.所属名 = null;
		curInput.生年月日2 = null;

		curInput.氏名 = helper.getEditTextString(R.id.editText1);
		curInput.フリガナ = helper.getEditTextString(R.id.editText2);
		curInput.所属 = helper.getEditTextString(R.id.editText3);
		curInput.給与 = Integer.parseInt(helper.getEditTextString(R.id.editText5));
		curInput.手当 = helper.getEditTextString(R.id.editText6);
		curInput.管理者 = helper.getEditTextString(R.id.editText7);
		curInput.生年月日 = helper.getEditTextString(R.id.editTexBirth);

		curInput.性別 = ((Spinner)(helper.mainActivity).findViewById(R.id.spinner)).getSelectedItemPosition()+"";

		String update = helper.buildUpdate("社員マスタ", curInput, WebData.class );
		String text = update + String.format(" where 社員コード = '%s' ", code );



null を設定して、除外するフィールドを決めています。なので、このコードでは意図的に空文字を文字列の列にセットする事ができません。そういう場合は、仕様を追加します。



タグ:java Reflect
posted by lightbox at 2017-10-21 01:15 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年09月23日


ViewSwitcher を使用した2画面アプリ (5) : SQLiteデータを更新する

この内容は、ViewSwitcher を使用した2画面アプリ (4) : SQLiteデータをインポートしてリストビューに表示するより続いています。
更新部分に特化したコードにする為に、MainControl をさらに継承して、マニフェストの初期画面を新しいクラス( MainUpdate ) に変更します。 特別な部分は特に無く、更新ボタンをクリックすると更新するだけです。但し、新規と修正の二通りがあるので、textViewCode の表示状態を取得して、どちらを実行するか決定しています。
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;

public class MainUpdate extends MainControl {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
				Log.i("lightbox","タップされました");

				String scode = arrayList.get(position);
				helper.getTextView(R.id.textViewCode).setText(scode);

				// キーを渡して SQLite にアクセスする
				loadData(scode);

				// 画面を切り替える
				helper.showEditView();

			}
		});

		// 更新ボタンの処理
		MainUpdate.this.findViewById(R.id.buttonUpdate).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Log.i("lightbox","更新ボタンがクリックされました");

				//alertDialog.show();
				helper.showDialog("更新してよろしいですか?", new Helper.OnDialogOkListener() {

					@Override
					public void onDialogOkListener() {
						Log.i("lightbox","ダイアログで、OK ボタンがクリックされました");

						if (  helper.getTextView(R.id.textViewCode).getVisibility() == View.VISIBLE  ) {
							updateData();
						}
						else {
							insertData();
						}

					}

				});

			}
		});
	}

	// *******************************
	// データの更新
	// *******************************
	private void updateData() {

		Log.i("lightbox","updateData");

		String scode = helper.getTextViewString(R.id.textViewCode);
		String sname = helper.getEditTextString(R.id.editText1);
		String furi = helper.getEditTextString(R.id.editText2);

		values.clear();
		values.put("sname", sname);
		values.put("furi", furi);
		db.update("syain", values, String.format("scode='%s'", scode), null);

		loadView();
		helper.showFirstView();
	}

	// *******************************
	// データの登録
	// *******************************
	private void insertData() {

		Log.i("lightbox","insertData");

		String scode = helper.getTextViewString(R.id.editTextCode);
		String sname = helper.getTextViewString(R.id.editText1);
		String furi = helper.getTextViewString(R.id.editText2);

		values.clear();
		values.put("scode", scode);
		values.put("sname", sname);
		values.put("furi", furi);
		db.insert("syain", null, values);

		loadView();
		helper.showFirstView();

	}

	// *******************************
	// 編集用データの表示
	// *******************************
	private void loadData(String scode) {

		String sql = String.format("select * from %s where scode='%s' ", "syain", scode);
		Cursor cursor = mySQLite.getReadableDatabase().rawQuery(sql, null);
		Boolean isData = null;
		isData = cursor.moveToFirst();

		String sname = cursor.getString(cursor.getColumnIndex("sname"));
		helper.setEditTextString(R.id.editText1,sname);
		String furi = cursor.getString(cursor.getColumnIndex("furi"));
		helper.setEditTextString(R.id.editText2,furi);

		cursor.close();

	}

}




posted by lightbox at 2017-09-23 17:38 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

ViewSwitcher を使用した2画面アプリ (4) : SQLiteデータをインポートしてリストビューに表示する

この内容は、ViewSwitcher を使用した2画面アプリ (3) : 画面部分の作成と画面切り替えテスト『画面をコントロールする Helper クラスの作成』より続いています。
表示するデータはなんでもいいのですが、他の環境に依存しないように、SQLIte3 の出来上がったデータを Android Studio にインポート(Push) して使用します。MainActivity もそのまま使わずに、MainControl というクラスを作成して MainActivity を継承し、メニュー部分の記述は見なくてもいいようにしています。 ※ AndroidManifest.xml の記述は、MainActivity を MainControl に変更すれば動作します データは、以下をダウンロードして、hanbai.sqlite3 を使用して下さい。 ※ 中のスクリプトに関しては、『Android Studio にインポートして使用する SQLite データベースを MDB より作成する VBScript』 を参照して下さい インポート前の準備 Android に、初期データベースを作成させて、それを上書きします。場所は data/data/パッケージ名/databases/hanbai.sqlite3 になります。 初期データベースを作成するには、SQLite 用の ヘルパークラスを作成して以下のように onStart と onStop に処理を書いて下さい。
public class MainControl extends MainActivity {

	// *******************************
	// SQLite用
	// *******************************
	public MySQLite mySQLite;
	public SQLiteDatabase db;
	public ContentValues values;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

	}

	// *******************************
	// SQLite用 初期処理
	// *******************************
	@Override
	protected void onStart() {
		super.onStart();

		Log.i("lightbox", "onStart");

		// hanbai.sqlite3 というファイル名で SQLite を作成
		mySQLite = new MySQLite(MainControl.this,"hanbai.sqlite3",1);
		// 更新用のデータベース・オブジェクトを取得
		db = mySQLite.getWritableDatabase();


	}

	// *******************************
	// SQLite用 終了処理
	// *******************************
	@Override
	protected void onStop() {
		super.onStop();

		Log.i("lightbox", "onStop");

		// 終了処理
		db.close();
		mySQLite.close();

	}

}


SQLite ヘルパークラス( MySQLite )
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class MySQLite extends SQLiteOpenHelper {

	// *********************************
	// コンストラクタ
	// *********************************
	public MySQLite(Context context, String dbfile, int dbversion) {
		super(context, dbfile, null, dbversion);
	}

	@Override
	public void onCreate(SQLiteDatabase sqLiteDatabase) {

	}

	@Override
	public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

	}
}


データのインポート

前述のコードをエミュレータで実行するだけです。実行したら、エミュレータの実行を停止して、Tools メニューの Android から、Android Device Monitor を起動して、data/data/パッケージ名/databases/hanbai.sqlite3 を選択し、右上の Push アイコンでインポートします。

今回は、syain を使用しますが、テーブルには以下のようなものがあります。


データの読み出しとリストビューへの表示

loadView というメソッドを作成して、onStart 内で呼び出しています。リストビューは、カスタマイズ無しの単純リストビューなので、リストの付加データーは ArrayList に保存して使用します。( Syain クラスを作成すればその必要はありません )
public class MainControl extends MainActivity {

	// *******************************
	// SQLite用
	// *******************************
	public MySQLite mySQLite;
	public SQLiteDatabase db;
	public ContentValues values;

	// *******************************
	// ListView用
	// *******************************
	public ListView listView;
	public ArrayAdapter<String> arrayAdapter;	// 名前を格納
	public ArrayList<String> arrayList;	// コードを格納

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// ListView
		listView = (ListView) MainControl.this.findViewById(R.id.listView);
		arrayAdapter = new ArrayAdapter<String>(MainControl.this,android.R.layout.simple_list_item_1);
		listView.setAdapter(arrayAdapter);
		arrayList = new ArrayList<String>();

	}

	// *******************************
	// リストビューの表示
	// *******************************
	public void loadView() {

		String sql = String.format("select * from %s order by scode desc", "syain");
		Cursor cursor = mySQLite.getReadableDatabase().rawQuery(sql, null);

		Boolean isData = null;
		// 何度も呼び出されるので一覧を初期化
		arrayList.clear();
		arrayAdapter.clear();

		// SQLite のレコードセットの読み込みループ
		while ( isData == null ? (isData = cursor.moveToFirst()) : (isData = cursor.moveToNext()) ) {

			// 社員コード
			String scode = cursor.getString(cursor.getColumnIndex("scode"));
			arrayList.add(scode);
			// 氏名
			String sname = cursor.getString(cursor.getColumnIndex("sname"));
			arrayAdapter.add(sname);

			Log.i("lightbox", String.format("%s : %s",scode,sname));

		}
		cursor.close();
	}

	// *******************************
	// SQLite用 初期処理
	// *******************************
	@Override
	protected void onStart() {
		super.onStart();

		Log.i("lightbox", "onStart");

		// hanbai.sqlite3 というファイル名で SQLite を作成
		mySQLite = new MySQLite(MainControl.this,"hanbai.sqlite3",1);
		// 更新用のデータベース・オブジェクトを取得
		db = mySQLite.getWritableDatabase();

		// データを ListView に表示
		loadView();

	}

	// *******************************
	// SQLite用 終了処理
	// *******************************
	@Override
	protected void onStop() {
		super.onStop();

		Log.i("lightbox", "onStop");

		// 終了処理
		db.close();
		mySQLite.close();

	}

}




posted by lightbox at 2017-09-23 16:22 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

Android Studio にインポートして使用する SQLite データベースを MDB より作成する VBScript



Windows 64bit 用です。
( ソースを変更すれば、32ビットでももちろん動作します )


Android 内でいろいろするのは大変なので、2005年に作成してずっと使っているテスト用データを SQLite3 に変換する VBScript を改めて最新にしました。

最初の実行で、pragma auto_vacuum = full を実行しているので、いろいろやっても肥大はしないと思います。MDB のアクセスに Microsoft.Jet.OLEDB.4.0 を使用しており、32ビット用の cscript.exe を実行する必要があるので、内部で SysWOW64 を参照しています
( strWinDir と SysWOW64 を省けば 32ビット用になります )

SQLite3 ODBC Driver が必要です

32ビット用の ODBC ドライバが必要になります。ですが、後々の事を考えれば 64 と 32 を両方インストールするのが良いと思います

ODBC ドライバ

ソースコード
Crun()


' 使用する ODBC ドライバです
strDriver = "{SQLite3 ODBC Driver}"

strMDBFile = "販売管理B.mdb"
strSQLiteFile = "hanbai.sqlite3"


' ************************************************
' 基本設定
' ************************************************
' このスクリプトが存在するディレクトリを取得
strCurDir = WScript.ScriptFullName
strCurDir = Replace( strCurDir, WScript.ScriptName, "" )
strMdbPath = strCurDir & strMDBFile
strSQLite3Path = strCurDir & strSQLiteFile

strMessage = "対象 MDB は " & strMdbPath & "です" & vbCrLf & vbCrLf

strMessage = strMessage & "↓SQLiteの環境です" & vbCrLf
strMessage = strMessage & "DB : " & strSQLite3Path & vbCrLf & vbCrLf

strMessage = strMessage & "既にテーブルが存在する場合はメッセージが出ません" & vbCrLf
strMessage = strMessage & "それ以外ではエラーメッセージが出ますが、問題ありません"
if vbCancel = MsgBox( strMessage, vbOkCancel ) then
	Wscript.Quit
end if

' ************************************************
' 処理用文字列設定
' ************************************************
' MDB の接続文字列
strConnectMdb = _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & strMdbPath & ";"

strConnectSQLite3 = _
"Provider=MSDASQL;Driver="&strDriver&";DATABASE=" & strSQLite3Path & ";"

' ************************************************
' 初期処理
' ************************************************
Set Cn = CreateObject("ADODB.Connection")
Cn.CursorLocation = 3
Set Rs = CreateObject( "ADODB.Recordset" )

Set Cn2 = CreateObject("ADODB.Connection")
Cn2.CursorLocation = 3
Set Rs2 = CreateObject( "ADODB.Recordset" )
Rs2.LockType = 3

Cn.Open strConnectMdb
Cn2.Open strConnectSQLite3

RunSS( "pragma auto_vacuum = full" )

' ************************************************
' コード名称マスタ
' ************************************************
Query = _
"create table コード名称マスタ (" & _
"	kubun INT" & _
"	,code VARCHAR(10)" & _
"	,name NVARCHAR(50)" & _
"	,num1 INT" & _
"	,num2 INT" & _
"	,cdate DATETIME" & _
"	,udate DATETIME" & _
"	,primary key(kubun,code)" & _
")"
Call SSTransfer( "コード名称マスタ", Query )

' ************************************************
' コントロールマスタ
' ************************************************
Query = _
"create table コントロールマスタ (" & _
"	ckey VARCHAR(1)" & _
"	,udate DATETIME" & _
"	,uno INT" & _
"	,primary key(ckey)" & _
")"
Call SSTransfer( "コントロールマスタ", Query )

' ************************************************
' メッセージマスタ
' ************************************************
Query = _
"create table メッセージマスタ (" & _
"	code VARCHAR(4)" & _
"	,message NVARCHAR(100)" & _
"	,primary key(code)" & _
")"
Call SSTransfer( "メッセージマスタ", Query )

' ************************************************
' 取引データ
' ************************************************
Query = _
"create table 取引データ (" & _
"	tkubun VARCHAR(2)" & _
"	,uno INT" & _
"	,row INT" & _
"	,tdate DATETIME" & _
"	,tcode VARCHAR(4)" & _
"	,scode VARCHAR(4)" & _
"	,su INT" & _
"	,tanka単価 INT" & _
"	,kin INT" & _
"	,primary key(tkubun,uno,row)" & _
")"
Call SSTransfer( "取引データ", Query )

' ************************************************
' 商品マスタ
' ************************************************
Query = _
"create table 商品マスタ (" & _
"	scode VARCHAR(4)" & _
"	,sname NVARCHAR(50)" & _
"	,ztanka INT" & _
"	,htanka INT" & _
"	,sbun VARCHAR(3)" & _
"	,skubun VARCHAR(1)" & _
"	,cdate DATETIME" & _
"	,udate DATETIME" & _
"	,primary key(scode)" & _
")"
Call SSTransfer( "商品マスタ", Query )

' ************************************************
' 商品分類マスタ
' ************************************************
Query = _
"create table 商品分類マスタ (" & _
"	sbun VARCHAR(3)" & _
"	,name NVARCHAR(50)" & _
"	,cdate DATETIME" & _
"	,udate DATETIME" & _
"	,primary key(sbun)" & _
")"
Call SSTransfer( "商品分類マスタ", Query )

' ************************************************
' 得意先マスタ
' ************************************************
Query = _
"create table 得意先マスタ (" & _
"	tcode VARCHAR(4)" & _
"	,tname NVARCHAR(50)" & _
"	,tkubun VARCHAR(1)" & _
"	,tanto VARCHAR(4)" & _
"	,zip VARCHAR(7)" & _
"	,ad1 NVARCHAR(100)" & _
"	,ad2 NVARCHAR(100)" & _
"	,cdate DATETIME" & _
"	,udate DATETIME" & _
"	,primary key(tcode)" & _
")"
Call SSTransfer( "得意先マスタ", Query )

' ************************************************
' 社員マスタ
' ************************************************
Query = _
"create table 社員マスタ (" & _
"	scode VARCHAR(4)" & _
"	,sname NVARCHAR(50)" & _
"	,furi NVARCHAR(50)" & _
"	,syozoku VARCHAR(4)" & _
"	,sex INT" & _
"	,cdate DATETIME" & _
"	,udate DATETIME" & _
"	,kyuyo INT" & _
"	,teate INT" & _
"	,kanri VARCHAR(4)" & _
"	,primary key(scode)" & _
")"
Call SSTransfer( "社員マスタ", Query )

' ************************************************
' 郵便番号マスタ
' ************************************************
Query = _
"create table 郵便番号マスタ (" & _
"	zip VARCHAR(7)" & _
"	,kana1 NVARCHAR(255)" & _
"	,kana2 NVARCHAR(255)" & _
"	,kana3 NVARCHAR(255)" & _
"	,name1 NVARCHAR(255)" & _
"	,name2 NVARCHAR(255)" & _
"	,name3 NVARCHAR(255)" & _
")"
Call SSTransfer( "郵便番号マスタ", Query )

RunSS( "drop table codename" )
RunSS("alter table コード名称マスタ rename to codename")
RunSS( "drop table control" )
RunSS("alter table コントロールマスタ rename to control")
RunSS( "drop table message" )
RunSS("alter table メッセージマスタ rename to message")
RunSS( "drop table tdata" )
RunSS("alter table 取引データ rename to tdata")
RunSS( "drop table goods" )
RunSS("alter table 商品マスタ rename to goods")
RunSS( "drop table sbun" )
RunSS("alter table 商品分類マスタ rename to sbun")
RunSS( "drop table tokui" )
RunSS("alter table 得意先マスタ rename to tokui")
RunSS( "drop table syain" )
RunSS("alter table 社員マスタ rename to syain")
RunSS( "drop table zip" )
RunSS("alter table 郵便番号マスタ rename to zip")

RunSS( "drop view v_goods" )
Query = _
"create view v_goods as" & _
"	SELECT goods.scode" & _
"	, goods.sname" & _
"	, goods.htanka" & _
"	, sbun.sbun" & _
"	, sbun.name AS bunrui_name" & _
"	, goods.skubun" & _
"	, codename.name AS kubun_name" & _
" from" & _
"	(goods LEFT JOIN sbun" & _
"	ON goods.sbun = sbun.sbun" & _
"	) LEFT JOIN codename" & _
"	ON goods.skubun = codename.code" & _
" where" & _
"	codename.kubun = 3"
RunSS( Query )

' ************************************************
' 終了
' ************************************************

Cn2.Close
Cn.Close

Wscript.Echo "処理が終了しました"

' ************************************************
' 転送
' ************************************************
function SSTransfer( strTable, QueryCreate )

	Wscript.Echo strTable & " の作成を開始します"

	Dim Query

	Query = "drop table " & strTable
	RunSS( Query )

	RunSS( QueryCreate )

	Query = "select * from " & strTable
	Rs.Open Query, Cn
	Rs2.Open Query, Cn2

	nCount = Rs.Fields.Count
	Do While not Rs.EOF
		Rs2.AddNew

		For I = 0 to nCount - 1
			Rs2.Fields(I).Value = Rs.Fields(I).value
		Next
		Rs2.Update

		' 更新
		Rs.MoveNext
	Loop

	Rs2.Close
	Rs.Close

	Wscript.Echo strTable & " の作成が終了しました"

end function

' ************************************************
' SQLLite 実行
' ************************************************
function RunSS( Query )

	on error resume next
	Cn2.Execute Query
	if Err.Number <> 0 then
		Wscript.Echo Err.Description
	end if
	on error goto 0

end function


' **********************************************************
' Cscript.exe で実行を強制
' ウィンドウをアクティブにし、最大化ウィンドウとして表示(3)
' Cscript.exe の実行終了後 pause で一時停止
' **********************************************************
Function Crun( )

	Dim str,WshShell

	str = WScript.FullName
	str = Right( str, 11 )
	str = Ucase( str )
	if str <> "CSCRIPT.EXE" then
		str = WScript.ScriptFullName

		Set WshShell = CreateObject( "WScript.Shell" )
		strWinDir = WshShell.ExpandEnvironmentStrings("%windir%")

		strParam = " "
		For I = 0 to Wscript.Arguments.Count - 1
			if instr(Wscript.Arguments(I), " ") < 1 then
				strParam = strParam & Wscript.Arguments(I) & " "
			else
				strParam = strParam & Dd(Wscript.Arguments(I)) & " "
			end if
		Next
		Call WshShell.Run( "cmd.exe /c " & strWinDir & "\SysWOW64\cscript.exe " & Dd(str) & strParam & " & pause", 3 )
		WScript.Quit
	end if

End Function
' **********************************************************
' 文字列を " で囲む関数
' **********************************************************
Function Dd( strValue )

	Dd = """" & strValue & """"

End function


Androd には、初回データベースを一つ作成してデータベース用のフォルダを作成させて Push するといいです。

SQLite 用クラス
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class MySQLite extends SQLiteOpenHelper {

	// *********************************
	// コンストラクタ
	// *********************************
	public MySQLite(Context context, String dbfile, int dbversion) {
		super(context, dbfile, null, dbversion);
	}

	@Override
	public void onCreate(SQLiteDatabase sqLiteDatabase) {

	}

	@Override
	public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

	}
}


db 作成およびテスト用コード
public class MainActivity extends AppCompatActivity {

	private MySQLite mySQLite;
	private SQLiteDatabase db;
	private ContentValues values;
	int counter;

	@Override
	protected void onStart() {
		super.onStart();
		Log.i("lightbox", "onStart");

		mySQLite = new MySQLite(MainActivity.this,"hanbai.sqlite3",1);
		// 書き込み用
		db = mySQLite.getWritableDatabase();
		// データ書き込み用
		values = new ContentValues();
		counter = 50;

	}

	@Override
	protected void onStop() {
		super.onStop();
		Log.i("lightbox", "onStop");

		db.close();
		mySQLite.close();

	}

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


		MainActivity.this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				counter++;
				values.put("scode", String.format("%04d", counter));
				values.put("sname", String.format("氏名%d", counter));
				values.put("kyuyo", counter*1000);
				db.insert("syain", null, values);

				Log.i("lightbox", String.format("%d",counter));

			}
		});

		MainActivity.this.findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				String sql = String.format("select * from %s order by scode desc", "syain");
				Cursor cursor = mySQLite.getReadableDatabase().rawQuery(sql, null);
				Boolean isData = null;
				while ( isData == null ? (isData = cursor.moveToFirst()) : (isData = cursor.moveToNext()) ) {

					String scode = cursor.getString(cursor.getColumnIndex("scode"));
					String sname = cursor.getString(cursor.getColumnIndex("sname"));

					Log.i("lightbox", String.format("%s : %s",scode,sname));

				}
				cursor.close();

			}
		});



	}
}




posted by lightbox at 2017-09-23 16:14 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

ViewSwitcher を使用した2画面アプリ (3) : 画面部分の作成と画面切り替えテスト『画面をコントロールする Helper クラスの作成』

この内容は、ViewSwitcher を使用した2画面アプリ (2) : 画面部分の作成と画面切り替えテスト『画面の作成』より続いています。
Helper クラスはその名の通り、『お助けクラス』です。一般的には『標準化』の為の部品です。名称はなんでもいいですが、ここでは一般名として『Helper』と名乗っています。 通常、省略せずに画面まわりのコードを書こうとすると指定しているのは id だけなのに、やたらと長いコードを書くはめになります。また、アプリが完成してから眺めてみると、相当無駄が多いことも多くなります。 ここでは、まず最初からその無駄を省くための画面周りのメソッドを作成した Helper クラスをプロジェクトに追加します。 Helper クラスを作成すると、画面切り替えのテストは以下の MainActivity で行えます
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

	public Helper helper;

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

		helper = new Helper(MainActivity.this);

	}

	// *******************************
	// メニュー
	// *******************************
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.menu_main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();

		if (id == R.id.action_1) {
			Log.i("lightbox","処理1");

			helper.showFirstView();

			return true;
		}

		if (id == R.id.action_2) {
			Log.i("lightbox","処理2");

			helper.showInsertView();

			return true;
		}

		return super.onOptionsItemSelected(item);
	}
}

※ メニュー部分も省略したい場合は、MainActivity を継承したクラス( 例えば、MainControl )を作って、それをマニフェストで初期画面にするといいです。

Helper.java

※ コピペしやすいように、先頭のパッケージ部分を省いています

とても単純なコードが並んでいます。これらは本来 MainActivity で記述していたものを引越ししただけのものです。なので、このプロジェクトでしか使えませんが、『標準化』のサンプルにはなると思います。

確認ダイアログ処理に関しても、MainActivity に書くと、『ただ確認したいだけで機能とは関係無い』のに、とても多くの行数を必要とします。ただ、処理的には、確認後の処理は MainActivity で記述したいので、OnDialogOkListener を作って使用しています。(ボタンのクリック処理と同じです)

※ 商品では無いので(テストしたいだけなので)、フラグメントとかも無しです
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.ViewSwitcher;

public class Helper {

	private MainActivity mainActivity;
	private ViewSwitcher viewSwitcher;

	// *******************************
	// 確認ダイアログ用
	// *******************************
	private AlertDialog.Builder alertDialogBuilder;

	// *******************************
	// 確認ダイアログ用インターフェイス
	// *******************************
	public interface OnDialogOkListener {
		abstract public void onDialogOkListener();
	}

	// *******************************
	// コンストラクタ
	// *******************************
	public Helper(MainActivity mainActivity) {

		this.mainActivity = mainActivity;
		this.viewSwitcher = (ViewSwitcher) mainActivity.findViewById(R.id.viewSwitcher);

		initSettings();
	}

	// *******************************
	// 初期処理
	// ******************************
	public void initSettings() {

		mainActivity.setTitle("社員一覧");

	}

	// *******************************
	// 確認ダイアログ処理
	// ******************************
	public void showDialog( String message, final OnDialogOkListener listener ) {

		Log.i("lightbox", "showDialog");

		alertDialogBuilder = new AlertDialog.Builder(mainActivity);
		alertDialogBuilder.setTitle("確認");
		alertDialogBuilder.setMessage(message);

		alertDialogBuilder.setPositiveButton("OK",
				new DialogInterface.OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						Log.i("lightbox", "OK");

						listener.onDialogOkListener();

					}
				});

		alertDialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() {
			@Override
			public void onCancel(DialogInterface dialog) {
				Log.i("lightbox", "CANCEL");
			}
		});

		// キャンセル可能
		alertDialogBuilder.setCancelable(true);
		alertDialogBuilder.show();

	}

	// *******************************
	// 初期画面
	// *******************************
	public void showFirstView() {

		// コード表示部分の表示
		Helper.this.getTextView(R.id.textViewCode).setVisibility(View.VISIBLE);
		// コード入力部分の非表示
		Helper.this.getEditText(R.id.editTextCode).setVisibility(View.GONE);
		mainActivity.setTitle("社員一覧");
		viewSwitcher.setDisplayedChild(0);

	}

	// *******************************
	// 修正画面
	// *******************************
	public void showEditView() {

		// コード表示部分の表示
		Helper.this.getTextView(R.id.textViewCode).setVisibility(View.VISIBLE);
		// コード入力部分の非表示
		Helper.this.getEditText(R.id.editTextCode).setVisibility(View.GONE);
		mainActivity.setTitle("社員修正");
		viewSwitcher.setDisplayedChild(1);

	}

	// *******************************
	// 新規画面
	// *******************************
	public void showInsertView() {

		// コード表示部分の表示
		Helper.this.getTextView(R.id.textViewCode).setVisibility(View.GONE);
		// コード入力部分の非表示
		Helper.this.getEditText(R.id.editTextCode).setVisibility(View.VISIBLE);
		mainActivity.setTitle("社員登録");
		Helper.this.setEditTextString(R.id.editTextCode,"");
		Helper.this.setEditTextString(R.id.editText1,"");
		Helper.this.setEditTextString(R.id.editText2,"");
		viewSwitcher.setDisplayedChild(1);

	}

	// *******************************
	// EditText 取得
	// *******************************
	public EditText getEditText(int id) {
		return (EditText)mainActivity.findViewById(id);
	}

	// *******************************
	// TextView 取得
	// *******************************
	public TextView getTextView(int id) {
		return (TextView)mainActivity.findViewById(id);
	}

	// *******************************
	// TextView より値取得
	// *******************************
	public String getTextViewString(int id) {
		return ((TextView)mainActivity.findViewById(id)).getText().toString();
	}

	// *******************************
	// EditText より入力値取得
	// *******************************
	public String getEditTextString(int id) {
		return ((EditText)mainActivity.findViewById(id)).getText().toString();
	}

	// *******************************
	// EditText に値セット
	// *******************************
	public void setEditTextString(int id,String text) {
		((EditText)mainActivity.findViewById(id)).setText(text);
	}

}





posted by lightbox at 2017-09-23 15:07 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

ViewSwitcher を使用した2画面アプリ (2) : 画面部分の作成と画面切り替えテスト『画面の作成』

この内容は、ViewSwitcher を使用した2画面アプリ (1) : 画面部分の作成と画面切り替えテスト『メニューの作成』より続いています。
ViewSwitcher 上で二つのレイアウトを作成し、ViewSwitcher のメソッドを使用してそのレイアウトを切り替えます。そのまま全てを一つの画面定義に記述してもいいのでが、そうすると2画面目の内容を『デザイナ』で確認する事ができません。 ですから、2画面目は別の画面定義で作成して、include で ViewSwitcher の中から呼び出すようにします。 activity_main.xml 画面の切り替えは、一般的には ViewAnimator クラスの showNext() と showPrevious() を使うのですが、実行するのがメニューなので、どこで実行しても正しい画面が表示されるように、setDisplayedChild(int whichChild) を使用します( whichChild は 0 かまたは 1 ) 多少無駄な動きは存在しますが、簡単に正しく動作するはずです。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:padding="16dp">

    <ViewSwitcher
        android:id="@+id/viewSwitcher"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <ListView
                android:id="@+id/listView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>

        </LinearLayout>

        <include
            android:id="@+id/entry"
            layout="@layout/entry"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </ViewSwitcher>

</LinearLayout>





entry.xml

editTextCode と textViewCode と  は、社員マスタのキー部分に使用します。機能としては、修正処理と新規登録処理の二つあるので、それぞれ editTextCode と textViewCode を使い、使用しないほうは setVisibility(View.GONE); で非表示にします
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:id="@+id/textViewCode"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="4dp"
        android:textSize="30sp"/>

    <EditText
        android:id="@+id/editTextCode"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"/>

    <EditText
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"/>

    <EditText
        android:id="@+id/editText2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"/>

    <Button
        android:id="@+id/buttonUpdate"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="更新"/>

</LinearLayout>





posted by lightbox at 2017-09-23 14:33 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

ViewSwitcher を使用した2画面アプリ (1) : 画面部分の作成と画面切り替えテスト『メニューの作成』

この内容は、ViewSwitcher を使用した2画面アプリ (2) : 画面部分の作成と画面切り替えテスト『画面の作成』に続いています。
2画面アプリと言えば、もう一つの Activity を作成して、Intent でデータを引き渡すタイプですが、初心者にとってちょっと敷居が高いので、ViewSwitcher を使用します。ViewSwitcher は2画面のみ対応ですが、同系統(ViewAnimator を継承)の ViewFlipper を使用すれば複数画面も簡単に作成できます。 この場合、見えている画面は一つでも常に同じ Activity で画面を参照できるので、大きな画面に全て表示されていると思って普通にコードが書けます。ですから、そこが楽なぶん、Helper クラスを作成して、画面の処理は全てそこにまかせるという工夫に力を入れたいと思います。 1) メニューの作成 メニューは、その場面で必要な画面切替えに使用します。画面上に機能に直接関係無いボタンを作りたく無いので作成します。Android Studio のテンプレートとしては 『Empty』を使用しますかので、メニュー部分は自分で作成する必要があります。 ▼ menu フォルダの作成 (最後に OK で menu フォルダがリソースに作成されます) ▼ メニューフォルダで、new => Menu resource file でメニュー定義を作成 メニューのファイル名は、何でもいいですが、通常の画面と同じく XML で作成されるので、小文字のアルファベットで、word 間は _(アンダーバー)を使います。ここでは、Android が一般的に使う 『menu_main』を使用しています メニューアイテムの追加 デザイナの画面に、MenuItem をドラッグ&ドロップして追加します。左下の『コンポーネント・ツリー』は動作しないので注意です。 次に右サイドの『プロパティ』で、id を入力します。icon と showAsAction は自由に選択してもいいのですが、ここでは以下のようにエディタで定義します
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/action_1"
        android:icon="@android:drawable/btn_dialog"
        android:title="戻る"
        app:showAsAction="always"/>

    <item
        android:id="@+id/action_2"
        android:title="新規登録"/>

</menu>



メニューの実行部分の作成

機能的には一度作ってしまえば変更する事は殆ど無いので、MainActivity の最後に追加します。(helper のメソッドは後で作成するのでコメントにしています)
	// *******************************
	// メニュー
	// *******************************
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.menu_main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();

		if (id == R.id.action_1) {
			Log.i("lightbox","処理1");

			// helper.showFirstView();

			return true;
		}

		if (id == R.id.action_2) {
			Log.i("lightbox","処理2");

			// helper.showInsertView();

			return true;
		}

		return super.onOptionsItemSelected(item);
	}



posted by lightbox at 2017-09-23 13:10 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年09月09日


OkHttp を使用した HttpAccess クラスで Web 上の 画像をダウンロードして表示するテンプレート

▼ HttpAccess パッケージのダウンロード


( 解凍して、main\java フォルダの中に コピーして下さい )
※ OkHttp より 最新の Gradle の記述を取得して build.gradle の dependencies の最後に貼り付けて下さい
例) compile 'com.squareup.okhttp3:okhttp:3.8.1'

処理概要

ボタンをクリックした後、HttpAccess クラスでファイルをダウンロードする download メソッドを実行するテンプレートです。サンプルでは画像を使用して、ダウンロードした後に ImageView に表示していますが、ダウンロード処理だけならば、どのようなファイルでもかまいません。

ここでは、パッケージ内に保存していますが、OutputStream を引き渡すので、他の保存場所でも問題はありません。

※ onAsyncTaskListener の引数の文字列は、http のステータスコードなので、通常は 200 が返ります。インターネットのアクセスそのものが成功すれば、null 以外が返ります。

MainActivity
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import com.example.lightbox.web.HttpAccess;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class MainActivity extends AppCompatActivity {

	private HttpAccess httpAccess;
	private ImageView imageView;
	private String image_url;
	private OutputStream outputStream;

	private Bitmap bitmap;
	private BitmapFactory.Options options;

	private InputStream inputStream;

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

		// ダウンロードでは、URL をメソッドで指定します
		httpAccess = new HttpAccess("");
		imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView);
		image_url = "https://lightbox.sakura.ne.jp/demo/image/sample.jpg";

		// ボタンをクリック
		MainActivity.this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				try {

					// 保存ファイル
					outputStream = MainActivity.this.openFileOutput("save.jpg", MODE_PRIVATE);
					// ダウンロード
					httpAccess.download(image_url,outputStream, new HttpAccess.OnAsyncTaskListener() {
						@Override
						public void onAsyncTaskListener(String s) {

							if ( s != null ) {
								Toast.makeText(MainActivity.this,"画像が保存されました",Toast.LENGTH_SHORT).show();

								// ステータスコード
								Log.i("lightbox", String.format("code = %s",s));

								// 画像を読み込む
								bitmap = null;
								try {
									options = new BitmapFactory.Options();
									inputStream = MainActivity.this.openFileInput("save.jpg");
									bitmap = BitmapFactory.decodeStream(inputStream, null, options);	// ここではオプションは未指定
									inputStream.close();

								} catch (IOException e) {
									e.printStackTrace();
								}

								// 表示
								if ( bitmap != null ) {
									imageView.setImageBitmap(bitmap);
								}
							}
						}
					});

				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

}


画面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              tools:context=".MainActivity">

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

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

※ android.permission.INTERNET が必要です
※ コピーして使いやすいように、ソースコードにパッケージに依存する記述が無いので注意して下さい。
(画面は、.MainActivity なのでそのまま使用可能です)



posted by lightbox at 2017-09-09 16:57 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年08月24日


OkHttp を使用した HttpAccess クラスで Web 上の PHP アプリに対してファイルをアップロードするテンプレート

▼ HttpAccess パッケージのダウンロード


( 解凍して、main\java フォルダの中に コピーして下さい )
※ OkHttp より 最新の Gradle の記述を取得して build.gradle の dependencies の最後に貼り付けて下さい
例) compile 'com.squareup.okhttp3:okhttp:3.8.1'

処理概要

assets フォルダにある画像ファイルを open して InputStream を取得し、PHP のファイルアップロード用のコードのある URL を呼び出して、ファイルをアップロードします。ファイルは、画像ファイルである必要はありませんが、確認作業が容易になるので画像を使用しています。

HttpAccess の sendUpload メソッドを使用しますが、InputStream を引き渡すようにしているので、元のデータはいろいろなところから用意する事ができるはずです。

引数は以下のようになっています。

1) String url
2) String field
3) String name
4) InputStream inputStream
5) String mime
6) OnAsyncTaskListener listener 

field は HTML で type="file" の INPUT 要素の name 属性の値になります。
name は ファイル名 で、mime は "image/jpeg" のようなものになります。

PHP からの戻りは、JSON 文字列ですが、ステータスとデバッグ用の情報なので、デシリアライズ用のクラスは作成せずにルートから解析してエラーコードを取得しています。そのエラーコードに対応する内容はあらかじめ assets に error.json として保存して読み込んでから、専用のプライベートクラス(ErrorText) を使用してデシリアライズして使用しています。

InputStream から テキストデータを一括で取得する為、HttpAccess 内に定義済みの static メソッドである readTextAll を 使用しています( okio 使用したコードです )

error.json
{
	"error": [
		"0 - 正常終了",
		"1 - アップロードされたファイルは、php.ini の upload_max_filesize ディレクティブの値を超えています",
		"2 - アップロードされたファイルは、HTML フォームで指定された MAX_FILE_SIZE を超えています",
		"3 - アップロードされたファイルは一部のみしかアップロードされていません",
		"4 - ファイルはアップロードされませんでした",
		"5 - 『アップロードに失敗しました』",
		"6 - テンポラリフォルダがありません。PHP 5.0.3 で導入されました",
		"7 - ディスクへの書き込みに失敗しました。PHP 5.1.0 で導入されました",
		"8 - PHP の拡張モジュールがファイルのアップロードを中止しました"
	]
}

PHP のコード
<?php
session_cache_limiter('nocache');
session_start();

header( "Content-Type: application/json; charset=utf-8" );

// *************************************
// 変数初期値
// *************************************
$debug = 1;
$upload_dir = "./upload";
if ( !$_FILES ) {
	// php.ini の upload_max_filesize を超えて、
	// Apache に制限が無いと $_FILE が空になる
	$_FILES['target']['error'] = 5;	// ユーザエラーメッセージ
}

// *************************************
// アップロード処理
// フィールド名 : target で固定
// *************************************
if ( $_SERVER['REQUEST_METHOD'] == "POST" ) {

	// 公開状態では実行しない
	if ( $debug == 1 ) {

		$upload = realpath ( $upload_dir );
		$upload .= ( DIRECTORY_SEPARATOR . $_FILES['target']['name'] );
		if ( move_uploaded_file(
			$_FILES['target']['tmp_name'], $upload ) ) {
			$_POST['result']  = "アップロードに成功しました";
		}
		else {
			if ( !$_FILES ) {
				$_FILES['target']['error'] = 5;	// ユーザエラーメッセージ
			}
			$_POST['result']  = "アップロードに失敗しました";
		}

	}
	else {
		if ( $_FILES['target']['error'] == 0 ) {
			$_POST['result']  = "アップロードに成功しました";
		}
		else {
			$_POST['result']  = "アップロードに失敗しました";
		}
	}

}
else {
	$_POST['result']  = "POST メソッドを使用して下さい";
}

$_POST['files'] = $_FILES;

// *************************************
// この JSON 文字列を HTML 内の json
// オブジェクト定義として埋め込みます
// *************************************
print json_encode($_POST, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE );
?>


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              tools:context=".MainActivity">

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

</LinearLayout>


MainActivity
import android.content.res.AssetManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.example.lightbox.web.HttpAccess;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import java.io.InputStream;

public class MainActivity extends AppCompatActivity {

	private HttpAccess httpAccess;
	private AssetManager assetManager;
	private ErrorText errorText;

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

		httpAccess = new HttpAccess("");
		assetManager = MainActivity.this.getResources().getAssets();
		// PHP のエラーメッセージのテキストを定義した JSON 文字列の取得
		try {
			InputStream inputStream = assetManager.open("error.json");
			String json = HttpAccess.readTextAll(inputStream,"UTF-8");
			Gson gson = new Gson();
			errorText = gson.fromJson(json,ErrorText.class);
		} catch (Exception e) {
			e.printStackTrace();
		}

		MainActivity.this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				try {
					InputStream inputStream = assetManager.open("image.jpg");
					httpAccess.sendUpload(
							// URL ( 必ず指定 )
							"http://10.0.2.2/r101/php_upload/file_upload.php",
							// フィールド名
							"target",
							// ファイル名
							"image.jpg",
							inputStream,
							// MIME
							"image/jpeg",
							new HttpAccess.OnAsyncTaskListener() {
								@Override
								public void onAsyncTaskListener(String s) {

									// JSON 文字列のツリー解析
									/* {
										"result": "アップロードに成功しました",
										"files": {
											"target": {
												"name": "image.jpg",
												"type": "image\/jpeg",
												"tmp_name": "",
												"error": 0,
												"size": 22652
											}
										}
									} */
									JsonParser parser = new JsonParser();
									JsonElement element = parser.parse(s);
									JsonObject root = element.getAsJsonObject();
									String result = root.get("result").getAsString();
									JsonObject files = root.get("files").getAsJsonObject();
									JsonObject target = files.get("target").getAsJsonObject();
									int error = target.get("error").getAsInt();

									String ErrorText = errorText.error[error];
									Log.i("lightbox", s);
									Log.i("lightbox", result);
									Log.i("lightbox", ErrorText);


								}
							}
					);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});

	}

	// *************************************
	// assets フォルダの error.json 用
	// *************************************
	private class ErrorText {
		String[] error;
	}

}



build.gradle( dependencies ) に追加する2行
    compile 'com.google.code.gson:gson:2.8.1'
    compile 'com.squareup.okhttp3:okhttp:3.8.1'


※ android.permission.INTERNET が必要です
※ コピーして使いやすいように、ソースコードにパッケージに依存する記述が無いので注意して下さい。
(画面は、.MainActivity なのでそのまま使用可能です)





posted by lightbox at 2017-08-24 16:14 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年08月23日


OkHttp を使用した HttpAccess クラスで Web 上の掲示板に投稿(POST)するテンプレート

▼ HttpAccess パッケージのダウンロード


( 解凍して、main\java フォルダの中に コピーして下さい )
※ OkHttp より 最新の Gradle の記述を取得して build.gradle の dependencies の最後に貼り付けて下さい
例) compile 'com.squareup.okhttp3:okhttp:3.8.1'

処理概要

本来ならば、直接JSON を返す PHP 側のアプリを呼び出すのですが、一般的な『掲示板』に実際 Android から POST して、HTML で返される結果はをアプリ内で Log.i() で表示します。但し、この掲示板のデータの保存形式を JSON にしていますので、投稿終了後再度データそのものを呼び出して ListView に表示しています。

掲示板アプリはこのテスト専用に作成したもので、最後の更新後5分でデータが削除されます。投稿と投稿の間には1分の間隔が必要で、固有コードを入力して、そのコード(スレッド)毎に内容を確認できるようにしています




WEB側の実行画面


簡易掲示板(5分限定)

※ このページはレスポンシブです



Android の実行画面



最初のフィールドは固有コードの入力で、自由な数字を入力して次のフィールドで本文を投稿します。そして、投稿後 WEB 側で 固有コードを入力して送信して確認します。

※ 入力された本文は、ListView に表示されます

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              xmlns:tools="http://schemas.android.com/tools"
              android:id="@+id/layout"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              tools:context=".MainActivity"
              android:weightSum="1">

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

        <EditText
            android:id="@+id/editTextCode"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ems="10"
            android:inputType="number"/>

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Button"/>

    </LinearLayout>

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="113dp"
        android:ems="10"
        android:gravity="top|left"
        android:inputType="textMultiLine"
        />

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>


MainActivity
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;

import com.example.lightbox.web.HttpAccess;
import com.google.gson.Gson;

import java.util.HashMap;

public class MainActivity extends AppCompatActivity {

	// *******************************
	// データ定義
	// *******************************
	private String url = "https://lightbox.sakura.ne.jp/demo/template/lightbox/bbs-normal/bbs.php";
	private String jsonurl = "https://lightbox.sakura.ne.jp/demo/template/lightbox/bbs-normal/data/";

	// *******************************
	// 変数定義
	// *******************************
	private ArrayAdapter<WebData> arrayAdapter;
	private ListView listView;
	private HttpAccess httpAccess;

	// *******************************
	// 初期処理
	// *******************************
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// *******************************
		// ボタンのイベント
		// *******************************
		Button button = (Button) MainActivity.this.findViewById(R.id.button);
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Log.i("lightbox", "クリックされました");
				CallPost();
			}
		});

		// *******************************
		// リストビューの準備
		// *******************************
		listView = (ListView) MainActivity.this.findViewById(R.id.listView);
		// データが入るオブジェクト
		arrayAdapter = new ArrayAdapter<WebData>(MainActivity.this, android.R.layout.simple_list_item_1);
		// 初期化
		arrayAdapter.clear();
		// リストビューにデータが入るオブジェクトをセット
		listView.setAdapter(arrayAdapter);

		// *******************************
		// 送信用オブジェクトを作成
		// *******************************
		httpAccess = new HttpAccess(url);

	}

	// *******************************
	// 送信処理
	// *******************************
	private void CallPost() {

		HashMap<String, String> hashMap = new HashMap<String, String>();

		// 投稿テキスト
		EditText editText = (EditText) MainActivity.this.findViewById(R.id.editText);
		String text = editText.getText().toString();
		hashMap.put("text", text);

		// 固有コード
		EditText editTextCode = (EditText) MainActivity.this.findViewById(R.id.editTextCode);
		String code = editTextCode.getText().toString();
		hashMap.put("mycode", code);

		httpAccess.setUrl(url);
		// 送信
		httpAccess.sendPost(hashMap, new HttpAccess.OnAsyncTaskListener() {
			@Override
			public void onAsyncTaskListener(String s) {
				Log.i("lightbox", s);

				// 固有コード
				EditText editTextCode = (EditText) MainActivity.this.findViewById(R.id.editTextCode);
				String code = editTextCode.getText().toString();

				// *******************************
				// 投稿(POST) 後、結果の JSON を取得する
				// *******************************
				httpAccess.setUrl(String.format("%s%s.json",jsonurl,code));
				httpAccess.sendGet(new HttpAccess.OnAsyncTaskListener() {
					@Override
					public void onAsyncTaskListener(String s) {
						Log.i("lightbox", s);

						// *******************************
						// JSON を ListView に表示する
						// *******************************
						Gson gson = new Gson();
						DataList dataList = gson.fromJson(s,DataList.class);
						// クリア
						arrayAdapter.clear();
						// データセット
						arrayAdapter.addAll(dataList.item);

					}
				});


			}
		});

	}

}



Google Gson 用のデータ格納用のクラス
package com.example.lightbox.posttest;

public class DataList {

	WebData[] item;

}

package com.example.lightbox.posttest;

public class WebData {

	String text;

	@Override
	public String toString() {
		return text;
	}
}



build.gradle( dependencies ) に追加する2行
    compile 'com.google.code.gson:gson:2.8.1'
    compile 'com.squareup.okhttp3:okhttp:3.8.1'


※ android.permission.INTERNET が必要です
※ コピーして使いやすいように、ソースコードにパッケージに依存する記述が無いので注意して下さい。
(画面は、.MainActivity なのでそのまま使用可能です)





posted by lightbox at 2017-08-23 20:17 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年08月17日


OkHttp を使用した HttpAccess クラスで Web 上のデータを取得(GET)して ListView を表示するテンプレート



コピーして使いやすいように、ソースコードにパッケージに依存する記述が無いので注意して下さい。
(画面は、.MainActivity なのでそのまま使用可能です)

ListView は、Android で定義済みの android.R.layout.simple_list_item_1 を使用しています。それに伴い、WebData クラスでは、toString を Override して 氏名が表示されるようにしています。

JSON データは、PHP でも使用し、コードの見通しを良くする為、プロバティは日本語(UTF8)で記述されています。
        {
            "社員コード": "0001",
            "氏名": "浦岡 友也",
            "フリガナ": "ウラオカ トモヤ",
            "所属": "0003",
            "性別": "0",
            "作成日": "2005-09-12 00:00:00",
            "更新日": "2005-11-28 00:00:00",
            "給与": "270000",
            "手当": "9000",
            "管理者": null,
            "生年月日": "2010-01-01",
            "削除区分": null,
            "所属名": "営業部第三"
        }
▼ HttpAccess パッケージのダウンロード


( 解凍して、main\java フォルダの中に コピーして下さい )
※ OkHttp より 最新の Gradle の記述を取得して build.gradle の dependencies の最後に貼り付けて下さい
例) compile 'com.squareup.okhttp3:okhttp:3.8.1'

JSON データへのリンク
※ fld_search=氏名の一部 で条件を指定できます

MainActivity
public class MainActivity extends AppCompatActivity {

	// *******************************
	// データ定義
	// *******************************
	private String url = "https://lightbox.sakura.ne.jp/demo/template/lightbox/syain-req-jquery/app-get.php";

	// *******************************
	// 変数定義
	// *******************************
	private ArrayAdapter<WebData> arrayAdapter;
	private ListView listView;
	private HttpAccess httpAccess;

	// *******************************
	// 初期処理
	// *******************************
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// リストビュー
		listView = (ListView) MainActivity.this.findViewById(R.id.listView);
		// リストビューの行をタップした時の処理
		listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

				// アダプターを取得
				ArrayAdapter<WebData> arrayAdapter = (ArrayAdapter<WebData>)parent.getAdapter();

				// 行データを取得
				WebData webData;
				webData = arrayAdapter.getItem(position);

				Log.i("lightbox", String.format("社員コード : %s",webData.社員コード));
				Log.i("lightbox", webData.氏名);
				Log.i("lightbox", webData.フリガナ);
				Log.i("lightbox", String.format("所属 : %s %s",webData.所属,webData.所属名));
				Log.i("lightbox", String.format("性別 : %s", webData.性別.equals("0") ? "男性" : "女性" ));
				NumberFormat numberFormat = NumberFormat.getNumberInstance();
				Log.i("lightbox", String.format("給与 : %s", numberFormat.format(webData.給与)));
				if ( webData.手当 == 0 ) {
					Log.i("lightbox", "手当てはありません");
				}
				else {
					Log.i("lightbox", String.format("手当 : %s", numberFormat.format(webData.手当)));
				}
				if ( webData.管理者 != null ) {
					Log.i("lightbox", String.format("管理者コード : %s",webData.管理者));
				}
				else {
					Log.i("lightbox", "管理者です");
				}
				if ( webData.生年月日 != null ) {
					Log.i("lightbox", String.format("生年月日 : %s",webData.生年月日));
				}
				else {
					Log.i("lightbox", "生年月日は登録されていません");
				}

			}
		});


		// データが入るオブジェクト
		arrayAdapter = new ArrayAdapter<WebData>(MainActivity.this, android.R.layout.simple_list_item_1);
		// 初期化
		arrayAdapter.clear();

		// リストビューにデータが入るオブジェクトをセット
		listView.setAdapter(arrayAdapter);

		// WEB アクセス用オブジェクト
		httpAccess = new HttpAccess(url + "?fld_search=村");

	}

	// *******************************
	// メニューの処理
	// *******************************
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.menu_main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();

		// *******************************
		// 全て表示
		// *******************************
		if (id == R.id.action_1) {

			httpAccess.setUrl(url);
			httpAccess.sendGet(new HttpAccess.OnAsyncTaskListener() {
				@Override
				public void onAsyncTaskListener(String s) {

					// Gson インスタンス
					Gson gson = new Gson();

					// Gson によるデシリアライズ
					DataList dataList = gson.fromJson(s,DataList.class);

					// dataList.row が WebData の配列
					for( WebData rowData : dataList.row) {
						Log.i("lightbox",rowData.氏名);
					}

					// クリア
					arrayAdapter.clear();
					// データセット
					arrayAdapter.addAll(dataList.row);

				}
			});
			return true;

		}

		// *******************************
		// 一部表示
		// *******************************
		if (id == R.id.action_2) {

			httpAccess.setUrl(url + "?fld_search=村");
			httpAccess.sendGet(new HttpAccess.OnAsyncTaskListener() {
				@Override
				public void onAsyncTaskListener(String s) {

					// Gson インスタンス
					Gson gson = new Gson();

					// Gson によるデシリアライズ
					DataList dataList = gson.fromJson(s,DataList.class);

					// dataList.row が WebData の配列
					for( WebData rowData : dataList.row) {
						Log.i("lightbox",rowData.氏名);
					}

					// クリア
					arrayAdapter.clear();
					// データセット
					arrayAdapter.addAll(dataList.row);

				}
			});
			return true;

		}


		return super.onOptionsItemSelected(item);
	}


}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>


menu_main.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <item
        android:id="@+id/action_1"
        android:orderInCategory="100"
        android:title="全て表示"
        app:showAsAction="never" />

    <item
        android:id="@+id/action_2"
        android:orderInCategory="100"
        android:title="一部表示"
        app:showAsAction="never" />

</menu>


DataList.java
public class DataList {

	WebData[] row;

}


WebData.java
public class WebData {

	String 社員コード;
	String 氏名;
	String フリガナ;
	String 所属;
	String 性別;
	int 給与;
	int 手当;
	String 管理者;
	String 生年月日;
	String 所属名;

	@Override
	public String toString() {
		return 氏名;
	}
}


build.gradle
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
    compile 'com.google.code.gson:gson:2.8.1'
    compile 'com.squareup.okhttp3:okhttp:3.8.1'
}






posted by lightbox at 2017-08-17 19:10 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年07月10日


Okhttp を使用した、GET、POST、ファイルアップロードを楽に実装できる HttpAccess クラス

▼ HttpAccess パッケージのダウンロード


( 解凍して、main\java フォルダの中に コピーして下さい )
※ OkHttp より 最新の Gradle の記述を取得して build.gradle の dependencies の最後に貼り付けて下さい
例) compile 'com.squareup.okhttp3:okhttp:3.8.1'

OkHttp
okio

厳密に言うと、GET 時に渡す URL の QueryString 部分はクラス外部で作成するようになっています。仮にデータ部分を日本語で指定しても、UTF8 であれば自動的にエンコードしてくれます。また、set メソッドで URL の変更も可能です。

コンストラクタで渡す URL は、GET では必ず使用されますが、POST では実行時に指定した url KEY を使う事ができます(省略すると、コンストラクタ作成時の url を使用します)

url に関しては、Getter と Setter があります。

ファイルアップロードは1件のみの処理を想定しています。入力データとして InputStream を使い、内部で ByteArray に変換しています。Okhttp のメソッドが InputStream には対応していないので、複数ファイルアップロードは、一度にひとつのファイルになるよう外部でコントロールが必要になるでしょう。
import android.os.AsyncTask;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.BufferedSink;
import okio.Okio;
import okio.Source;

public class HttpAccess {

	private OkHttpClient okHttpClient;
	private String url;

	public interface OnAsyncTaskListener {
		abstract public void onAsyncTaskListener(String s);
	}

	// url の Getter と Setter
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}

	// *******************************
	// コンストラクタ
	// *******************************
	public HttpAccess(String url) {
		this.okHttpClient = new OkHttpClient();
		this.url = url;
	}

	// *******************************
	// GET
	// *******************************
	public void sendGet(final OnAsyncTaskListener listener ) {

		new AsyncTask<Void,Void,String>(){
			@Override
			protected String doInBackground(Void... params) {
				String result = "[\"error\"]";

				Request.Builder builder = new Request.Builder();
				builder.url(HttpAccess.this.url);
				Request request = builder.build();

				Response response = null;
				try {
					response = HttpAccess.this.okHttpClient.newCall(request).execute();
					result = response.body().string();
				}
				catch (IOException e) {
					e.printStackTrace();
				}

				return result;
			}

			// UI スレッド処理
			@Override
			protected void onPostExecute(String s) {
				super.onPostExecute(s);

				listener.onAsyncTaskListener(s);

			}
		}.execute();

	}

	// *******************************
	// POST
	// *******************************
	public void sendPost(HashMap<String,String> postData, final OnAsyncTaskListener listener ) {

		new AsyncTask<HashMap,Void,String>(){
			@Override
			protected String doInBackground(HashMap... params) {
				String result = "[\"error\"]";

				HashMap<String,String> postData = (HashMap<String,String>)params[0];

				// POST 用 FormBody の内容の作成
				FormBody.Builder formbodyBuilder = new FormBody.Builder();
				for(Map.Entry<String, String> e : postData.entrySet()) {
					if (  !(e.getKey()).equals("url") ) {
						formbodyBuilder.add(e.getKey(), e.getValue());
					}
				}

				// 送信用ユニットの作成
				FormBody formbody = formbodyBuilder.build();
				Request.Builder builder = new Request.Builder();

				// 引数の Map に url が無い場合は、コンストラクタの url を使用する
				if ( postData.get("url") == null ) {
					builder.url(HttpAccess.this.url) ;
				}
				else {
					builder.url( postData.get("url") );
				}

				builder.post(formbody);
				Request request = builder.build();

				Response response = null;
				try {
					response = HttpAccess.this.okHttpClient.newCall(request).execute();
					result = response.body().string();
				}
				catch (IOException e) {
					e.printStackTrace();
				}

				return result;
			}

			// UI スレッド処理
			@Override
			protected void onPostExecute(String s) {

				listener.onAsyncTaskListener(s);

			}
		}.execute(postData);

	}

	// *******************************
	// 1ファイルのアップロード(  Byte 配列使うので一つ )
	// *******************************
	public void sendUpload(String url, String field, String name, InputStream inputStream, String mime, final OnAsyncTaskListener listener ) {

		// 引数は、専用クラス
		UploadParam uploadParam = new UploadParam(url,field,name,inputStream,mime);
		new AsyncTask<UploadParam,Void,String>(){
			@Override
			protected String doInBackground(UploadParam... params) {
				String result = "[\"error\"]";

				UploadParam uploadParam = params[0];
				InputStream inputStream = uploadParam.getInputStream();
				Source source = Okio.source(inputStream);
				ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
				BufferedSink bufferedSink = Okio.buffer(Okio.sink(byteArrayOutputStream));
				try {
					bufferedSink.writeAll(source);
					bufferedSink.close();
					source.close();
				} catch (IOException e) {
					e.printStackTrace();
				}

				MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder();
				multipartBodyBuilder.setType(MultipartBody.FORM);
				multipartBodyBuilder.addFormDataPart(
						uploadParam.getField(),
						uploadParam.getName(),
						RequestBody.create(MediaType.parse(uploadParam.getMime()),byteArrayOutputStream.toByteArray())
				);
				RequestBody requestBody = multipartBodyBuilder.build();

				// 送信用のデータを作成
				Request.Builder requestBuilder = new Request.Builder();
				requestBuilder.url(uploadParam.getUrl());
				requestBuilder.post(requestBody);
				Request request = requestBuilder.build();

				// 受信用のオブジェクトの準備
				Call call = HttpAccess.this.okHttpClient.newCall(request);

				// 送信と受信
				try {

					Response response = call.execute();
					result = response.body().string();

				} catch (Exception ex) {
					ex.printStackTrace();
				}

				return result;

			}

			@Override
			protected void onPostExecute(String s) {

				listener.onAsyncTaskListener(s);

			}

		}.execute(uploadParam);


	}

	private class UploadParam {
		private String url;
		private String mime;
		private String field;
		private String name;
		private InputStream inputStream;

		public UploadParam(String url, String field, String name, InputStream inputStream, String mime) {
			this.url = url;
			this.mime = mime;
			this.field = field;
			this.name = name;
			this.inputStream = inputStream;
		}

		public String getUrl() {
			return url;
		}

		public String getMime() {
			return mime;
		}

		public String getField() {
			return field;
		}

		public String getName() {
			return name;
		}

		public InputStream getInputStream() {
			return inputStream;
		}
	}

}


ファイルアップロード呼び出しサンプル
				try {
					InputStream inputStream = assetManager.open("sample.jpg");
					httpAccess.sendUpload(
						"http://10.0.2.2/r101/php_upload/file_upload.php",
						"target",
						"sample.jpg",
						inputStream,
						"image/jpeg",
						new HttpAccess.OnAsyncTaskListener() {
							@Override
							public void onAsyncTaskListener(String s) {

								Log.i("lightbox", s);

							}
						}
					);
				} catch (IOException e) {
					e.printStackTrace();
				}

Post 呼び出しサンプル
					HashMap<String,String> postData = new HashMap<String,String>();
					// localhost の 環境
					postData.put("url", "http://10.0.2.2/r101/login-action/syain/syain_update.php");
					postData.put("fld_code", syain_base.code );
					postData.put("fld_simei", syain_base.name );

					httpAccess.sendPost(postData,new HttpAccess.OnAsyncTaskListener() {
						@Override
						public void onAsyncTaskListener(String s) {

							Log.i("lightbox",s);

							if ( !s.equals("[\"error\"]") ) {
								// 再表示
								listView.setAdapter(arrayAdapter);
							}
							else {
								Toast.makeText(MainActivity.this,"インターネットアクセスに失敗しました",Toast.LENGTH_LONG).show();
							}

						}
					});






posted by lightbox at 2017-07-10 12:04 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年07月04日


Android の assets フォルダーに保存した 400x320 の画像ファイルの扱い

『Android の drawable フォルダーに保存した 400x320 の画像ファイルの6種類の扱いと Density』では、ファイルでは無く『表示用のリソース』という扱いでした。

それに対して assets フォルダーに保存するファイルは画像である必要も無く、単純に InputStream としてアクセスできます。(但し、assets からは読み込む事のみ可能です)

そこで、直接画像として扱う方法と、一旦別の場所に保存してから画像として扱う方法を試してみました。

前提条件

1) エミュレータとして Nexus 4 ( 768x1280 : xhdpi : 320 ) を使用しています。
2) 画像サイズは 400x320 です。
3) 画像は、assets フォルダに保存しました。
4) ImageView に表示します
5) 以下の3つの画像です(いずれも 400x320 jpeg)
  

drawable との違い

1) 画像をファイルとして扱えるので、基本的には Bitmap として扱います。

2) ファイルのコピーには okio を使用して InputStream から OutputStream にコピーします。
	// *************************
	// ファイルコピー
	// *************************
	private void streamCopy(InputStream inputStream, OutputStream outputStream) throws Exception {

		Source source = Okio.source(inputStream);
		BufferedSink bufferedSink = Okio.buffer(Okio.sink(outputStream));
		bufferedSink.writeAll(source);

		bufferedSink.close();
		source.close();

	}


Drawable で表示 (27行〜)

ファイルからの読み込みであっても、リソースとして Drawable に読み込む場合はやはり、幅が 200 に設定されて表示は小さくなりました。



Drawable から Bitmap に変換して大きく表示 (47行〜)

Drawable から Bitmap に変換すると本来の横幅(400) になり、Density も 320 でこのまま表示すると正しい大きさで表示されるので、setDensity(160) で大きく表示するようにしました。



Bitmap で表示 (74行〜)

普通に Bitmap として読み込んで、Bitmap として表示します。サイズも密度も思った通りの状態となります。



いったんコピーしてBitmap で表示 (95行〜)

コピー後は書き込み可能なフォルダに環境が変わる事を除いて、assets から読み込むのと変わり無く普通に表示されます。



コピー後のファイルを Bitmap で大きく表示 (123行〜)

setDensity(160) で大きく表示します



コピー後のファイルを Bitmap で小さく表示 (154行〜)

setDensity(640) で小さく表示します



public class MainActivity extends AppCompatActivity {

	AssetManager assetManager;
	ImageView imageView;
	ImageView imageViewSaved;
	Drawable drawable;
	Bitmap bitmap;
	InputStream inputStream;
	OutputStream outputStream;

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

		assetManager = MainActivity.this.getResources().getAssets();

		imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView);
		imageViewSaved = (ImageView) MainActivity.this.findViewById(R.id.imageViewSaved);

		// カラー(Drawable)
		MainActivity.this.findViewById(R.id.buttonAsset1).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				try {
					inputStream = assetManager.open("sample.jpg");
					drawable = new BitmapDrawable(MainActivity.this.getResources(), inputStream);
					inputStream.close();

					Log.i("lightbox", drawable.getIntrinsicWidth()+"");		// Nexus 4 : 200, Nexus S : 267

				} catch (IOException e) {
					e.printStackTrace();
				}
				imageView.setImageDrawable(drawable);

			}
		});

		// モノクロ最大(Drawable > Bitmap > DENSITY_DEFAULT)
		MainActivity.this.findViewById(R.id.buttonAsset2).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				try {
					inputStream = assetManager.open("sample_mono.jpg");
					drawable = new BitmapDrawable(MainActivity.this.getResources(), inputStream);
					inputStream.close();

					Log.i("lightbox", drawable.getIntrinsicWidth()+"");		// Nexus 4 : 200, Nexus S : 267

					bitmap = ((BitmapDrawable) drawable).getBitmap();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

					// 大きく
					bitmap.setDensity(160);

				} catch (IOException e) {
					e.printStackTrace();
				}
				imageView.setImageBitmap(bitmap);

			}
		});

		// スケッチ、そのまま(Bitmap)
		MainActivity.this.findViewById(R.id.buttonAsset3).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				try {
					inputStream = assetManager.open("sample_sketch.jpg");
					bitmap = BitmapFactory.decodeStream(inputStream);
					inputStream.close();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

				} catch (IOException e) {
					e.printStackTrace();
				}
				imageView.setImageBitmap(bitmap);

			}
		});

		// カラーを保存して表示
		MainActivity.this.findViewById(R.id.buttonSave1).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				try {
					// 読み込むファイル
					inputStream = assetManager.open("sample.jpg");
					// 書き込むファイル
					outputStream = MainActivity.this.openFileOutput("sample.jpg", MODE_PRIVATE);
					streamCopy(inputStream,outputStream);

					// コピーしたファイルを読み込む
					inputStream = MainActivity.this.openFileInput("sample.jpg");
					bitmap = BitmapFactory.decodeStream(inputStream);
					inputStream.close();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

				} catch (Exception e) {
					e.printStackTrace();
				}

				imageViewSaved.setImageBitmap(bitmap);

			}
		});

		// モノクロを保存して表示
		MainActivity.this.findViewById(R.id.buttonSave2).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				try {
					// 読み込むファイル
					inputStream = assetManager.open("sample_mono.jpg");
					// 書き込むファイル
					outputStream = MainActivity.this.openFileOutput("sample_mono.jpg", MODE_PRIVATE);
					streamCopy(inputStream,outputStream);

					// コピーしたファイルを読み込む
					inputStream = MainActivity.this.openFileInput("sample_mono.jpg");
					bitmap = BitmapFactory.decodeStream(inputStream);
					inputStream.close();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

					// 大きく
					bitmap.setDensity(160);

				} catch (Exception e) {
					e.printStackTrace();
				}

				imageViewSaved.setImageBitmap(bitmap);

			}
		});

		// スケッチ
		MainActivity.this.findViewById(R.id.buttonSave3).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				try {
					// 読み込むファイル
					inputStream = assetManager.open("sample_sketch.jpg");
					// 書き込むファイル
					outputStream = MainActivity.this.openFileOutput("sample_sketch.jpg", MODE_PRIVATE);
					streamCopy(inputStream,outputStream);

					// コピーしたファイルを読み込む
					inputStream = MainActivity.this.openFileInput("sample_sketch.jpg");
					bitmap = BitmapFactory.decodeStream(inputStream);
					inputStream.close();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

					// 小さく
					bitmap.setDensity(640);

				} catch (Exception e) {
					e.printStackTrace();
				}

				imageViewSaved.setImageBitmap(bitmap);

			}
		});
	}

}





posted by lightbox at 2017-07-04 20:21 | Comment(0) | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

Android の drawable フォルダーに保存した 400x320 の画像ファイルの6種類の扱いと Density

前提条件

1) エミュレータとして Nexus 4 ( 768x1280 : xhdpi : 320 ) を使用しています。
2) 画像サイズは 400x320 です。

3) 画像は、drawable フォルダの直下に置かれています。


4) ImageView に表示します
5) 以下の3つの画像です(いずれも 400x320 jpeg)
  

6) デバイスを変更すると、getWidth()・getDensity() の値は場合によって変化します


リソース ID で表示 (25行〜)

setImageResource で リソース IDを使用するので、Android が自動的に表示する状態です。



これは、Nexus 4 の解像度(768x1280)から言えば大きく拡大して表示されています。

Drawable に変換後、Bitmap に変換して表示  (35行〜)

単純に表示してしまうと、リソース ID で表示したものと同じ大きさになります。ここでは、横幅情報を取得して表示しており、その情報では横幅は 800(Nexus4 の場合) になっています。

そこで、setDensity で密度を倍にする事によって結果的に表示サイズを半分にしています。



これは、Nexus 4 の解像度(768x1280)から言えば正しい大きさで表示されています。

Bitmap に直接変換後表示  (54行〜)

リソースを経由して変換しているので、リソース ID で表示したものと同じ大きさになります。ここでも、横幅情報を取得して表示していますが、その情報では横幅は 800(Nexus4 の場合) になっています。

なので、setDensity で密度を倍にして妥当な大きさで表示するようにしています。



本来のサイズで保存し、そこから読み込んで表示  (70行〜)

リソースを経由した場合倍のサイズになっているので、BitmapFactory.OptionsinScaled を false にして Bitmap に変換します。この Bitmap は本来のサイズになっているので、compress メソッドでこのまま保存します。ただし、元が JPEG であるため、クオリティを保持する為に PNG として保存しています。

そして、再度読み込みむと幅は 400 で Density は 320 になっているので、そのまま表示します。



保存後 Dawable として読込んで Bitmap に変換して表示  (105行〜)

BitmapFactory.OptionsinScaled を false にして Bitmap に変換して保存します(PNG として保存)。

そして、Drawable として読み込みむと幅が 200 になるようです。こちらの思った通りの大きさにする為にさらに Bitmap に変換すると、幅は 400 に戻りました。



しかし、最初の Drawable のまま setImageDrawable で表示すると以下のように小さくなります( 200 )



JPEG 保存後 大きくして表示  (142行〜)

クオリティが劣化しますが、90% クオリティでJPEG として保存して Bitmap として読み込みます

そして、大きく表示する為に Density を小さく(160)して表示しています。




public class MainActivity extends AppCompatActivity {

	ImageView imageView;
	ImageView imageViewSaved;
	Drawable drawable;
	Bitmap bitmap;
	BitmapFactory.Options options;
	InputStream inputStream;
	OutputStream outputStream;

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

		imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView);
		imageViewSaved = (ImageView) MainActivity.this.findViewById(R.id.imageViewSaved);

		// カラー
		MainActivity.this.findViewById(R.id.buttonResource1).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				// リソース ID
				imageView.setImageResource(R.drawable.sample);

			}
		});

		// モノクロ
		MainActivity.this.findViewById(R.id.buttonResource2).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				drawable =  MainActivity.this.getDrawable(R.drawable.sample_mono);
				Log.i("lightbox", drawable.getIntrinsicWidth()+"");	// Nexus 4 : 800, Nexus S : 600 (注) setDensity 後変化します

				bitmap = ((BitmapDrawable)drawable).getBitmap();
				Log.i("lightbox", bitmap.getWidth()+"");		// Nexus 4 : 800, Nexus S : 600
				Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

				// 密度を倍にすれば、表示サイズは半分に
				bitmap.setDensity(640);
				imageView.setImageBitmap(bitmap);

			}
		});

		// スケッチ
		MainActivity.this.findViewById(R.id.buttonResource3).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				bitmap = BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.sample_sketch);
				Log.i("lightbox", bitmap.getWidth()+"");		// Nexus 4 : 800, Nexus S : 600
				Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

				// 密度を倍にすれば、表示サイズは半分に
				bitmap.setDensity(640);
				imageView.setImageBitmap(bitmap);

			}
		});

		// カラー
		MainActivity.this.findViewById(R.id.buttonSave1).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				options = new BitmapFactory.Options();
				options.inScaled = false;
				bitmap = BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.sample,options);

				Log.i("lightbox", bitmap.getWidth()+"");		// 400
				Log.i("lightbox", bitmap.getDensity()+"");	// 160

				try {
					// 書き込みするファイル名
					outputStream = MainActivity.this.openFileOutput("sample.png", MODE_PRIVATE);
					// PNG なので quality は無視される( クオリティ維持のため PNG として保存 )
					bitmap.compress(Bitmap.CompressFormat.PNG, 0, outputStream);
					outputStream.close();

					inputStream = MainActivity.this.openFileInput("sample.png");
					bitmap = BitmapFactory.decodeStream(inputStream);
					inputStream.close();

					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

					imageViewSaved.setImageBitmap(bitmap);

				} catch (Exception e) {
					e.printStackTrace();
				}

			}
		});

		// モノクロ
		MainActivity.this.findViewById(R.id.buttonSave2).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				options = new BitmapFactory.Options();
				options.inScaled = false;
				bitmap = BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.sample_mono,options);

				Log.i("lightbox", bitmap.getWidth()+"");		// 400
				Log.i("lightbox", bitmap.getDensity()+"");	// 160

				try {
					// 書き込みするファイル名
					outputStream = MainActivity.this.openFileOutput("sample_mono.png", MODE_PRIVATE);
					// PNG なので quality は無視される( クオリティ維持のため PNG として保存 )
					bitmap.compress(Bitmap.CompressFormat.PNG, 0, outputStream);
					outputStream.close();

					inputStream = MainActivity.this.openFileInput("sample_mono.png");
					drawable = new BitmapDrawable(MainActivity.this.getResources(), inputStream);
					inputStream.close();
					Log.i("lightbox", drawable.getIntrinsicWidth()+"");	// Nexus 4 : 200, Nexus S : 267

					bitmap = ((BitmapDrawable)drawable).getBitmap();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

					imageViewSaved.setImageBitmap(bitmap);

				} catch (Exception e) {
					e.printStackTrace();
				}

			}
		});

		// スケッチ
		MainActivity.this.findViewById(R.id.buttonSave3).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {

				options = new BitmapFactory.Options();
				options.inScaled = false;
				bitmap = BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.sample_sketch,options);

				Log.i("lightbox", bitmap.getWidth()+"");		// 400
				Log.i("lightbox", bitmap.getDensity()+"");	// 160

				try {
					// 書き込みするファイル名
					outputStream = MainActivity.this.openFileOutput("sample_sketch.jpg", MODE_PRIVATE);
					// 90% JPEGとして保存
					bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);
					outputStream.close();

					inputStream = MainActivity.this.openFileInput("sample_sketch.jpg");
					bitmap = BitmapFactory.decodeStream(inputStream);
					inputStream.close();
					Log.i("lightbox", bitmap.getWidth()+"");		// 400
					Log.i("lightbox", bitmap.getDensity()+"");	// Nexus 4 : 320, Nexus S : 240

					// 大きく
					bitmap.setDensity(160);
					imageViewSaved.setImageBitmap(bitmap);

				} catch (Exception e) {
					e.printStackTrace();
				}

			}
		});

	}

}

関連する記事

Android の assets フォルダーに保存した 400x320 の画像ファイルの扱い



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

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

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

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

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


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

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

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

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

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


Windows
container 終わり

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

Android SDK ポケットリファレンス
改訂版 Webデザイナーのための jQuery入門
今すぐ使えるかんたん ホームページ HTML&CSS入門
CSS ドロップシャドウの参考デモ
Google Hosted Libraries
cdnjs
BUTTONS (CSS でボタン)
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり