SQLの窓

2016年12月05日


Android Studio で Twitter4J で ListView に検索結果を表示する



Twitter4J

非同期処理は、常に TwitterListener を作成して、AsyncTwitter からの実行を リスナー側で受けるという形になります。( 今回の場合、searched(QueryResult queryResult) で受けて、search(Query query) で呼び出す )

関連する記事

Android Studio で Twitter4J で画像付きツイート

注意する事
1) トークンの保存場所が、app\src\main\resources の twitter4j.properties

2) TwitterListener で受けたところから画面にアクセスするには、UI スレッドに対する考慮が必要
   ( runOnUiThread メソッドが簡単 )

3) ライブラリは twitter4j-core-4.0.4.jar と twitter4j-async-4.0.4.jar をコピーして core のみ Gradle に登録
MainActivity
public class MainActivity extends AppCompatActivity {

    // リストビュー
    private MyArrayAdapter adapter;
    private ListView listview;

    // 日付データ用
    private Calendar cal;
    private SimpleDateFormat sf;

    // Twitter 用
    private TwitterListener listener;
    private AsyncTwitter asyncTwitter;
    private ArrayList<TwitterData> twitterData;

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


        // アダプタを作成
        adapter = new MyArrayAdapter(MainActivity.this,R.layout.list_item);

        // リストビューの取得
        listview = (ListView) MainActivity.this.findViewById(R.id.listView);
        // リストビューにデータを表示
        listview.setAdapter(adapter);

        Button buttonSearch = (Button) MainActivity.this.findViewById(R.id.buttonSearch);
        buttonSearch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                adapter.clear();
                loadDataList();
            }
        });

        cal = Calendar.getInstance();
        sf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        // リスナーを作成する
        listener = new TwitterAdapter() {
            @Override
            public void searched(QueryResult queryResult) {

                twitterData = new ArrayList<TwitterData>();
                List<Status> list = queryResult.getTweets();

                Iterator<Status> it = list.iterator();
                while( it.hasNext() ) {

                    Status statudData = it.next();

                    twitterData.add(new TwitterData(
                        // ScreenName と Name
                        String.format("%s : %s", statudData.getUser().getScreenName(), statudData.getUser().getName()),
                        sf.format(statudData.getCreatedAt()),
                        statudData.getText()));

                }

                // UI スレッドで アダプタにデータをセット
                MainActivity.this.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        adapter.addAll(twitterData);
                    }
                });


            }

            @Override
            public void onException(TwitterException te, TwitterMethod method) {

                Log.i("lightbox", "onException");
                te.printStackTrace();

            }

        };

        // リスナーを登録する
        AsyncTwitterFactory factory = new AsyncTwitterFactory();
        asyncTwitter = factory.getInstance();
        asyncTwitter.addListener(listener);

    }

    // *****************************************
    // Twitter から リストビューにデータを表示
    // *****************************************
    public void loadDataList() {

        EditText editSearch = (EditText) MainActivity.this.findViewById(R.id.editSearch);
        String searchString = editSearch.getText().toString();

        Query query = new Query(searchString);
        asyncTwitter.search(query);


    }
}


MyArrayAdapter
public class MyArrayAdapter extends ArrayAdapter<TwitterData> {

	// コンストラクタで渡された画面の保存
	private int mResource;
	// 画面作成用
	LayoutInflater mInflater;

	public MyArrayAdapter(Context context, int resource) {
		super(context, resource);
		// ArrayAdapter でも、このようにして保存して利用してます
		mResource = resource;
		mInflater = LayoutInflater.from(context);
	}

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

		if (convertView == null) {
			convertView = mInflater.inflate(mResource, parent, false);
		}

		// アダプターより行データを取得
		TwitterData twitterData = MyArrayAdapter.this.getItem(position);

		// 画面にデータをセット
		TextView tv;

		// キー
		tv = (TextView) convertView.findViewById(R.id.textKey);
		tv.setText(twitterData.getScreenName());

		tv	= (TextView) convertView.findViewById(R.id.textItem1);
		tv.setText(twitterData.getStringDate());

		tv = (TextView) convertView.findViewById(R.id.textItem2);
		tv.setText(twitterData.getText());

		// 行の画面をシステムに返す
		return convertView;
	}

}


TwitterData
public class TwitterData {

	private String stringDate;
	private String screenName;
	private String text;

