無料のアセットとかあるけどさぁ、いろいろやったけどさぁ。SimpleSQLいちばん。
4000円出して、SimpleSQL買っとけ。便利だし安定してるし4000円以上の価値ある。
1から組むとかやめとけ、時間もったいねぇ。SQLマニアというのなら止めん。
SimpleSQL
https://assetstore.unity.com/packages/tools/integration/simplesql-38

無料のアセットとかあるけどさぁ、いろいろやったけどさぁ。SimpleSQLいちばん。
4000円出して、SimpleSQL買っとけ。便利だし安定してるし4000円以上の価値ある。
1から組むとかやめとけ、時間もったいねぇ。SQLマニアというのなら止めん。
SimpleSQL
https://assetstore.unity.com/packages/tools/integration/simplesql-38
UnityでSQLを利用したい。ちょっと大きめのプログラムになるとデータを管理するSQLをすぐ導入したほうがいい。
最初はめんどくさいけど、やっぱりSQL入れた方がいい。SQLiteのことね。サーバーで動作する奴じゃなくて、アプリ内のみで動くスタンドアロン的なSQLね。SQLiteね。その管理DB用のPGはいろいろ使って見たんだけど以下が一番良かった。
DB Browser for SQLite
https://sqlitebrowser.org/
シンプルだし使いやすかった。
iOS + PHPでPush Notificationを実装する、の参考サイト
みんな大好きグリッドビュー。
// 入れ込むべきレイアウト GridView gridView = (GridView) v.findViewById(R.id.gridView); gridView.setAdapter(new ImageAdapter(context, imageUrlList));
こうやって使いまっさ。ImageAdapterは独自のもの。そこに、context imageUrlListを渡します。
しかし、GridView、文字をいれるだけだと楽勝であるのだが、ネットから画像をよみこんでいれるとなるときっついのだ。それぞれの大きさがバラバラになったりして泣きそうになるのだ。他のサイトではすごく簡単にやり遂げているが。
import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.support.v4.util.LruCache; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import com.android.volley.RequestQueue; import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.Volley; import java.util.List; /** * イメージローダー */ public class ImageAdapter extends ArrayAdapter<String> { public final String TAG = "ImageAdapter"; private Context context; private RequestQueue mQueue; private ImageLoader mImageLoader; public ImageAdapter(Context context, List<String> objects) { super(context, 0, objects); this.context = context; mQueue = Volley.newRequestQueue(getContext()); mImageLoader = new ImageLoader(mQueue, new BitmapCache()); } @Override public View getView(int position, View convertView, ViewGroup parent) { String url = getItem(position); ImageView imageView; if (convertView == null) { imageView = new ImageView(getContext()); } else { imageView = (ImageView) convertView; } // 画像取得処理 ImageLoader.ImageListener listener = ImageLoader.getImageListener(imageView, android.R.drawable.ic_menu_rotate, android.R.drawable.ic_delete); // BASIC認証 mImageLoader = new BasicAuthImageLoader(mQueue, new ImageLoader.ImageCache() { private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(100); public void putBitmap(String url, Bitmap bitmap) { mCache.put(url, bitmap); } public Bitmap getBitmap(String url) { return mCache.get(url); } }); mImageLoader.get(url, listener); // 画像のサイズをここで固定 imageView.setMinimumWidth(300); imageView.setMinimumHeight(200); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); // クリックイベント imageView.setOnClickListener(new View.OnClickListener() { public String url; public View.OnClickListener setUrl(String url) { this.url = url; return this; } public void onClick(View v) { // イメージ画像がクリックされたときに実行される処理 Log.d(TAG, "url= " + url); // 画面遷移 Intent intent = new Intent(context, TSUGINO_GAMEN.class); intent.putExtra("photoUrlAddress", url); context.startActivity(intent); } }.setUrl(url) ); return imageView; } }
ここで、
// 画像のサイズをここで固定
imageView.setMinimumWidth(300);
imageView.setMinimumHeight(200);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
これが必要だった。setScaleTypeはおこのみで。
クリックイベントにも注目。
// クリックイベント imageView.setOnClickListener(new View.OnClickListener() { public String url; public View.OnClickListener setUrl(String url) { this.url = url; return this; } public void onClick(View v) { // イメージ画像がクリックされたときに実行される処理 Log.d(TAG, "url= " + url); // 画面遷移 Intent intent = new Intent(context, TSUGINO_GAMEN.class); intent.putExtra("photoUrlAddress", url); context.startActivity(intent); } }.setUrl(url) );
setUrlを使ってurlをイベントリスナー内に入れ込んでいる。イベントに値を渡す方法。少し凝ったことをしたいなら必要になってくるだろう。
BASIC認証は使わないだろうからはずしてもいいじゃろう。
// BASIC認証 mImageLoader = new BasicAuthImageLoader(mQueue, new ImageLoader.ImageCache() { private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(100); public void putBitmap(String url, Bitmap bitmap) { mCache.put(url, bitmap); } public Bitmap getBitmap(String url) { return mCache.get(url); } });
こちらを参考にして、
intent.setFlagsメソッドの引数について
定数 | 内容 |
---|---|
FLAG_ACTIVITY_NEW_TASK | 常に新しいタスクで起動する ※デフォルト |
FLAG_ACTIVITY_SINGLE_TOP | 現在のActivityと同じアクティビティは起動しない |
FLAG_ACTIVITY_CLEAR_TOP | スタックをクリアしてから起動する |
FLAG_ACTIVITY_NO_ANIMATION | トランジションアニメーションを行わずに起動する |
FLAG_ACTIVITY_NO_HISTORY | スタックに追加せずに起動する |
FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | スタック内のActivityを利用して起動する(使いまわす) |
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | 履歴内の同一Acrivityを利用して起動する(使いまわす) |
FLAG_ACTIVITY_PREVIOUS_IS_TOP | 呼び出し元のActivityをタスクのトップと見なして起動する |
FLAG_ACTIVITY_REORDER_TO_FRONT | スタック内の同一Activityを最前面に移動させる |
画面を遷移するときなどにこれを渡せばいいらしい。使い方は以下のような、
// 引数1:自身のActivity、引数2:移動先のActivity名 Intent intent = new Intent(MainActivity.this, SubActivity.class); // オプションを指定 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Activityの移動 startActivity(intent);
あとタスクに関係するのであればマニフェストの、
android:launchMode=”singleTask”
なども考慮するといいかもしれない。
また一つ賢くなったね!!
二つのフォルダー内のファイルを見比べるときはDiffMergeをつかう
アラートダイアログの外側を押せないようにする、ダイアログは便利ですが外側を押しても反応しちゃう。
そこで
// ダイアログ表示 public AlertDialog.Builder builder;
こんな感じで、アラートダイアログを宣言して
builder = new AlertDialog.Builder(this); builder.setTitle("登録しました"); // 外側を押せないようにする builder.setCancelable(false); builder.setNegativeButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // 遷移 Intent intent = new Intent(getApplicationContext(), hogehoge.class); startActivity(intent); } }); // 表示 builder.create().show();
こうすればいいですね。
builder.create().show(); の create() はわけわかめ。なんだろうか、こいつは。
自分的Volleyの定番コード
CustomRequestクラスを作成して以下の文章でvolley使う。例文はPOSTを使用する場合です。
HashMap<String, String> postMap = new HashMap<String, String>(); postMap.put("uuid", uuId); RequestQueue requestQueue = Volley.newRequestQueue(this); CustomRequest jsObjRequest = new CustomRequest(Request.Method.POST, Constant.USER_ADD_URL, postMap, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { Log.v("初期ユーザー登録、JSON", response.toString()); // ここでタイマー起動 // 3秒カウント開始のスケジュール timer = new Timer(); timer.schedule(new MyTimer(), Constant.TITLE_COUNT_SEC); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.v("初期ユーザー登録、VolleyErr", "" + error); } }); requestQueue.add(jsObjRequest);
public class CustomRequest extends Request<JSONObject> { private Response.Listener<JSONObject> listener; private Map<String, String> params; public CustomRequest(String url, Map<String, String> params, Response.Listener<JSONObject> reponseListener, Response.ErrorListener errorListener) { super(Request.Method.GET, url, errorListener); this.listener = reponseListener; this.params = params; } public CustomRequest(int method, String url, Map<String, String> params, Response.Listener<JSONObject> reponseListener, Response.ErrorListener errorListener) { super(method, url, errorListener); this.listener = reponseListener; this.params = params; } protected Map<String, String> getParams() throws com.android.volley.AuthFailureError { return params; } ; @Override protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) { try { String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); return Response.success(new JSONObject(jsonString), HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { return Response.error(new ParseError(e)); } catch (JSONException je) { return Response.error(new ParseError(je)); } } @Override protected void deliverResponse(JSONObject response) { // TODO Auto-generated method stub listener.onResponse(response); } }
JSONでキーがあらかじめわかっていれば、使うのは簡単だった。
でもキーがわからないときはどうすればいいのか?
以下のとおり。
JSONObject menuJson = new JSONObject(MENU_JSON); Iterator<?> keys = menuJson.keys(); Log.d(TAG, "keys= " + keys.hasNext()); while (keys.hasNext()) { String key = (String) keys.next(); Log.d(TAG, "" + key); JSONObject j = new JSONObject(menuJson.getString(key)); Iterator<?> keyss = j.keys(); while (keyss.hasNext()) { String keyy = (String) keyss.next(); Log.d(TAG, "" + keyy); } }
下がミソじゃの
while (keys.hasNext()) { String key = (String) keys.next(); }
基本はJavascript。
doOutput(e)関数で吐き出すらしい。これをAPIにすると、超便利である。
まず、
var summaryData = sh.getDataRange().getValues();
がミソ。これで一度すべてのシート内容を読む。シートが巨大な時はこれをつかう。早いから。
リターンで返す時は、
return ContentService.createTextOutput(JSON.stringify(json)).setMimeType(ContentService.MimeType.JSON);
などなど。単純に文字列を返してもだめっすよ、怒られます。
function doGet(e){ var json = myFunction(); Logger.log(JSON.stringify(json)); return ContentService.createTextOutput(JSON.stringify(json)).setMimeType(ContentService.MimeType.JSON); } function doPost(e) { var json = myFunction(); Logger.log(JSON.stringify(json)); return ContentService.createTextOutput(JSON.stringify(json)).setMimeType(ContentService.MimeType.JSON); } function myFunction() { var url = 'https://docs.google.com/spreadsheets/xxxxxxxxxxxxxxxxxxxxxxxxxx'; var bk = SpreadsheetApp.openByUrl(url); var sh = bk.getSheetByName("food_drink"); // 最後行を取得 var lastRow = sh.getLastRow() - 1; // 最後列を取得 var lastColumn = sh.getLastColumn(); var list = []; var obj = {}; var objShou = {}; var objChuu = {}; var summaryData = sh.getDataRange().getValues(); Logger.log(lastRow); for (var y = 1; y <= lastRow; y++) { // 名前取得 var obj1 = {}; obj1.name = summaryData[y][4]; obj1.money = summaryData[y][5]; obj1.iconImageUrl = summaryData[y][6]; obj1.photoImageUrl = summaryData[y][7]; obj1.id = summaryData[y][0]; list.push(obj1); // 小カテゴリー、次に変化があればオブジェクトへいれる if (y < lastRow) { var dy1 = summaryData[y][3]; var dy2 = summaryData[y+1][3]; if (dy1 != dy2) { objShou[dy1] = list; list = []; } } else { var dy1 = summaryData[y][3]; objShou[dy1] = list; list = []; } // 中カテゴリー、次に変化があればオブジェクトへいれる if (y < lastRow) { var cy1 = summaryData[y][2]; var cy2 = summaryData[y+1][2]; if (cy1 != cy2) { objChuu[cy1] = objShou; objShou = {}; } } else { var cy1 = summaryData[y][2]; objChuu[cy1] = objShou; objShou = {}; } } return objChuu; }
タイマーをよく使うのですが、備忘録として。
// タイマー作動 Timer timer1 = new Timer(); Handler handler1 = new Handler(); timer1.schedule(new DownloadTimer(), 0, 2000);
タイマー、ハンドラーを宣言。その後のスケジュールで0秒から開始、その後2000ミリ秒ごと(2秒)にタイマー作動。となっています。 new DownloadTimer() を宣言してそれを実行することになっています。
実際の処理内は、
class DownloadTimer extends TimerTask { @Override public void run() { handler1.post(new Runnable(){ @Override public void run() { } }); } }
こんな感じでクラスをつくり public void run() { } に処理をかきます。それが実行されます。
public class ImageManager { private Context mContext; private String fileFullPath; public ImageManager(Context context) { mContext = context; } /** * 画像の保存 * * @param bitmap * @param albumName */ public void save(Bitmap bitmap, String albumName) { if (!canUseSd()) { Log.e("Error", "Can't use SD Card"); return; } saveToSd(getSdStorageDir(albumName), bitmap); } /** * ストレージに画像保存 * * @param dir * @param bitmap */ private void saveToSd(File dir, Bitmap bitmap) { String fileName = getFileName(); fileFullPath = dir.getAbsolutePath() + "/" + fileName; Log.d("保存パス","fileFullPath= " + fileFullPath); try { // 保存処理 FileOutputStream fos = new FileOutputStream(fileFullPath); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); fos.flush(); fos.close(); } catch (Exception e) { Log.e("Error", "" + e.toString()); } finally { // アルバムに反映 addGallery(fileName); } } /** * 保存した画像をギャラリーに追加 * * @param fileName */ private void addGallery(String fileName) { try { ContentValues values = new ContentValues(); ContentResolver contentResolver = mContext.getContentResolver(); values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); values.put(MediaStore.Images.Media.TITLE, fileName); values.put("_data", fileFullPath); contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } catch (Exception e) { Log.e("Error", "" + e); } } /** * 画像のファイル名を日付から生成し取得 * * @return */ private String getFileName() { Date mDate = new Date(); SimpleDateFormat fileNameFormat = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH); String fileName = fileNameFormat.format(mDate) + ".jpg"; return fileName; } /** * ストレージのストレージパス取得 * * @param albumName * @return */ private File getSdStorageDir(String albumName) { File dir = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), albumName); if (!dir.exists()) { if (!dir.mkdirs()) { Log.e("Error", "Directory not created"); } } return dir; } /** * ストレージが読み込み可能か * * @return */ public boolean canReadSd() { String status = Environment.getExternalStorageState(); if (!status.equals(Environment.MEDIA_MOUNTED)) { return false; } File file = Environment.getExternalStorageDirectory(); if (file.canRead()) { return true; } return false; } /** * ストレージに書き込み可能か * * @return */ public boolean canWriteSd() { String status = Environment.getExternalStorageState(); if (!status.equals(Environment.MEDIA_MOUNTED)) { return false; } File file = Environment.getExternalStorageDirectory(); if (file.canWrite()) { return true; } return false; } /** * ストレージが使用可能か * * @return */ public boolean canUseSd() { return canReadSd() && canWriteSd(); } }
使い方は、
ImageView imageView = (ImageView) findViewById(R.id.photo_image); Bitmap imageForScale = ((BitmapDrawable) imageView.getDrawable()).getBitmap(); ImageManager imageManager = new ImageManager(this); try { String albumName = "Save image sample"; imageManager.save(imageForScale, albumName); } catch (Error e) { Log.e("MainActivity", "onCreate: " + e); }