Pages

2013年4月9日火曜日

ViewPagerで画面数に合わせた背景画像の移動を実現する(後編)

前回の続きで、ViewPagerでページを切り替える度に背景が移動していく箇所を実装します。

ここでの大雑把な仕様は、
・最初のページは全ページの真ん中のページ(例えば全5ページなら3ページが初期位置)
・背景画像は必ず横長の画像
・背景は横にしか移動しない
ということにします。

ちなみに私はAndroidに関しては素人なので、この実装よりもいいやり方があれば是非是非教えて頂きたい。
というか、こんな実装を必要とする人っているのか??

それでは、とりあえず実装後のMainActivityはこんな感じとなります。
public class MainActivity extends Activity {
 private CustomAdapter adapter;
 
 private ViewPager mViewPager;
 private ImageView mImageView;
 private Bitmap mBitmap;
 
 private float displayW;
 private float displayH;
 
 // -----追記
 private Matrix matrix;
 private float currentBmpX;
    private int maxMoveBmpX;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        
        // カスタム PagerAdapter を生成
        adapter = new CustomAdapter(this);
        adapter.add(Color.RED);
        adapter.add(Color.GREEN);
        adapter.add(Color.BLUE);
        adapter.add(Color.BLACK);
        adapter.add(Color.WHITE);
 
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        displayW = metrics.widthPixels;
        displayH = metrics.heightPixels;
        
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true; // 画像そのものは読み込まない
        BitmapFactory.decodeResource(getResources(), R.drawable.wall, options);
        
        // dpi計算
        int bmpNativeHeight = options.outHeight;
        float dpiRatio = bmpNativeHeight / displayH;
        float calcDpi = metrics.densityDpi * dpiRatio;
        if (mBitmap == null || mBitmap.isRecycled()) {
            options.inJustDecodeBounds = false;
            options.inDensity = (int) calcDpi;
            mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.wall, options);
        }
        
        
        mImageView = new ImageView(this);
        mImageView.setImageBitmap(mBitmap);
        mImageView.setScaleType(ScaleType.MATRIX);
        addContentView(mImageView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        
        // ViewPager を生成
        mViewPager = new ViewPager(this);
        mViewPager.setAdapter(adapter);
        
        // -----追記
        matrix = new Matrix();
        currentBmpX = (mBitmap.getScaledWidth(metrics) / 2) - (displayW / 2);
        maxMoveBmpX = (int) (mBitmap.getScaledWidth(metrics) - displayW);
        mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
         private int currentPage;
         
   @Override
   public void onPageSelected(int pageNumber) {
    currentPage = pageNumber;
    currentBmpX = getAbsPositionX(pageNumber);
   }
   
   @Override
   public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    if (positionOffsetPixels == 0) {
     return; 
    }
    
    //page移動中の処理
    float scaledOffsetPixels = positionOffsetPixels * getScaleValue();
    
    // ページ毎の始点のx座標
    float absolutePosition = getAbsPositionX(position);
    currentBmpX = absolutePosition + scaledOffsetPixels;
    
          mImageView.setImageMatrix(getMatrix(matrix, -currentBmpX));
          mImageView.invalidate();
   }
   
   @Override
   public void onPageScrollStateChanged(int state) {
   }
   
   protected float getAbsPositionX(int page) {
    return (maxMoveBmpX / (adapter.getCount() - 1)) * page;
   }
  });
        
        addContentView(mViewPager, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
    }
    
    @Override
    protected void onResume() {
     super.onResume();
     mViewPager.setCurrentItem((adapter.getCount()/2), true);
     mImageView.setImageMatrix(getMatrix(matrix, -currentBmpX));
    }
    
 protected Matrix getMatrix(Matrix matrix, float xPosition) {
     if (xPosition > 0) {
      xPosition = 0;
     }
     if (Math.abs(xPosition) > maxMoveBmpX) {
      xPosition = -maxMoveBmpX;
     }
      
     matrix.setTranslate(xPosition, 0);
     return matrix;
    }
    
 public float getScaleValue() {
  float displayWidth = getWindowManager().getDefaultDisplay().getWidth();
  float fullScreenSize = displayWidth * (adapter.getCount()-1); 
  return maxMoveBmpX / fullScreenSize;
 }
}

前回と比較して、onCreate()で今回追加実装した箇所には「// -----追記」とコメントしています。
上の行番号で言うと、
11〜14行目
59〜96行目
その他のメソッドは今回追加実装してます。

まぁ、これだけで読む気も失せてくるんですが、説明を(今後の自分の為にも)。
まず、61行目と62行目の
currentBmpX = (mBitmap.getScaledWidth(metrics) / 2) - (displayW / 2);
maxMoveBmpX = (int) (mBitmap.getScaledWidth(metrics) - displayW);
ですが、
currentBmpXは、画像の左上を原点としたときの、現在画面に表示されている背景画像の左上のX座標です。
これの初期位置(つまり、中央のページで表示される背景画像の左上のX座標)を計算しています。
分かりにくいけど、計算で出しているのは上の画像の赤い点の位置ですね。
次に、maxMoveBmpXですが、これは最後のページで表示される背景画像のX座標の位置となります。

まぁ、このへんは特にポイントでも何でもないです。
今回のポイントとなる実装はViewPagerのsetOnPageChangeListenerですね。
上記コードの63行目〜96行目の部分です。

このListnerはページが動いているときにその状態を通知しまくってくれるので、その情報を使って背景画像の位置をImageView.setImageMatrixでズラしていくって感じですね。

OnPageChangeListenerでのポイントといえばポイントなんですが、
ページが進むと背景画像がマイナス方向(左方向)に移動する点。
何か、感覚的に理解しにくいのは私だけ??
それ以外は特にポイントってものは無いですね。実装も大したことしてないし。

あとポイントじゃないけど74行目〜76行目のif文。
別にこれなくても普通に動くんですけど、ページ移動中にそのときの移動距離が引数で渡ってくるんですが(positionOffsetPixels)、ページ移動が完了してからも何故かしばらく0が渡ってくるので、余計な処理を省く為に入れてます。
まぁ、なんとなくです。いらない気がしますね。。。。

長々と書きましたが、これでそれなりの動きになるはずです。
備忘録ついでに誰かのお役に立てれば幸いかと。

あ、変数名やメソッド名はあまり突っ込まないでね。

0 件のコメント:

コメントを投稿