	public TwitterData(String screenName, String stringDate, String text) {
		this.screenName = screenName;
		this.stringDate = stringDate;
		this.text = text;
	}

	public String getScreenName() {
		return screenName;
	}

	public void setScreenName(String screenName) {
		this.screenName = screenName;
	}

	public String getStringDate() {
		return stringDate;
	}

	public void setStringDate(String stringDate) {
		this.stringDate = stringDate;
	}

	public String getText() {
		return text;
	}

	public void setText(String text) {
		this.text = text;
	}
}




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

2016年11月29日


Android Studio で Twitter4J で画像付きツイート

Twitter API は使用回数制限があるので、現在それほど魅力的なものではありませんが、BOT 的な使い道や、ログとして使うのならば、Java のサンプルとしては魅力的ではあると思います。特に、Twitter4J を使うと、いとも簡単にツイートは可能なのです。

ダウンロードしてインストール

ダウンロードリンクは Twitter4J にあります。解凍して Android Studio のプロジェクトの libs に twitter4j-core-4.0.4.jar と twitter4j-async-4.0.4.jar をコピーします。

この後、メニューから add as library でもかまいませんが、app の gradle が以下のようになればいいです
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:22.2.1'
    compile files('libs/twitter4j-core-4.0.4.jar')

}

twitter4j-async-4.0.4.jar は非同期処理に必要ですが、gradle に記述する必要はありません。

ちなみに、他のライブラリを全てコピーするとエラーになります(たぶん内容が重複してるとかかもしれません)

設定ファイル

twitter4j.properties が必要です。場所は app\src\main\resources です。

エクスプローラから作成してテキストファイルを作成して、自分の Twitter から、右下の『開発』をクリックして、My Apps で アプリを作るか既存のアプリからトークンをコピペして twitter4j.properties に書き込むだけです

このへんは、Twitter の API を使った人なら常識の範疇ですが、初めてでもインターネットでたくさん紹介されているので問題は無いはずです。
debug=true
oauth.consumerKey=*********************
oauth.consumerSecret=******************************************
oauth.accessToken=**************************************************
oauth.accessTokenSecret=******************************************

実行コード

画像は、あらかじめ用意しておいてパスを取得しておきます。カメラを使用するなら、ギャラリーに登録までした後のパスをそのまま使えばいいし、一時的に使うだけなら後から削除でいいですね(もちろん終了イベント後に削除)

カメラを起点とした画像データの処理 / Android

配布ページのサンプルコードは、タイプミスがあるので注意

▼ 実行
				// 入力テキストを取得
				EditText editText = (EditText) rootView.findViewById(R.id.tweetText);
				String text = editText.getText().toString();

				// 入力なしなら、テキストを作成
				if ( text.trim().equals("") ) {
					Calendar cal = Calendar.getInstance();
					SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
					text = String.format( "%s %s", sf.format(cal.getTime()), "に Android からツイートしました");
				}
				// デバッグ用
				Log.i("lightbox",text);

				// 非同期のリスナーを作成
				TwitterListener listener = new TwitterAdapter() {
					@Override
					public void updatedStatus(Status status) {
						System.out.println("Successfully updated the status to [" +
							status.getText() + "].");
					}

					@Override
					public void onException(TwitterException te, TwitterMethod method) {
						if (method == TwitterMethod.UPDATE_STATUS) {
							te.printStackTrace();
						} else {
							throw new AssertionError("Should not happen");
						}
					}

				};

				// 非同期の準備
				AsyncTwitterFactory factory = new AsyncTwitterFactory();
				AsyncTwitter asyncTwitter = factory.getInstance();
				// リスナーを登録
				asyncTwitter.addListener(listener);

				// StatusUpdate ノインスタンスを作成
				// ※ 画像ツイートに必要
				StatusUpdate statusUpdate = new StatusUpdate(text);
				// 画像のファイルオブジェクト
				File file = new File( mainActivity.getimagePath() );
				// 画像を設定
				statusUpdate.media(file);

				// ツイート
				asyncTwitter.updateStatus(statusUpdate);


何故か非同期のサンプルが世の中に無かったですが、Android でツイートしようなんて思う人がたぶんもういないのでしょう( 殆どメリットや魅力が無いので )

updatedStatus イベント内の注意

この中は UI スレッドでは無いようなので、成功の後画面処理する場合は、適宜必要な UI スレッド呼び出しを実装します。

