SQLの窓

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 1 | このブログの読者になる | 更新情報をチェックする

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 1 | このブログの読者になる | 更新情報をチェックする

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 1 | このブログの読者になる | 更新情報をチェックする

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 1 | このブログの読者になる | 更新情報をチェックする

2015年07月09日


Tools クラスの Activity を必要とする static メソッドをインスタンスメソッドに書き換えた AndroidTools クラス

tools.jar に追加しました

tools.jar

(Tools/Tools2/TestArrayAdapter/AndroidTools)

当たり前ですが、インスタンスにすると static メソッドに渡していた Activity をインスタンス内で保持できるので、引数が一つ減る事になります。しかし、これで各処理に Activity が密接に結びついている事が解ってかつ static と インスタンスの違いも明確になる『初心者訓練用』の色が明確になったテストライブラリセットとなりました。

AndroidTools.java
package jp.android.work;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Serializable;

/**
 * Created by lightbox on 2015/07/06.
 */
public class AndroidTools {
	private Activity context = null;

	public AndroidTools(Activity context) {
		this.context = context;
	}

	// 確認 メッセージボックス( メッセージのみ )
	public void messageBox(String message, final Tools.OnMessageBoxListener listener) {

		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) {
					if (listener != null) {
						listener.onMessageBoxYesListener();
					}
				}
			});

		alertDialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() {
			@Override
			public void onCancel(DialogInterface dialog) {
				if (listener != null) {
					listener.onMessageBoxNoListener();
				}
			}
		});

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

	}

	// YES or NO メッセージボックス( タイトルとメッセージ両方指定)
	public void messageBox(String title, String message, final Tools.OnMessageBoxListener listener) {

		// ダイアログ作成用オブジェクト作成
		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) {
					if (listener != null) {
						listener.onMessageBoxYesListener();
					}
				}
			});

		// NO ボタン作成
		alertDialogBuilder.setNegativeButton("NO",
			new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					if (listener != null) {
						listener.onMessageBoxNoListener();
					}
				}
			});

		alertDialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() {
			@Override
			public void onCancel(DialogInterface dialog) {
				if (listener != null) {
					listener.onMessageBoxNoListener();
				}
			}
		});

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

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

		// 対応するアプリが無い
		Tools.longToast(
			context,
			"ブラウザを呼び出せません"
		);
		return;

	}

	// 短いトースト
	public void shortToast(String message) {
		Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
	}
	// 長いトースト
	public void longToast(String message) {
		Toast.makeText(context, message, Toast.LENGTH_LONG).show();
	}

	// 単純なアクティビテイの呼び出し
	public void callActivity( Class<?> cls, int requestCode) {
		// Activity呼び出し
		Intent intent = new Intent(context, cls);
		context.startActivityForResult(intent, requestCode);

	}

	// オブジェクトを引き渡しての呼び出し
	public void callActivity( Class<?> cls, int requestCode, String name, Serializable data) {
		// Activity呼び出し
		Intent intent = new Intent(context, cls);
		intent.putExtra(name,data);
		context.startActivityForResult(intent, requestCode);
	}

	// OK で終了
	public void okFinish(Intent intent) {
		context.setResult(context.RESULT_OK, intent);
		context.finish();
	}
	// CANCELED で終了
	public void cancelFinish(Intent intent) {
		context.setResult(context.RESULT_CANCELED, intent);
		context.finish();
	}


	// Private な アプリデータの保存
	public void putPreferences(String target, String key, String data) {
		SharedPreferences sp = context.getSharedPreferences(target, context.MODE_PRIVATE);
		SharedPreferences.Editor editor = sp.edit();
		editor.putString(key, data);
		editor.commit();

	}

	// Private な アプリデータの読み込み
	public String getPreferences( String target, String key) {
		SharedPreferences sp = context.getSharedPreferences(target, context.MODE_PRIVATE);
		return sp.getString(key, "_nodata");

	}

	// 汎用型コントロール参照
	public <T> T useView(int id) {
		T view = null;
		view = (T) context.findViewById(id);
		return view;
	}

	// ボタン用コントロール参照( イベント引数付き )
	public Button useButton(int id, View.OnClickListener listener) {
		Button button = (Button) context.findViewById(id);
		if (listener != null) {
			button.setOnClickListener(listener);
		}
		return button;
	}

	// ボタン用コントロール参照( イベントなし )
	public Button useButton(int id) {
		Button button = (Button) context.findViewById(id);
		return button;
	}

	// テキストファイル( プライベート書き込み )
	public void saveText(String name, String text) throws Exception {

		FileOutputStream outStream = context.openFileOutput(name, android.content.Context.MODE_PRIVATE);
		OutputStreamWriter writer = new OutputStreamWriter(outStream);
		writer.write(text);
		writer.flush();
		writer.close();

	}

	// テキストファイル( 読み込み )
	public String loadText(String name) throws Exception {

		FileInputStream fis = context.openFileInput(name);
		int size = fis.available();
		InputStreamReader isr = new InputStreamReader(fis);
		BufferedReader br = new BufferedReader(isr);
		StringBuffer all_string = new StringBuffer(size);
		String str = null;
		while ((str = br.readLine()) != null) {
			// 初回以外は前に改行を挿入する
			if (!all_string.toString().equals("")) {
				all_string.append("\n");
			}
			all_string.append(str);
		}
		br.close();

		return all_string.toString();

	}
}


関連する記事



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

2015年07月02日


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

ちゃんと実装する場合はそれなりのコードに差し替えたほうがいいと思いますが、とにかくエミュレータ起動した時点で時間のかかる作業が確定してしまうので、できるかぎり楽したい場合に使えます。

tools.jar

(Tools/Tools2/TestArrayAdapter)

