SQLの窓

2017年04月15日


Tools クラスを使って Livedoor の お天気Webサービスを呼び出して Google gson でリストビューに一括で投入するとこうなります

関連する記事

13行目の Tools クラスに関しては以下を参照して下さい

Android Studio のテストの時間を短縮する為のいくつかの static メソッドをまとめた Tools クラス

簡単に使える API

Livedoor の お天気Webサービスは、特別な制限無く使える API で、しかもリストビューのテストには都合のいい、各地域の名前とその地域の天気の情報を表示する URL の配列を JSON 文字列から手に入れる事ができます。

リストビューは、オブジェクトの配列を addAll するだけで表示可能な手順があるので、そのオブジェクトを JSON 文字列から Google Gson に自動的に作ってもらっています。( Google Gson にとって、クラスのコンストラクタは必要ありません。この実装では、Google Gson 以外から追加する事を想定しています )

Google Gson

ダウンロード(GitHub)
Gson Download and Maven => Gson Download => Browse Central For => gson-x.x.x.jar  

オンラインドキュメント

Google Gson 用のクラス定義

Google Gson は、fromJson メソッドで、文字列の JSON と一致するクラス定義を使ってそのクラスのインスタンスを作成してくれます。クラス定義は必要な部分だけでよく、他のものは省略してかまいません。ここでは、PinpointLocation というクラスを定義して、その配列を JSON 文字列が対応しています。

PinpointLocation で注意するのは、ListView で表示して欲しい内容を toString メソッドを Override して定義しておく事です。

表示する以外の内容は、ListView をクリックした時のイベントで取得可能です。(97 〜 100 行)
package app.lightbox.winofsql.jp.listviewa;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;

import com.google.gson.Gson;
import jp.android.work.Tools;

public class MainActivity extends Activity {

	// Livedoor の お天気Webサービス用 (配列データ用)
	class PinpointLocation {
		String link;
		String name;
		public PinpointLocation(String myLink, String myName) {
			name = myName;
			link = myLink;
		}

		@Override
		public String toString() {
			return name;
		}
	}
	// Livedoor の お天気Webサービス用 (JSON用)
	class Weather {
		PinpointLocation[] pinpointLocations;
	}

	ListView listView = null;
	ArrayAdapter<PinpointLocation> adapter = null;

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

		// リストビュー(PinpointLocation)用のアダプターを作成
		adapter =
			new ArrayAdapter<PinpointLocation>(
					MainActivity.this,
					android.R.layout.simple_list_item_1
			);

		// ボタンがクリックされたら読み込み
		Button button = (Button) this.findViewById(R.id.button);
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// 単純確認メッセージボックス
				Tools.messageBox(MainActivity.this, "API呼び出し", "実行しますか?", new Tools.OnMessageBoxListener() {
					@Override
					public void onMessageBoxYesListener() {

						// データをセット
						Tools.callHttpGet(
							"http://weather.livedoor.com/forecast/webservice/json/v1?city=270000",
							"utf-8",
							new Tools.OnAsyncTaskListener() {
								@Override
								public void onAsyncTaskListener(String s) {

									Gson gson = new Gson();
									Weather weatherData = gson.fromJson(s, Weather.class);
									adapter.clear();
									adapter.addAll(weatherData.pinpointLocations);
									// リストビューに適用
									listView.setAdapter(adapter);

								}
							}
						);
					}

					@Override
					public void onMessageBoxNoListener() {
						Log.i("lightbox","キャンセルしました");
					}
				});

			}
		});

		// リストビューのアイテムがクリックされた時の処理
		listView = (ListView) MainActivity.this.findViewById(R.id.listView);
		listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

				// クリックされたアイテムを取得
				PinpointLocation mydata = (PinpointLocation) parent.getItemAtPosition(position);
				// LogCatに表示
				Log.i("lightbox", mydata.name);
				Log.i("lightbox", mydata.link);

				// URL を開く
				Tools.callBrowser(MainActivity.this, mydata.link);

			}
		});
	}

}


関連する記事

Android の単純な ListView の ArrayAdapter による応用の効く 4バリエーション と複数項目に即対応できる SimpleAdapter による実装



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

2015年07月15日


Android の ListView で ArrayAdapter をカスタマイズして表示する時、リストビューのアイテム内にボタン等がある場合のイベント登録時の注意事項



ListView に自由なレイアウトの表示を行う場合、以下のような手順になると思います。

1) 自分が表示したいレイアウトを作成
2) そのレイアウトに対するデータを保存するクラスを作成
3) そのクラスを扱う専用アダプタを作成

この際、getView メソッドを Override する事になっています。その中で、convertView == null の場合は各行の初回の処理にはなりますが、画面に同時に表示される行以上を作成する事が無いので、getView 内で常にデータをセットする事になっています。という事は、実際に作成されるボタンのインスタンスは最大行数ぶんしか無いので、それぞれのボタンにイベントを設定しても、イベント数そのものが行数ぶんになってしまうので、ボタンが常に正しい行の処理ができるように、ボタンにとっての最新 position をボタン自身に setTag で登録しておきます。

MainActivity.java
package sample.lightbox.listview02;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;


public class MainActivity extends Activity {

	private static final String DEBUG_TAG = "ListView :";
	MyArrayAdapter adapter = null;

	// MyArratAdapter 用プライベートクラス
	private class MyLinkData {
		String myText;
		String myUrl;

		public MyLinkData(String myText, String myUrl) {
			this.myText = myText;
			this.myUrl = myUrl;
		}
	}

	// ボタンが3つあるカスタム行をを使った MyLinkData 専用 ArrayAdapter
	private class MyArrayAdapter extends ArrayAdapter<MyLinkData> {

		// プライベートなので、コンストラクタに渡す情報はありません
		public MyArrayAdapter() {
			// スーパークラスに必要なのは、アクティビティのインスタンスのみ
			super(MainActivity.this, 0);
		}

		// 一般的な getView 内のカスタマイズ
		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View rowView = convertView;

			if (rowView == null) {
				LayoutInflater inflater = MainActivity.this.getLayoutInflater();
				// ボタンが3つある行
				rowView = inflater.inflate(R.layout.row, null);
			}

			// myText の表示
			// Adapter の position の位置のデータを取得して、行の画面にセット
			((TextView)rowView.findViewById(R.id.textView)).setText(adapter.getItem(position).myText);

			Button button = (Button) rowView.findViewById(R.id.button);
			Button button2 = (Button) rowView.findViewById(R.id.button2);
			Button button3 = (Button) rowView.findViewById(R.id.button3);
			// 初回のみイベント登録
			if ( button.getTag() == null ) {
				// ボタン A
				button.setOnClickListener(new View.OnClickListener() {
					@Override
					public void onClick(View v) {
						// 最新の position を取得
						int buttonPosition = (int) v.getTag();
						// その position から データを取得
						MyLinkData data = adapter.getItem(buttonPosition);
						Toast.makeText(MainActivity.this,data.myText,Toast.LENGTH_SHORT).show();
					}
				});
				// ボタン B
				button2.setOnClickListener(new View.OnClickListener() {
					@Override
					public void onClick(View v) {
						// 最新の position を取得
						int buttonPosition = (int) v.getTag();
						// その position から データを取得
						MyLinkData data = adapter.getItem(buttonPosition);
						Log.i(DEBUG_TAG, data.myText);
					}
				});
				// ボタン C
				button3.setOnClickListener(new View.OnClickListener() {
					@Override
					public void onClick(View v) {
						// 最新の position を取得
						int buttonPosition = (int) v.getTag();
						// その position から データを取得
						MyLinkData data = adapter.getItem(buttonPosition);
						Toast.makeText(MainActivity.this,data.myUrl,Toast.LENGTH_SHORT).show();
					}
				});
			}

			// getView が呼び出されるという事は、その position が最新の状態なのでボタンに保存
			// して、ボタンイベント内でその position を使用する
			button.setTag(position);
			button2.setTag(position);
			button3.setTag(position);