runOnUiThread( android.app.Activity )
UIスレッドで指定されたアクションを実行します。 現在のスレッドがUIスレッドの場合、アクションは直ちに実行されます。 現在のスレッドがUIスレッドでない場合、アクションはUIスレッドのイベントキューにポストされます。
posted by lightbox at 2016-11-29 20:10 | 2016 Android Studio | このブログの読者になる | 更新情報をチェックする

2016年11月08日


内部ストレージを起点とした画像データの処理 / Android


Uri => File => FileInputStream => Bitmap というルートもあります

ストレージから画像を ImageView に表示する

※ ファイル名やフォルダは既知である事を前提としています。
// *************************************
// パスの取得
// *************************************
String path = String.format(
	"%s/firebase/20161108_011922.jpg",
	Environment.getExternalStorageDirectory().getPath()
);

// *************************************
// ImageView
// *************************************
Bitmap image = null;
FileInputStream fis;
try {
	fis	= new FileInputStream(path);
	image = BitmapFactory.decodeStream(fis);
} catch (Exception e) {
	e.printStackTrace();
}

// 画像データが取得されていたら
if ( image != null ) {
	// imageView へ表示
	imageView.setImageBitmap(image);
}

パスから FileInputStream を作成して、BitmapFactory.decodeStream でビットマップに変換します。画像表示は ImageView の setImageBitmap です。

Bitmap を使用した画像の回転
// バイト配列を Bitmap に変換( 内容は既に jpeg )
Bitmap image1 = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
// 90度回転
int width = image1.getWidth();
int height = image1.getHeight();
Matrix matrix = new Matrix();
matrix.postRotate (90);
Bitmap image2 = Bitmap.createBitmap (image1, 0, 0, width, height, matrix, true);
(※参考) ImageView を使用して画像を回転

ギャラリーから画像を ImageView に表示する

ギャラリーから返されるのは、Uri です。Uri を直接使用する setImageURI を使用します。

setImageURI(null) は、キャッシュされないようにする効果が期待できると想像しています。
// ギャラリーの呼び出し
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, 10);		// 10 は任意

// -------------------------------

// ギャラリーからの戻り
// ※ onActivityResult は MainActivity のメソッドです
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	super.onActivityResult(requestCode, resultCode, data);

	if (requestCode == 10 && resultCode == Activity.RESULT_OK) {
		if (data != null) {
			Uri uri = data.getData();
			imageView.setImageURI(null);
			imageView.setImageURI(uri);
		}

	}
}

(※参考) Exif情報を利用して、画像を回転

Firebase の putFile と putStream

どちらも大差はありません。アップロード前に加工が必要なら、putStream になるかもしれませんし、コードを簡潔にするには、putFile だと例外処理がありませんから、キャッシュに書き込んでからアップロードしてもいいかもしれません。
// *************************************
// FireBase putFile
// *************************************
Uri file = Uri.fromFile(new File(path));
imageRef = storageRef.child(file.getLastPathSegment());
UploadTask uploadTask = imageRef.putFile(file);
uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
	@Override
	public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {

		Log.i("lightbox","アップロードに成功しました");
		long size = taskSnapshot.getMetadata().getSizeBytes();
		Log.i("lightbox",String.format("サイズ : %d",size));

	}
}).addOnFailureListener(new OnFailureListener() {
	@Override
	public void onFailure(@NonNull Exception e) {

		Log.i("lightbox","アップロードに失敗しました");
	}
});


// *************************************
// FireBase putStream
// *************************************
Uri file = Uri.fromFile(new File(path));
imageRef = storageRef.child(file.getLastPathSegment());
InputStream stream = null;
try {
	stream = new FileInputStream(new File(path));
} catch (Exception e) {
	e.printStackTrace();
}
if ( stream != null ) {
	UploadTask uploadTask = imageRef.putStream(stream);
	uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
		@Override
		public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {

			Log.i("lightbox", "アップロードに成功しました");
			long size = taskSnapshot.getMetadata().getSizeBytes();
			Log.i("lightbox", String.format("サイズ : %d", size));

		}
	}).addOnFailureListener(new OnFailureListener() {
		@Override
		public void onFailure(@NonNull Exception e) {

			Log.i("lightbox", "アップロードに失敗しました");
		}
	});
}




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

2016年11月07日


カメラを起点とした画像データの処理 / Android

全てのソースコードはこちら
▼ テスト用のカメラ処理はこちら
Android : 画像関連のテスト用カメラアプリ




