SQLの窓

2016年06月26日


カスタム・リストビュー・ダイアログ : DialogFragment 内の ListView を ArrayAdapter でカスタムする



結果的には、DialogFragment を使用しない場合より簡単に実装できました。正直これで正しいのかどうかは解りませんが、ダイアログのインスタンスはシステムで再作成される事を前提にしているので、DialogFragment 内の onCreateDialog で全て実装する事によって、再度 View をセットするというような処理は必要ありません。但し、行を選択して閉じる時に、dismiss() によってダイアログを終わらせています。

リストビューのデータは、本来 newInstance で put すべきでしょうが、onCreateDialog 内で全て完結するほうがスッキリしているのでそうしました。

行のクリックでは、View からデータを取得していますが、MyData に何かメソッドを実装して実行したい場合は、
MyAdapter adapter = (MyAdapter) parent.getAdapter();
MyData my_data = (MyData) adapter.getItem(position);
で問題無いと思います MainActivity 内のソースコード
@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 v) {

			 MyDialogFragment my_dialog_fragment = MyDialogFragment.newInstance("地域一覧");

			// ダイアログの表示
			// ※ この環境では、getSupportFragmentManager が必要です
			my_dialog_fragment.show(getSupportFragmentManager(),"dialog");

		}
	});

}

// *********************************
// static クラスの定義
// 1) DialogFragment
// 2) ArrayAdapter
// 3) MyData
// *********************************
public static class MyDialogFragment extends DialogFragment {

	// ユーザが呼び出す為のもの( 外側で処理しても良い )
	public static MyDialogFragment newInstance(String title) {
		// インスタンス作成
		MyDialogFragment my_dialog_fragment = new MyDialogFragment();
		// システムに引数を保存
		Bundle args = new Bundle();
		args.putString("title", title);
		my_dialog_fragment.setArguments(args);
		return my_dialog_fragment;
	}

	// *********************************
	// ここが、ダイアログの作成時に呼ばれます
	// 初回はユーザがダイアログを作成
	// デバイスを横にした時はシステムが
	// ダイアログを作成します
	// *********************************
	@Override
	public Dialog onCreateDialog(Bundle savedInstanceState) {

		// この中で使用するダイアログ作成用
		final AlertDialog.Builder ad_builder_in_fragment;

		// この DialogFragment に保存されているデータを取得
		// ( システムが再作成した場合でも Bundle よりデータを取得できます )
		String title = MyDialogFragment.this.getArguments().getString("title");

		// DialogFragment から Activity を取得して使う
		// ( ダイアログが使われる Activity を MyDialogFragment 内で取得しています )
		ad_builder_in_fragment = new AlertDialog.Builder(MyDialogFragment.this.getActivity());
		// タイトル設定
		ad_builder_in_fragment.setTitle(title);
		ad_builder_in_fragment.setNegativeButton("キャンセル", new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// キャンセル
			}
		});
		// *********************************
		// ダイアログのメインビューに
		// リストビューを設定
		// *********************************
		LayoutInflater inflater = (LayoutInflater) MyDialogFragment.this.getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		ListView list_view = (ListView) inflater.inflate(R.layout.listview, null);
		// *********************************
		// 行をクリックした時のイベント
		// *********************************
		list_view.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

				// View から行データを取得
				TextView tv;
				tv = (TextView) view.findViewById(R.id.textPlace);
				String textPlace = tv.getText().toString();
				tv = (TextView) view.findViewById(R.id.textCode);
				String textCode = tv.getText().toString();

				// メッセージの作成
				String message = String.format("%s : %s", textPlace, textCode);
				// logcat に表示
				Log.i("lightbox", message );
				// Toast で表示
				Toast.makeText(MyDialogFragment.this.getActivity(),message, Toast.LENGTH_SHORT).show();
				// TextView に表示
				tv = (TextView) MyDialogFragment.this.getActivity().findViewById(R.id.textView);
				tv.setText(message);

				// 破棄
				MyDialogFragment.this.dismiss();
			}
		});
		// *********************************
		// カスタム・アダプター
		// *********************************
		MyAdapter adapter = new MyAdapter(MyDialogFragment.this.getActivity(),R.layout.listview_item);

		// *********************************
		// ListView に表示するデータ
		// *********************************
		List<MyData> list = new ArrayList<MyData>();
		list.add(new MyData("大阪","27"));
		list.add(new MyData("東京","13"));
		list.add(new MyData("岡山","33"));
		list.add(new MyData("北海道","1"));
		list.add(new MyData("沖縄","47"));
		list.add(new MyData("京都","26"));
		list.add(new MyData("滋賀","25"));

		// *********************************
		// 新しく領域確保して配列データを作成する
		// *********************************
		MyData[] my_data = list.toArray( new MyData[0] );

		// *********************************
		// カスタム・アダプターにデータをセット
		// *********************************
		adapter.addAll(my_data);
		// *********************************
		// ListView にカスタム・アダプターをセット
		// *********************************
		list_view.setAdapter(adapter);

		ad_builder_in_fragment.setView(list_view);

		// ダイアログを作成してシステムに返す
		return ad_builder_in_fragment.create();
	}
}