			return rowView;
		}
	}

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

		// リストビューに表示させる情報
		MyLinkData[] myLinks = {
			new MyLinkData( "SQLの窓", "http://winofsql.jp" ),
			new MyLinkData( "logical error", "http://logicalerror.seesaa.net/" ),
			new MyLinkData( "琴線に触れる", "http://heart.winofsql.jp/"),
			new MyLinkData( "GINPRO", "http://ginpro.winofsql.jp/"),
			new MyLinkData( "SQLの窓 2", "http://winofsql.jp" ),
			new MyLinkData( "logical error 2", "http://logicalerror.seesaa.net/" ),
			new MyLinkData( "琴線に触れる 2", "http://heart.winofsql.jp/"),
			new MyLinkData( "GINPRO 2", "http://ginpro.winofsql.jp/")
		};

		// リストビュー用のアダプターを作成
		adapter = new MyArrayAdapter();
		adapter.addAll(myLinks);

		// リストビューを取得
		ListView listView = (ListView) MainActivity.this.findViewById(R.id.listView);

		// リストビューにデータ( アダプター ) をセット
		listView.setAdapter(adapter);

		// リストビューのアイテムがクリックされた時の処理
		listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			public void onItemClick(AdapterView<?> parent, View view,
									int position, long id) {

				MyLinkData item = adapter.getItem(position);
				// LogCatに表示
				Log.i(DEBUG_TAG, item.myText);
				Log.i(DEBUG_TAG, item.myUrl);
				Log.i(DEBUG_TAG, Integer.toString(position));

				// URL を開く
				startActivity( new Intent(Intent.ACTION_VIEW, Uri.parse(item.myUrl)) );

			}
		});
	}


}


row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:descendantFocusability="blocksDescendants"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="A"
        android:id="@+id/button"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="B"
        android:id="@+id/button2"
        android:layout_below="@+id/button"
        android:layout_alignParentStart="true"
        android:layout_marginTop="38dp"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="C"
        android:id="@+id/button3"
        android:layout_alignBottom="@+id/button2"
        android:layout_alignParentEnd="true"/>

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="New Text"
        android:id="@+id/textView"
        android:layout_below="@+id/button"
        android:layout_alignParentStart="true"
        android:textSize="25dp"
        android:layout_marginTop="3dp"/>
</RelativeLayout>
あまり必要性を感じませんが、ListView の行そのものをタップした場合も処理を実行できるように、android:descendantFocusability="blocksDescendants" を行の root の要素に設定しています。



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

2015年07月14日


できる限り不必要な記述は省いて simple_list_item_2 を使った 自前クラスを扱う専用 ArrayAdapter(private) でリストビューの表示



android.R.layout にあるものを適当に使えるのですが、とりあえず simple_list_item_2 を使って実装しました。

一般的な ListView のカスタマイズですが、できる限り不必要な記述は省いて作成しています。( 正確には、『不必要な記述は省く』では無く『書かなくても動作する部分は省く』です。 )

MyArrayAdapter はプライベートクラスなので、getView で必要なものは直接参照するようにして、コンストラクタの引数は無くしました。スーパークラスのコンストラクタは呼んでおかないと、内部の List のインスタンスが作成されないので呼んでいますが、MainActivity の インスタンスにしても、直接参照できるので必要無いのですが、渡さないとスーパークラスの初期処理でエラーとなるのでセットしています。
package sample.lightbox.listview02;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;


public class MainActivity extends Activity {

	private static final String DEBUG_TAG = "ListView :";
	MyArrayAdapter adapter = null;

	// MyArratAdapter 用プライベートクラス
	private class MyLinkData {
		String myText;
		String myUrl;

		public MyLinkData(String myText, String myUrl) {
			this.myText = myText;
			this.myUrl = myUrl;
		}
	}

	// simple_list_item_2 を使った MyLinkData 専用 ArrayAdapter
	private class MyArrayAdapter extends ArrayAdapter<MyLinkData> {

		// プライベートなので、コンストラクタに渡す情報はありません
		public MyArrayAdapter() {
			// スーパークラスに必要なのは、アクティビティのインスタンスのみ
			super(MainActivity.this, 0);
		}

		// 一般的な getView 内のカスタマイズ
		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View rowView = convertView;

			if (rowView == null) {
				LayoutInflater inflater = MainActivity.this.getLayoutInflater();
				rowView = inflater.inflate(android.R.layout.simple_list_item_2, null);
			}

			MyLinkData myLinkData = MyArrayAdapter.this.getItem(position);
			TextView textView1 = (TextView) rowView.findViewById(android.R.id.text1);
			textView1.setText(myLinkData.myText);
			TextView textView2 = (TextView) rowView.findViewById(android.R.id.text2);
			textView2.setText(myLinkData.myUrl);

			return rowView;
		}
	}

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

		// リストビューに表示させる情報
		MyLinkData[] myLinks = {
			new MyLinkData( "SQLの窓", "http://winofsql.jp" ),
			new MyLinkData( "logical error", "http://logicalerror.seesaa.net/" ),
			new MyLinkData( "琴線に触れる", "http://heart.winofsql.jp/"),
			new MyLinkData( "GINPRO", "http://ginpro.winofsql.jp/")
		};

		// リストビュー用のアダプターを作成
		adapter = new MyArrayAdapter();
		adapter.addAll(myLinks);

		// リストビューを取得
		ListView listView = (ListView) MainActivity.this.findViewById(R.id.listView);

		// リストビューにデータ( アダプター ) をセット
		listView.setAdapter(adapter);

		// リストビューのアイテムがクリックされた時の処理
		listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			public void onItemClick(AdapterView<?> parent, View view,
									int position, long id) {

				MyLinkData item = adapter.getItem(position);
				// LogCatに表示
				Log.i(DEBUG_TAG, item.myText);
				Log.i(DEBUG_TAG, item.myUrl);
				Log.i(DEBUG_TAG, Integer.toString(position));

				// URL を開く
				startActivity( new Intent(Intent.ACTION_VIEW, Uri.parse(item.myUrl)) );

			}
		});
	}


}

関連する記事

できるだけ難しい事をせずに、Android のリストビューでタイトルをクリックしたらそのページを表示する(1)
できるだけ難しい事をせずに、Android のリストビューでタイトルをクリックしたらそのページを表示する(2)

画面定義
<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:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin"
                tools:context=".MainActivity">

    <ListView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/listView"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"/>
</RelativeLayout>

R.layout は、ユーザー定義で、android.R.layout は システム定義です



posted by lightbox at 2015-07-14 20:25 | Android Studio | このブログの読者になる | 更新情報をチェックする

2015年07月13日


できるだけ難しい事をせずに、Android のリストビューでタイトルをクリックしたらそのページを表示する( 自前のオブジェクトを ArrayAdapter にセットする )

ArrayAdapter.java の中身を見ると、getView では、ListView に使われた ArrayAdapter 内のクラスに対して、toString() で文字列を取得して行のテキストにセットしています。

そして、行のテキストは TextView という前提で作られており、コンストラクタにその TextView の id が引き渡されているかどうかで行の画面定義が2通りである事が解ります。

コンストラクタに TextView の id を渡さない

この場合は、行の画面定義は TextView だけで成り立っている必要があり、ここで使っている android.R.layout.simple_list_item_1 には、TextView の定義しかありません。

コンストラクタに TextView の id を渡す

この場合は、行の画面定義内に少なくとも一つ TextView があれば良く、その TextView の id を引き渡す必要があります。
        try {
            if (mFieldId == 0) {
                //  If no custom field is assigned, assume the whole resource is a TextView
                text = (TextView) view;
            } else {
                //  Otherwise, find the TextView field within the layout
                text = (TextView) view.findViewById(mFieldId);
            }
        } catch (ClassCastException e) {
            Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
            throw new IllegalStateException(
                    "ArrayAdapter requires the resource ID to be a TextView", e);
        }


package sample.lightbox.listview01;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.ArrayList;


public class MainActivity extends Activity {

	static final String DEBUG_TAG = "ListView :";

	// ArrayAdapter 用プライベートクラス
	private class MyLinkData {
		String myText;
		String myUrl;

		MyLinkData( String myText, String myUrl ) {
			this.myText = myText;
			this.myUrl = myUrl;
		}
		public String toString() {
			return myText;
		}
	}

