C# でちょっと作ったプログラムが落ちたという話があがって。問題が起きたのは
ListBox で、動的にリストボックスの内容を作成しているのだけど、たまたま
数が少なかった場合に起きた。実際に起きるのか、テストしてみた。こんな感じ
の見た目のプログラムを作成。
実行時の何も選択されていない状態のとき、「いちご」の下にある ListBox の
空白部分をクリックすると落ちる感じだったらしいので、元のコードからこんな
感じで書いてみた。
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
MessageBox.Show("selected:" + listBox1.SelectedItem.ToString());
}
これで実験してみたら、見事に落ちた。SelectedItem が null になっていることが
原因みたい。この ListBox は項目が多くて、下まで埋まっている場合がほとんど
だったので、まったく気が付かなかった。このとき、listBox1.SelectedIndex は
-1 が返ってくるので、-1 だったら何もせずに return して回避した。それに
しても、急に Android というか Java から C# だと頭が切り替わらないな。
先月の26日に少し書いたけど、画像をピンチアウト~ピンチインで拡大縮小が
できるようにやってみる。まずは、いつものように xml の用意。
<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" >
<ImageView
android:id="@+id/flowerImageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/d2016030100" />
<TextView
android:id="@+id/descriptionTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This photo was taken in Kamakura" />
</LinearLayout>
<ScrollView>
これだけで実行してみたら、実機でもエミュレータでも上下におかしな空白の
領域ができた。これは何なんだろう。
|
本来の画像の高さを確保して、それから縮小している感じだな。ScrollView に
置かない場合だとこんなことが起きないのに。ちょっと調べて、ImageView に
android:adjustViewBounds="true"
を追加すると消えた。この画像だと、スクロールすることがなくなってしまう
ので、TextView の text に改行を10回入れて実験。画像の位置は問題なし。
|
スクロールも下までできるようになった。
ピンチイン/ピンチアウトか、拡大した画像をスワイプで移動できるようにするか
どちらを先に実装しようか悩んで、ピンチの動作を先に入れることにした。
まずは、画像の拡大縮小について簡単なコードで試してみる。画像をタッチすると
画像が拡大するコードを。
public class MainActivity extends AppCompatActivity {
ImageView flowerImage;
Bitmap originalBitmap;
Matrix matrix;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
flowerImage = (ImageView) findViewById(R.id.flowerImageView);
originalBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.d2016030100);
matrix = new Matrix();
matrix.setScale(0.545f, 0.545f);
Bitmap bitmap = Bitmap.createBitmap(originalBitmap, 0, 0,
originalBitmap.getWidth(), originalBitmap.getHeight(),
matrix, true);
flowerImage.setScaleType(ImageView.ScaleType.MATRIX);
flowerImage.setImageBitmap(bitmap);
flowerImage.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
matrix.postScale(1.2f, 1.2f);
Bitmap bitmap = Bitmap.createBitmap(originalBitmap,
0, 0, originalBitmap.getWidth(),
originalBitmap.getHeight(), matrix, true);
flowerImage.setImageBitmap(bitmap);
return false;
}
return false;
}
});
}
}
bitmap を1つで使いまわししていたら変な動きをしたので、元画像の bitmap は
originalBitmap で保存してそれを元に createBitmap をした。setScale で指定
している 0.545f は画面の横幅に合うような比率。これで実行してみると。
|
画像をクリックすると、どんどん拡大していく。
|
元の画像が大きいせいか、4回もタッチすると。
java.lang.OutOfMemoryError: Failed to allocate (省略)
で落ちてしまう。今日はここまでにして、明日は落ちないようにするところから
再開する予定。
昨日の続き。1.2倍にしている値は4回行うと 2.0736 になり、約2倍の大きさに
なる。私の予想だと、Bitmap.createBitmap では表示領域に関係なくその大きさに
拡大しているので、サイズ的には元の画像の4倍になってしまうと。
元の画像のサイズは 800×600 とそれほど大きくないが、本当に 24bit の bitmap
で扱っていたら、1.4M ぐらいになる。これを4倍すると 5.5M になり、Android
の端末レベルじゃ厳しくなるのだろうかと予測しながら、Bitmap.createBitmap の
4番目と5番目の値をいじって、元画像の必要な部分だけ使って拡大するように
いじってみた。
case MotionEvent.ACTION_DOWN:
float matrixValue[] = new float[9];
matrix.postScale(1.2f, 1.2f);
matrix.getValues(matrixValue);
int readWidth = (int)(originalBitmap.getWidth() / matrixValue[0]);
int readHeight = (int)(originalBitmap.getHeight() / matrixValue[4]);
Bitmap bitmap = Bitmap.createBitmap(originalBitmap, 0, 0, readWidth,
readHeight, matrix, true);
flowerImage.setImageBitmap(bitmap);
return false;
これでやると、画像が大きくなってしまっていきなり落ちてしまった。onCreate
で使った 0.545 の値で割らないといけないことに気が付く。この固定値も気持ち
悪いので、計算から出すように変更したりで。
public class MainActivity extends AppCompatActivity {
ImageView flowerImage;
Bitmap originalBitmap;
Matrix matrix;
float scale;
int imageWidth;
int imageHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
flowerImage = (ImageView) findViewById(R.id.flowerImageView);
originalBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.d2016030100);
// to get ImageView size
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
imageWidth = (int) (displayMetrics.widthPixels -
(2 * getResources().getDimension(
R.dimen.activity_horizontal_margin)));
imageHeight = (int)((float)imageWidth *
((float)originalBitmap.getHeight() / originalBitmap.getWidth()));
scale = (float)imageWidth / originalBitmap.getWidth();
matrix = new Matrix();
matrix.setScale(scale, scale);
Bitmap bitmap = Bitmap.createBitmap(originalBitmap, 0, 0,
originalBitmap.getWidth(), originalBitmap.getHeight(),
matrix, true);
flowerImage.setScaleType(ImageView.ScaleType.MATRIX);
flowerImage.setImageBitmap(bitmap);
flowerImage.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float matrixValue[] = new float[9];
matrix.postScale(1.2f, 1.2f);
matrix.getValues(matrixValue);
float scaleFromFit = matrixValue[0] / scale;
int readWidth = (int)(originalBitmap.getWidth() /
scaleFromFit);
int readHeight = (int)(originalBitmap.getHeight() /
scaleFromFit);
Bitmap bitmap = Bitmap.createBitmap(originalBitmap,
0, 0, readWidth, readHeight, matrix, true);
flowerImage.setImageBitmap(bitmap);
return false;
}
return false;
}
});
}
}
早々にクラス化したい感じだな。多少の誤差はありそうだが、いつも同じサイズで
作成しているので、何回タップしても、落ちないようにはなった。
|
これをピンチアウトにつなげる。いきなりピンチアウトはハードルが高いので
スワイプで始める。スワイプしたとき単純に拡大していくように書いてみた。
clickStartX/Y と clicking がメンバ変数扱い。
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
clickStartX = (int)event.getX();
clickStartY = (int)event.getY();
clicking = true;
return true;
case MotionEvent.ACTION_MOVE:
// to get distance
double distance = Math.sqrt((double)
((clickStartX - event.getX()) * (clickStartX - event.getX()) +
(clickStartY - event.getY()) * (clickStartY - event.getY())));
// to calculate scale
float scaleA = (float)((distance * 0.001 + 1.0));
matrix.postScale(scaleA, scaleA);
float matrixValue[] = new float[9];
matrix.getValues(matrixValue);
float scaleFromFit = matrixValue[0] / scale;
int readWidth = (int)(originalBitmap.getWidth() / scaleFromFit);
int readHeight = (int)(originalBitmap.getHeight() / scaleFromFit);
Bitmap bitmap = Bitmap.createBitmap(originalBitmap, 0, 0,
readWidth, readHeight, matrix, true);
flowerImage.setImageBitmap(bitmap);
return true;
case MotionEvent.ACTION_UP:
clicking = false;
return true;
}
return false;
}
});
ScrollView のスクロールとイベントがぶつかるようなので、一旦スクロールを
しないようにして、実験した。
distance で移動距離を測定して、適当な値を掛けて拡大率を出している。最初は
0.1 を掛けたけどとんでもなく拡大するので、0.01 にした。このままだと加速度
的に大きくなって、あまり具合が良くない。前回の distance の値を保存して、
それよりも小さくなったら、縮小もしないといけないので書き換え。
case MotionEvent.ACTION_MOVE:
// to get distance
double distance = Math.sqrt((double)
((clickStartX - event.getX()) * (clickStartX - event.getX()) +
(clickStartY - event.getY()) * (clickStartY - event.getY())));
double deltaD = distance - beforeDistance;
beforeDistance = distance;
// to calculate scale
float scaleA = (float)((deltaD * 0.01 + 1.0));
matrix.postScale(scaleA, scaleA);
float matrixValue[] = new float[9];
matrix.getValues(matrixValue);
float scaleFromFit = matrixValue[0] / scale;
int readWidth = (int)(originalBitmap.getWidth() / scaleFromFit);
int readHeight = (int)(originalBitmap.getHeight() / scaleFromFit);
Bitmap bitmap = Bitmap.createBitmap(originalBitmap, 0, 0, readWidth,
readHeight, matrix, true);
flowerImage.setImageBitmap(bitmap);
return true;
case MotionEvent.ACTION_UP:
beforeDistance = 1.0;
clicking = false;
return true;
あとは、ピンチにして二本指間の距離にすれば完成だな。長くなったので、続きは
明日に。
昨日の続きで。まずは、二本指のイベントが取れるのか実験をしてみる。タッチ
している数は event.getPointerCount を使って。
flowerImage.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getPointerCount() > 1) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d("onTouch", "ACTION_DOWN");
return true;
case MotionEvent.ACTION_MOVE:
Log.d("onTouch", "ACTION_MOVE");
return true;
case MotionEvent.ACTION_UP:
Log.d("onTouch", "ACTION_UP");
return true;
}
}
return true;
}
});
onTouch 最後の return を false にするとうまく取得できなかった。このコードで
動かしても、ACTION_MOVE は来るけど ACTION_DOWN/ACTION_UP が来ないようで、
出力されなかった。ACTION_MOVE だけで実装してみる。
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (event.getPointerCount() > 1) {
double distance = Math.sqrt((double)
((event.getX(0) - event.getX(1)) * (event.getX(0) - event.getX(1)) +
(event.getY(0) - event.getY(1)) * (event.getY(0) - event.getY(1))));
if (beforeDistance == 1.0) {
beforeDistance = distance;
return true;
}
double deltaD = distance - beforeDistance;
beforeDistance = distance;
float scaleA = (float) ((deltaD * 0.01 + 1.0));
matrix.postScale(scaleA, scaleA);
float matrixValue[] = new float[9];
matrix.getValues(matrixValue);
float scaleFromFit = matrixValue[0] / scale;
int readWidth = (int) (originalBitmap.getWidth() / scaleFromFit);
int readHeight = (int) (originalBitmap.getHeight() / scaleFromFit);
Bitmap bitmap = Bitmap.createBitmap(originalBitmap, 0, 0,
readWidth, readHeight, matrix, true);
flowerImage.setImageBitmap(bitmap);
}
return true;
case MotionEvent.ACTION_UP:
beforeDistance = 1.0;
clicking = false;
return true;
}
return true;
}
多少動きが怪しいし、Bitmap.createBitmap で落ちることがあるけど、なんとなく
形が見えてきた。大きさの制限をする。matrix.getValues(matrixValue); の後に
最大と最小の値を入れた。
if(matrixValue[0] < scale){
matrix.setScale(scale, scale);
matrix.getValues(matrixValue);
} else if(matrixValue[0] > 8.0){
matrix.setScale(8.0f, 8.0f);
matrix.getValues(matrixValue);
}
これで落ちることはなくなって、怪しい動作もなくなった。次は拡大するときの
中心位置を捕まえて、そこを中心に拡大・縮小するようにする。頭がついていか
ないので、画像中心からの拡大・縮小をまずやってみる。
float scaleFromFit = matrixValue[0] / scale;
int readWidth = (int) (originalBitmap.getWidth() / scaleFromFit);
int readHeight = (int) (originalBitmap.getHeight() / scaleFromFit);
int readStartX = (int)((originalBitmap.getWidth() - readWidth) / 2);
int readStartY = (int)((originalBitmap.getHeight() - readHeight) / 2);
Bitmap bitmap = Bitmap.createBitmap(originalBitmap, readStartX, readStartY,
readWidth, readHeight, matrix, true);
flowerImage.setImageBitmap(bitmap);
これで画像の中心の拡大縮小になった。ここからずらしていかないといけない
のだけど、比率でやれば良いのかな。やってみた。
case MotionEvent.ACTION_MOVE:
if (event.getPointerCount() > 1) {
double distance = Math.sqrt((double)
((event.getX(0) - event.getX(1)) * (event.getX(0) - event.getX(1)) +
(event.getY(0) - event.getY(1)) * (event.getY(0) - event.getY(1))));
if (beforeDistance == 1.0) {
centreX = (int)((event.getX(0) + event.getX(1)) / 2);
centreY = (int)((event.getY(0) + event.getY(1)) / 2);
if(centreX == 0)
centreX = 1;
if(centreY == 0)
centreY = 1;
beforeDistance = distance;
return true;
}
double deltaD = distance - beforeDistance;
beforeDistance = distance;
float scaleA = (float) ((deltaD * 0.01 + 1.0));
matrix.postScale(scaleA, scaleA);
float matrixValue[] = new float[9];
matrix.getValues(matrixValue);
if(matrixValue[0] < scale){
matrix.setScale(scale, scale);
matrix.getValues(matrixValue);
} else if(matrixValue[0] > 8.0){
matrix.setScale(8.0f, 8.0f);
matrix.getValues(matrixValue);
}
float scaleFromFit = matrixValue[0] / scale;
int readWidth = (int) (originalBitmap.getWidth() / scaleFromFit);
int readHeight = (int) (originalBitmap.getHeight() / scaleFromFit);
int readStartX = (int)((originalBitmap.getWidth() - readWidth) /
((float)imageWidth / centreX));
int readStartY = (int)((originalBitmap.getHeight() - readHeight) /
((float)imageHeight / centreY));
Bitmap bitmap = Bitmap.createBitmap(originalBitmap, readStartX,
readStartY, readWidth, readHeight, matrix, true);
flowerImage.setImageBitmap(bitmap);
}
return true;
これで最初の1回とか、ズームしていないときからのピンチアウトでのズームは、
指の中間を中心としたズームとなった。2回目以降の場合は、ずれ具合を考慮して
ズームする必要がある。readStartX/readStartY をメンバ変数にして計算に加える
ようにする。
case MotionEvent.ACTION_MOVE:
if (event.getPointerCount() > 1) {
double distance = Math.sqrt((double)
((event.getX(0) - event.getX(1)) * (event.getX(0) - event.getX(1)) +
(event.getY(0) - event.getY(1)) * (event.getY(0) - event.getY(1))));
if (beforeDistance == 1.0) {
centreX = (int)((event.getX(0) + event.getX(1)) / 2);
centreY = (int)((event.getY(0) + event.getY(1)) / 2);
if(centreX == 0)
centreX = 1;
if(centreY == 0)
centreY = 1;
beforeDistance = distance;
return true;
}
double deltaD = distance - beforeDistance;
beforeDistance = distance;
float scaleA = (float) ((deltaD * 0.01 + 1.0));
matrix.postScale(scaleA, scaleA);
float matrixValue[] = new float[9];
matrix.getValues(matrixValue);
if(matrixValue[0] < scale){
matrix.setScale(scale, scale);
matrix.getValues(matrixValue);
readStartX = 0;
readStartY = 0;
readWidth = originalBitmap.getWidth();
readHeight = originalBitmap.getHeight();
} else if(matrixValue[0] > 8.0){
matrix.setScale(8.0f, 8.0f);
matrix.getValues(matrixValue);
}
float scaleFromFit = matrixValue[0] / scale;
int increaseWidth = readWidth -
(int)(originalBitmap.getWidth() / scaleFromFit);
int increaseHeight = readHeight -
(int)(originalBitmap.getHeight() / scaleFromFit);
readWidth = (int) (originalBitmap.getWidth() / scaleFromFit);
readHeight = (int) (originalBitmap.getHeight() / scaleFromFit);
readStartX = (int)(readStartX +
(increaseWidth / ((float)imageWidth / centreX)));
readStartY = (int)(readStartY +
(increaseHeight / ((float)imageHeight / centreY)));
Bitmap bitmap = Bitmap.createBitmap(originalBitmap, readStartX, readStartY,
readWidth, readHeight, matrix, true);
flowerImage.setImageBitmap(bitmap);
}
return true;
たまに計算の誤差のせいか、readStartX/readStartY が負の値になって落ちたり、
readWidth/readHeight と足した値がはみ出て落ちたりするので、チェックを入れて
完成かな。
readStartX/readStartY を動かせば画像位置の変更もできるので、スワイプの
実装自体は思ったよりも楽そう。ScrollView との競合を考えなければの話だけど。
昨日の続き。Bitmap.createBitmap で落ちる対策として、値のチェックを入れる
ようにした。
if(readStartX < 0) {
readStartX = 0;
} else if(readStartX + readWidth > originalBitmap.getWidth() ) {
readStartX = originalBitmap.getWidth() - readWidth;
}
if(readStartY < 0){
readStartY = 0;
} else if(readStartY + readHeight > originalBitmap.getHeight() ) {
readStartY = originalBitmap.getHeight() - readHeight;
}
これでピンチアウト・ピンチインでのズーム機能の完成。次は拡大した画像の
位置をスワイプで動かせるようにする。この辺りは3日に作ったものを利用すれば
できそう。case MotionEvent.ACTION_MOVE: の中に指1本のときのコードを追加。
if (event.getPointerCount() == 1) {
if(clicking == false){
moveStartX = (int)event.getX();
moveStartY = (int)event.getY();
clicking = true;
return true;
}
int moveX = moveStartX - (int)(event.getX());
int moveY = moveStartY - (int)(event.getY());
moveStartX = (int)event.getX();
moveStartY = (int)event.getY();
float matrixValue[] = new float[9];
matrix.getValues(matrixValue);
readStartX += moveX / matrixValue[0];
readStartY += moveY / matrixValue[0];
Bitmap bitmap = Bitmap.createBitmap(originalBitmap, readStartX, readStartY,
readWidth, readHeight, matrix, true);
flowerImage.setImageBitmap(bitmap);
}
あっさりできた。領域外に出るとやっぱり落ちるので、さっき追加したコードも
ここに入れたが、最後は動作が同じなので合わせることにした。
case MotionEvent.ACTION_MOVE:
if (event.getPointerCount() == 1) {
if(clicking == false){
moveStartX = (int)event.getX();
moveStartY = (int)event.getY();
clicking = true;
return true;
}
int moveX = moveStartX - (int)(event.getX());
int moveY = moveStartY - (int)(event.getY());
moveStartX = (int)event.getX();
moveStartY = (int)event.getY();
float matrixValue[] = new float[9];
matrix.getValues(matrixValue);
readStartX += moveX / matrixValue[0];
readStartY += moveY / matrixValue[0];
} else if(event.getPointerCount() > 1) {
double distance = Math.sqrt((double)
((event.getX(0) - event.getX(1)) * (event.getX(0) - event.getX(1)) +
(event.getY(0) - event.getY(1)) * (event.getY(0) - event.getY(1))));
if (beforeDistance == 1.0) {
centreX = (int)((event.getX(0) + event.getX(1)) / 2);
centreY = (int)((event.getY(0) + event.getY(1)) / 2);
if(centreX == 0)
centreX = 1;
if(centreY == 0)
centreY = 1;
beforeDistance = distance;
return true;
}
double deltaD = distance - beforeDistance;
beforeDistance = distance;
float scaleA = (float) ((deltaD * 0.01 + 1.0));
matrix.postScale(scaleA, scaleA);
float matrixValue[] = new float[9];
matrix.getValues(matrixValue);
if(matrixValue[0] < scale){
matrix.setScale(scale, scale);
matrix.getValues(matrixValue);
readStartX = 0;
readStartY = 0;
readWidth = originalBitmap.getWidth();
readHeight = originalBitmap.getHeight();
} else if(matrixValue[0] > 8.0){
matrix.setScale(8.0f, 8.0f);
matrix.getValues(matrixValue);
}
float scaleFromFit = matrixValue[0] / scale;
int increaseWidth = readWidth -
(int) (originalBitmap.getWidth() / scaleFromFit) ;
int increaseHeight = readHeight -
(int) (originalBitmap.getHeight() / scaleFromFit);
readWidth = (int) (originalBitmap.getWidth() / scaleFromFit);
readHeight = (int) (originalBitmap.getHeight() / scaleFromFit);
readStartX = (int)(readStartX +
(increaseWidth / ((float)imageWidth / centreX)));
readStartY = (int)(readStartY +
(increaseHeight / ((float)imageHeight / centreY)));
}
if(readStartX < 0) {
readStartX = 0;
} else if(readStartX + readWidth > originalBitmap.getWidth() ) {
readStartX = originalBitmap.getWidth() - readWidth;
}
if(readStartY < 0){
readStartY = 0;
} else if(readStartY + readHeight > originalBitmap.getHeight() ) {
readStartY = originalBitmap.getHeight() - readHeight;
}
Bitmap bitmap = Bitmap.createBitmap(originalBitmap, readStartX, readStartY,
readWidth, readHeight, matrix, true);
flowerImage.setImageBitmap(bitmap);
return true;
case MotionEvent.ACTION_UP:
beforeDistance = 1.0;
clicking = false;
return true;
これで ImageView 自体の動きは完成。移動ごときで毎回 createBitmap している
のは、効率悪い感じはするが、少し置いて ScrollView との連携。
スワイプする邪魔にならないように TextView を削除して ScrollView が動かない
ようにしていたが、ScrollView をスクロールできるように元に戻して動かすと、
作った ImageView の動作がちぐはぐになってしまった。イベントが全て下りて
こないような感じ。大元の MainActivity と、その中の ScrollView と、さらに
その中の ImageView の onTouch でログを出力して、スワイプの動作をしてみた。
D/ImageView: onTouch
D/ImageView: onTouch
D/ScrollView: onTouch
D/ScrollView: onTouch
D/ScrollView: onTouch
D/ScrollView: onTouch
D/ScrollView: onTouch
MainActivity へはイベントが行かないみたい。ACTION_UP とか ACTION_MOVE とかの
判定まで入れてみる。
D/ImageView: onTouch
D/ImageView: ACTION_DOWN
D/ImageView: onTouch
D/ImageView: ACTION_MOVE
D/ImageView: onTouch
D/ImageView: other
D/ScrollView: onTouch
D/ScrollView: ACTION_MOVE
D/ScrollView: onTouch
D/ScrollView: ACTION_MOVE
D/ScrollView: onTouch
D/ScrollView: ACTION_MOVE
D/ScrollView: onTouch
D/ScrollView: ACTION_MOVE
D/ScrollView: onTouch
D/ScrollView: ACTION_MOVE
D/ScrollView: onTouch
D/ScrollView: ACTION_MOVE
D/ScrollView: onTouch
D/ScrollView: ACTION_MOVE
D/ScrollView: onTouch
D/ScrollView: ACTION_MOVE
D/ScrollView: onTouch
D/ScrollView: ACTION_MOVE
D/ScrollView: onTouch
D/ScrollView: ACTION_MOVE
D/ScrollView: onTouch
D/ScrollView: ACTION_UP
ImageView で ACTION_UP が起きてないことがおかしな動作の原因の1つみたい。
先月24日も書いたけど、画像が拡大されていて移動できる場合の理想的な動きは
何なのか、というところから考えないといけない。悩ましいところだけど、一度
ImageView 画像の幅を小さくしてみる。
効率的じゃないけど、右側に ScrollView を動かすための場所を開けて、下に
スクロールするときは右側をスワイプさせるのがわかりやすいかな。このまま
だと少し反応が悪いので、右側の padding 狭めてやって。
他に何か美しい方法がないか、一晩考えてみる。
一晩考えたけど、良い考えは閃かなかった。と言うことで昨日の続き。見た目は
昨日やった ScrollView の右に逃げ道のスペースを開けて、動作は画像の拡大中
のときに ACTION_MOVE などのイベントを横取りする。そうすることで、ScrollView
でスクロールしないようにする。requestDisallowInterceptTouchEvent を使えば
ScrollView に載った ImageView のマウスイベントが優先できるとのことなので、
onCreate で入れてみたが、優先される様子がない。ACTION_DOWN などで毎回呼び
出す必要があるみたい。入れてみた。
case MotionEvent.ACTION_DOWN:
if(this.readWidth != originalBitmap.getWidth())
this.getParent().requestDisallowInterceptTouchEvent(true);
return true;
スクロールの横取りは出来たが、拡大中ではないときからピンチアウトでズーム
するときの動きが渋い。ACTION_MOVE で指の数を検出して、2以上のときも横取り
するようにしてみたら改善した。
動かして思ったが、拡大中に画像を一番下まで動かして、それ以上スワイプしたら
ScrollView の方をスクロールにするべきかどうかも悩むところ。
一番下に来たときの実装は入れないことにして、この状態で仮完成にし、クラス化
することにした。引っかかったところが2つ。最初は、ImageView からのクラスを
作成するときに。
public class ZoomImageView extends ImageView implements OnTouchListener
と OnTouchListener も継承して、この継承で必要な onTouch を
@Override
public boolean onTouch(View v, MotionEvent event){
でオーバーライドし、中でゴリゴリやっていたが、動かしてみるとイベントが
一向にやってこない。何でだろうと、ImageView の onTouchEvent をオーバー
ライドして書いてみたら動いた。OnTouchListener は継承する必要がなくなった
ので、やめることにした。
もう1つは、サイズ決定後に1度だけ実行したいことをどうやって実装しようか
調べたりして、
onLayout(boolean changed, int left, int top, int right, int bottom)
をオーバーライドし、changed が true になったときに実行することで何とか
なった。おかげで端末を回転したときは元のサイズに戻ってしまうけど、それは
問題ないかな。
小さいところでは、onTouchEvent で MotionEvent.ACTION_UP のときは true を
返していたけど、そうすると onDraw イベントが全然呼ばれなくてちょっと嵌った
ぐらい。Android の動きは掴みにくいところが多いと何度も感じた日だった。
慣れてないだけなのかもしれないが。
画像を拡大したりするところは完成したので、もう1つの悩みどころ、横長の
ときのレイアウトをどうするか。スマートフォンだったらまだしも、画面の大きな
タブレットなんかで。
こんな配置じゃ、見られたものじゃないし。画面が大きくて余裕があれば画像を
縦に合わせて。
こうしたい。右側の説明は長くなる可能性があるので TextView だけスクロール
できるように ScrollView に収めるようにすると、縦で使った View の再配置で
何とかならなくなる。ここは、縦用と横用のレイアウトを持って、setVisibility
の VISIBLE/INVISIBLE をするのが早そう。
端末の回転をすると、何も設定しなければ onCreate から始まり直すようなので、
onCreate で縦横の判定をして setVisibility をしてみた。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
int dpWidth = (int)(displayMetrics.widthPixels / displayMetrics.density);
int dpHeight = (int)(displayMetrics.heightPixels / displayMetrics.density);
if((dpWidth > dpHeight) && (dpWidth > 550)){
((ScrollView)findViewById(R.id.imageScrollView)).
setVisibility(View.INVISIBLE);
((ImageView)findViewById(R.id.horizontalFlowerImageView)).
setVisibility(View.VISIBLE);
((ScrollView)findViewById(R.id.horizontalTextScrollView)).
setVisibility(View.VISIBLE);
} else {
((ScrollView)findViewById(R.id.imageScrollView)).
setVisibility(View.VISIBLE);
((ImageView)findViewById(R.id.horizontalFlowerImageView)).
setVisibility(View.INVISIBLE);
((ScrollView)findViewById(R.id.horizontalTextScrollView)).
setVisibility(View.INVISIBLE);
}
}
横幅が 600dp を越えるときにしようとしたが、エミュレータの Nexus 7 だと
598 になって実験ができないので、550 にした。Nexus7 は 600 あると思って
いたが、displayMetrics.widthPixels が 1196 で、displayMetrics.density
が 2.0 で 598 となった。AVD Manager で見ると 800×1280 になっているし、
何で 1196 が返ってくるのだろう。動くには動いたので、深く考えないように
した。
昨日の続きで。Android で使われる単位で dp があるのだけど、HDMI に刺して
使うようなスティック型の端末の場合、表示されるサイズはモニタ依存なので
この dp という単位はどうなるのか気になった。検索を少ししてみたけれど、
スティック型端末で開発をしている人が少ないのか、良い情報が見つからず。
同じような位置づけである Amazon の Fire の情報はあったので、見てみた。
端末およびプラットフォームの仕様 - アマゾン アプリ 開発者ポータル
Amazon Fire TV Stick では、密度(dp)が
320 (1080p)
213 (720p)
となっている。1920×1080 の1080側が 320dp ということかな。そうすると、
1920 は 569 になる。600 ないのか。40 インチのテレビに写しても、最近売って
いるスマートフォンぐらいの扱いになってしまうな。
市販されている Android のスティック端末もそんなもんなんだろうか。
話は C# に飛んで。ファイルの保存とかしない自由気ままに動くソフトを作り
たかったのだけど、毎回毎回設定を変更し直すのも面倒になってきて、仕方なく
設定を保存しようかと。
古い人間なのでレジストリかなぁ、と考えていたけど、今は xml なんかに保存
するのが流れらしい。そう言えば、レジストリが肥大化して Windows が重いと
言った話は聞かなくなったな。C# だと簡単に設定を保存したりできるらしいので
MSDN なんかを見ながらやってみた。
アプリケーションの設定で、値を指定。スコープのユーザとアプリケーションの
違いを見たいので、両方で設定。
Form1 実行時に保存して、どうなるかやってみる。
public Form1()
{
InitializeComponent();
Properties.Settings.Default.Save();
}
このプログラムは1日に作ったコードの再利用をしたもので、リビルドをすると
ListBoxTest.exe のバイナリができて、バイナリを実行すると、バイナリと同じ
場所に、ListBoxTest.exe.config というファイルができた。先月22日に書いた
けれど、Program Files の下にバイナリを置くとエラーが出るので、できれば
別な場所に保存したい。そうすると、Application.UserAppDataPath で取得した
場所を使うのが筋らしい。MSDN を見ると、
A typical base path is C:\Documents and Settings\username\Application Data.
とのことで、現在どんなファイルがあるのか見てみることにした。
Explorer で C:\Documents and Settings に行こうとすると、アクセスが拒否
されましたとかなんとか。C:\Documents and Settings\username は C:\User に
なっただろうから、C:\User\toyota の下の Application Data に行こうとする
も。
やっぱりアクセスが拒否されたで。自分の PC にもアクセスできない時代なの
かと嘆く前に、プロパティを見てみた。
使えない。嘆こう。でも、解決しないので、困ったときの cygwin で見てみる
ことにした。
$ cd c:
$ ls -l Documents\ and\ Settings
lrwxrwxrwx 1 SYSTEM SYSTEM 17 8月 22 2013 Documents and Settings -> /cygdrive
/c/Users
$ cd Users/toyota
$ ls -l Application\ Data
lrwxrwxrwx 1 toyota SYSTEM 40 12月 31 2013 Application Data -> /cygdrive/c/Use
rs/toyota/AppData/Roaming
username\Application Data は username\AppData\Roaming へのシンボリック
リンクになっている。何でこれを Explorer で辿ってくれないのかな。そんな
ことは置いて、その AppData\Roaming の中を見てみた。
この下にフォルダを作ったり、消したりしてみたが、管理者権限がどうこうと
文句を言われることがなかった。この下だったら、ファイルを作っても大丈夫
そうなので、Application.UserAppDataPath で取得した場所を使うことにする。
長くなったので、続きはまた今度。
昨日の続きで。Application.UserAppDataPath の値の確認をしてみたら、
C:\Users\toyota\AppData\Roaming\ListBoxTest\ListBoxTest\1.0.0.0
が返ってきた。で、この値を取得した瞬間にこのフォルダも作成された。こんな
フォルダ嫌だな。MSDN によると。
If a path does not exist, one is created in the following format:
Base Path\CompanyName\ProductName\ProductVersion
だそうで。AssemblyInfo.cs は [assembly: AssemblyCompany("")] になっていて
CompanyName が無い場合だと、ProductName が入るということなのかな。返って
きたフォルダの2つ上のフォルダに settings.xml というファイルを作ることに
した。
string readFile = Path.Combine(Directory.GetParent(Directory.GetParent(
Application.UserAppDataPath).FullName).FullName, "settings.xml");
1つにまとめてしまったけど、これと等価。
string a = Application.UserAppDataPath;
string b = Directory.GetParent(a).FullName;
string c = Directory.GetParent(b).FullName;
string readFile = Path.Combine(c, "settings.xml");
これで、目的の場所に settings ファイルが作れそうなので、簡単にクラスに
収めた。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml.Serialization;
namespace ListBoxTest
{
public class SettingFile
{
SettingData setting;
public SettingFile()
{
setting = new SettingData();
setting.PositionX = -1;
setting.PositionY = -1;
}
public void readSetting()
{
string readFile = Path.Combine(Directory.GetParent(
Directory.GetParent(Application.UserAppDataPath).FullName)
.FullName, "settings.xml");
if (File.Exists(readFile))
{
XmlSerializer xmlSerializer =
new XmlSerializer(typeof(SettingData));
StreamReader streamReader =
new StreamReader(readFile, new UTF8Encoding(false));
setting =
((SettingData)xmlSerializer.Deserialize(streamReader));
}
else
{
saveSetting();
}
}
public void saveSetting()
{
string writeFile = Path.Combine(Directory.GetParent(
Directory.GetParent(Application.UserAppDataPath).FullName)
.FullName, "settings.xml");
XmlSerializer xmlSerializer =
new XmlSerializer(typeof(SettingData));
StreamWriter streamWriter =
new StreamWriter(writeFile, false, new UTF8Encoding(false));
xmlSerializer.Serialize(streamWriter, setting);
}
}
public class SettingData
{
public int PositionX { get; set; }
public int PositionY { get; set; }
}
}
これで readSetting を実行したら、
C:\Users\toyota\AppData\Roaming\ListBoxTest\settings.xml
ができた。中身を見てみると。
<?xml version="1.0" encoding="utf-8"?>
<SettingData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="h
ttp://www.w3.org/2001/XMLSchema">
<PositionX>-1</PositionX>
<PositionY>1</PositionY>
</SettingData>
大丈夫そう。
昨日の続きで。設定ファイルのテストなんかをしていて、ファイルが無かった
場合は、終了するというコードを書いていた。
public void readSetting()
{
string readFile = Path.Combine(Directory.GetParent(
Directory.GetParent(Application.UserAppDataPath).FullName)
.FullName, "settings.xml");
if (File.Exists(readFile))
{
XmlSerializer xmlSerializer =
new XmlSerializer(typeof(SettingData));
StreamReader streamReader =
new StreamReader(readFile, new UTF8Encoding(false));
setting =
((SettingData)xmlSerializer.Deserialize(streamReader));
}
else
{
Application.Exit();
//saveSetting();
}
}
しかし、プログラムが終了しない。Application.Exit で調べてみると、MSDN にも、
This method does not necessarily force the application to exit.
と書いてある。じゃ、ここで必ず終わらせるにはどうしたら良いんだ、と考えて
しまった。出た結果が。
//Application.Exit();
int i = 0;
i = 1 / i;
//saveSetting();
直接やったらコンパイル時にエラーが起きたので、一度代入しているのだけど、
これで終了できた。落ち方が綺麗か汚いかどうでも良かったので。
久しぶりに昔聴いていた音楽を聴きたくなり、実家に行って CD を取ってくるのが
面倒だったので、旅するとき用に CD から作った MP3 を見つけて聞こうとしたが。
その昔は winamp で聴いていたけど、winamp なんて何年か前に無くなってしまった
ものだし、Windows Media Player は変なところにアクセスしたりするので、一度も
使っていない状態だし。
Program Files の下を見ていたら vlc を見つけた。大丈夫だとは思うけど、MP3
が再生できるか投げてみたら問題なく。vlc ってプレイリストって作れるのだっけ、
とやってみたら作れた。なんだか、変な気もするが vlc で再生することにした。
使っている Windows PC のメモリがやっぱり足りなくなるので、追加で買おうか
少し悩む。規格は DDR3-1600 で4スロットあり、4GB が3本刺さっているので、
あと 4GB 追加できる。今あるメモリと同じものを買えば、デュアルチャンネルで
動作させることができるので、調べてみた。
まずは bios を見てみると、CL11 みたい。
メモリを1本引っこ抜いてみた。ASint というシールがあって、チップは hynix の
もので、両面実装。
ちょっと調べてみたけど、同じメモリを入手するのは難しそう。そもそも、この
構成でデュアルチャンネルで動いているのか気になったので cpu-z で見てみた。
Dual と出ているので、デュアルチャンネルみたい。メモリ速度がちょっとぐらい
遅くなっても良いから、増設優先で 4GB を1枚買うか、速度も考え1枚無駄にして
4GB の同じものを2枚買うか、悩むところ。
先月植えたジャガイモ畑に、ちょっと前から穴ができていた。埋めても3日も
すると穴がまた開いていたり、ちょっと離れたところに新しい穴ができたり。
玄関近くにモグラ塚があるので、モグラの仕業かなと思っていたが、この前の
日曜日に穴からネズミが飛び出してくるのを見てしまった。モグラだった肉食
なので問題ないけど、ネズミだと植えたジャガイモを食べられてしまう。これは
困ったということで、ネズミ捕りを買った。
良くある挟んで捕まえるネズミ捕りは、鳥や猫が掛かったらかわいそうだし、
外に置くので粘着タイプは使えないので、籠タイプにした。間違って鳥は入って
しまったら、そのまま逃がしてあげる予定。
昨日の続き。ネズミ捕りに付ける餌を何にしようか冷蔵庫を開けて、ベーコンを
見つけたので小さくちぎって仕掛けてみた。ネズミ捕りの周りにも置いたりしたが
今日の朝見てみたらアリが集まっただけだった。明日見て、餌が無くなっていたら
厚揚げに変えるかな。
html に SVG ファイルの画像を表示しようと思ったが、img タグを使わず object
タグを使っている人が多かったりと、どう指定すべきか迷った。wikipedia から
SVG ファイルを持ってきて、img タグなどでテストしてみた。ちょっと大きかった
ので、別ファイルで。
手元のブラウザ4つで試したが、img タグで問題なく表示できた。object タグで
指定する必要ないな。
昨日使った SVG ファイルの先頭部分は。
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30"
width="400" height="200">
この width と height を消したとき、ブラウザでの表示がどうなるかやってみた。
ブラウザで直接表示したら、ブラウザに収まるいっぱいのサイズで表示された。
昨日の html ファイルをちょっといじって、html で表示してみた。div タグは
サイズがないと表示しないので省略して、出てきた画像は iframe タグ以外、
やっぱり収まる最大サイズになった。サイズの概念がないので、こうなるのか。
作ったアプリを Google Play に登録してみた。わかるところから1つ1つ進めて
いったけれど、2日経っても「コンテンツのレーティング」だけ一向にチェックが
付かない。
何でだろうかと思ってもう一度やってみたら、前に入力したときよりも先の画面に
進むことができて、すぐにチェックが付いた。順番通りに入力していかないと、
ここは先に進めないものなのかもしれない。ということで、無事に登録できた。
出かけるので、3日ほど更新はありません。
Google Play にアプリを登録した。登録する際にメールアドレスを入力する必要が
あったので、専用のメールアドレスを作り、それを使って登録した。登録して
数日経って、メールが届いていた。
中を確認すると、広告メールのような感じで。全てのアプリを登録している会社
からで、ページを用意しているだとかプレミアムアカウントになれるだとか、
そんな感じの内容。やっぱりアプリを登録すると、広告メールとか来るリスクが
あるみたい。分けておいて良かった。
使っていないドメインがあったので、更新手続きをしないで放置をした。その
ドメインが1週間ほど前に切れたのだけど、今日変なメールが来た。
切れたドメインを更新料1年分の3倍近い値段で売ると。つまり、うっかり更新
し忘れた人のドメインを買って、高く売る商売なのかな。この値段で買ってくれる
人の割合とか考えると、良い商売じゃないと思うのだけど、どうなんだろう。
それにしても、色々な商売があるものだな。
Android の話。設定で1つ選択するするものに対して、RadioGroup を使って、
スクロールにも対応するよう ScrollView と LinearLayout の中に収めていた。
id とかだいぶ省略したけど、xml はこんな感じ。
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="select year" />
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" >
<RadioButton
android:layout_width="wrap_content"
android:layout_height="40dp"
android:text="2006" />
<RadioButton
android:layout_width="wrap_content"
android:layout_height="40dp"
android:text="2007" />
~省略~
<RadioButton
android:layout_width="wrap_content"
android:layout_height="40dp"
android:text="2016" />
</RadioGroup>
<Button
android:text="OK"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</LinearLayout>
</ScrollView>
チェックの色がピンクになるし、文字とのバランスも良くないと思っていたが、
動いているのでそのままにしていた。
そんな話は一度置いて。今日、別なアプリで ListView を使っていて、
ArrayAdapter<String> arrayAdapter =
new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
arrayAdapter.add("Yokohama");
arrayAdapter.add("Kobe");
arrayAdapter.add("Nagoya");
yearListView.setAdapter(arrayAdapter);
と動的にリストを追加していたとき、simple_list_item_1 以外にどんなものが
あるのか疑問になって調べてみた。AppData/Local/Android 以下を探してみると
いくつか見つかった。
$ pwd
/cygdrive/c/Users/toyota/AppData/Local/Android
$ find ./ -name "simple_list_item_1.xml"
./android-sdk/platforms/android-19/data/res/layout/simple_list_item_1.xml
./android-sdk/platforms/android-21/data/res/layout/simple_list_item_1.xml
./android-sdk/platforms/android-22/data/res/layout/simple_list_item_1.xml
./android-sdk/platforms/android-23/data/res/layout/simple_list_item_1.xml
./sdk/platforms/android-19/data/res/layout/simple_list_item_1.xml
./sdk/platforms/android-23/data/res/layout/simple_list_item_1.xml
バージョンとかファイルの違いを見てみた。
$ diff ./sdk/platforms/android-23/data/res/layout/simple_list_item_1.xml ./andr
oid-sdk/platforms/android-23/data/res/layout/simple_list_item_1.xml
$ diff ./android-sdk/platforms/android-19/data/res/layout/simple_list_item_1.xm
l ./android-sdk/platforms/android-23/data/res/layout/simple_list_item_1.xml
25,26c25
< android:minHeight="?android:attr/listPreferredItemHeightSmall"
< />
---
> android:minHeight="?android:attr/listPreferredItemHeightSmall" />
同じみたい。android-23 で simple_list と名前を付くファイルを出してみた。
$ ls sdk/platforms/android-23/data/res/layout/simple_list*.xml
sdk/platforms/android-23/data/res/layout/simple_list_item_1.xml
sdk/platforms/android-23/data/res/layout/simple_list_item_2.xml
sdk/platforms/android-23/data/res/layout/simple_list_item_2_single_choice.xml
sdk/platforms/android-23/data/res/layout/simple_list_item_activated_1.xml
sdk/platforms/android-23/data/res/layout/simple_list_item_activated_2.xml
sdk/platforms/android-23/data/res/layout/simple_list_item_checked.xml
sdk/platforms/android-23/data/res/layout/simple_list_item_multiple_choice.xml
sdk/platforms/android-23/data/res/layout/simple_list_item_single_choice.xml
1があるから2もあるだろうとは思っていたが、意外とあった。中に choice が
あるので、やってみようか、というのが最初の文に繋がる。1つだけ選択できる
感じの simple_list_item_single_choice.xml を試してみた。
ListView で choiceMode という指定ができるようで、選べるのは4種類あった。
singleChoice にして、xml はこんな感じになった。
<ListView
android:id="@+id/mainListView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/cities"
android:choiceMode="singleChoice" />
これだけで実行してみたけど、選べる感じはなくてただのリストになった。
entries を一旦削除し、ArrayAdapter を使って simple_list_item_single_choice
の指定で、ListView にアタッチしてみた。
ArrayAdapter<String> arrayAdapter =
new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_single_choice,
getResources().getStringArray(R.array.cities));
((ListView)findViewById(R.id.mainListView)).setAdapter(arrayAdapter);
これで実行してみた。
相変わらず、チェックはピンクなんだな。こういうのって、どれが動作が軽い
とか考えるべきなのか、拡張性をとるべきなのか、考えてしまう。
昨日の続き。simple_list_item_1 と simple_list_item_single_choice を試した
けれど、それ以外はどうなるのかやってみることにした。layout のフォルダから
list_item と付くものを出す。
$ ls *list_item*
activity_chooser_view_list_item.xml
activity_list_item.xml
activity_list_item_2.xml
floating_popup_overflow_image_list_item.xml
floating_popup_overflow_list_item.xml
media_route_list_item.xml
permissions_package_list_item.xml
resolve_list_item.xml
simple_expandable_list_item_1.xml
simple_expandable_list_item_2.xml
simple_list_item_1.xml
simple_list_item_2.xml
simple_list_item_2_single_choice.xml
simple_list_item_activated_1.xml
simple_list_item_activated_2.xml
simple_list_item_checked.xml
simple_list_item_multiple_choice.xml
simple_list_item_single_choice.xml
simple_selectable_list_item.xml
test_list_item.xml
two_line_list_item.xml
これを昨日のコードの simple_list_item_single_choice に差し込んで、どんな
表示になるのかやってみた。
指定時のコンパイルエラーが出たり、データの違いのせいなのか起動時に落ちて
しまうものがほとんどで、表示できたのは下の8つ。
simple_expandable_list_item_1.xml
simple_list_item_1.xml
simple_list_item_activated_1.xml
simple_list_item_checked.xml
simple_list_item_multiple_choice.xml
simple_list_item_single_choice.xml
simple_selectable_list_item.xml
test_list_item.xml
せっかくなので、それぞれの見た目を出してみた。
simple_expandable_list_item_1
|
simple_list_item_1
|
simple_list_item_activated_1
|
simple_list_item_checked
|
simple_list_item_multiple_choice
|
simple_list_item_single_choice
|
simple_selectable_list_item
|
test_list_item
|
simple_list_item_checked が良い感じだな。
EXCEL でシートのコピーをしようと思ったら、何か違うところをクリックして
しまったようで、保存する度にプライバシーどうこうと出るようになった。
プライバシーに関する注意: このドキュメントには、マクロ、ActiveX コントロール、
XML 拡張パックの情報、または Web コンポーネントが含まれています。これらには
ドキュメント検査機能で削除することができない個人情報が含まれている場合が
あります。
多分だけど、シートをコピーしようとして、その下の「コードの表示」をして
しまったみたい。
コードを閉じても、保存する度に出てくるし、嫌な感じなので、調べてみたけど
良い解決法がない。しばらくそのまま編集していたが、別なファイルを開くために
一度ファイルを閉じたら、次からは出なくなった。なんだ、閉じれば良かった
のか。無駄な時間を使ってしまった。
一昨日の続き。ListView に選択できる item をアタッチしたとき、初期選択を
したり、選択されたものを取得したりする。
目的のアプリでは1つだけ選択するものなので、simple_list_item_checked か
simple_list_item_single_choice が良さそうな感じなので、見た目的に良い感じの
simple_list_item_checked でやってみることにする。まずは、初期選択をして
みる。調べると ListView で setItemChecked を使えば良いみたい。こんな感じ。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayAdapter<String> arrayAdapter =
new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_checked,
getResources().getStringArray(R.array.cities));
((ListView)findViewById(R.id.mainListView)).setAdapter(arrayAdapter);
((ListView)findViewById(R.id.mainListView)).setItemChecked(1, true);
}
これでちゃんと初期選択できるようになった。
最初は1じゃなくて、0から始まるみたい。この ListView では、
android:choiceMode="singleChoice"
の指定をしているのだけど、複数 Check したらどうなるのかやってみた。上記の
コードの最後を。
((ListView)findViewById(R.id.mainListView)).setItemChecked(1, true);
((ListView)findViewById(R.id.mainListView)).setItemChecked(2, true);
にして、動作させてみた。
singleChoice の方が優先されるようで、最後に setItemChecked したものが有効
になった。どこが取得しているのか、のコードは明日に。
昨日の続き。ListView で選択したときには、ListView のときのイベントのまま
OnItemClickListener で場所を拾えば良いのだけど。
((ListView)findViewById(R.id.mainListView)).setOnItemClickListener(
new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(
AdapterView<?> parent, View view, int posision, long id) {
}
});
OK ボタンを押したりしたとき、どこが選択しているのか取得したい。どうやって
取得するのか、調べてみたら setItemChecked のように getCheckedItemPosition
なんかで簡単に取得できそうな感じ。
やってみると、あっさり取得できた。
((ListView)findViewById(R.id.mainListView)).getCheckedItemPosition()
複数の場合は、getCheckedItemIds か getCheckedItemPositions を使うみたい
なので、明日時間があったらやってみる。
昨日の続き。ListView で複数選択できるように、
android:choiceMode="multipleChoice"
として、複数選択した item を取得する。getCheckedItemPositions で取得する
ときに、SparseBooleanArray を使うようなのだけど、初めてなので少し調べて。
SparseBooleanArray sparseBooleanArray =
((ListView) findViewById(R.id.mainListView)).getCheckedItemPositions();
for(int i = 0; i < sparseBooleanArray.size(); i++){
if(sparseBooleanArray.get(i) == true){
// コード
}
}
起動した後、うまく取得できなかったりする。動かしていくうちにきちんと取得
できるようになる。どうも sparseBooleanArray はチェックされたりすると、
動的に追加したりするみたい。つまり、返ってくる数字は信用ならないもの。
なのでどうするのかと他の人のコードを見ていると keyAt を使って実際の位置を
取得している。
SparseBooleanArray sparseBooleanArray =
((ListView) findViewById(R.id.mainListView)).getCheckedItemPositions();
for(int i = 0; i < sparseBooleanArray.size(); i++){
int position = sparseBooleanArray.keyAt(i);
if(sparseBooleanArray.get(position) == true){
// コード
}
}
こうすることでちゃんと取得できるようになった。なんだか美しくないので、
少しだけ変えてみた。
for(int i = 0; i < sparseBooleanArray.size(); i++){
if(sparseBooleanArray.valueAt(i) == true){
int position = sparseBooleanArray.keyAt(i);
// コード
}
}
もう少し美しくならないかと調べて、getCheckedItemIds があるようなので、
やってみることにした。
for(long id:
((ListView) findViewById(R.id.mainListView)).getCheckedItemIds()){
// コード
}
美しくなったけど、これだと何故か動かない。getCheckedItemIds が何も返さない
のが原因だけど、その原因を調べてみたがわからないまま。deprecated な扱い
だけど、getCheckItemIdsにすると、きちんと動く。Android のバグなのかな。
素直に getCheckedItemPositions を使うしかない。
1月16日の続きで。いつも使っている Windows 8.1 64bit のメモリ、4日ほど
起動した状態を見てみると、Explorer が 1.5GB もコミットチャージで使って
いる。
これ、なんとかならないかと調べてみたら、Microsoft のコミュニティにそれ
っぽい質問があった。
Microsoft コミュニティ - メモリが開放されない?
最後まで読んだけど、わかったことは。
・解決策が見つかっていない
・多くの人が同じ現象に悩んでいる
根本的ではない解決策は、Explorer を再起動することなのだけど、それはそれで
美しくない。そもそも Microsoft のお膝元に書かれている内容で、2年もそれを
放置している Microsoft はどうなのかなと思ってしまう。Windows 10 にしたら
解決するもんなのかな。
Apache のログを見ていたら、変なアクセスがいくつかあった。パッと見て bot
っぽいのだけど。
183.206.173.117 - - [31/Mar/2016:19:08:22 +0900] "GET /fckeditor/editor/ HTTP/1
.1" 404 290 "-" "compatible;Baiduspider/2.0; +http://www.baidu.com/search/spide
r.html"
fckeditor って何か調べたら web ベースのエディタらしい。つまり、エディタを
設置しているかチェックして、あったら入口にするのでしょう。念のため、IP
アドレスを調べてみると、中国は中国なのだけど baidu からの IP ではなく、
China Mobile の IP アドレスだった。面白そうだから、200番応答させて何を
するのか観察しても良いな。
|