byte 配列を Bitmap に変換して ImageView に表示する

1) BitmapFactory.decodeByteArray => bitmap
2) imageView.setImageBitmap(bitmap);
// バイト配列を Bitmap に変換( 内容は既に jpeg )
Bitmap image1 = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
// 90度回転
int width = image1.getWidth();
int height = image1.getHeight();
Matrix matrix = new Matrix();
matrix.postRotate (90);
Bitmap image2 = Bitmap.createBitmap (image1, 0, 0, width, height, matrix, true);

// 環境依存(これが無いと落ちるデバイスもある)
imageView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

// imageView へ表示
imageView.setImageBitmap(image2);


byte 配列を内部ストレージに保存

byte[] data を FileOutputStream で write
// 内部ストレージに保存
FileOutputStream jpg;
try {
	jpg = new FileOutputStream(imagePath);
	jpg.write(data);
	jpg.close();

	// ギャラリーに反映
	MediaScannerConnection.scanFile(
		MainActivity.this,
		new String[]{imagePath},
		new String[]{"image/jpg"},
		null);

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

保存用の内部ストレージのパスの取得
	// *************************************
	// 保存用内部ストレージパス取得
	// *************************************
	private String buildPath(String folder, String ext) {

		String path = null;

		// 内部ストレージにフォルダを作成
		String TargetRootDir
			= String.format("%s/%s",Environment.getExternalStorageDirectory().getPath() , folder );
		File file = new File(TargetRootDir);
		// ディレクトリ初期作成
		if (!file.exists()) {
			if (file.mkdir() == false) {
				Log.i("lightbox", "ディレクトリを作成できませんでした");
				return path;
			}
		}

		// 画像保存パス
		Calendar cal = Calendar.getInstance();
		SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss");
		path = String.format("%s/%s.%s", TargetRootDir,sf.format(cal.getTime()),ext);

		Log.i("lightbox",path);

		return path;

	}


Firebase よりbyte 配列で画像を取得する

本来は、File か ストリームで取得しますが(メモリ効率)、カメラの画像が byte 配列なので、putBytes でそのままアップロードが可能です。なので、その反対である getBytes のサンプルです

putBytes のサンプルはこちら

Stream でダウンロードして ImageView に表示する
ファイルとしてダウンロードしてギャラリーに保存する
FirebaseStorage storage;
StorageReference storageRef;
StorageReference imageRef;

storage = FirebaseStorage.getInstance();
storageRef = storage.getReferenceFromUrl("gs://freebase-654b7.appspot.com/image");

imageRef = storageRef.child("sworc.png");
// ダウンロード
imageRef.getBytes(MainActivity.MAX_SIZE_BYTE)
	.addOnSuccessListener(new OnSuccessListener<byte[]>() {
		@Override
		public void onSuccess(byte[] bytes) {
			if (bytes != null) {
				Bitmap image = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
				imageView.setImageBitmap(image);
			}
		}
	}).addOnFailureListener(new OnFailureListener() {
	@Override
	public void onFailure(@NonNull Exception e) {
		Log.i("lightbox","データ取得に失敗しました");
		e.printStackTrace();
		Toast.makeText(
			MainActivity.this,
			"Firebase からのダウンロードに失敗しました",
			Toast.LENGTH_LONG)
			.show();
	}
});


関連する資料

ImageView を起点とした画像データの処理 / Android




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

2016年11月03日


バグ : Android Studio 2.2.2 で、spinnerMode を dialog に設定すると app:spinnerMode となってしまいます

通常は、エラーになるので気づくと思いますが、android:spinnerModeが正解です。ただ、DataBinding を使用すると(layout 要素内)、app: でカスタムセッターの名称を指定するのでエラーにならないようなのです。

気づかずにいると、当然ダイアログ表示がされないので注意して下さい。

設定すると



通常はエラー



Data Binding 時



エラーにならないので気が付かない・・・

app:syozokuData="@{jdata.syozoku}" は以下の定義で正しく動作します

▼ Data Binding の @BindingAdapter によるカスタム Setter の定義
	@BindingAdapter("syozokuData")
	public static void setSyozokuData(Spinner spinnerSyozoku, String syozoku) {
		if ( syozoku != null && !syozoku.equals("")) {
			ArrayAdapter adapter = (ArrayAdapter) spinnerSyozoku.getAdapter();
			int count = adapter.getCount();
			for( int i = 0; i < count; i++ ) {
				KeyValue kv = (KeyValue) adapter.getItem(i);
				if ( kv.getCode().equals(syozoku)) {
					spinnerSyozoku.setSelection(i);
					break;
				}
			}
		}
	}




タグ:トラブル
posted by lightbox at 2016-11-03 01:17 | 2016 Android Studio | このブログの読者になる | 更新情報をチェックする

2016年11月01日


ListView + カスタム ArrayAdapter + ViewSwitcher + Firebase API + Data Binding : 更新処理 / Android Studio

Firebase の環境とプロジェクトの作成方法

シンプル Android Data Binding : Android Studio 2.2

tools.jar

画面定義
MainActivity 主な処理は、初期画面に表示する ListView の処理です。ListView 用の一覧データは、Firebase より取得します。API を使用して(orderByChild メソッド) でフリガナでソートしています。 また、初期画面には表示されていませんが、ViewSwitcher を使用しているので、次画面用の処理として NextPage クラスを用意して、ここで初期化しています。 特に、Spinner 用データ(性別・所属)も Firebase より取得し、KeyValue という汎用クラスを用いて処理を構築しています。(この参照用のデータの取得が成功してから、一覧データを取得しています) ListView 用の MyArrayAdapter は、Data Binding を使用しています
public class MainActivity extends AppCompatActivity {

	// ViewSwitcher
	public static int FIRST_PAGE = 0;
	public static int NEXT_PAGE = 1;

	// Data Binding
	private ActivityMainBinding binding;

	// Firebase
	private FirebaseDatabase database;
	private DatabaseReference mDatabase;

	// データフォーマット
	private JsonData json;

	// ローディング中のダイアログ
	private ProgressDialog progress;

	// 画面関連
	private ViewSwitcher vs;
	private NextPage nextPage;
	private ListView listview;
	private MyArrayAdapter adapter;

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

		// Data Binding を使用した画面表示
		binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

		// Firebase Database 用
		database = FirebaseDatabase.getInstance();
		mDatabase = database.getReference();

		// ローディング中のダイアログ
		progress = new ProgressDialog(MainActivity.this);

		// ViewSwitcher で複数画面処理
		vs = (ViewSwitcher) MainActivity.this.findViewById(R.id.viewSwitcher);
		// 次画面処理
		nextPage = new NextPage(MainActivity.this);
		// 次画面の初期処理( イベント登録等 )
		nextPage.initAction();

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

				// アダプターを取得( ここ専用 )
				MyArrayAdapter adapter = (MyArrayAdapter)parent.getAdapter();
				// 行データを取得
				json = (JsonData)adapter.getItem(position);
				// この行データを Data Binding で画面にセット
				binding.setJdata( json );
				// この行データを次画面で使用
				nextPage.setData( json );

				// 画面移動
				vs.setDisplayedChild(MainActivity.NEXT_PAGE);
			}
		});

		// ListView 用の アダブターを作成
		adapter = new MyArrayAdapter(MainActivity.this,R.layout.list_item);

		// 最新の Firebase のデータを取得する
		loadData();

	}

	// ******************************
	// データロード用の public メソッド
	// ******************************
	public void loadData() {
		// データロード前に表示
		progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
		progress.setMessage("データをロードしています");
		progress.show();

		// Firebase の item ツリーを name でソートして取得
		mDatabase.child("refdata")
			.addListenerForSingleValueEvent(new ValueEventListener() {
				@Override
				public void onDataChange(DataSnapshot dataSnapshot) {
					if ( dataSnapshot.exists() ) {

						// 性別コンボボックスの作成
						DataSnapshot ref1 = dataSnapshot.child("sex");
						GenericTypeIndicator<ArrayList<String>> t1 = new GenericTypeIndicator<ArrayList<String>>() {};
						ArrayList<String> sexData = ref1.getValue(t1);
						ArrayAdapter adapter = new ArrayAdapter(
							MainActivity.this,
							android.R.layout.simple_spinner_item);
						Spinner spinner = (Spinner) MainActivity.this.findViewById(R.id.spinnerSex);
						adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
						adapter.addAll(sexData);
						spinner.setAdapter(adapter);

						// 所属コンボボックスの作成
						DataSnapshot ref2 = dataSnapshot.child("syozoku");
						Iterator<DataSnapshot> child = ref2.getChildren().iterator();
						ArrayList<KeyValue> syozokuData = new ArrayList<KeyValue>();
						while(child.hasNext()) {
							DataSnapshot next = child.next();
							// Firebase API でJsonData に変換
							KeyValue kv = new KeyValue(next.getKey(),next.getValue().toString());
							// ArrayList に追加
							syozokuData.add(kv);
						}
						ArrayAdapter adapter2 = new ArrayAdapter(
							MainActivity.this,
							android.R.layout.simple_spinner_item);
						Spinner spinner2 = (Spinner) MainActivity.this.findViewById(R.id.spinnerSyozoku);
						adapter2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
						adapter2.addAll(syozokuData);
						spinner2.setAdapter(adapter2);

						loadDataList();
					}
					else {
						Log.i("lightbox","コンボボックス用データを読み込めませんでした");

						// ローディングダイアログを閉じる
						progress.dismiss();
					}

				}

				@Override
				public void onCancelled(DatabaseError databaseError) {
					databaseError.toException().printStackTrace();
					Log.i("lightbox","StackTrace(コンボボックス用データ) を出力しました");

					// ローディングダイアログを閉じる
					progress.dismiss();
				}
			});


	}

	public void loadDataList() {

		// Firebase の class ツリーを furi でソートして取得
		mDatabase.child("class").orderByChild("furi")
			.addListenerForSingleValueEvent(new ValueEventListener() {
				@Override
				public void onDataChange(DataSnapshot dataSnapshot) {
					if ( dataSnapshot.exists() ) {

						// 空の ArrayList
						ArrayList<JsonData> listData = new ArrayList<JsonData>();

						// name でソートされたデータを dataSnapshot より取得
						Iterator<DataSnapshot> child = dataSnapshot.getChildren().iterator();
						while(child.hasNext()) {
							DataSnapshot next = child.next();
							// Firebase API でJsonData に変換
							json = next.getValue(JsonData.class);

							// ArrayList に追加
							listData.add(json);
						}

						// アダプタをクリア
						adapter.clear();
						// アダプタにデータセット ( listData は name でソートされた ArrayList )
						adapter.addAll(listData);
						// リストビューにデータを表示
						listview.setAdapter(adapter);

					}
					else {
						Log.i("lightbox","データを読み込めませんでした");

					}

					// ローディングダイアログを閉じる
					progress.dismiss();
				}

				@Override
				public void onCancelled(DatabaseError databaseError) {
					databaseError.toException().printStackTrace();
					Log.i("lightbox","StackTrace を出力しました");

					// ローディングダイアログを閉じる
					progress.dismiss();
				}
			});
	}
}


