2012年4月25日水曜日

AndroidでViewのスケーリングをするには?(Honeycomb以前のAPIで)

AndroidでViewのスケーリングをするにはどうしたらいいだろう。

ドキュメントを見ると、setScaleX()とsetScaleY()があるのでこれを使えばいいだろう・・・と思った。

しかし、これはHoneycomb以降の話。API Level 11以降だ_| ̄|○

setRotation()やsetAlpha()もAPI Level 11以降だった。このあたりの「常識だよね?」と思われるAPIは大体Honeycomb以降に追加されたのだとわかった。

では、API Level 11以前でViewを拡大縮小する方法は無いのか?

ここでScaleAnimationの事を思い出した。「実際には」無理かもしれないけど、ScaleAnimationを使えば「見え」だけは拡大縮小するはず・・・と考えてやってみた。





Eclipseプロジェクトファイル:ViewScaling.zip(153KB)

res/layout/main.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:id="@+id/target"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#660000"
        android:gravity="center"
        android:text="Pinch-IN or Pinch-OUT" />

</LinearLayout>

ViewScalingActivity.java
package jp.example;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
import android.view.animation.ScaleAnimation;
import android.widget.TextView;


public class ViewScalingActivity extends Activity {


  protected TextView target;
  protected ScaleGestureDetector sgd;
  protected boolean isPinch = false;
  protected float scalePrev = 1.0f;
  protected float spanPrev = 0.0f;


  @Override
  public void onCreate(Bundle savedInstanceState) {

    // お約束
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // ScaleGestureDetector作成
    sgd = new ScaleGestureDetector(this, new MySimpleOnScaleGestureListener());

    // ターゲット取得
    target = (TextView) findViewById(R.id.target);

  }


  @Override
  public boolean onTouchEvent(MotionEvent event) {
    sgd.onTouchEvent(event);
    switch (event.getAction()) {
      case MotionEvent.ACTION_UP:
        procActionUp();
        break;
    } // END switch
    return false;
  } // END onTouchEvent()


  protected void procActionUp() {

    // ピンチ動作だったらスケーリングなし状態までアニメーションさせる
    if (isPinch) {
      ScaleAnimation anim = new ScaleAnimation(scalePrev, 1.0f, scalePrev, 1.0f, target.getWidth() / 2, target.getHeight() / 2);
      anim.setDuration(250);
      target.startAnimation(anim);
    }

    // 各種パラメーターリセット
    isPinch = false;
    scalePrev = 1.0f;
    spanPrev = 0.0f;

  } // END procActionUp()


  protected class MySimpleOnScaleGestureListener extends SimpleOnScaleGestureListener {


    @Override
    public boolean onScale(ScaleGestureDetector detector) {

      // 2つの指の距離を使って遊びを持たせる(一定の距離以下ならピンチとみなさない)
      float spanCurr = detector.getCurrentSpan();
      if (Math.abs(spanCurr - spanPrev) < 20) {
        return false;
      }

      // ピンチ動作とみなす
      isPinch = true;

      // アニメーションが走っていたら止める
      if (target.getAnimation() != null) {
        target.getAnimation().cancel();
      }

      // 現在のスケーリング
      float scaleCurr = detector.getScaleFactor();
      Log.v("onScale", "scalePrev=" + scalePrev + " scaleCurr=" + scaleCurr + " spanCurr=" + spanCurr + " spanPrev=" + spanPrev);

      // スケールアニメーション開始
      ScaleAnimation anim = new ScaleAnimation(scalePrev, scaleCurr, scalePrev, scaleCurr, target.getWidth() / 2, target.getHeight() / 2);
      anim.setDuration(100); // あまり速すぎるとガクガクする
      anim.setFillEnabled(true); // チラつき防止
      anim.setFillAfter(true); // チラつき防止
      target.startAnimation(anim);

      // 各種パラメーター保存
      spanPrev = spanCurr;
      scalePrev = scaleCurr;

      return super.onScale(detector);

    }
  } // END class MySimpleOnScaleGestureListener


} // END class


ポイントはonScale()でチラつき防止のためにScaleAnimationにsetFillEnabled(true)とsetFillAfter(true)しているところ。

API Level 10でも何とかViewのスケーリングができたε-(´∀`*)ホッ