// *********************************
// MyData 専用アダブター
// *********************************
public static class MyAdapter extends ArrayAdapter {

	public MyAdapter(Context context, int resource) {
		super(context, resource);
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {

		View rowView = convertView;
		if (rowView == null) {
			// 現在の View の取得
			LayoutInflater inflater = (LayoutInflater) getContext().getSystemService
				(Context.LAYOUT_INFLATER_SERVICE);
			rowView = inflater.inflate(R.layout.listview_item, null);
		}

		MyData my_data = (MyData)this.getItem(position);

		TextView tv;
		// listview_item 内の textPlace にデータをセット
		tv = (TextView) rowView.findViewById(R.id.textPlace);
		tv.setText(my_data.toString());
		// listview_item 内の textCode にデータをセット
		tv = (TextView) rowView.findViewById(R.id.textCode);
		tv.setText(my_data.getValue());

		return rowView;
	}
}

// *********************************
// MyData
// *********************************
public static class MyData {

	private String myString;
	private String myValue;

	public MyData(String myString,String myValue) {
		this.myString = myString;
		this.myValue = myValue;
	}

	// データ取得用
	String getValue() {
		return myValue;
	}

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


※ 全てのソースコードはこちらにあります




posted by lightbox at 2016-06-26 01:33 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

カスタム・リストビュー・ダイアログ : ダイアログ内の ListView を ArrayAdapter でカスタムする


※ この処理には、DialogFragment は使用していません。

AlertDialog 内で表示されるリストビューを外側で定義してカスタマイズするサンプルです。AlertDialog.Builder の setView で、自由にダイアログ内のメインコンテンツを差し替えれるので、ListView をそのままセットしてみました。

もちろん、ArrayAdapter を継承した独自クラスを作成して、お決まりのその中での getView の処理も実装します。問題は、表示される度に ListView をセットしなおす必要がある事をテストして知りましたが、ListView を setView でセットした後に再度 setView する場合、いったん ListView を ダイアログ内の View から削除する必要がありました。

通常、DialogFragment では無い使い方では、 AlertDialog.Builder で作成された AlertDialog は常にメモリに残っているのでそのような処理が必要なのだと思います。ボタンがクリックされる度に、AlertDialog を 毎回 1 から作成して、使うのであればそのような必要も無いのでしょうが、そうなると使用後に削除する必要が出て来るのでこのような実装になっています。

行のクリックは、ListView の OnItemClickListener で行う事になるので、AlertDialog の hide メソッドで閉じています。

また、行データの参照は、Adapter を直接参照できるので、Adapter の getItem 経由で行っています。

MainActivity 内のソースコード
private AlertDialog ad = null;
private AlertDialog.Builder ad_builder;
private MyData[] my_data;
private ListView list_view;
private MyAdapter adapter;

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

	// *********************************
	// ListView に表示するデータ
	// *********************************
	List<MyData> list = new ArrayList<MyData>();
	list.add(new MyData("大阪","27"));
	list.add(new MyData("東京","13"));
	list.add(new MyData("岡山","33"));
	list.add(new MyData("北海道","1"));
	list.add(new MyData("沖縄","47"));
	list.add(new MyData("京都","26"));
	list.add(new MyData("滋賀","25"));

	// *********************************
	// 新しく領域確保して配列データを作成する
	// *********************************
	my_data = list.toArray( new MyData[0] );

	// *********************************
	// ノーマルダイアログ作成用インスタンス作成
	// *********************************
	ad_builder = new AlertDialog.Builder(MainActivity.this);
	// *********************************
	// タイトル設定
	// *********************************
	ad_builder.setTitle("地域一覧");
	// *********************************
	// キャンセルイベント
	// *********************************
	ad_builder.setNegativeButton("キャンセル", new DialogInterface.OnClickListener() {
		@Override
		public void onClick(DialogInterface dialog, int which) {
			// キャンセル
		}
	});
	// *********************************
	// ダイアログのメインビューに
	// リストビューを設定
	// *********************************
	LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	list_view = (ListView) inflater.inflate(R.layout.listview, null);
	// *********************************
	// 行をクリックした時のイベント
	// *********************************
	list_view.setOnItemClickListener(new AdapterView.OnItemClickListener() {
		@Override
		public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

			// Adapter 内の行データを取得
			MyData my_data = (MyData) adapter.getItem(position);

			// メッセージの作成
			String message = String.format("%s : %s", my_data.toString(), my_data.getValue());
			// logcat に表示
			Log.i("lightbox", message );
			// Toast で表示
			Toast.makeText(MainActivity.this,message,Toast.LENGTH_SHORT).show();
			// TextView に表示
			TextView tv;
			tv = (TextView) MainActivity.this.findViewById(R.id.textView);
			tv.setText(message);

			// 非表示
			ad.hide();
		}
	});
	// *********************************
	// カスタム・アダプター
	// *********************************
	adapter = new MyAdapter(MainActivity.this,R.layout.listview_item);
	// *********************************
	// カスタム・アダプターにデータをセット
	// *********************************
	adapter.addAll(my_data);
	// *********************************
	// ListView にカスタム・アダプターをセット
	// *********************************
	list_view.setAdapter(adapter);

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