NextPage クラス

ViewSwitcher の中に include で用意した次画面の処理を行います。MainActivity の中での処理になりますが、別クラスで分離して管理するようにしています。

この中では、Firebase の API を使用して更新処理を行っています。Firebase の更新は、JsonData クラスに必要なデータをセットし、そのまま API へ渡すというとても簡単で解り易い仕様となっています。
public class NextPage {

	private MainActivity mainActivity;
	private ViewSwitcher vs;
	private JsonData json;
	private View include1;

	// Firebase
	private FirebaseDatabase database;
	private DatabaseReference mDatabase;

	// コンストラクタ
	public NextPage(MainActivity mainActivity) {
		this.mainActivity = mainActivity;
	}

	// 処理する行データが外部からセットされる
	public void setData( JsonData json )  {
		this.json = json;
	}

	// 初期処理
	public void initAction(){

		// ViewSwitcher( 複数画面処理 )
		vs = (ViewSwitcher) mainActivity.findViewById(R.id.viewSwitcher);

		// activity_next.xml の 親 view を取得
		include1 = mainActivity.findViewById(R.id.include1);

		// Firebase Database 用
		database = FirebaseDatabase.getInstance();
		mDatabase = database.getReference();

		// *****************************************
		// 戻るボタンの処理 ( include1 より取得 )
		// *****************************************
		Button backButton = (Button)include1.findViewById(R.id.backButton);
		backButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				// 最初の画面へ移動
				vs.setDisplayedChild(MainActivity.FIRST_PAGE);

			}

		});

		// *****************************************
		// 更新ボタンの処理 ( include1 より取得 )
		// *****************************************
		Button updateButton = (Button)include1.findViewById(R.id.updateButton);
		updateButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				EditText et;

				et = (EditText) include1.findViewById(R.id.editName);
				json.setName(et.getText().toString());

				et = (EditText) include1.findViewById(R.id.editFuri);
				json.setFuri(et.getText().toString());

				et = (EditText) include1.findViewById(R.id.editKyuyo);
				json.setKyuyo(Integer.parseInt(et.getText().toString()));

				Spinner spinner;

				spinner = (Spinner) include1.findViewById(R.id.spinnerSex);
				json.setSex(String.format("%d",spinner.getSelectedItemPosition()));

				spinner = (Spinner) include1.findViewById(R.id.spinnerSyozoku);
				json.setSyozokuFromSpinner(spinner);

				DatePicker dp = (DatePicker) include1.findViewById(R.id.datePickerBirthday);
				String stringDate = String.format("%d/%02d/%02d",
					dp.getYear(),dp.getMonth()+1,dp.getDayOfMonth()
					);
				json.setBirthday(stringDate);

				Tools.messageBox(mainActivity, "確認", "更新しますか?", new Tools.OnMessageBoxListener() {
					@Override
					public void onMessageBoxYesListener() {
						// 更新
						mDatabase
							.child("class")
							.child(json.getCode())
							.setValue(json)
							.addOnCompleteListener(new OnCompleteListener<Void>() {
								@Override
								public void onComplete(@NonNull Task<Void> task) {
									if (task.isSuccessful()) {
										Log.i("lightbox", "書き込みに成功しました");

										mainActivity.loadData();
										// 最初の画面へ移動
										vs.setDisplayedChild(MainActivity.FIRST_PAGE);
									}
									else {
										Log.i("lightbox", task.getException().toString());
										task.getException().printStackTrace();
										Toast.makeText(mainActivity,"更新に失敗しました",Toast.LENGTH_SHORT).show();
									}
								}
							});
					}

					@Override
					public void onMessageBoxNoListener() {

					}
				});


			}

		});
	}
}