とりあえずテストしたい場合、jar で使えるのなら簡単に済みます。libs フォルダに保存して、右クリックから Add as Library を実行すると参照可能になります。

あるいは、ソースコードをそのまま作成してカスタマイズするといいと思います。

1) Tools.httpGet

インターネットからテキスト情報を取得する為の最低限の実装です。後述の、callHttpGet では、AsyncTask 内から呼び出しています。

2) Tools.callHttpGet

そもそも、インターネットアクセスをしたいから AsyncTask を使う事になるのですが、目的は文字列が欲しいだけなので、最低限のコード記述で処理できるようにしています。

AsyncTask では、onPostExecute で UI スレッドの処理を行いますが、その中から引数のインターフェイス(Tools.OnAsyncTaskListener)内のメソッド(onAsyncTaskListener)を呼び出して、呼び出し側の記述を簡潔にする事ができます。

▼ 呼び出し方法
Tools.callHttpGet(
	"目的の URL",
	"utf-8",
	new Tools.OnAsyncTaskListener() {
		@Override
		public void onAsyncTaskListener(String s) {

			// s が欲しかった文字列
			// ここで UI スレッドの処理を行います

		}
	}
);


3) Tools.callBrowser

URL を扱う処理で良く使う事になる、ACTION_VIEW によるブラウザの呼び出しです

4) Tools.messageBox

YES か NO、または単純に OK だけのいずれかのメッセージボックスを引数の種類で差し替え可能な簡単な使いやすいものです。
// 単純確認メッセージボックス
Tools.messageBox(MainActivity.this, "こんにちは", new Tools.OnMessageBoxListener() {
	@Override
	public void onMessageBoxYesListener() {

	}

	@Override
	public void onMessageBoxNoListener() {

	}
});

// 単純確認メッセージボックス(イベントなし)
Tools.messageBox(MainActivity.this, "こんにちは", null);

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

	}

	@Override
	public void onMessageBoxNoListener() {

	}
});



5) Tools.callActivity

単純なアクティビテイの呼び出しです。putExtra を使わずに単に Activity を呼び出す時に使用します

6) Tools.putPreferences

MODE_PRIVATE でデフォルトでは無い データを書き込むのに使用します。書き込んだデータは、アプリ内で読み込む事ができます。

7) Tools.getPreferences

Tools.putPreferences で書き込んだデータを読み込む事ができます。

8) Tools.getDateString

現在の日付・時間文字列をフォーマット文字列を渡して取得します。

9) Tools.shortToast / Tools.longToast

Toast を表示します


tools.java
package jp.android.work;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.TimeZone;

/**
 * Created by lightbox on 2015/06/11.
 */
public class Tools {

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

	public static void callHttpGet(String url, String charset, final OnAsyncTaskListener listener) {
		new AsyncTask<String, Void, String>() {

			// 非同期処理
			@Override
			protected String doInBackground(String... params) {
				String result = null;
				result =
					Tools.httpGet(
						params[0],
						params[1],
						null
					);

				return result;
			}

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

				listener.onAsyncTaskListener(json);

			}
		}.execute(url, charset);
	}

	public interface OnMessageBoxListener {
		abstract public void onMessageBoxYesListener();

		abstract public void onMessageBoxNoListener();
	}

	// 確認 メッセージボックス( メッセージのみ )
	public static void messageBox(Activity context, String message, final OnMessageBoxListener listener) {

		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) {
					if (listener != null) {
						listener.onMessageBoxYesListener();
					}
				}
			});

		alertDialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() {
			@Override
			public void onCancel(DialogInterface dialog) {
				if (listener != null) {
					listener.onMessageBoxNoListener();
				}
			}
		});

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

	}

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

		// ダイアログ作成用オブジェクト作成
		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) {
					if (listener != null) {
						listener.onMessageBoxYesListener();
					}
				}
			});

		// NO ボタン作成
		alertDialogBuilder.setNegativeButton("NO",
			new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					if (listener != null) {
						listener.onMessageBoxNoListener();
					}
				}
			});

		alertDialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() {
			@Override
			public void onCancel(DialogInterface dialog) {
				if (listener != null) {
					listener.onMessageBoxNoListener();
				}
			}
		});

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

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

		// 対応するアプリが無い
		Tools.longToast(
			context,
			"ブラウザを呼び出せません"
		);
		return;

	}

	// 短いトースト
	public static void shortToast(Activity context,String message) {
		Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
	}
	// 長いトースト
	public static void longToast(Activity context,String message) {
		Toast.makeText(context, message, Toast.LENGTH_LONG).show();
	}

	// 単純なアクティビテイの呼び出し
	public static void callActivity(Activity context, Class<?> cls, int requestCode) {
		// Activity呼び出し
		Intent intent = new Intent(context, cls);
		context.startActivityForResult(intent, requestCode);

	}

	// Private な アプリデータの保存
	public static void putPreferences(Activity context, String target, String key, String data) {
		SharedPreferences sp = context.getSharedPreferences(target, context.MODE_PRIVATE);
		SharedPreferences.Editor editor = sp.edit();
		editor.putString(key, data);
		editor.commit();

	}

	// Private な アプリデータの読み込み
	public static String getPreferences(Activity context, String target, String key) {
		SharedPreferences sp = context.getSharedPreferences(target, context.MODE_PRIVATE);
		return sp.getString(key, "_nodata");

	}

	// 日付・時間文字列取得
	public static String getDateString(String format) {

		TimeZone tz = TimeZone.getTimeZone("Asia/Tokyo");
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		sdf.setTimeZone(tz);
		String result = sdf.format(date);

		return result;

	}

	// 指定した URL へ 任意の charset で処理
	public static String httpGet(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();
	}
}

関連する記事


posted by lightbox at 2015-07-02 23:57 | 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 終わり