			// 初回はなにもしない
			if ( ad != null ) {
				// ダイアログ内の親 View から ListView を削除
				ViewGroup vg = (ViewGroup) list_view.getParent();
				vg.removeView(list_view);
			}
			// ダイアログに ListView をセット
			ad_builder.setView(list_view);
			// ダイアログの表示
			// ( AlertDialog は 非表示処理に使用 )
			ad = ad_builder.show();

		}
	});

}

// *********************************
// MyData 専用アダブター
// *********************************
private class MyAdapter extends ArrayAdapter {

	public MyAdapter(Context context, int resource) {
		super(context, resource);
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {

		View rowView = convertView;
		if (rowView == null) {
			// 現在の View の取得
			LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService
				(Context.LAYOUT_INFLATER_SERVICE);
			rowView = inflater.inflate(R.layout.listview_item, null);
		}

		MyData my_data = (MyData)this.getItem(position);

		TextView tv;
		// listview_item 内の textPlace にデータをセット
		tv = (TextView) rowView.findViewById(R.id.textPlace);
		tv.setText(my_data.toString());
		// listview_item 内の textCode にデータをセット
		tv = (TextView) rowView.findViewById(R.id.textCode);
		tv.setText(my_data.getValue());

		return rowView;
	}
}

// *********************************
// MyData
// *********************************
private class MyData {

	private String myString;
	private String myValue;

	public MyData(String myString,String myValue) {
		this.myString = myString;
		this.myValue = myValue;
	}

	// データ取得用
	String getValue() {
		return myValue;
	}

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


※ 全てのソースコードはこちらにあります



posted by lightbox at 2016-06-26 01:15 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

AlertDialog.Builder の setItems(int itemsId, DialogInterface .OnClickListener listener) を使用した、ListView ダイアログ : ノーマルと DialogFragment の共存サンプル




ListView ダイアログの中の ListView を ArrayAdapter でカスタマイズする前のテンプレートとして作成しました。一般的には、setItems で、文字列の配列をセットするのが通常だと思われるので、どうせなら、と思ってリソースにデータを定義して使用しています。

カスタム・リストビュー・ダイアログ : ダイアログ内の ListView を ArrayAdapter でカスタムする

カスタム・リストビュー・ダイアログ : DialogFragment 内の ListView を ArrayAdapter でカスタムする

1) MainActivity 内でのリソースの参照
MainActivity.this.getResources()
2) public static class MyDialogFragment 内でのリソースの参照
MyDialogFragment.this.getActivity()

MainActivity 内のソースコード
// *********************************
// ノーマルでは ダイアログフラグメントは
// 使用していません
// *********************************
private AlertDialog.Builder ad_builder;

// *********************************
// Android 初期設定
// *********************************
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	// 画面の表示
	setContentView(R.layout.activity_main);

	// *********************************
	// ノーマルダイアログ作成用インスタンス
	// *********************************
	ad_builder = new AlertDialog.Builder(MainActivity.this);
	// タイトル設定
	ad_builder.setTitle("地域一覧");
	ad_builder.setNegativeButton("キャンセル", new DialogInterface.OnClickListener() {
		@Override
		public void onClick(DialogInterface dialog, int which) {
			// キャンセル
		}
	});
	// *********************************
	// ノーマル用データのセットと、行を選択時の処理
	// *********************************
	ad_builder.setItems(R.array.ken, new DialogInterface.OnClickListener() {
		@Override
		public void onClick(DialogInterface dialog, int which) {

			String[] ken = MainActivity.this.getResources().getStringArray(R.array.ken);
			int[] ken_value = MainActivity.this.getResources().getIntArray(R.array.ken_value);

			// メッセージの作成
			String message = String.format("%s : %d", ken[which], ken_value[which]);

			// logcat に表示
			Log.i("lightbox", message );

			// Toast で表示
			Toast.makeText(MainActivity.this,message,Toast.LENGTH_SHORT).show();

			// TextView に表示
			TextView tv;
			tv = (TextView) MainActivity.this.findViewById(R.id.textView);
			tv.setText(message);

		}
	});

	// *********************************
	// ボタンをクリック
	// ( ノーマル表示 )
	// *********************************
	MainActivity.this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
		@Override
		public void onClick(View v) {

			// ダイアログの表示
			ad_builder.show();

		}
	});
	

	// *********************************
	// ボタンをクリック
	// ( ダイアログフラグメント で表示 )
	// *********************************
	MainActivity.this.findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
		@Override
		public void onClick(View v) {

			// インスタンス作成
			MyDialogFragment my_dialog_fragment = new MyDialogFragment();
			// システムに引数を保存
			Bundle args = new Bundle();
			args.putString("title", "地域一覧");
			my_dialog_fragment.setArguments(args);

			// 定義しておいた newInstance メソッドを使う場合
			// MyDialogFragment my_dialog_fragment = MyDialogFragment.newInstance("地域一覧");

			// ダイアログの表示
			// ※ この環境では、getSupportFragmentManager が必要です 
			my_dialog_fragment.show(getSupportFragmentManager(),"dialog");

		}
	});

}