JsonData クラス

データの表示時に Data Binding で使用され、データの取得と更新時には Firebase で使用される、とても重要なクラスです。

特に、テキスト以外の入力値が必要な画面コントロールに対して、Data Binding の @BindingAdapter を使用したカスタムセッターを、数値データ(画面へは文字列なので)、DatePicker、Spinner に合わせて作成してあります。
public class JsonData extends BaseObservable {

	private String code;
	private String name;
	private String furi;

	// 画面用のカスタムセッターと保存用の カスタムセッター が必要です
	private String birthday;
	private long kyuyo;
	private String sex;		// コンボボックス
	private String syozoku;		// コンボボックス

	// 未使用( 無いと Firebase のデータが消失するので )
	public String teate;
	public String kanri;

	// **************************************
	// Firebase で必要な空のコンストラクタ
	// **************************************
	public JsonData(){}

	// キーなので setter はありません
	public String getCode() {
		return code;
	}

	// **************************************
	// setter 内で通知するのに、@Bindable を getter に
	// **************************************
	@Bindable
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
		notifyPropertyChanged(lightbox.july.listviewandnextactivity.BR.name);
	}

	// **************************************
	// フリガナ
	// **************************************
	@Bindable
	public String getFuri() {
		return furi;
	}
	public void setFuri(String furi) {
		this.furi = furi;
		notifyPropertyChanged(lightbox.july.listviewandnextactivity.BR.furi);
	}

	// **************************************
	// 生年月日
	// **************************************
	@Bindable
	public String getBirthday() {
		return birthday;
	}
	public void setBirthday(String birthday) {
		this.birthday = birthday;
		notifyPropertyChanged(lightbox.july.listviewandnextactivity.BR.birthday);
	}
	@BindingAdapter("dateText")
	public static void setDateText(DatePicker datePicker, String text) {

		if ( text != null && !text.equals("")) {
			int year = Integer.parseInt(text.substring(0, 4));
			int month = Integer.parseInt(text.substring(5, 7)) - 1;
			int day = Integer.parseInt(text.substring(8, 10));
			datePicker.updateDate(year, month, day);
		}

	}
	// **************************************
	// 給与
	// **************************************
	@Bindable
	public long getKyuyo() {
		return kyuyo;
	}
	public void setKyuyo(long kyuyo) {
		this.kyuyo = kyuyo;
		notifyPropertyChanged(lightbox.july.listviewandnextactivity.BR.kyuyo);
	}
	@BindingAdapter("longKyuyo")
	public static void setLongKyuyo(EditText editKyuyo, long kyuyo) {
		editKyuyo.setText(String.format("%d",kyuyo));
	}

	// **************************************
	// 性別
	// **************************************
	@Bindable
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
		notifyPropertyChanged(lightbox.july.listviewandnextactivity.BR.sex);
	}
	@BindingAdapter("stringSex")
	public static void setStringSex(Spinner spinnerSex, String sex) {
		if ( sex != null && !sex.equals("")) {
			spinnerSex.setSelection(Integer.parseInt(sex));
		}
	}

	// **************************************
	// 所属
	// **************************************
	@Bindable
	public String getSyozoku() {
		return syozoku;
	}
	public void setSyozoku(String syozoku) {
		this.syozoku = syozoku;
		notifyPropertyChanged(lightbox.july.listviewandnextactivity.BR.syozoku);
	}
	// コントロールから、syozoku をセットする  Setter
	// ※ setSyozoku という名称は使えません
	public void setSyozokuFromSpinner(Spinner spinnerSyozoku) {
		int position = spinnerSyozoku.getSelectedItemPosition();
		ArrayAdapter adapter = (ArrayAdapter) spinnerSyozoku.getAdapter();
		KeyValue kv = (KeyValue)adapter.getItem(position);
		this.setSyozoku(kv.getCode());
	}
	@BindingAdapter("syozokuData")
	public static void setSyozokuData(Spinner spinnerSyozoku, String syozoku) {
		if ( syozoku != null && !syozoku.equals("")) {
			ArrayAdapter adapter = (ArrayAdapter) spinnerSyozoku.getAdapter();
			int count = adapter.getCount();
			for( int i = 0; i < count; i++ ) {
				KeyValue kv = (KeyValue) adapter.getItem(i);
				if ( kv.getCode().equals(syozoku)) {
					spinnerSyozoku.setSelection(i);
					break;
				}
			}
		}
	}

	@Override
	public String toString() {
		return this.name;
	}
}

