Androidは視差効果のあるListViewを実現します。


視差効果は何ですか?
いわゆる視差効果はWebデザインとモバイルアプリケーションでよく見られます。いくつかの主要なプラットフォームでその姿を見つけることができます。Windows PhoneからiOS、Androidまで。ウィキペディアによると、視差スクロールはコンピュータグラフィックスの中の特殊なスクロール技術であり、このカメラの背景画像の移動は前景画像よりも遅く、視覚深度の偽画像を引き起こしている。
一体何が視差効果ですか?一緒に効果図を見れば分かります。
ListView HeaderView ListView のスライドに従って大きくなり、HeaderViewの中の写真はスケーリング効果があると見られます。これらは属性アニメーションを使用して実装できます。さあ、私たちは着手しましょう。
まずいくつかの属性をカスタマイズして、その後に使用できます。

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="ZoomListView">
 <!-- headerView    -->
  <attr name="header_height" format="dimension|reference"></attr>
  <!-- headerView      -->
 <attr name="header_max_height" format="dimension|reference"></attr>
 <!-- headerView            -->
  <attr name="header_max_scale" format="float"></attr>
 </declare-styleable>
</resources>
ZoomListView クラスを作成し、ListView から継承する。

public class ZoomListView extends ListView {
 
 //       
 private final float defaultHeaderMaxScale = 1.2f;
 //        
 private float headerMaxHeight;
 //       
 private float headerHeight;
 //         
 private float defaultHeaderHeight;
 //          
 private float defaultHeaderMaxHeight;
 private ImageView headerView;
 private ViewGroup.LayoutParams layoutParams;
 private LinearLayout linearLayout;
 //       
 private float headerMaxScale;

 public ZoomListView(Context context) {
  this(context, null);
 }

 public ZoomListView(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }

 public ZoomListView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  defaultHeaderHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 160, context.getResources().getDisplayMetrics());
  defaultHeaderMaxHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 240, context.getResources().getDisplayMetrics());
  TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ZoomListView);
  headerHeight = a.getDimension(R.styleable.ZoomListView_header_height, defaultHeaderHeight);
  headerMaxHeight = a.getDimension(R.styleable.ZoomListView_header_max_height, defaultHeaderMaxHeight);
  headerMaxScale = a.getFloat(R.styleable.ZoomListView_header_max_scale, defaultHeaderMaxScale);
  a.recycle();
  initView();
 }
 ...
}
ここまでは順番に属性の初期値を設定してinitView() を呼び出します。 initView() 方法を見てみます。

private void initView() {
 headerView = new ImageView(getContext());
 headerView.setScaleType(ImageView.ScaleType.CENTER_CROP);
 linearLayout = new LinearLayout(getContext());
 linearLayout.addView(headerView);
 layoutParams = headerView.getLayoutParams();
 if (layoutParams == null) {
  layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) headerHeight);
 } else {
  layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
  layoutParams.height = (int) headerHeight;
 }
 headerView.setLayoutParams(layoutParams);
 addHeaderView(linearLayout);
}

public void setDrawableId(int id) {
 headerView.setImageResource(id);
}
initView() headerView を作成し、ListViewの頭部に追加したことが分かる。setDrawableId(int id) は、headerView に関連する画像を設定するものである。
以下は視差効果の主な実現コードです。

@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
 if (deltaY < 0 && isTouchEvent) {
  if (headerView.getHeight() < headerMaxHeight) {
   int newHeight = headerView.getHeight()
     + Math.abs(deltaY / 3);
   headerView.getLayoutParams().height = newHeight;
   headerView.requestLayout();
   float temp = 1 + (headerMaxScale - 1f) * (headerView.getHeight() - headerHeight) / (headerMaxHeight - headerHeight);
   headerView.animate().scaleX(temp)
     .scaleY(temp).setDuration(0).start();
  }
 }
 return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
overScrollBy()方法を書き換えました。deltaYが0以下の場合(ListView は既にトップに到達していますが、ユーザのジェスチャーは下に引くべきです。)は、headerView の高さとheaderView scale の値を動的に設定します。これにより、headerView が高くなり、画像の拡大効果が得られます。
次に考えるべき問題は、ユーザが指を離すと元の姿に戻ります。したがって、私たちはonTouchEvent(MotionEvent ev) で関連操作を実現するべきです。

@Override
public boolean onTouchEvent(MotionEvent ev) {
 switch (ev.getAction()) {
  case MotionEvent.ACTION_UP:
    startAnim();
   break;
 }
 return super.onTouchEvent(ev);
}

//       
private void startAnim() {
 ValueAnimator animator = ValueAnimator.ofFloat(headerView.getHeight(), headerHeight);
 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
   float fraction = (float) animation.getAnimatedValue();
   headerView.getLayoutParams().height = (int) fraction;
   headerView.requestLayout();
  }
 });
 animator.setDuration(500);
 animator.setInterpolator(new LinearInterpolator());

 ValueAnimator animator2 = ValueAnimator.ofFloat(headerView.getScaleX(), 1f);
 animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
   float fraction = (float) animation.getAnimatedValue();
   headerView.setScaleX(fraction);
   headerView.setScaleY(fraction);
  }
 });
 animator2.setDuration(500);
 animator2.setInterpolator(new LinearInterpolator());
 animator.start();
 animator2.start();
}
上のコードは簡単に言えばACTION_uです。UPの時、2つの属性アニメーションを開始します。一つの属性アニメーションはheaderView の高さを元の値に戻します。もう一つの属性アニメーションはheaderView のscaleを再び1 fに回復します。みんなが分かると信じています。
締め括りをつける
以上がこの文章のすべての内容です。この文章の内容はAndroid開発者の皆さんの助けになります。質問があれば、メッセージを残して交流してください。