// *********************************
// ここから、ダイアログフラグメントを使用して
// 表示する処理用のクラス定義です
// ダイアログフラグメント( static )
// *********************************
public static class MyDialogFragment extends DialogFragment {

	// ユーザが呼び出す為のもの( 外側で処理しても良い )
	public static MyDialogFragment newInstance(String title) {
		// インスタンス作成
		MyDialogFragment my_dialog_fragment = new MyDialogFragment();
		// システムに引数を保存
		Bundle args = new Bundle();
		args.putString("title", title);
		my_dialog_fragment.setArguments(args);
		return my_dialog_fragment;
	}

	// *********************************
	// ここが、ダイアログの作成時に呼ばれます
	// 初回はユーザがダイアログを作成
	// デバイスを横にした時はシステムが
	// ダイアログを作成します
	// *********************************
	@Override
	public Dialog onCreateDialog(Bundle savedInstanceState) {

		// この中で使用するダイアログ作成用
		AlertDialog.Builder ad_builder_in_fragment;

		// この DialogFragment に保存されているデータを取得
		// ( システムが再作成した場合でも Bundle よりデータを取得できます )
		String title = MyDialogFragment.this.getArguments().getString("title");

		// DialogFragment から Activity を取得して使う
		// ( ダイアログが使われる Activity を MyDialogFragment 内で取得しています )
		ad_builder_in_fragment = new AlertDialog.Builder(MyDialogFragment.this.getActivity());
		// タイトル設定
		ad_builder_in_fragment.setTitle(title);
		ad_builder_in_fragment.setNegativeButton("キャンセル", new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// キャンセル
			}
		});
		// データのセットと、行を選択時の処理
		ad_builder_in_fragment.setItems(R.array.ken, new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {

				// 現在の Activity
				// ( ダイアログが使われる Activity を MyDialogFragment 内で取得しています )
				FragmentActivity this_activity = MyDialogFragment.this.getActivity();

				// リソースより表示データと対応する内部コードを取得しています
				String[] ken = this_activity.getResources().getStringArray(R.array.ken);
				int[] ken_value = this_activity.getResources().getIntArray(R.array.ken_value);

				// メッセージの作成
				String message = String.format("%s : %d", ken[which], ken_value[which]);

				// logcat に表示
				Log.i("lightbox", message );

				// Toast で表示
				Toast.makeText(this_activity,message,Toast.LENGTH_SHORT).show();

				// TextView に表示
				TextView tv;
				tv = (TextView) this_activity.findViewById(R.id.textView);
				tv.setText(message);

			}
		});


		// ダイアログを作成してシステムに返す
		return ad_builder_in_fragment.create();
	}
}


※ 全てのソースコードはこちらにあります




posted by lightbox at 2016-06-26 00:49 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

2016年06月15日


AsyncTask<Params, Progress, Result> の Progress を無しにして、onPostExecute 内の処理を interface を使って MainActivity 側で実行できるようにするクラスの作成と実行 : ジェネリクスの継承

通常使うのが、doInBackground で、その戻り値が UI スレッドで実行できればいい、と言うことを大目的としたクラスです。

UI スレッドに処理を投げる為に、専用の execute メソッドほもう一つ作って、そこの第一引数にイベントとしてインターフェイスを使うようにしています。

doInBackground は、Override される事が前提なので、なにもしていません。

StdAsyncTask
package lightbox.june.loadimage;

import android.os.AsyncTask;

public class StdAsyncTask<Params, Result> extends AsyncTask<Params, Void, Result> {

	UiAction<Result> ua = null;

	public interface UiAction<Result> {
		public void actUiAction(Result result);
	}

	public void execute(UiAction<Result> ua, Params... params) {
		this.ua = ua;
		this.execute(params);
	}

	@Override
	protected Result doInBackground(Params... params) {
		return null;
	}

	@Override
	protected void onPostExecute(Result result) {
		if( ua != null ) {
			ua.actUiAction(result);
		}
	}

}