	// 変数定義
	ArrayList<MyLinkData> myLinks = null;
	ArrayAdapter<MyLinkData> adapter = null;
	ListView listView = null;

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

		myLinks = new ArrayList<MyLinkData>();
		myLinks.add(new MyLinkData( "SQLの窓", "http://winofsql.jp" ));
		myLinks.add(new MyLinkData("logical error", "http://logicalerror.seesaa.net/"));
		myLinks.add(new MyLinkData("琴線に触れる", "http://heart.winofsql.jp/"));
		myLinks.add(new MyLinkData("GINPRO", "http://ginpro.winofsql.jp/"));

		// リストビュー用のアダプターを作成
		adapter = new ArrayAdapter<MyLinkData>(this, android.R.layout.simple_list_item_1);
		adapter.addAll(myLinks);

		// リストビューのインスタンスを取得
		listView = (ListView) MainActivity.this.findViewById(R.id.listView);
		// リストビューにデータ( アダプター ) をセット
		listView.setAdapter(adapter);

		// イベント
		listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

				MyLinkData item = adapter.getItem(position);
				// LogCatに表示
				Log.i(DEBUG_TAG, item.myUrl);
				Log.i(DEBUG_TAG, item.toString());
				Log.i(DEBUG_TAG, Integer.toString(position));

				// URL を開く
				startActivity( new Intent(Intent.ACTION_VIEW, Uri.parse(item.myUrl)) );

			}
		});

	}


}

onItemClick の中身については、以下のように SDK で書かれています。
Parameters
parent => The AdapterView where the click happened.
※ クリックされた View( Adapter をセットした View / ここでは ListView )

view => The view within the AdapterView that was clicked (this will be a view provided by the adapter)
※ クリックされた View の 中の行の View( ArrayAdapter で用意されたもの )

position => The position of the view in the adapter.
※ ArrayAdapter 内の位置

id => The row id of the item that was clicked.
※ 行の id ( position とどう違うが不明。おそらく、ArrayAdapter をカスタマイズした時に自分で実装するものと思われる )


posted by lightbox at 2015-07-13 15:35 | Android Studio | このブログの読者になる | 更新情報をチェックする

2015年07月10日


インターネットからアイコン画像を取得してリストビューを表示する

Google が「Android Developer Tools (ADT) in Eclipse」の開発とサポートを2015年末で終了するので、過去の中途半端なコードを Android Studio でテストして書き直しました。

ArrayAdapter を継承したカスタムな ListView 内でアイコン画像を表示するサンプルです。できる限り無駄な記述は省いたつもりです。内部のプラベートなクラスを使うので、参照はどこからでも可能な変数に任せて、AsyncTask では引数による受け渡しをしていません。また、ArrayAdapter 内でも、ListViewActivity からの参照で全てまかなえています。



※ インターネットにアクセスするので、AndroidManifest.xml に必要な定義を追加しています
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="sample.lightbox.listviewactivity" >

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".ListViewActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.INTERNET"/>

</manifest>


画面には、 TextView と ImageView を設置しています
lightbox.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:paddingTop="1dp"
              android:paddingBottom="1dp"
              android:paddingLeft="8dp"
              android:paddingRight="8dp">

    <ImageView
        android:id="@+id/icon"
        android:layout_width="60dp"
        android:layout_height="60dp" />

    <TextView
        android:id="@+id/text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="30dp"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"/>

</LinearLayout>

ListViewActivity.java
package sample.lightbox.listviewactivity;

import android.app.Activity;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

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

public class ListViewActivity extends Activity {

	// 画像表示用 MyArratAdapter
	MyArrayAdapter adapter = null;
	// ListView
	ListView listView = null;

	// MyArratAdapter 用プライベートクラス
	private class MyLinkData {
		String myText;
		String myUrl;
		String myIconUrl;
		Drawable myDrawable = null;

		MyLinkData( String myUrl, String myText, String myIconUrl ) {
			this.myText = myText;
			this.myUrl = myUrl;
			this.myIconUrl = myIconUrl;
		}
	}

	// R.layout.lightbox を使った MyLinkData 専用 ArrayAdapter
	private class MyArrayAdapter extends ArrayAdapter<MyLinkData> {

		public MyArrayAdapter() {
			// MyArrayAdapter に必要なのは ListViewActivity のインスタンスのみです
			// ( 実際はそれも必要ありませんが、内部の初期化の都合で渡さないとエラーになります )
			super(ListViewActivity.this, 0);
		}

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

			// 継承元の ArrayAdapter の内部とほぼ同等の行の view の処理
			View rowView = convertView;
			if (rowView == null) {
				LayoutInflater inflater = ListViewActivity.this.getLayoutInflater();
				rowView = inflater.inflate(R.layout.lightbox, null);
			}

			// R.layout.lightbox 用のデータ
			MyLinkData myLinkData = MyArrayAdapter.this.getItem(position);
			// R.layout.lightbox 用のコンテンツへデータをセット
			// 1) 名前
			TextView textView1 = (TextView) rowView.findViewById(R.id.text1);
			textView1.setText(myLinkData.myText);
			// 2) アイコン画像
			ImageView imageView = (ImageView) rowView.findViewById(R.id.icon);
			imageView.setImageDrawable(myLinkData.myDrawable);

			return rowView;
		}
	}

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

		listView = (ListView)ListViewActivity.this.findViewById(R.id.listView);

		// リストビューに表示させる文字列の配列( URL )
		// 画像は Google にある(長い)ので短縮しています
		final MyLinkData[] myLinks = {
			new MyLinkData( "http://winofsql.jp",
				"SQLの窓",
				"https://goo.gl/zDLKbz" ),
			new MyLinkData( "https://www.google.co.jp/",
				"Google",
				"https://goo.gl/t2AYIL"),
			new MyLinkData( "http://www.yahoo.co.jp/",
				"Yahoo! JAPAN",
				"https://goo.gl/oF41g6")
		};

		// リストビュー用のアダプターを作成
		adapter = new MyArrayAdapter();
		// 以下は、画面の行数をオーバーさせる為に追加(3x3)しています
		adapter.addAll(myLinks);
		adapter.addAll(myLinks);
		adapter.addAll(myLinks);

		// 非同期インターネットアクセス
		// 画像はあらかじめ全て取得しておきます
		new AsyncTask<Void,Void,Void>() {
			@Override
			protected Void doInBackground(Void... params) {

					int count = adapter.getCount();
					for( int i = 0;  i < count; i++ ) {
						try {
							URL url = new URL( adapter.getItem(i).myIconUrl );
							InputStream is = (InputStream)url.getContent();
							adapter.getItem(i).myDrawable = Drawable.createFromStream(is, "");
						}
						catch(Exception e) {
							Log.i("lightbox", "error:" + adapter.getItem(i).myIconUrl );
						}
					}

				return null;
			}

			@Override
			protected void onPostExecute(Void aVoid) {
				// 画像を全て取得してからリストビューにデータ( アダプター ) をセット
				listView.setAdapter(adapter);
			}
		}.execute();


		// リストビューのアイテムがクリックされた時の処理
		listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
				ListView listView = (ListView) parent;
				// クリックされたアイテムを取得
				// MyLinkData をセットしているので、MyLinkData として取り出す
				MyLinkData item = (MyLinkData) listView.getItemAtPosition(position);
				// LogCatに表示
				Log.i("lightbox", item.myUrl);
				Log.i("lightbox", item.myText);
				Log.i("lightbox", Integer.toString(position));

				// URL を開く
				startActivity( new Intent(Intent.ACTION_VIEW, Uri.parse(item.myUrl)) );

			}
		});
	}

}


主画面
<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:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin"
                tools:context=".ListViewActivity">

    <ListView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/listView"
        android:layout_alignParentStart="true"/>
</RelativeLayout>



posted by lightbox at 2015-07-10 19:36 | Android Studio | このブログの読者になる | 更新情報をチェックする

2015年06月11日


Android の AlertDialog を使いやすくする MessageBox クラス

Java に精通してはいないので、Windows のメッセージ処理の発想で作成しています。MessageBox 内の onYesClick メソッドは抽象メソッドなので、使用する際に必ず記述する必要がありますが、onNoClick メソッドは 内部で実装しているので、書いても書かなくても OK です。

