2012年5月30日水曜日

動的にImageViewのサイズと位置を同時に変更する

動的にImageViewのサイズと位置を同時に変更する

動的にImageViewのサイズと位置を変更することは、簡単そうに思える。本来なら簡単であらねばならない。しかし、苦労をしたので備忘録として書き留めておく。

静的にサイズを決定するのであれば、xmlにサイズを記載すれば良い。

TextViewやButtonは、その枠の大きさを動的に変更するメソッドが用意されている。そして、また、TextViewやButtonに多くの文字列が代入された場合、TextViewやButtonの枠が自動的に拡張されて、挿入した文字列の全てが表示される。

このような機能がImageViewにも用意されているだろうと期待する。しかし、残念なことに、そのようなものは無い。

ImageViewに描画されている画像を拡大しても、ImageViewのは拡大しない。画像を拡大した場合、ImageViewの枠内に収まっている画像は表示される。しかし、枠外に行ってしまった部分の画像は表示されない。
例えば「能」と描いた画像をImageViewに表示していたとしよう。この画像を2倍に拡大した場合、ImageViewには「ム」という画像(能という文字の左上部分)が表示される。「月」や「ヒ」は表示されない。
ImageViewのの大きさと、ImageViewに表示される画像の大きさとは、別物であり、各々別々に取り扱わねばならない。むむむ。

では、どのようにして、ImageViewの枠を拡大するのであろうか。
ImageViewを載せたxmlは次のとおりである。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    >
    <TextView
        android:text="@string/Menu_PieceSize"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textAppearance="?android:attr/textAppearanceMedium"
        >
    </TextView>
    <SeekBar
        android:id="@+id/SeekBarPieceSize"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        >
    </SeekBar>
    <TextView
        android:id="@+id/TextPieceSize"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        >
    </TextView>
    <ImageView
        android:id="@+id/ImagePieceSize"
        android:contentDescription="@string/Menu_PieceSize"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:src="@drawable/ic_launcher"
        android:scaleType="matrix"
        >
    </ImageView>
</LinearLayout>

ここでのポイントはImageViewのandroid:layout_widthやandroid:layout_heightに"fill_parent"を設定していることだ。xmlにおいて、ImageViewの枠を画面一杯に広げてしまおう、ということである。この技法により、枠に関する問題は解消する。

次の話題は、画像の拡大・縮小と移動とを同時に行うことである。これはJavaでMatrixを使って実現する。
下記のsample codeは、SeekBarをスライドすれば、その移動量に応じて、ドロイド君が拡大・縮小する。
拡大・縮小は、ImageViewの左上端を起点にして行われる。画像を常に画面の中央に位置付けるために、拡大・縮小の程度に応じて、画像の左上端を上下左右に移動させている。
public class PieceSizeActivity extends Activity

    implements
    OnSeekBarChangeListener
    {
    final String TAG = "PieceSizeActivity";
    SeekBar sb;
    ImageView iv;
    final int iMulti = 4;//1倍から4倍までの間で拡大縮小させる。
    final int iMaxSeekBar = 30;//1.0から4.0まで31個の目盛がある。
    int iImageSize;//ドロイド君の画像の一辺の長さ
    int iPieceSize;//利用者が選択した長さ
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.piecesize);
        Intent it = getIntent();
        
        //他のActivityからstartActivityForResultメソッドで呼ばれる。
        iPieceSize = it.getIntExtra("PieceSize", -1);
        if(iPieceSize<100 || iPieceSize>400) iPieceSize = 100;
        float f = iPieceSize;//整数から浮動小数点数へ変換する。
        f /= 100;//小数点数にする。
        TextView tv = (TextView)findViewById(R.id.TextPieceSize);
        tv.setText(String.valueOf(f));//小数点数を画面に表示する。
        
        sb = (SeekBar)findViewById(R.id.SeekBarPieceSize);
        sb.setMax(iMaxSeekBar);//最大値設定
        iPieceSize /= 10;
        iPieceSize -= 10;
        sb.setProgress(iPieceSize);//現在値設定
        sb.setOnSeekBarChangeListener(this);
        
        //ドロイド君の画像サイズを取得する。
        Resources r = getResources();
        Bitmap b = BitmapFactory.decodeResource(r, R.drawable.ic_launcher);
        iImageSize = b.getWidth();
        
        //ImageViewのアドレスを取得
        iv = (ImageView)findViewById(R.id.ImagePieceSize);
    }
    
    @Override  
    public void onWindowFocusChanged(boolean hasFocus) {  
        super.onWindowFocusChanged(hasFocus);
        if(iPieceSize>=0){//起動直後に表示する画像の設定
            Matrix m = new Matrix();
            m = new Matrix();
            float f = iPieceSize + 10;
            f /= 10;
            float fX = iImageSize * f;
            fX = (sb.getWidth() - fX) / 2;
            m.setTranslate(fX, 0);
            m.preScale(f, f);
            iv.setImageMatrix(m);
            iPieceSize = -1;
        }
    }  

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        if(seekBar==sb && fromUser==true){
            Matrix m = new Matrix();
            float f = progress + 10;
            f /= 10;//倍率としての値に変換する。
            float fX = iImageSize * f;
            fX = (sb.getWidth() - fX) / 2;//横位置を計算する。
            m.setTranslate(fX, 0);//最初に位置を設定する
            m.preScale(f, f);//次に拡大倍率を設定する。
            iv.setImageMatrix(m);//位置及び倍率を適用する。
            
            TextView tv;
            tv = (TextView)findViewById(R.id.TextPieceSize);
            tv.setText(String.valueOf(f));//画面に倍率を表示する。
            return;
        }
    }
(以下省略)

0 件のコメント:

コメントを投稿