AsyncTask の 二番目の 仮型引数 を Void にして、継承したほうでは必要無いようにしています。

そして、これをこのまま使ってもいいのですが、Drawable のデータを取得するという目的に特化した、StdAsyncTask を継承した HttpDrawable クラスが以下になります。
package lightbox.june.loadimage;

import android.graphics.drawable.Drawable;
import android.util.Log;

import java.io.InputStream;
import java.net.URL;

public class HttpDrawable extends StdAsyncTask<String,Drawable> {

	@Override
	protected Drawable doInBackground(String... params) {

		Drawable image = null;
		try {
			// インターネット上の画像を取得して、Drawable に変換
			URL url = new URL((String) params[0]);
			InputStream is = (InputStream)url.getContent();
			image = Drawable.createFromStream(is, "");
			is.close();
		}
		catch(Exception e) {
			Log.i("button", "インターネットのアクセスでエラーが発生しました");
		}
		return image;
	}

}


実行は、URL を用意して、イベント処理で画面の処理を行うのみとなります。
package lightbox.june.loadimage;

import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

	@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 v) {

				String url = "http://news.biglobe.ne.jp/img/blnews/animal160128_02.jpg";
				HttpDrawable hd = new HttpDrawable();
				hd.execute(new StdAsyncTask.UiAction<Drawable>() {
					@Override
					public void actUiAction(Drawable drawable) {

						ImageView iv = (ImageView) MainActivity.this.findViewById(R.id.imageView);
						if ( drawable != null ) {
							iv.setImageDrawable(drawable);
						}

					}
				},url);

			}
		});
	}
}



posted by lightbox at 2016-06-15 21:53 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

2016年06月06日


javamail-android + AsyncTask でメール送信を行う為のテンプレート

ソースのみのダウンロード


Gmail を使用する場合は、安全性の低いアプリの許可を『有効』にする 必要があります。

Gmail のサーバ : smtp.gmail.com
▼ 以下参考
Yahoo メールのサーバ : smtp.mail.yahoo.co.jp
Microsoft のサーバ : smtp.live.com

※ AndroidManifest.xml で、インターネットアクセスの許可が必要です。

▼ 実行画面



▼ javamail-android ライブラリ

ダウンロード
libs フォルダに 3つとも ダウンロードして、mail.jar のみ 右クリックから Add as Library で登録します

▼ AndroidSendmail クラス
package lightbox.june.mailpost;

import android.os.AsyncTask;

import java.util.Properties;

import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;

/**
 * Created by lightbox on 2016/06/06.
 */
public class AndroidSendmail {

	// AsyncTask のインラインで参照する為の final
	private final String server;
	private final String port;
	private final String userid;
	private final String password;
	private final String username;

	// ************************************
	// コンストラクタ
	// ************************************
	public AndroidSendmail(
		String server,
		String port,
		String userid,
		String password,
		String username) {

		this.server = server;
		this.port = port;
		this.userid = userid;
		this.password = password;
		this.username = username;

//		Log.i("lightbox","コンストラクタ");
//		Log.i("lightbox",server);
//		Log.i("lightbox",port);
//		Log.i("lightbox",userid);
	}

	// ************************************
	// AsyncTask の onPostExecute から外部イベントとして
	// 呼び出す為のインターフェイス
	// ************************************
	public interface SendMailed {
		public void onMailResult(String result);
	}