onYesClick と onNoClick は、該当場所で、CTRL + O で Override 用のダイアログが表示されるので選択して実装します。onYesClick は必須ですが、onNoClick は省略可能で、Yes ボタン以外のアクションの場合に呼び出されます。
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;

/**
 * Created by lightbox on 2015/06/11.
 */
public abstract class MessageBox {
	private AlertDialog alertDialog = null;

	public MessageBox() {
	}

	// YES or NO メッセージボックス( タイトルとメッセージ両方指定)
	public void show(Activity context,String title,String message) {

		// ダイアログ作成用オブジェクト作成
		AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
		alertDialogBuilder.setTitle(title);
		alertDialogBuilder.setMessage(message);

		// YES ボタン作成
		alertDialogBuilder.setPositiveButton("YES",
				new DialogInterface.OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						MessageBox.this.onYesClick(dialog);
					}
				});

		// NO ボタン作成
		alertDialogBuilder.setNegativeButton("NO",
			new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					MessageBox.this.onNoClick(dialog);
				}
			});

		alertDialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() {
			@Override
			public void onCancel(DialogInterface dialog) {
				MessageBox.this.onNoClick(dialog);
			}
		});

		alertDialogBuilder.setCancelable(true);
		// アラートダイアログを作成します
		alertDialog = alertDialogBuilder.create();
		alertDialog.show();

	}

	// 確認 メッセージボックス( メッセージのみ )
	public void show(Activity context,String message) {

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

		alertDialogBuilder.setPositiveButton("OK",
			new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					MessageBox.this.onYesClick(dialog);
				}
			});

		alertDialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() {
			@Override
			public void onCancel(DialogInterface dialog) {
				MessageBox.this.onNoClick(dialog);
			}
		});

		alertDialogBuilder.setCancelable(true);
		// アラートダイアログを作成します
		alertDialog = alertDialogBuilder.create();
		alertDialog.show();

	}

	// Override は必須
	abstract void onYesClick(DialogInterface dialog);
	// Override を省略できるように protected
	protected void onNoClick(DialogInterface dialog) {
	}

}


呼び出し1 / OK ボタンのみ



メッセージボックス以外をタップしたり、戻ったりするとキャンセルとなります。show メソッドの引数が、context と メッセージと二つだけの時にこの処理となります。
// 単純確認メッセージボックス
new MessageBox(){
	@Override
	void onYesClick(DialogInterface dialog) {

		// 処理

	}

}.show(MainActivity.this, "こんにちは");


呼び出し2 / YES ボタンと NO ボタン



NO ボタンをタップするか、メッセージボックス以外をタップするか、戻ったりいるとキャンセルとなります。show メソッドの引数が、context と タイトルとメッセージの時にこの処理となります。
// 単純確認メッセージボックス
new MessageBox(){
	@Override
	void onYesClick(DialogInterface dialog) {

		// 処理

	}

}.show(MainActivity.this, "タイトル","メッセージ");




posted by lightbox at 2015-06-11 16:30 | Android Studio | このブログの読者になる | 更新情報をチェックする

2015年06月10日


できるだけ難しい事をせずに、Android のリストビューでタイトルをクリックしたらそのページを表示する

Android Studio + Android 5.1(Lollipop)



これでは、リストビュー内のコンテンツに汎用性が全くありませんが、そういう複雑なリストビューはきちんと設計して誰でも使えるようにするべきなので、これはこれである意味汎用性があると思います。
package app.lightbox.winofsql.jp.listviewa;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;


public class MainActivity extends Activity {

	class MyData {
		public String title = null;
		public String url = null;
		public MyData(String myTitle,String myUrl) {
			title = myTitle;
			url = myUrl;
		}
		@Override
		public String toString() {
			return title;
		}
	}

	private MyData[] mydata	 = new MyData[] {
			new MyData("Yahoo! ニュース","http://news.yahoo.co.jp/"),
			new MyData("Yahoo! 雨雲ズームレーダー","http://weather.yahoo.co.jp/weather/zoomradar/"),
			new MyData("ねとらぼ","http://nlab.itmedia.co.jp/")
	};

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

		// リストビュー(MyData)用のアダプターを作成
		ArrayAdapter<MyData> adapter =
			new ArrayAdapter<MyData>(MainActivity.this, android.R.layout.simple_list_item_1);
		// データをセット
		adapter.addAll( mydata );
		// リストビューに適用
		ListView listView = (ListView) MainActivity.this.findViewById(R.id.listView);
		listView.setAdapter(adapter);

		// リストビューのアイテムがクリックされた時の処理
		listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

				// クリックされたアイテムを取得
				MyData mydata = (MyData)parent.getItemAtPosition(position);
				// LogCatに表示
				Log.i("lightbox", mydata.title);
				Log.i("lightbox", mydata.url);

				// URL を開く
				callBrowser(mydata.url );

			}
		});

	}

	private void callBrowser( String url ) {
		// ブラウザの呼び出し
		Uri uri = Uri.parse(url);
		Intent intent = new Intent(Intent.ACTION_VIEW, uri);
		if (intent.resolveActivity(getPackageManager()) != null) {
			startActivity(intent);
			return;
		}

		// 対応するアプリが無い
		Toast.makeText(
				MainActivity.this,
				"ブラウザを呼び出せません",
				Toast.LENGTH_LONG
		).show();
		return;

	}

}

※ 配列部分を外部 JSON にすれば読み替えは簡単にできる予定。

画面定義
<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:paddingLeft="@dimen/activity_horizontal_margin"
				android:paddingRight="@dimen/activity_horizontal_margin"
				android:paddingTop="@dimen/activity_vertical_margin"
				android:paddingBottom="@dimen/activity_vertical_margin"
				tools:context=".MainActivity">

	<ListView
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:id="@+id/listView"
		android:layout_alignParentTop="true"
		android:layout_alignParentStart="true"/>
</RelativeLayout>



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

2015年06月06日


Android の単純な ListView の ArrayAdapter による応用の効く 4バリエーション と複数項目に即対応できる SimpleAdapter による実装

  

リストビューの一行ぶんの View の定義としては、android.R.layout.simple_list_item_1android.R.layout.simple_list_item_2 を使用しています。たいていは android.R.layout.simple_list_item_1 でまかなえると思いますが、項目が二つの場合が必要な場合として、SimpleAdapter が最も簡潔に書けるのでそれを利用しています。

ArrayAdapter で、android.R.layout.simple_list_item_2 に対応するのには、ArrayAdapter を継承したユーザクラスが必要になりますが、ここでは使用していません。

全体のコードは こちら で参照して下さい

ArrayAdapter で文字列を一つづつ追加
	private ArrayAdapter<String> strArrayAdapter = null;
	private ListView listview = null;
	private String [] strData1 = null;

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

		listview = (ListView) MainActivity.this.findViewById(R.id.listView);

		// 単純な文字列配列用
		strArrayAdapter = new ArrayAdapter<String>(
			MainActivity.this,
			android.R.layout.simple_list_item_1,
			android.R.id.text1
		);

		// タイトルデータ(1)
		strData1 = new String[]{
			"Yahoo! ニュース","Yahoo! 雨雲ズームレーダー",
			"ねとらぼ","GIGAZINE","Twitter","OneDrive",
			"楽天市場","ウィキペディア","Yahoo!テレビ",
			"@IT","Stack Overflow","Android Developers"
		};

	}
			// ArrayAdapter をクリア
			strArrayAdapter.clear();
			for( int i = 0; i < strData1.length; i++ ) {
				strArrayAdapter.add(strData1[i]);
			}
			listview.setAdapter(strArrayAdapter);


ArrayAdapter で配列を一括で追加

前提となるパーツは一つ前と同じです。配列が固定で既に作成されている場合にすぐに投入できます。外部から JSON で読み込んで、結果的に配列が作成されているような場合に最適です。
			// ArrayAdapter をクリア
			strArrayAdapter.clear();
			// でき上がっている配列をそのままセット
			strArrayAdapter.addAll(strData1);
			listview.setAdapter(strArrayAdapter);


ArrayAdapter で List を一括で追加

既にできあがっているデータが配列ではなく、List インターフェイスを持つ場合に使用します