activity_next.xml

第二画面の定義です。Data Binding 用の記述として、app 名前空間で、カスタムセッターの名称を定義しています。
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable name="jdata" type="lightbox.july.listviewandnextactivity.JsonData"/>
    </data>

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

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

            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/btn_back"
                android:id="@+id/backButton"
                android:layout_alignParentTop="true"
                android:layout_alignParentStart="true"
                android:layout_weight="1"/>

            <Button
                android:text="@string/btn_update"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/updateButton"
                android:layout_weight="1"/>
        </LinearLayout>

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

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

                <TextView
                    android:text="@{jdata.code}"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textAppearance="?android:attr/textAppearanceLarge"
                    android:id="@+id/textCode"/>

                <EditText
                    android:text="@{jdata.name}"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="textPersonName"
                    android:ems="10"
                    android:id="@+id/editName"/>

                <EditText
                    android:text="@{jdata.furi}"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="textPersonName"
                    android:ems="10"
                    android:id="@+id/editFuri"/>

                <EditText
                    app:longKyuyo="@{jdata.kyuyo}"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="number"
                    android:ems="10"
                    android:id="@+id/editKyuyo"/>

                <LinearLayout
                    android:orientation="horizontal"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:padding="10dp"
                    android:layout_marginLeft="5dp">

                    <Spinner
                        app:stringSex="@{jdata.sex}"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:id="@+id/spinnerSex"
                        android:layout_weight="1"
                        android:layout_marginRight="5dp"/>

                    <Spinner
                        app:syozokuData="@{jdata.syozoku}"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:id="@+id/spinnerSyozoku"
                        android:layout_weight="1"/>

                </LinearLayout>

                <DatePicker
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:datePickerMode="spinner"
                    android:calendarViewShown="false"
                    android:id="@+id/datePickerBirthday"
                    app:dateText="@{jdata.birthday}"/>

            </LinearLayout>

        </ScrollView>

    </LinearLayout>
</layout>

ListView 用の画面定義( Data Binding 仕様 )
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="jdata" type="lightbox.july.listviewandnextactivity.JsonData"/>
    </data>
    <LinearLayout
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:padding="10dp">

        <TextView
            android:text="@{jdata.name}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:id="@+id/textItem1" />

        <TextView
            android:text="@{jdata.furi}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/textItem2"
            android:layout_marginTop="5dp"/>

    </LinearLayout>
</layout>






posted by lightbox at 2016-11-01 20:17 | 2016 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 %>
この記述は、以下の場所で使用します
container 終わり

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

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