	// ************************************
	// メール送信
	// ************************************
	public void SendMail(String to, String from, String subject, String body,final SendMailed sm) {

		new AsyncTask<String, Void, String>() {

			// ************************************
			// 非同期処理
			// ************************************
			@Override
			protected String doInBackground(String... params) {

//				Log.i("lightbox","開始");

				String result_string = "";
				try {
					// ************************************
					// プロパティオブジェクトを作成
					// プロパティオブジェクトは、extends Hashtable(連想配列)
					// ************************************
					Properties props = new Properties();

					// ************************************
					// * 連想配列に送信用サーバのアドレスをセット
					// ************************************
//					props.put("mail.smtp.host","smtp.mail.yahoo.co.jp");	// Yahoo
//					props.put("mail.smtp.host","smtp.live.com");	// Microsoft
					props.put("mail.smtp.host", server);
					props.put("mail.smtp.port", port);
					props.put("mail.smtp.auth", "true" );	// SMTP 認証を行う

					// ************************************
					// SSL関連設定
					// ************************************
					if ( port.equals("465") ) {
						props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
						props.put("mail.smtp.socketFactory.fallback", "false");
						props.put("mail.smtp.socketFactory.port", port);
					}

					// ************************************
					// 暗号化
					// ************************************
					if ( port.equals("587") ) {
						props.put("mail.smtp.starttls.enable", "true");
					}


					// ************************************
					// メール用のセッションを作成
					// ************************************
					SimpleAuthenticator sa =
						new SimpleAuthenticator(userid, password);
					Session MailSession =
						Session.getInstance( props, sa );

					// ************************************
					// メール用のメッセージオブジェクトを作成
					// ************************************
					MimeMessage msg = new MimeMessage(MailSession);

					// ************************************
					// 宛先
					// ( 第一引数では、CC や BCC を指定できます。)
					// ( 第二引数は、配列も使えます / その場合は、setFrom に準じます )
					// ************************************
					msg.setRecipients(
						Message.RecipientType.TO,
						params[0]
					);

					// ************************************
					// 送信者
					// ************************************
					msg.setFrom(
						new InternetAddress( params[1], username, "ISO-2022-JP" )
					);

					// ************************************
					// 件名
					// ************************************
					msg.setSubject(
						MimeUtility.encodeText(
							params[2],
							"iso-2022-jp",
							"B"
						)
					);

					// ************************************
					// 本文
					// ************************************
					msg.setContent(
						params[3],
						"text/plain; charset=\"iso-2022-jp\""
					);

					// ************************************
					// 送信
					// ************************************
					Transport.send( msg );

					result_string = "メールの送信を完了しました";

				}
				catch( Exception e ) {
					e.printStackTrace();
					result_string = "メールの送信に失敗しました";
				}

//				Log.i("lightbox","終了");

				return result_string;
			}

			// ************************************
			// 非同期処理終了後の処理( 画面へのアクセスが可能 )
			// ************************************
			@Override
			protected void onPostExecute(String result) {
				// 引数のインターフェイス内のメソッドを呼び出す
				sm.onMailResult(result);
			}

		}.execute(to, from, subject, body);

	}

	// ************************************
	// 認証用のプライベートクラス
	// ************************************
	private class SimpleAuthenticator extends Authenticator {

		private String user_string = null;
		private String pass_string = null;

		public SimpleAuthenticator( String user_s, String pass_s ) {
			super();
			user_string = user_s;
			pass_string = pass_s;
		}

		protected PasswordAuthentication getPasswordAuthentication(){
			return new PasswordAuthentication( this.user_string, this.pass_string );
		}
	}
}

通常の Javamail と仕様は同じ(のよう)です。

ポートの 465(SSL) を使うか 587(TSL) を使うかで設定するプロパティが変わって来ます。テストは、Yahoo メールとHotmailで行いましたが、それぞれ 465 と 587 です。Gmail は両方可能だと思います。

Hotmail は、Outlook エイリアスという呼び名で名前を追加できますが、その名前を使用する事ができました( メールヘッダはオリジナルに戻ってました )

※ 『メール アカウント』受信トレイ>オプション>メール アカウント

呼び出し
package lightbox.june.mailpost;

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

public class MainActivity extends AppCompatActivity {

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

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

				EditText passText = (EditText)MainActivity.this.findViewById(R.id.passText);
				String pass = passText.getText().toString();

				AndroidSendmail as = new AndroidSendmail(
					"smtp.gmail.com", // サーバ( 例 : "smtp.mail.yahoo.co.jp" or "smtp.live.com" )
					"465", // 465 または 587
					"", // アカウント
					pass, // パスワード
					"日本語ユーザ名"
				);

				// 宛先
				EditText to = (EditText)MainActivity.this.findViewById(R.id.editText2);
				String atesaki = to.getText().toString();

				// from の メールアドレス
				String myAddress = "";

				// 件名
				EditText subject = (EditText)MainActivity.this.findViewById(R.id.editText3);
				String kenmei = subject.getText().toString();

				// 本文
				EditText body = (EditText)MainActivity.this.findViewById(R.id.editText1);
				String honbun = body.getText().toString();

				as.SendMail(
					atesaki,
					myAddress,
					kenmei,
					honbun,
					new AndroidSendmail.SendMailed() {
						@Override
						public void onMailResult(String result) {
							Log.i("lightbox", result);
						}
					}
				);


			}
		});

	}
}


画面定義

エミュレータ上でパスワードを入れる事を想定して、入力時にパスが解らないように android:textColor="#efefef" となっています。