ここでは、テストなので配列から List を作成して使用しています
			// ArrayAdapter をクリア
			strArrayAdapter.clear();
			// 配列から List を作成
			List<String> list = Arrays.asList(strData1);
			// List を指定する addAll
			strArrayAdapter.addAll(list);
			listview.setAdapter(strArrayAdapter);



ArrayAdapter で 内部に別の情報を持つ

専用のクラスを作成して、それ用の ArrayAdapter で処理します。表示は 1つの内容しかできませんが、内部に別の情報を持つので、行をクリックした時にそれを使用する事ができます
	private ArrayAdapter<MyData> arrayMyData = null;
	private ListView listview = null;
	private String [] strData1 = null;
	private String [] strData1Url = null;

	// ListView にセットする配列用クラス
	class MyData {
		public String title = null;
		public String url = null;
		public MyData(String myTitle,String myUrl) {
			title = myTitle;
			url = myUrl;
		}
		// toString で呼び出された内容が ListView に表示されます
		@Override
		public String toString() {
			return title;
		}
	}

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

		// ListView のインスタンスを取得
		listview = (ListView) MainActivity.this.findViewById(R.id.listView);
		// 専用クラス用
		arrayMyData = new ArrayAdapter<MyData>(
				MainActivity.this,
				android.R.layout.simple_list_item_1,
				android.R.id.text1
		);

		// タイトルデータ(1)
		strData1 = new String[]{
			"Yahoo! ニュース","Yahoo! 雨雲ズームレーダー",
			"ねとらぼ","GIGAZINE","Twitter","OneDrive",
			"楽天市場","ウィキペディア","Yahoo!テレビ",
			"@IT","Stack Overflow","Android Developers"
		};
		// タイトルデータ(1) の URL データ
		strData1Url = new String[]{
			"http://news.yahoo.co.jp/",
			"http://weather.yahoo.co.jp/weather/zoomradar/",
			"http://nlab.itmedia.co.jp/",
			"http://gigazine.net/",
			"http://twitter.com/",
			"http://onedrive.live.com/",
			"http://www.rakuten.co.jp/",
			"https://ja.wikipedia.org",
			"http://tv.yahoo.co.jp/",
			"http://www.atmarkit.co.jp/",
			"http://stackoverflow.com/",
			"http://developer.android.com/index.html"
		};

	}
			// ArrayAdapter をクリア
			arrayMyData.clear();
			// 配列から作成
			for( int i = 0; i < strData1.length; i++ ) {
				arrayMyData.add(new MyData(strData1[i],strData1Url[i]));
			}
			listview.setAdapter(arrayMyData);


SimpleAdapter で二つの情報を表示

ArrayAdapter よりも汎用性がなくなります( メソッドでできる事がほとんどなくなってしまいます )が、手早く複数項目を持つ View への対応が簡単です。
	private ListView listview = null;
	private String [] strData1 = null;
	private String [] strData1Url = null;
	private SimpleAdapter itemsAdapter = null;

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

		// ListView のインスタンスを取得
		listview = (ListView) MainActivity.this.findViewById(R.id.listView);

		// タイトルデータ(1)
		strData1 = new String[]{
			"Yahoo! ニュース","Yahoo! 雨雲ズームレーダー",
			"ねとらぼ","GIGAZINE","Twitter","OneDrive",
			"楽天市場","ウィキペディア","Yahoo!テレビ",
			"@IT","Stack Overflow","Android Developers"
		};
		// タイトルデータ(1) の URL データ
		strData1Url = new String[]{
			"http://news.yahoo.co.jp/",
			"http://weather.yahoo.co.jp/weather/zoomradar/",
			"http://nlab.itmedia.co.jp/",
			"http://gigazine.net/",
			"http://twitter.com/",
			"http://onedrive.live.com/",
			"http://www.rakuten.co.jp/",
			"https://ja.wikipedia.org",
			"http://tv.yahoo.co.jp/",
			"http://www.atmarkit.co.jp/",
			"http://stackoverflow.com/",
			"http://developer.android.com/index.html"
		};

	}
			// 内部データを作成
			List list = new ArrayList<Map<String, ?>>();
			for( int i = 0; i < strData1.length; i++ ) {
				Map map = new HashMap();
				// 名前は文字列で適当に付けます( item1, item2 )
				map.put("item1", strData1[i]);
				map.put("item2", strData1Url[i]);
				list.add(map);
			}

			// 複数項目のビュー用のインスタンス
			itemsAdapter = new SimpleAdapter(
				// 1)
				MainActivity.this,
				// 2)
				list,
				// 3) 二つの項目を持つビュー
				android.R.layout.simple_list_item_2,
				// 4) ここで項目を一致させる
				new String[] { "item1", "item2" },
				// 5) 実際のビューの id と一致させる
				new int[] { android.R.id.text1, android.R.id.text2 }
			);
			listview.setAdapter(itemsAdapter);

クリックした時の処理

URL 情報を持つ二つのパターンでは、ブラウザを呼び出しています。
		// クリックした時のイベント作成
		listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
				// クリックされたビューの内部データ
				Object data = (Object)parent.getItemAtPosition(position);
				// クラス名を取得
				Log.i("lightbox","data type:" + data.getClass().getName());
				String myClass = "app.lightbox.winofsql.jp.listviewaction.MainActivity$MyData";
				String simple = "java.util.HashMap";
				// 専用クラスの場合
				if ( myClass.equals(data.getClass().getName()) ) {
					MyData myData = (MyData)data;
					Log.i("lightbox", "title:" + myData.title);
					Log.i("lightbox", "url:" + myData.url);

					// ブラウザの呼び出し
					callBrowser(myData.url);
					return;
				}

				if ( simple.equals(data.getClass().getName()) ) {
					Map map = (Map)data;
					Log.i("lightbox", "title:" + map.get("item1"));
					Log.i("lightbox", "url:" + map.get("item2"));

					// ブラウザの呼び出し
					callBrowser(map.get("item2").toString());
					return;
				}

				// 単純な文字列の場合
				String myData = (String)data;
				Log.i("lightbox", "text:" + (String)data);

			}
		});



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

2015年06月01日


Android Studio : インターネット上の画像を取得して、Bitmap か Drawable で ImageView に表示する



ただ表示するだけなので、Bitmap も Drawable もあまり違いはありませんが、ただ読みたい場合は Drawable のほうが簡潔です。(Bitmap の場合は書く事が多いぶん、メタデータのみを取得する事ができますね)

MainActivity
package app.lightbox.winofsql.jp.storageaccess;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

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


public class MainActivity extends Activity {

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

		// ボタンのイベント
		Button button = (Button) this.findViewById(R.id.button);
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				new AsyncTask<String, Void, Bitmap>() {

					// 非同期処理
					@Override
					protected Bitmap doInBackground(String... params) {
						Bitmap image = null;
						BitmapFactory.Options options;
						try {
							// インターネット上の画像を取得して、Bitmap に変換
							URL url = new URL(params[0]);
							options = new BitmapFactory.Options();
							// 実際に読み込む
							options.inJustDecodeBounds = false;
							InputStream is = (InputStream) url.getContent();
							image = BitmapFactory.decodeStream(is, null, options);
							is.close();
						} catch (Exception e) {
							Log.i("button", e.getMessage());
						}
						return image;
					}

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

						// Bitmap 取得に成功している場合は表示します
						if (image != null) {
							ImageView imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView);
							imageView.setImageBitmap(image);
						}

					}
				}.execute("https://goo.gl/fc07Q6");		// JPEG 画像の 短縮 URL
			}
		});

		// ボタンのイベント
		Button button2 = (Button) this.findViewById(R.id.button2);
		button2.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				new AsyncTask<String, Void, Drawable>() {

					// 非同期処理
					@Override
					protected Drawable doInBackground(String... params) {
						Drawable image = null;
						try {
							// インターネット上の画像を取得して、Drawable に変換
							URL url = new URL( params[0] );
							InputStream is = (InputStream)url.getContent();
							image = Drawable.createFromStream(is, "");
							is.close();
						}
						catch(Exception e) {
							Log.i("button", e.getMessage());
						}
						return image;
					}

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

						// Bitmap 取得に成功している場合は表示します
						if (image != null) {
							ImageView imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView);
							imageView.setImageDrawable(image);
						}

					}
				}.execute("https://goo.gl/WNzpRl");	// JPEG 画像の 短縮 URL
			}
		});
	}

	@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_settings) {
			return true;
		}

		return super.onOptionsItemSelected(item);
	}
}


