Androidはすぐにトップページの垂直スクロール図をまねて、クールに最後まで!


プロジェクト住所:https://github.com/JeasonWong/JikeGallery
話を多くしないで、先に効果をあげます。

この効果はすぐにアプリで見たので、いいと思いました。
まず、私の構想を述べます。
カスタムView Group
二つのImageViewと一つの影を積載してView
二つのImageViewとそれらのmargingTopを一定の法則で交互に制御することによって、onLayout()で実現します。
margingTopの具体的な値は、プロパティアニメーションによってコントロールされ、常にrequest Layout()を呼び出します。
次に順次説明します。
一、カスタムView Group

 //    
 protected static final int STATUS_SMOOTHING = 0;
 //    
 protected static final int STATUS_STOP = 1;

 //ViewGroup  
 protected int mWidth, mHeight;
 //   marginTop 
 protected int mSmoothMarginTop;
 //    
 protected int mStatus = STATUS_STOP;
 //      
 protected int mDuration = 500;
 //    
 protected int mRepeatTimes = 0;

 ...

 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  mWidth = w;
  mHeight = h;
  mSmoothMarginTop = -h;
  initView();
 }

 protected abstract void initView();

 ...

 /**
  *       
  *
  * @return   
  */
 protected boolean isOddCircle() {
  return mRepeatTimes % 2 == 1;
 }

まずメンバー変数を調べてみてください。その中で一番重要なのはmSmooth Margin Topです。多くの人が知っていると思います。ViewのmargingTopは負数に設定できます。この負数は私たちに多くの便利をもたらします。

上図の図0はスクリーンに表示されているImageViewです。図1はスクリーンの外にあるmarget Topが-heightのImageViewです。これは必ず分かります。これからは続けて実現します。
二つのImageViewと一つの影を積載してView

 private List<String> mImgList = new ArrayList<>();
 private ImageView[] mImgs = new ImageView[2];
 private View mShadowView;

 ...

 @Override
 protected void initView() {

  //      ,         
  if (mImgList.size() == 0) {
   return;
  }

  removeAllViews();

  MarginLayoutParams params = new MarginLayoutParams(mWidth, mHeight);

  //  ImageView      
  for (int i = 0; i < mImgs.length; i++) {
   mImgs[i] = new ImageView(getContext());
   addViewInLayout(mImgs[i], -1, params, true);
   Glide.with(getContext()).load(getImgPath(i)).centerCrop().into(mImgs[i]);
  }

  //    View
  mShadowView = new View(getContext());
  mShadowView.setBackgroundColor(Color.parseColor("#60000000"));
  mShadowView.setAlpha(0);
  addViewInLayout(mShadowView, -1, params, true);
 }

 ...

 /**
  *       
  * 
  * @param position   
  * @return     
  */
 private String getImgPath(int position) {
  position = position % mImgList.size();
  return mImgList.get(position);
 } 

ポイント説明:
Margin LayoutParaamsは後で便利にmagin値を取り出します。
addView InLayout()request Layoutに対する絶対制御のために
getImgPath()循環スクロールを実現するために
そうすれば、私たちが必要なViewは全部作成されました。
三、一定の法則によって、二つのImageViewとそれらのmargingTopを交互に制御して、onLayout()の中で実現します。

 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {

  int cCount = getChildCount();
  MarginLayoutParams cParams;

  for (int i = 0; i < cCount; i++) {
   View childView = getChildAt(i);
   cParams = (MarginLayoutParams) childView.getLayoutParams();

   int cl = 0, ct = 0, cr, cb;

   if (isOddCircle()) {
    if (i == 1) {
     cl = cParams.leftMargin;
     ct = mSmoothMarginTop + mHeight;
    } else if (i == 0) {
     cl = cParams.leftMargin;
     ct = mSmoothMarginTop;
    }
   } else {
    if (i == 0) {
     cl = cParams.leftMargin;
     ct = mSmoothMarginTop + mHeight;
    } else if (i == 1) {
     cl = cParams.leftMargin;
     ct = mSmoothMarginTop;
    }
   }
   //  shadowView
   if (i == 2) {
    cl = cParams.leftMargin;
    ct = mSmoothMarginTop + mHeight;
   }

   cr = cl + mWidth;
   cb = ct + mHeight;
   childView.layout(cl, ct, cr, cb);
  }

 }

以上の実装は、図1と図2のどちらが上で下の図と同じようにシャドウが入れ替わりますか?
四、margingTopの具体的な値は属性アニメーションによってコントロールされ、絶えずrequest Layoutを呼び出す()
まず基質View Groupを見ます。

 /**
  *     
  *
  */
 public void startSmooth() {

  if (mStatus != STATUS_STOP) {
   return;
  }

  ValueAnimator animator = ValueAnimator.ofFloat(-mHeight, 0);
  animator.setDuration(mDuration);
  animator.setInterpolator(new AccelerateDecelerateInterpolator());
  animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   @Override
   public void onAnimationUpdate(ValueAnimator animation) {

    float marginTop = (float) animation.getAnimatedValue();
    mSmoothMarginTop = (int) marginTop;

    if (marginTop == 0) {

     postDelayed(new Runnable() {
      @Override
      public void run() {

       mRepeatTimes++;

       mSmoothMarginTop = -mHeight;

       doAnimFinish();

       mStatus = STATUS_STOP;

      }
     }, 50);

    } else {
     doAnim();
    }
   }
  });
  animator.start();
  mStatus = STATUS_SMOOTHING;
 }

 //    
 protected abstract void doAnimFinish();

 //     
 protected abstract void doAnim();

ポイント説明:
属性アニメーションはmSmooth Markingtopをコントロールしています。[-mHeight,0]で変化します。
周を完成するごとに、mRepeatTimesは1を増加します。
ギャラリー実現類をもう一度見てください。

 @Override
 protected void doAnimFinish() {
  if (isOddCircle()) {
   Glide.with(getContext()).load(getImgPath(mRepeatTimes + 1)).centerCrop().into(mImgs[0]);
  } else {
   Glide.with(getContext()).load(getImgPath(mRepeatTimes + 1)).centerCrop().into(mImgs[1]);
  }
  mShadowView.setAlpha(0);
 }

 @Override
 protected void doAnim() {
  mShadowView.setAlpha(((1 - (-mSmoothMarginTop) / (float) mHeight)));
  requestLayout();
 }

ポイント説明:
mSmooth MargingTopとmHeightの比でシェーディングViewの透明度を制御します。
動画が完成するたびに、下の図(この時は下の図が画面を超えて表示されますが、上の図が画面に表示されます)は3枚目の図をロードして、getImgPath()を使って取り出します。
pic 1

以上は写真のスクロールの実現で、文字のスクロールの90%は同じで、少し区別するのは文字が下に垂直に中に置くことを制御する必要があるので、私は余計に述べません。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。