枠線は、drawable に border.xml を定義して android:background="@drawable/border" と指定しています
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="lightbox.june.mailpost.MainActivity">

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:text="送信" />

    <EditText
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_below="@+id/textView1"
        android:layout_marginTop="2dp"
        android:background="@drawable/border"
        android:ems="10"
        android:gravity="top|left"
        android:paddingBottom="10dp"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="10dp"
        android:inputType="textMultiLine">

        <requestFocus />
    </EditText>

    <TextView
        android:id="@+id/textView1"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_below="@+id/button1"
        android:layout_marginTop="15dp"
        android:background="@drawable/border"
        android:paddingLeft="10dp"
        android:text="メール送信"
        android:textSize="30sp" />

    <EditText
        android:id="@+id/editText2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/editText1"
        android:layout_marginTop="2dp"
        android:background="@drawable/border"
        android:ems="10"
        android:paddingBottom="10dp"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="10dp"
        android:layout_alignEnd="@+id/editText1"
        android:layout_toRightOf="@+id/textView2"
        android:layout_marginLeft="18dp"
        />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="宛先"
        android:textSize="20sp"
        android:layout_below="@+id/editText1"
        android:gravity="center_vertical"
        android:layout_alignBottom="@+id/editText2"/>

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView2"
        android:text="件名"
        android:textSize="20sp"
        android:layout_alignEnd="@+id/textView2"
        android:layout_marginTop="2dp"/>

    <EditText
        android:id="@+id/editText3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView3"
        android:background="@drawable/border"
        android:ems="10"
        android:textSize="20sp"
        android:paddingBottom="10dp"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="10dp"
        android:layout_alignEnd="@+id/editText2"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="パス"
        android:id="@+id/textView"
        android:layout_below="@+id/editText3"
        android:textSize="20dp"
        android:layout_marginTop="2dp"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textPassword"
        android:ems="10"
        android:id="@+id/passText"
        android:background="@drawable/border"
        android:padding="10dp"
        android:layout_below="@+id/textView"
        android:textColor="#efefef"/>

</RelativeLayout>


border.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">

    <corners
        android:radius="4dp" />

    <stroke
        android:width="1dp"
        android:color="@android:color/black" />

</shape>

関連する記事

VS2010(C#) : TKMP.DLL(3.1.2)を使った、メール送信テンプレート
javamail 1.4.4 でメール送信




posted by lightbox at 2016-06-06 14:54 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする

2016年05月30日


tools.jar の callHttpGet のテストと include による画面再利用と HttpPost クラスで掲示板書き込み / Android Studio

tools.jar

libs フォルダに保存して、右クリックから Add as Library を実行すると参照可能になります。

▼ ソースコード
Tools, Tools2, TestArrayAdapter, AndroidTools.java )

tools.jar は、気軽にアンドロイドのアプリケーションのテストができるように作成したライブラリです。Android の動作テストはとても時間がかかり、結構デリケートな部分が多いので、できればこのライブラリの中にあるような内容は適当にしたい事が多くあります。特に、商品では無く他人への説明や他の機能の習熟目的で使用し、自分自身は学生をターゲットにしており、授業で使用しています。

それらの中でも、特に面倒くさいのがインターネットに対するアクセスです。tools.jar では、GET アクセスは実装していますが、POST アクセスは実装していません。POST には POST するだけの意味がある時にしか利用されないので、ここで HttpPost クラスをソースで実装して 超簡易掲示板(CSV) を使って投稿するテストをしています。



画面を二つ使い、include でボタン3つの定義を両方で利用しています。
(Android Studio では、新しいアクティビティを作成すると、AndroidManifest も自動的に書き換えられます。)
※ 具体的なタイムアウト時の処理は行っていません(何も起きません)

 

MainActivity.java

次画面2ボタンでインターネットよりテキストデータを取得し(tools.jar の callHttpGet)、次画面に引き渡してテキストエリアに表示します。そこで投稿ボタンで実際の WEB に対する投稿(HttpPost) となります。
package lightbox.may.toolstest;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import jp.android.work.Tools;

public class MainActivity extends AppCompatActivity {

	// テキストビュー( 表示用 )
	private TextView tv;

	// *****************************************
	// 初期画面構築
	// *****************************************
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// 表示部分のインスタンスの取得
		tv = (TextView) MainActivity.this.findViewById(R.id.textView);

		// *****************************************
		// ボタン1 の設定
		// *****************************************
		Button btn1 = (Button)MainActivity.this.findViewById(R.id.button);
		btn1.setText("次画面");
		MainActivity.this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

			// 呼び出し番号 (1)
			Tools.callActivity(MainActivity.this,Main2Activity.class, 1);

			}

		});

		// *****************************************
		// ボタン2 の設定
		// インターネットのデータを次画面に引き渡す
		// *****************************************
		Button btn2 = (Button)MainActivity.this.findViewById(R.id.button2);
		btn2.setText("次画面2");
		btn2.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				Tools.callHttpGet(
					"http://toolbox.winofsql.jp/kumonoito.txt",
					"utf-8",
					new Tools.OnAsyncTaskListener() {
						@Override
						public void onAsyncTaskListener(String s) {
							Intent intent = new Intent(MainActivity.this, Main2Activity.class);
							intent.putExtra("requestCode",2);
							intent.putExtra("text",s);
							startActivityForResult(intent, 2);
						}
					});

			}

		});

		// *****************************************
		// ボタン3 の設定
		// ブラウザ呼び出し
		// *****************************************
		Button btn3 = (Button)MainActivity.this.findViewById(R.id.button3);
		btn3.setText("ブラウザ");
		btn3.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				Tools.callBrowser(MainActivity.this,"http://weather.yahoo.co.jp/weather/jp/27/6200.html");

			}

		});

	}

	// *****************************************
	// 第二画面から戻って来た時の処理
	// onActivityResult
	// *****************************************
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);

		Log.i("lightbox", String.format("呼び出し番号 : %d", requestCode)  );
		Log.i("lightbox", String.format("戻り番号 : %d", resultCode)  );

		if ( data != null ) {
			// 戻りデータの取得
			String returnData = data.getStringExtra("returnData");
			if (returnData != null) {
				// テキストビューに表示
				tv.setText(returnData);
			}
		}

	}
}