画面定義
<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:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin"
                tools:context=".MainActivity">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"
        android:id="@+id/linearLayout">

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

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

    </LinearLayout>

    <ImageView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/imageView"
        android:layout_below="@+id/linearLayout"
        android:layout_alignParentStart="true"
        android:scaleType="fitStart"/>


</RelativeLayout>




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

Storage Access Framework のガイドラインに基づく、Android 4.4 とそれ以降における 画像参照

Storage Access Framework
On Android 4.3 and lower, if you want your app to retrieve a file from another app, it must invoke an intent such as ACTION_PICK or ACTION_GET_CONTENT. The user must then select a single app from which to pick a file and the selected app must provide a user interface for the user to browse and pick from the available files.

On Android 4.4 and higher, you have the additional option of using the ACTION_OPEN_DOCUMENT intent, which displays a picker UI controlled by the system that allows the user to browse all files that other apps have made available. From this single UI, the user can pick a file from any of the supported apps.
要するに、ACTION_OPEN_DOCUMENT では全部用意しているので呼び出すだけで便利です。みたいな意味だと思います。画像関係のリソースを統一したインターフェィスを利用して選択できるはずです。 最初の表示は『最近』になりますが、以降は直前の表示になりました。 タイトル左端のメニューから対象を変更します。 画像はあらかじめ、Android Device Monitor(ツールバーの右から2番目 / HELP の左横) で該当場所にインポートしておきます。 画像の参照
package app.lightbox.winofsql.jp.storageaccess;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

import java.io.FileDescriptor;
import java.io.IOException;


public class MainActivity extends Activity {

	// 画像呼び出しからの戻りコードの定義
	private static final int READ_REQUEST_CODE = 42;

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

		// ボタンのイベント
		Button button = (Button) this.findViewById(R.id.button);
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// On Android 4.4 and higher
				// ( Storage Access Framework )
				Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
				intent.addCategory(Intent.CATEGORY_OPENABLE);
				intent.setType("image/*");
				startActivityForResult(intent, READ_REQUEST_CODE);
			}
		});
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);

		// 意図した結果の場合の処理
		if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
			Uri uri = null;
			if (data != null) {
				// Uri が戻されます
				uri = data.getData();
				Log.i("lightbox", "Uri: " + uri.toString());

				// Storage Access Framework では、Bitmap の処理を
				// UI スレッドで行うべきでは無いとの記述があります
				// ( 使わなくともいちおうエミュレータでは動作しました )
				new AsyncTask<Uri, Void, Bitmap>() {

					// 非同期処理
					@Override
					protected Bitmap doInBackground(Uri... params) {
						Bitmap image = null;
						// Storage Access Framework にある Bitmap 取得処理です
						try {
							ParcelFileDescriptor parcelFileDescriptor =
									getContentResolver().openFileDescriptor(params[0], "r");
							FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
							image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
							parcelFileDescriptor.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
						return image;
					}

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

						// Bitmap 取得に成功している場合は表示します
						if (image != null) {
							ImageView imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView);
							imageView.setImageBitmap(image);
						}

					}
				}.execute(uri);

			}
		}
	}

	@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_settings) {
			return true;
		}

		return super.onOptionsItemSelected(item);
	}
}

Bitmap 作成の説明のところで、UI スレッドから実行すべきでは無いとのコメントがありました。AsyncTask を使用しなくでもエラーは出ませんでしたが、AsynTask 使っておいたほうが他で使う時に変更が少なくて済みますし、書いてある以上そのほうが安全なのでしょう。

画面定義

android:scaleType="fitStart" で、画像を ImageView の TOP に表示しています
<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:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin"
                tools:context=".MainActivity">

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="New Button"
        android:id="@+id/button"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"/>

    <ImageView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/imageView"
        android:layout_below="@+id/button"
        android:layout_alignParentStart="true"
        android:scaleType="fitStart"/>

</RelativeLayout>




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

Android Studio : GET(POST)メソッドで呼び出す際に、PHP 側のセッションを有効にする為に CookieManager を static で使用する



GET メソッドで URL を呼び出して、呼び出し先のセッションが有効である事を確認します

HttpGet.java

1) CookieManager を static で作成して呼び出し先のセッションを有効にします。
2) 実行時の引数に QueryString 用の Map を使用可能にして、必要なければ null を指定します
package app.lightbox.winofsql.jp.sessionid;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.Map;

/**
 * Created by lightbox on 2015/06/01.
 */
public class HttpGet {

	// セッションを有効にする為、常に存在させるよう static で定義
	private static CookieManager manager = null;

	// **********************************************
	// コンストラクタ
	// **********************************************
	public HttpGet() {
		if ( manager == null ) {
			manager = new CookieManager();
			// 以下をコメントにするとセッションは働きません
			CookieHandler.setDefault(manager);
		}
	}

	// **********************************************
	// 指定した URL へ 任意の charset で処理
	// **********************************************
	public String execute(String targetUrl,String targetCharset,Map<String,String> params) {

		StringBuffer web_data = new StringBuffer();

		try {
			// **********************************************
			// Query String の作成( 必要無ければ引数を null とする )
			// **********************************************
			String data = "";
			if ( params != null ) {
				Iterator<String> it =  params.keySet().iterator();
				String key = null;
				String value = null;
				while(it.hasNext()) {
					key = it.next().toString();
					value = params.get(key);
					if ( !data.equals("") ) {
						data += "&";
					}
					data += key + "=" + URLEncoder.encode(value, targetCharset) ;
				}
				if ( !data.equals("") ) {
					targetUrl = targetUrl + "?" + data;
				}
			}

			// **********************************************
			// インターネットへの接続
			// **********************************************
			// 読み込む WEB上のターゲット
			URL url = new URL(targetUrl);
			// 接続オブジェクト
			HttpURLConnection http = (HttpURLConnection)url.openConnection();
			// GET メソッド
			http.setRequestMethod("GET");
			// 接続
			http.connect();

			// **********************************************
			// ストリームとして読み込む準備
			// **********************************************
			// 以下読み込み3点セット InputStream / InputStreamReader / BufferedReader
			InputStream input_stream = http.getInputStream();
			// UTF-8 でリーダーを作成
			InputStreamReader input_stream_reader = new InputStreamReader(input_stream, targetCharset);
			// 行単位で読み込む為の準備
			BufferedReader buffered_reader = new BufferedReader(input_stream_reader);

			// **********************************************
			// 行の一括読み込み
			// **********************************************
			String line_buffer = null;
			// BufferedReader は、readLine が null を返すと読み込み終了
			while ( null != (line_buffer = buffered_reader.readLine() ) ) {
				web_data.append( line_buffer );
				web_data.append( "\n" );
			}

			// **********************************************
			// 接続解除
			// **********************************************
			http.disconnect();
		}
		catch(Exception e) {
			// 失敗
			System.out.println( e.getMessage());
		}
		return web_data.toString();
	}
}

関連する記事

Eclipse + JFace : HttpURLConnection で GET

session_id.php : セッション確認用の PHPコード

QueryString のテスト用に、$_GET['display'] をセットした場合、$_SERVER の内容を返すようにしています。

Android 側で複数回呼び出して、session_id の表示が毎回同じであればセッションが有効です
<?php
session_start();
header( "Content-Type: text/html; Charset=utf-8" );
header( "pragma: no-cache" );
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );
header( "Cache-control: no-cache" );

print session_id();

if ( $_GET['display'] == '環境' ) {
	print "\n";
	print_r( $_SERVER );
}

?>


HttpGet を呼び出して画面へ結果をセット

ボタンを配置し、その下に ScrollView を置いて、その中の TextView に表示します。( Android のインターネットアクセスなので、AsyncTask を使用しています )