Main2Activity.java
package lightbox.may.toolstest;

import android.content.Intent;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class Main2Activity extends AppCompatActivity {

	// 呼び出し番号
	private int requestCode;
	// テキストエリア
	private EditText et;

	// *****************************************
	// 初期画面構築
	// *****************************************
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main2);

		// 入力部分のインスタンスの取得
		et = (EditText) Main2Activity.this.findViewById(R.id.editText);

		// *****************************************
		// 前の画面から送られたデータの取得
		// *****************************************
		Intent intent = getIntent();

		// 1) テキストデータ
		String text = intent.getStringExtra("text");
		if ( text != null ) {
			et.setText(text);
		}
		// 2) 整数データ
		requestCode = intent.getIntExtra("requestCode", 0);
		if ( requestCode != 0 ) {
			Log.i("lightbox", String.format("呼び出し番号 : %d", requestCode));
		}

		// *****************************************
		// ボタン1 の設定
		// 整数のみを戻す
		// *****************************************
		Button btn1 = (Button)Main2Activity.this.findViewById(R.id.button);
		btn1.setText("戻る");
		btn1.setTextColor(Color.parseColor("#f84848"));
		btn1.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// 戻り値のみ返す
				setResult(100);

				// 第二画面を終了して初期画面に戻る
				finish();

			}

		});

		// *****************************************
		// ボタン2 の設定
		// *****************************************
		Button btn2 = (Button)Main2Activity.this.findViewById(R.id.button2);
		btn2.setText("投稿");
		btn2.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				Intent intent = new Intent();
				setResult(200,intent);

				// エミュレータから PC の WEBサーバへアクセスする場合
//				String url = "http://10.0.2.2/log/easy_board/csvtype/board.php";

				// 実機を PC につないで、WiFi 接続を可能にした場合
				// 192.168.1.2 は PC の IPアドレス
				String url = "http://192.168.1.2/log/easy_board/csvtype/board.php";

				Map<String,String> url_param = new HashMap<String,String>();
				url_param.put("url", url);

				Map<String,String> param = new HashMap<String,String>();
				param.put("send", "send");
				param.put("subject", "テスト投稿");
				param.put("name", "ウインドウビルダー");
				param.put("text", et.getText().toString());
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
				param.put("datetime", sdf.format(new Date()));

				new AsyncTask<Map,Void,String>(){
					@Override
					protected String doInBackground(Map... params) {

						// 引数より URL 部分を取得
						String url = params[1].get("url").toString();

						Log.i("lightbox",url);

						// インターネットアクセス
						return HttpPost.execute(url, params[0], "utf-8");

					}

					@Override
					protected void onPostExecute(String s) {
						Log.i("lightbox",s);
						finish();

					}
				}.execute(param,url_param);



			}

		});

		// *****************************************
		// ボタン3 の設定
		// 整数と文字列を戻す
		// *****************************************
		Button btn3 = (Button)Main2Activity.this.findViewById(R.id.button3);
		btn3.setText("戻る+データ");
		btn3.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// 渡す値
				Intent intent = new Intent();
				intent.putExtra("returnData", et.getText().toString());
				// 戻り値と値を返す
				setResult(300, intent);

				finish();

			}

		});

	}

}


ボタン部分の画面パーツ( action_buttons.xml )



デザインタブから <include> を選択できます。クリックすると参照可能な画面一覧が表示されるので、選択するとカーソルがコントロールを選択した状態になり、デザイン画面やツリーにドロップ可能になります。

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

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

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

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

</LinearLayout>

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="lightbox.may.toolstest.MainActivity">


    <include
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        layout="@layout/action_buttons"
        android:id="@+id/include2"/>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/scrollView"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/include2">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/textView"/>
    </ScrollView>
</RelativeLayout>


activity_main2.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="lightbox.may.toolstest.Main2Activity">

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

    <EditText
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:inputType="textMultiLine"
        android:ems="10"
        android:id="@+id/editText"
        android:textSize="20dp"
        android:layout_below="@+id/include"
        android:layout_alignParentStart="true"
        android:gravity="top|left"/>

</RelativeLayout>



posted by lightbox at 2016-05-30 18:56 | Android Studio 2 | このブログの読者になる | 更新情報をチェックする
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 終わり