呼び出し先は、localhost に用意しているので、Android の中からは 10.0.2.2 で呼び出します
( Special alias to your host loopback interface / i.e., 127.0.0.1 on your development machine )
		Button button = (Button) this.findViewById(R.id.button);
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

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

					// 非同期処理
					@Override
					protected String doInBackground(String... params) {
						// インターネットアクセス
						HttpGet hg = new HttpGet();
						Map map = new HashMap();
						map.put("display", "環境");
						String result = hg.execute(params[0], params[1],map);
						return result;
					}

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

						TextView tv = (TextView) MainActivity.this.findViewById(R.id.textView);
						tv.setText(s);

					}
				}.execute("http://10.0.2.2/basic/session_id.php", "utf-8");

			}
		});

※ インターネットアクセスには、AndroidManifest に android.permission.INTERNET が必要です

関連する記事

Android Studio で AsyncTask / Android Studio ならではの操作でコードを楽々作成

画面定義
<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:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin"
                tools:context=".MainActivity">

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="表示"
        android:id="@+id/button"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"/>

    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/scrollView"
        android:layout_below="@+id/button"
        android:layout_marginTop="16dp"
        android:layout_alignParentStart="true"
        android:background="@android:color/white">

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:id="@+id/textView"
            android:layout_below="@+id/button"
            android:layout_alignParentStart="true"
            android:background="#ffffffff"/>

    </ScrollView>>

</RelativeLayout>


CookieManager を使用した HttpPost クラス


package app.lightbox.winofsql.jp.sessionid;

import android.util.Log;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Created by lightbox on 2015/06/01.
 */
public class HttpPost {

	// セッションを有効にする為、常に存在させるよう static で定義
	private static CookieManager manager = null;

	// **********************************************
	// コンストラクタ
	// **********************************************
	public HttpPost() {
		if ( manager == null ) {
			manager = new CookieManager();
			// 以下をコメントにするとセッションは働きません
			buildCookieManager();
		}
	}

	// **********************************************
	// CookieManager 作成
	// **********************************************
	private void buildCookieManager() {
		manager = new CookieManager();
		CookieHandler.setDefault(manager);
	}

	// **********************************************
	// Cookie の表示テスト
	// **********************************************
	public void showCookie() {
		CookieStore store = manager.getCookieStore();
		List<HttpCookie> cookies = store.getCookies();
		for (int i = 0; i < cookies.size(); i++) {
			HttpCookie cookie = cookies.get(i);
			Log.i("lightbox", "Cookie[" + i + "]: " + cookie);
		}
	}

	// **********************************************
	// 指定した内容で処理
	// **********************************************
	public String execute(String targetUrl,String targetCharset,Map<String,String> params) {

		StringBuffer get_data = null;

		try {

			// ターゲット
			URL url = new URL(targetUrl);
			// 接続オブジェクト
			HttpURLConnection http = (HttpURLConnection)url.openConnection();
			http.setConnectTimeout(30000);
			http.setReadTimeout(30000);
			http.setDoInput(true);    // application/x-www-form-urlencoded がセットされる
			http.setRequestMethod("POST");

			OutputStreamWriter osw = new OutputStreamWriter(http.getOutputStream());
			BufferedWriter bw = new BufferedWriter(osw);

			Iterator<String> it =  params.keySet().iterator();
			String key = null;
			String value = null;
			String data = "";
			while(it.hasNext()) {
				key = it.next().toString();
				value = params.get(key);
				if ( !data.equals("") ) {
					data += "&";
				}
				data += key + "=" + URLEncoder.encode(value, targetCharset) ;
			}

			Log.i("lightbox", "送信内容 : " + data);

			bw.write(data);

			bw.close();
			osw.close();

			// 以下読み込み3セット
			InputStream i_stream = http.getInputStream();
			// UTF-8 でリーダーを作成
			InputStreamReader i_stream_reader = new InputStreamReader(i_stream, targetCharset);
			// 行単位で読み込む為の準備
			BufferedReader b_reader = new BufferedReader(i_stream_reader);

			// 行の一括読みこみ
			String line_buffer;
			get_data = new StringBuffer();
			// BufferedReader は、readLine が null を返すと読み込み終了
			while ( null != (line_buffer = b_reader.readLine() ) ) {
				get_data.append( line_buffer );
				get_data.append( "\n" );
			}

			// 接続解除
			http.disconnect();
		}
		catch(Exception e) {
			// 失敗
			System.out.println( e.getMessage());
		}
		return get_data.toString();
	}

}

http://toolbox.winofsql.jp/log.php は、セッションを使用した簡易掲示板のデモページです。

デモページ


タグ:Android Studio
posted by lightbox at 2015-06-01 16:27 | Android Studio | このブログの読者になる | 更新情報をチェックする

2015年05月24日


Android Studio で AsyncTask / Android Studio ならではの操作でコードを楽々作成

処理そのものは簡単ですが、Android Studio ならではの、操作が満載です。

MainActivity.java

1) ボタンクリック
2) AsyncTask のインスタンスから、execute
3) doInBackground で非同期処理
4) onPostExecute で UI スレッド処理
package app.lightbox.winofsql.jp.httpget;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;


public class MainActivity extends Activity {

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

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

				new AsyncTask<String, Void, String>() {
					@Override
					protected String doInBackground(String... params) {
						HttpGet hg = new HttpGet();
						String result = hg.execute(params[0],params[1]);
						return result;
					}

					@Override
					protected void onPostExecute(String s) {
						super.onPostExecute(s);

						TextView tv = (TextView) MainActivity.this.findViewById(R.id.textView);
						tv.setText(s);

					}
				}.execute("http://weather.livedoor.com/forecast/webservice/json/v1?city=270000", "utf-8");

			}
		});

	}

	@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_settings) {
			return true;
		}

		return super.onOptionsItemSelected(item);
	}
}

AsyncTask の概要
( AsyncTaskは、UIスレッドの適切かつ容易な使用を可能にします )

AsyncTask<String, Void, String> の一つ目の String は、doInBackground の引数の型です。実際には、文字列の配列で、doInBackground へ引き渡されます。最後の String は、onPostExecute の引数の型で、doInBackground の戻り値でもあります。つまり、非同期スレッドから、UI スレッドへの引渡しが String だと言う事です。

仕様として、インターネットへのアクセスは非同期処理で実行する必要があるので、doInBackground で行いますが、そこから直接画面の操作ができないので、onPostExecute で処理する事になります。onPostExecute で必要な情報は、基本的には doInBackground の戻り値でまかなうようになっています。

さらに、真ん中の Void は、onProgressUpdate と言う実行中の進行状況の処理に使われますが、ほぼ使わないので Void にしています。面倒ならば、AsyncTask<String, String, String> でもいいです。

関連情報

▼ HttpGet クラスについてはこちら
Eclipse + JFace : HttpURLConnection で GET

▼ 呼び出している API についてはこちら
お天気Webサービス仕様 - Weather Hacks - livedoor 天気情報

▼ API をもっとテストしやすくする情報はこちら
Livedoor の お天気Webサービス API は、JSON のテストするのに重宝しています。

インターネットにアクセスするので、AndroidManifest を変更



<u と入力すると候補が表示されます( CTRL + SPACE でも表示されます )



uses-permission を選択すると、次は属性値の候補



終了タグ無しで閉じたい( つまり入力の完了 )は、CTRL + SHIFT + ENTER です。



入力が完了しました

styles.xml (v21) の変更 

最小バージョンを Android 5.1.1 にしていると、画面のタイトル部分が白くなってコンテンツとの境界がわかり難いので変更します。最小バージョンを Android 4 にしていると、そうはならないと思います。( 古いほうに合わされるようです )

▼ AndroidManifest の要領で以下のように変更します。


ボタンイベントの作成 

基本的に問題は『ALT + Enter』で解決して行きます。ほぼ勝手に import とかしてくれますが、候補が出る事もあります。



上では、まだ Button をタイプしたけれど、import はされていません。しかし、ALT + Enter で import されます。



上の状態では、import は終わっていますが、お約束のキャストがされていません。左に表示されているアイコンを開くと、キャストの選択肢が現れます。

▼ イベント作成


イベントの setOnClickListener を候補から選択します。



引数の中で new と入力すると、候補が現れて、選択すると自動的に処理用のブロックが作成されます



記述完了です


AsyncTask の入力も基本的には同じようなものです。ただ、@Override の選択は、ブロック内の挿入可能箇所で、CTRL + O で表示されるダイアログで選択します。




さて、画面作成



ボタンの下に ScrollView を配置して、その中に TextView を使って長い JSON の文字列をスクロールして表示できるようにしています
<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:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin"
                tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Button"
        android:id="@+id/button"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"/>

    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/scrollView"
        android:layout_alignParentStart="false"
        android:layout_below="@+id/button"
        android:fillViewport="false">
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:id="@+id/textView"
            android:textSize="16sp"/>

    </ScrollView>
</RelativeLayout>

ボタン

ボタンは、デザイナで適当に配置して、wrap_content( 表示内容に合わせる ) です。

ScrollView

その下にデザイナから ScrollView を配置すると、android:layout_below が設定されますが、これはプロパティからは layout:alignComponent の中で設定されています。実際の内容と、Android Studio のプロパティ表現が違う場合があるので注意です。さらに、プロパティからは、fill_parent( 親に対して最も大きく ) を設定して、ボタンの下いっぱいのスクロールビューを完成させます。

TextView

デサイナからはうまく入らないかもしれないですが、エディタで ScrollView 内に配置すればいいと思います。横幅のみ、fill_parent にして、高さは、wrap_content です。

テキストサイズは数字を入力すると、Android Studio の仕様だと思いますが、単位が dp になります。しかし、結局左側に問題解決用のアイコンが出て、sp に変更するようになります。


タグ:Android Studio
posted by lightbox at 2015-05-24 21:49 | Android Studio | このブログの読者になる | 更新情報をチェックする

2015年05月22日


Android Studio : 現在の時間を ライフサイクルのアクティブな時に処理される onStart で表示する( 5.1.1 )

 
 

実行後、現在の時刻を表示します。
アプリがアクティブになった場合にも、その時の現在の時刻を表示します
MainActivity.java
package app.lightbox.winofsql.jp.whattime;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;


public class MainActivity extends Activity {

	// *************************************
	// 自動作成( onCreate )
	// *************************************
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

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

	}

	// *************************************
	// CTRL + O から追加( onStart, onStop, onPause )
	// *************************************
	@Override
	protected void onStart() {
		super.onStart();

		TimeZone tz = TimeZone.getTimeZone("Asia/Tokyo");
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("HH'時'mm'分'ss'秒'");
		sdf.setTimeZone(tz);
		String time = sdf.format(date);
		Log.i("lightbox", time);

		TextView tv = (TextView) this.findViewById(R.id.text);
		tv.setText(time);

	}

	@Override
	protected void onStop() {
		super.onStop();

		Log.i("lightbox", "画面が切り替わりました");

	}

	@Override
	protected void onPause() {
		super.onPause();

		Log.i("lightbox", "画面が切り替わろうとしています");

	}

	// *************************************
	// 自動作成( onCreateOptionsMenu )
	// *************************************
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.menu_main, menu);
		return true;
	}

	// *************************************
	// 自動作成( onOptionsItemSelected )
	// *************************************
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();

		if (id == R.id.action_settings) {

			Log.i("lightbox", "メニュー");

			// 第二画面へ移動
			Intent intent = new Intent(MainActivity.this, NextPage.class);
			startActivity(intent);

			return true;
		}

		return super.onOptionsItemSelected(item);
	}
}

TimeZone

エミュレータで、TimeZone.getDefault() は正しい時間がとれませんでした。確実に、『Asia/Tokyo』を指定するのが良いと思います。

onStart

onCreate では、アプリの開始時にしか実行されないので画面が切り替わって戻って来た時にもその時点での時間を表示する為に、onStart で実行しています。

Activity のライフサイクル( Android Developers )

画面の切り替えは、エミュレータのコントロールで出来ますが、内部のメニューから次の画面に遷移するようにして状況を確認できるようにしています。次画面は、Activity を継承したクラスの onCreate で初期画面( 元々のHello World ) を表示しています。



Activity の追加( AndroidManifest )

その為に必要な Activity の定義は、AndroidManifest にも追加しています。その際の入力は、Android Studio が、.NextPage を候補として表示してくれました。



Override

Override するメソッドは、必要なところにカーソルを置いて、CTRL + O でダイアログが表示されるので、とても便利です。



Project Structure





 

Min SDK Version と Max SDK Version は同じで一番新しいものを、プロジェクト作成時に指定しています。ただ、これの影響でスキンのタイトルが白くなるので、styles.xml( v21 ) の内容を変更しています。
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
    </style>
</resources>

画面の TextView
<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:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin"
                tools:context=".MainActivity">

    <TextView
        android:id="@+id/text"
        android:text="@string/hello_world"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textSize="40dp"
        android:textColor="#ff000000"/>

</RelativeLayout>

プロジェクト作成時の HelloWorld をそのまま使っていますが、テキストを中央に表示する為に、以下のプロパティで both を指定します。



あとは、テキストサイズと、テキストの色と id をプロパティから変更しています



タグ:Android Studio
posted by lightbox at 2015-05-22 19:39 | Android Studio | このブログの読者になる | 更新情報をチェックする

2015年04月19日


Android Studio でボタンをクリックして トースト(Toast) を表示する / onClick プロパティと Button へのイベント登録( Listener )

Android でボタンに対する処理を作成したい場合、Hello World! 的には Toast を表示するのが最もてっとり早く動作の確認をする事ができます。

Android Studio で大きなボタンを一つ作成

ボタンに対するイベントの登録方法は、一般的には Button(TextView) のメソッドを使って Listener を作成しますが、クリックだけに関して言うと、onClick プロパティを使用できます。

Android Studio でボタンをクリックして トースト(Toast) を表示する



public void メソッド(View view)

このような形式のメソッド( 本来の setOnClickListener 用 )を事前に作成しておくと、プロパティの設定でそのメソッドが表示されるので、選択するたけでコード作成が完了します。

▼ CTRL + O で既存のコードを利用する

setContentView のブロックが作成されるので、clickButton に変更して中身を作成します。

以下は具体的なコードです
	public void clickButton(View view) {
		Toast toast;
		toast = Toast.makeText(
				MainActivity.this,
				"こんにちは",
				Toast.LENGTH_LONG
		);
		toast.show();
		Log.i("lightbox", "こんにちは");

	}

その他のイベントに対しては、以下のようにして Listener を作成します

setOnLongClickListener( 長押し )
		// ボタン
		Button button;
		// 作成したボタンより ID でインスタンスをセット
		button = (Button)MainActivity.this.findViewById(R.id.button);

		// 長押し用のイベントを作成
		button.setOnLongClickListener(new View.OnLongClickListener() {
			@Override
			public boolean onLongClick(View v) {
				Log.i("lightbox", "長押し");
				return false;
			}

		});


setOnTouchListener( タッチのダウンとアップ )

上のコードで定義した button に対して新たに setOnTouchListener を実行します
		// タッチ用のイベントを作成
		button.setOnTouchListener(new View.OnTouchListener() {
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				int action = event.getAction();
				switch(action) {
					case MotionEvent.ACTION_DOWN:
						Log.i("lightbox", "ACTION_DOWN");
						break;
					case MotionEvent.ACTION_UP:
						Log.i("lightbox", "ACTION_UP");
						break;
				}

				return false;
			}

		});


logcat のフィルターを作成





▼ フィルタの名前を任意に変更


▼ タグでフィルタする






posted by lightbox at 2015-04-19 17:00 | Android Studio | このブログの読者になる | 更新情報をチェックする

2015年04月12日


Google スプレッドシートで、Android Studio の重要な操作のまとめ

HTML で表を作るより、Google スプレッドシートで作って埋め込んだほうが簡単で便利でメンテナンス性が高くて使いまわしができて印刷も PDF でできるという事に最近気が付きました。

印刷用の PDF を取得するには、こちらのリンクから開いて下さい



posted by lightbox at 2015-04-12 00:45 | 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 ドロップシャドウの参考デモ
PHP正規表現チェッカー
Google Hosted Libraries
cdnjs
BUTTONS (CSS でボタン)
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり