Android实现层叠卡片式banner

作者:Banboofly 时间:2023-03-04 03:57:08 

本文实例为大家分享了Android实现层叠卡片式banner的具体代码,供大家参考,具体内容如下

效果图如下:

Android实现层叠卡片式banner

背景

由于公司VIP模块项目需要,本着对ui设计师的尊重,需要实现以上效果图,在网上找了很多博客,都不能满足上面的需求,所以就只能自己硬着头皮自定义了,下面就是我自定义的view代码,做个记录:


package cn.com.cunw.familydesk.view.vipBanner;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import androidx.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.widget.RelativeLayout;
import android.widget.Scroller;

import java.util.ArrayList;

import cn.com.cunw.familydesk.R;

/**
* @Description:
* @Author: wuJie
* @CreateDate: 2020/1/4 10:14
* Copyright (C), 2015-2020,
*/
public class CoverFlowView extends RelativeLayout {

public enum CoverFlowGravity {
 TOP, BOTTOM, CENTER_VERTICAL
}

public enum CoverFlowLayoutMode {
 MATCH_PARENT, WRAP_CONTENT
}

protected CoverFlowGravity mGravity;

protected CoverFlowLayoutMode mLayoutMode;

private Scroller mScroller;
/**
 * To store reflections need to remove
 */
private ArrayList<View> removeViewArray;

private SparseArray<View> showViewArray;

private int paddingLeft;
private int paddingRight;
private int paddingTop;
private int paddingBottom;

private int mWidth; // 控件的宽度

private float reflectHeightFraction;
private int reflectGap;

private int mChildHeight; // child的高度
private int mChildTranslateY;
//private int mReflectionTranslateY;

private int mVisibleChildCount; // 一屏显示的图片数量

protected int VISIBLE_VIEWS = 3; // the visible views left and right 左右两边显示的个数

private ICoverFlowAdapter mAdapter;

private float mOffset;
//private int mLastOffset;

private final int ALPHA_DATUM = 200; // 基础alphaֵ
private int STANDARD_ALPHA;
// 基础缩放值
//private static final float CARD_SCALE = 0.15f;

private static float MOVE_POS_MULTIPLE = 3.0f;
private static final int TOUCH_MINIMUM_MOVE = 5;
private static final float MOVE_SPEED_MULTIPLE = 1;
private static final float MAX_SPEED = 6.0f;
private static final float FRICTION = 10.0f;

private VelocityTracker mVelocity;

private int firstIndex = 0;

public CoverFlowView(Context context) {
 super(context);
 init();
}

public CoverFlowView(Context context, AttributeSet attrs) {
 super(context, attrs);
 initAttributes(context, attrs);
 init();
}

public CoverFlowView(Context context, AttributeSet attrs, int defStyle) {
 super(context, attrs, defStyle);
 initAttributes(context, attrs);
 init();
}

private void initAttributes(Context context, AttributeSet attrs) {
 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ImageCoverFlowView);
 int totalVisibleChildren = a.getInt(
   R.styleable.ImageCoverFlowView_visibleImage, 3);
 if (totalVisibleChildren % 2 == 0) { // 一屏幕必须是奇数显示
  throw new IllegalArgumentException("visible image must be an odd number");
 }

VISIBLE_VIEWS = totalVisibleChildren >> 1; // 计算出左右两两边的显示个数

reflectHeightFraction = a.getFraction(
   R.styleable.ImageCoverFlowView_reflectionHeight, 100, 0, 0.0f);

if (reflectHeightFraction > 100) {
  reflectHeightFraction = 100;
 }
 reflectHeightFraction /= 100;
 reflectGap = a.getDimensionPixelSize(
   R.styleable.ImageCoverFlowView_reflectionGap, 0);

mGravity = CoverFlowGravity.values()[a.getInt(
   R.styleable.ImageCoverFlowView_coverflowGravity,
   CoverFlowGravity.CENTER_VERTICAL.ordinal())];

mLayoutMode = CoverFlowLayoutMode.values()[a.getInt(
   R.styleable.ImageCoverFlowView_coverflowLayoutMode,
   CoverFlowLayoutMode.WRAP_CONTENT.ordinal())];

a.recycle();
}

private void init() {
 removeAllViews();
 setWillNotDraw(false);
 setClickable(true);

if (mScroller == null) {
  mScroller = new Scroller(getContext(), new AccelerateDecelerateInterpolator());
 }
 if (showViewArray == null) {
  showViewArray = new SparseArray<View>();
 } else {
  showViewArray.clear();
 }
 if (removeViewArray == null) {
  removeViewArray = new ArrayList<View>();
 } else {
  removeViewArray.clear();
 }

firstIndex = 0;
 mChildHeight = 0;
 mOffset = 0;
 //mLastOffset = -1;

isFirstIn = true;
 lastMid = 1;
 isChange = true;

// 计算透明度
 STANDARD_ALPHA = (255 - ALPHA_DATUM) / VISIBLE_VIEWS;

if (mGravity == null) {
  mGravity = CoverFlowGravity.CENTER_VERTICAL;
 }

if (mLayoutMode == null) {
  mLayoutMode = CoverFlowLayoutMode.WRAP_CONTENT;
 }

// 一屏 显示的图片数量
 int visibleCount = (VISIBLE_VIEWS << 1) + 1;

for (int i = 0; i < visibleCount && mAdapter != null && i < mAdapter.getCount(); ++i) {

View convertView = null;
  if (removeViewArray.size() > 0) {
   convertView = removeViewArray.remove(0);
  }
  View view = mAdapter.getView(i, convertView, this);
  showViewArray.put(i, view);
  addView(view);
 }

requestLayout();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);

if (mAdapter == null || showViewArray.size() <= 0) {
  return;
 }

paddingLeft = getPaddingLeft();
 paddingRight = getPaddingRight();
 paddingTop = getPaddingTop();
 paddingBottom = getPaddingBottom();

int heightMode = MeasureSpec.getMode(heightMeasureSpec);
 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
 int heightSize = MeasureSpec.getSize(heightMeasureSpec);

// 一屏显示的图片数量
 int visibleCount = (VISIBLE_VIEWS << 1) + 1;

// 控件高度
 int availableHeight = heightSize - paddingTop - paddingBottom;

int maxChildTotalHeight = 0;
 for (int i = 0; i < getChildCount() && i < visibleCount && i < showViewArray.size(); ++i) {

//View view = showViewArray.get(i+firstIndex);
  View view = getChildAt(i);
  measureChild(view, widthMeasureSpec, heightMeasureSpec);

//final int childHeight = ScreenUtil.dp2px(getContext(), 110);
  final int childHeight = view.getMeasuredHeight();
  final int childTotalHeight = (int) (childHeight + childHeight
    * reflectHeightFraction + reflectGap);

// 孩子的最大高度
  maxChildTotalHeight = (maxChildTotalHeight < childTotalHeight) ? childTotalHeight
    : maxChildTotalHeight;
 }

// 如果控件模式为确切值 或者 最大时
 if (heightMode == MeasureSpec.EXACTLY || heightMode == MeasureSpec.AT_MOST) {
  // if height which parent provided is less than child need, scale
  // child height to parent provide
  // 如果控件高度小于孩子控件高度,则缩放孩子高度为控件高度
  if (availableHeight < maxChildTotalHeight) {
   mChildHeight = availableHeight;
  } else {
   // if larger than, depends on layout mode
   // if layout mode is match_parent, scale child height to parent
   // provide
   // 如果是填充父窗体模式 则将孩子的高度 设为控件高度
   if (mLayoutMode == CoverFlowLayoutMode.MATCH_PARENT) {
    mChildHeight = availableHeight;
    // if layout mode is wrap_content, keep child's original
    // height
    // 如果是包裹内容 则将孩子的高度设为孩子允许的最大高度
   } else if (mLayoutMode == CoverFlowLayoutMode.WRAP_CONTENT) {
    mChildHeight = maxChildTotalHeight;

// adjust parent's height
    // 计算出控件的高度
    if (heightMode == MeasureSpec.AT_MOST) {
     heightSize = mChildHeight + paddingTop + paddingBottom;
    }
   }
  }
 } else {
  // height mode is unspecified
  // 如果空间高度 没有明确定义
  // 如果孩子的模式为填充父窗体
  if (mLayoutMode == CoverFlowLayoutMode.MATCH_PARENT) {
   mChildHeight = availableHeight;
   // 如果孩子的模式为包裹内容
  } else if (mLayoutMode == CoverFlowLayoutMode.WRAP_CONTENT) {
   mChildHeight = maxChildTotalHeight;

// 计算出控件的高度
   // adjust parent's height
   heightSize = mChildHeight + paddingTop + paddingBottom;
  }
 }

// Adjust movement in y-axis according to gravity
 // 计算出孩子的原点 Y坐标
 if (mGravity == CoverFlowGravity.CENTER_VERTICAL) {// 竖直居中
  mChildTranslateY = (heightSize >> 1) - (mChildHeight >> 1);
 } else if (mGravity == CoverFlowGravity.TOP) {// 顶部对齐
  mChildTranslateY = paddingTop;
 } else if (mGravity == CoverFlowGravity.BOTTOM) {// 底部对齐
  mChildTranslateY = heightSize - paddingBottom - mChildHeight;
 }

//mReflectionTranslateY = (int) (mChildTranslateY + mChildHeight - mChildHeight
 //  * reflectHeightFraction);

setMeasuredDimension(widthSize, heightSize);
 mVisibleChildCount = visibleCount;
 mWidth = widthSize;

}

boolean isFirstIn = true; // 第一次初始化该控件
int lastMid = 1;
boolean isChange = true;

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
 if (mAdapter == null || mAdapter.getCount() <= 0 || showViewArray.size() <= 0) {
  return;
 }

final float offset = mOffset;
 int i = 0;
 int mid = (int) Math.floor(offset + 0.5);

//右边孩子的数量
 int rightChild = (mVisibleChildCount % 2 == 0) ? (mVisibleChildCount >> 1) - 1
   : mVisibleChildCount >> 1;
 //左边孩子的数量
 int leftChild = mVisibleChildCount >> 1;

if (!isFirstIn) {
  if (lastMid + 1 == mid) {
   int actuallyPositionStart = getActuallyPosition(lastMid - leftChild);
   View view = showViewArray.get(actuallyPositionStart);
   showViewArray.remove(actuallyPositionStart);
   removeViewArray.add(view);
   removeView(view);

View convertView = null;
   if (removeViewArray.size() > 0) {
    convertView = removeViewArray.remove(0);
   }
   int actuallyPositionEnd = getActuallyPosition(mid + rightChild);

//TODO
   View viewItem = mAdapter.getView(actuallyPositionEnd, convertView, this);

showViewArray.put(actuallyPositionEnd, viewItem);
   addView(viewItem);

isChange = true;

} else if (lastMid - 1 == mid) {
   int actuallyPositionEnd = getActuallyPosition(lastMid + rightChild);
   View view = showViewArray.get(actuallyPositionEnd);
   showViewArray.remove(actuallyPositionEnd);
   removeViewArray.add(view);
   removeView(view);

View convertView = null;
   if (removeViewArray.size() > 0) {
    convertView = removeViewArray.remove(0);
   }
   int actuallyPositionStart = getActuallyPosition(mid - leftChild);

//TODO
   View viewItem = mAdapter.getView(actuallyPositionStart, convertView, this);

showViewArray.put(actuallyPositionStart, viewItem);
   addView(viewItem, 0);

isChange = true;
  }

} else {
  isFirstIn = false;
 }

lastMid = mid;

// draw the left children
 // 计算左边孩子的位置
 int startPos = mid - leftChild;
 for (i = startPos; i < mid; ++i) {
  //TODO 计算左边孩子的位置
  View child = layoutLeftChild(i, i - offset);

if (isChange) {
   int actuallyPosition = getActuallyPosition(i);
   mAdapter.getData(child, actuallyPosition);
  }
 }

// 计算 右边 和 中间
 int endPos = mid + rightChild;

int j = -VISIBLE_VIEWS;
 for (i = endPos; i >= mid; --i, j = j + 2) {
  //TODO 计算右边和中间孩子的位置
  View child = layoutRightChild(i + j, i - offset);

if (isChange) {
   int actuallyPosition = getActuallyPosition(i);
   mAdapter.getData(child, actuallyPosition);
  }
 }

isChange = false;
}

private View layoutLeftChild(int position, float offset) {
 //获取实际的position
 int actuallyPosition = getActuallyPosition(position);
 View child = showViewArray.get(actuallyPosition);
 if (child != null) {
  makeChildTransfromer(child, actuallyPosition, offset);
 }
 return child;
}

private View layoutRightChild(int position, float offset) {

//获取实际的position
 int actuallyPosition = getActuallyPosition(position);
 View child = showViewArray.get(actuallyPosition);
 if (child != null) {
  makeChildTransfromer(child, actuallyPosition, offset);
 }

return child;
}

/**
 * 对 bitmap 进行伪 3d 变换
 *
 * @param child
 * @param position
 * @param offset
 */
private void makeChildTransfromer(View child, int position, float offset) {
 //child.layout(0, 0, ScreenUtil.dp2px(getContext(), 200),ScreenUtil.dp2px(getContext(), 110));
 child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());

float scale = 0;
 scale = 1 - Math.abs(offset) * 0.25f;

// 延x轴移动的距离应该根据center图片决定
 float translateX = 0;

final int originalChildHeight = (int) (mChildHeight - mChildHeight
   * reflectHeightFraction - reflectGap);

//final int childTotalHeight = (int) (child.getHeight()
 //  + child.getHeight() * reflectHeightFraction + reflectGap);

final float originalChildHeightScale = (float) originalChildHeight / child.getHeight();

final float childHeightScale = originalChildHeightScale * scale;

final int childWidth = (int) (child.getWidth() * childHeightScale);

final int centerChildWidth = (int) (child.getWidth() * originalChildHeightScale);

//final int centerChildWidth = (int) (child.getWidth() * childHeightScale);

int leftSpace = ((mWidth >> 1) - paddingLeft) - (centerChildWidth >> 1);
 int rightSpace = (((mWidth >> 1) - paddingRight) - (centerChildWidth >> 1));

//计算出水平方向的x坐标
 if (offset <= 0)
  translateX = ((float) leftSpace / VISIBLE_VIEWS)
    * (VISIBLE_VIEWS + offset) + paddingLeft;

else
  translateX = mWidth - ((float) rightSpace / VISIBLE_VIEWS)
    * (VISIBLE_VIEWS - offset) - childWidth - paddingRight;

//根据offset 算出透明度
 float alpha = 254 - Math.abs(offset) * STANDARD_ALPHA;

child.setAlpha(0);
 if (alpha < 0) {
  alpha = 0;
 } else if (alpha > 254) {
  alpha = 254;
 }
 child.setAlpha(alpha / 254.0f);

float adjustedChildTranslateY = 0;

ObjectAnimator anim1 = ObjectAnimator.ofFloat(child, "scaleX", 1.0f, childHeightScale);
 ObjectAnimator anim2 = ObjectAnimator.ofFloat(child, "scaleY", 1.0f, childHeightScale);
 AnimatorSet animSet = new AnimatorSet();
 animSet.setDuration(0);
 //两个动画同时执行
 animSet.playTogether(anim1, anim2);
 child.setPivotX(0);
 child.setPivotY(child.getHeight() / 2);
 //显示的调用invalidate
 child.invalidate();
 animSet.setTarget(child);
 animSet.start();

child.setTranslationX(translateX);
 child.setTranslationY(mChildTranslateY + adjustedChildTranslateY);

}

/**
 * 获取顶部Item position
 *
 * @return
 */
public int getTopViewPosition() {
 return getActuallyPosition(lastMid);
}

/**
 * 获取顶部Item View
 *
 * @return
 */
public View getTopView() {
 return showViewArray.get(getActuallyPosition(VISIBLE_VIEWS + lastMid));
}

/**
 * Convert draw-index to index in adapter
 *
 * @param position position to draw
 * @return
 */
private int getActuallyPosition(int position) {
 if (mAdapter!=null){

int max = mAdapter.getCount();
 position += VISIBLE_VIEWS;
 while (position < 0 || position >= max) {
  if (position < 0) {
   position += max;
  } else if (position >= max) {
   position -= max;
  }
  }
 }
 return position;
}

public void setAdapter(@NonNull ICoverFlowAdapter adapter) {
 mAdapter = adapter;
 init();
}

public ICoverFlowAdapter getAdapter() {
 return mAdapter;
}

private boolean onTouchMove = false; //是否正在执行触摸移动逻辑

private View touchViewItem = null;
private boolean isOnTopView = false;

@Override
public boolean onTouchEvent(MotionEvent event) {
 if (getParent() != null) {
  getParent().requestDisallowInterceptTouchEvent(true);
 }

if (isOnAnimator) {
  return false;
 }
 int action = event.getAction();

switch (action) {
  case MotionEvent.ACTION_DOWN:
   if (mAdapter==null){
    return false;
   }
   onTouchMove = true;
   if (mScroller.computeScrollOffset()) {
    mScroller.abortAnimation();
    invalidate();
    requestLayout();
   }
   touchBegan(event);
   touchViewItem = getTopView();
   isOnTopView = inRangeOfView(touchViewItem, event);
   if (isOnTopView) {
    sendLongClickAction();
   }
   return true;
  case MotionEvent.ACTION_MOVE:
   touchMoved(event);
   removeLongClickAction();
   touchViewItem = null;
   isOnTopView = false;
   return true;
  case MotionEvent.ACTION_UP:
   removeLongClickAction();
   if (isOnTopView && touchViewItem == getTopView()
     && inRangeOfView(touchViewItem, event)) {

if (mTopViewClickListener != null) {
     mTopViewClickListener.onClick(getTopViewPosition(), getTopView());
    }
   }
   touchViewItem = null;
   isOnTopView = false;
   touchEnded(event);
   return true;
 }

return false;
}

private Runnable longClickRunnable = null;

/**
 * 发送长点击事件 Runnable
 */
private void sendLongClickAction() {
 removeLongClickAction();
 longClickRunnable = new Runnable() {
  @Override
  public void run() {
   touchViewItem = null;
   isOnTopView = false;
   if (mTopViewLongClickListener != null) {
    mTopViewLongClickListener.onLongClick(getTopViewPosition(), getTopView());
   }
  }
 };
 postDelayed(longClickRunnable, 600);
}

/**
 * 移除长点击事件 Runnable
 */
private void removeLongClickAction() {
 if (longClickRunnable != null) {
  removeCallbacks(longClickRunnable);
  longClickRunnable = null;
 }
}

private boolean inRangeOfView(View view, MotionEvent ev) {
 Rect frame = new Rect();
 view.getHitRect(frame);
 if (frame.contains((int) ev.getX(), (int) ev.getY())) {
  return true;
 }
 return false;
}

private OnTopViewClickListener mTopViewClickListener;
private OnTopViewLongClickListener mTopViewLongClickListener;
private OnTopViewChangeListener mTopViewChangeListener;

/**
 * 设置 TopView 的点击监听
 *
 * @param topViewClickListener
 */
public void setOnTopViewClickListener(OnTopViewClickListener topViewClickListener) {
 this.mTopViewClickListener = topViewClickListener;
}

/**
 * 获取 TopView 的点击监听
 *
 * @return
 */
public OnTopViewClickListener getOnTopViewClickListener() {
 return this.mTopViewClickListener;
}

/**
 * 设置 TopView 的长点击监听
 *
 * @param topViewLongClickListener
 */
public void setOnTopViewLongClickListener(OnTopViewLongClickListener topViewLongClickListener) {
 this.mTopViewLongClickListener = topViewLongClickListener;
}

/**
 * 设置 TopView 的长点击监听
 *
 * @param mTopViewChangeListener
 */
public void setOnTopViewChangeListener(OnTopViewChangeListener mTopViewChangeListener) {
 this.mTopViewChangeListener = mTopViewChangeListener;
}

/**
 * 获取 TopView 的长点击监听
 *
 * @return
 */
public OnTopViewLongClickListener getOnTopViewLongClickListener() {
 return this.mTopViewLongClickListener;
}

public interface OnTopViewClickListener {

void onClick(int position, View itemView);

}

public interface OnTopViewLongClickListener {

void onLongClick(int position, View itemView);

}

public interface OnTopViewChangeListener {

void onItemChange(int position);

}

private boolean mTouchMoved;
private float mTouchStartPos;
private float mTouchStartX;
private float mTouchStartY;

private long mStartTime;
private float mStartOffset;

private void touchBegan(MotionEvent event) {
 endAnimation();

float x = event.getX();
 mTouchStartX = x;
 mTouchStartY = event.getY();
 mStartTime = AnimationUtils.currentAnimationTimeMillis();
 mStartOffset = mOffset;

mTouchMoved = false;

mTouchStartPos = (x / mWidth) * MOVE_POS_MULTIPLE - 5;
 mTouchStartPos /= 2;

mVelocity = VelocityTracker.obtain();
 mVelocity.addMovement(event);
}

private Runnable mAnimationRunnable;

private void endAnimation() {
 if (mAnimationRunnable != null) {
  mOffset = (float) Math.floor(mOffset + 0.5);

invalidate();
  requestLayout();

removeCallbacks(mAnimationRunnable);
  mAnimationRunnable = null;
 }
}

private void touchMoved(MotionEvent event) {
 float pos = (event.getX() / mWidth) * MOVE_POS_MULTIPLE - 5;
 pos /= 2;

if (!mTouchMoved) {
  float dx = Math.abs(event.getX() - mTouchStartX);
  float dy = Math.abs(event.getY() - mTouchStartY);
  if (dx < TOUCH_MINIMUM_MOVE && dy < TOUCH_MINIMUM_MOVE)
   return;
  mTouchMoved = true;
 }

mOffset = mStartOffset + mTouchStartPos - pos;

invalidate();
 requestLayout();
 mVelocity.addMovement(event);
}

private void touchEnded(MotionEvent event) {
 float pos = (event.getX() / mWidth) * MOVE_POS_MULTIPLE - 5;
 pos /= 2;

if (mTouchMoved || (mOffset - Math.floor(mOffset)) != 0) {
  mStartOffset += mTouchStartPos - pos;
  mOffset = mStartOffset;

mVelocity.addMovement(event);

mVelocity.computeCurrentVelocity(1000);
  float speed = mVelocity.getXVelocity();

speed = (speed / mWidth) * MOVE_SPEED_MULTIPLE;
  if (speed > MAX_SPEED)
   speed = MAX_SPEED;
  else if (speed < -MAX_SPEED)
   speed = -MAX_SPEED;

startAnimation(-speed);

if (mTopViewChangeListener != null) {
   mTopViewChangeListener.onItemChange(getTopViewPosition());
  }
 } else {
  Log.e("xyw", " touch ==>" + event.getX() + " , " + event.getY());
  onTouchMove = false;
 }

mVelocity.clear();
 mVelocity.recycle();
}

private float mStartSpeed;
private float mDuration;

private void startAnimation(float speed) {
 if (mAnimationRunnable != null) {
  onTouchMove = false;
  return;
 }

float delta = speed * speed / (FRICTION * 2);
 if (speed < 0)
  delta = -delta;

float nearest = mStartOffset + delta;
 nearest = (float) Math.floor(nearest + 0.5f);

mStartSpeed = (float) Math.sqrt(Math.abs(nearest - mStartOffset) * FRICTION * 2);
 if (nearest < mStartOffset)
  mStartSpeed = -mStartSpeed;

mDuration = Math.abs(mStartSpeed / FRICTION);
 mStartTime = AnimationUtils.currentAnimationTimeMillis();

mAnimationRunnable = new Runnable() {
  @Override
  public void run() {
   driveAnimation();
  }
 };
 post(mAnimationRunnable);
}

private void driveAnimation() {
 float elapsed = (AnimationUtils.currentAnimationTimeMillis() - mStartTime) / 1000.0f;
 if (elapsed >= mDuration) {
  endAnimation();
  onTouchMove = false;
 } else {
  updateAnimationAtElapsed(elapsed);
  post(mAnimationRunnable);
 }
}

private void updateAnimationAtElapsed(float elapsed) {
 if (elapsed > mDuration)
  elapsed = mDuration;

float delta = Math.abs(mStartSpeed) * elapsed - FRICTION * elapsed * elapsed / 2;
 if (mStartSpeed < 0)
  delta = -delta;

mOffset = mStartOffset + delta;
 invalidate();
 requestLayout();
}

@Override
public void computeScroll() {
 super.computeScroll();

// 算出移动到某一个虚拟点时 mOffset 的值,然后 invalidate 重绘
 if (mScroller.computeScrollOffset()) {
  final int currX = mScroller.getCurrX();
  mOffset = (float) currX / 100;

invalidate();
 }
}

private boolean isOnAnimator = false; //是否正在进行点击切换动画

/**
 * 翻到前页
 */
public void gotoPrevious() {
 doAnimator(-1.0f);

}

/**
 * 前进到后一页
 */
public void gotoForward() {
 doAnimator(1.0f);
}

public void doAnimator(float target) {
 if (isOnAnimator || onTouchMove) { //如果正在执行点击切换动画 或者 正在执行触摸移动
  return;
 }
 isOnAnimator = true;
 ValueAnimator animator = ValueAnimator.ofFloat(mOffset, mOffset + target);
 animator.setDuration(300).start();
 animator.setInterpolator(new AccelerateDecelerateInterpolator());
 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override
  public void onAnimationUpdate(ValueAnimator animation) {
   mOffset = (Float) animation.getAnimatedValue();

invalidate();
   requestLayout();
  }
 });
 animator.addListener(new Animator.AnimatorListener() {
  @Override
  public void onAnimationStart(Animator animation) {
  }

@Override
  public void onAnimationCancel(Animator animation) {
  }

@Override
  public void onAnimationRepeat(Animator animation) {
  }

@Override
  public void onAnimationEnd(Animator animation) {
   isOnAnimator = false;
  }
 });
}

// @Override
// public boolean dispatchTouchEvent(MotionEvent ev) {
//  if (ev.getAction() == MotionEvent.ACTION_MOVE) {
//   return true; // 禁止滑动
//  }
//  return super.dispatchTouchEvent(ev);
// }
}

相关回调接口类


public interface ICoverFlowAdapter {

int getCount();
Object getItem(int position);
long getItemId(int position);
View getView(int position, View convertView, ViewGroup parent);

void getData(View view, int position);
}

来源:https://blog.csdn.net/Banboofly/article/details/109185721

标签:Android,层叠卡片,banner
0
投稿

猜你喜欢

  • Java 常量与变量的区别详细介绍

    2023-03-09 13:30:48
  • java并发之原子操作类和非阻塞算法

    2022-04-13 04:39:01
  • Android控件之Gallery用法实例分析

    2021-07-15 06:49:09
  • 解析Java内存分配和回收策略以及MinorGC、MajorGC、FullGC

    2023-02-06 08:22:19
  • 基于java集合中的一些易混淆的知识点(详解)

    2023-08-29 03:06:26
  • 全面详解Spring Bean生命周期教程示例

    2023-08-09 11:41:15
  • Android使用Handler和Message更新UI

    2023-01-13 21:22:21
  • Java 守护线程_动力节点Java学院整理

    2023-11-28 07:51:14
  • Android中使用ZXing生成二维码(支持添加Logo图案)

    2023-12-24 21:34:58
  • 如何在springboot中实现页面的国际化

    2021-08-13 03:33:07
  • 剑指Offer之Java算法习题精讲链表与二叉树专项训练

    2022-01-12 19:19:01
  • SpringMVC教程之文件上传与下载详解

    2022-12-21 03:49:09
  • JDK线程池和Spring线程池的使用实例解析

    2023-02-24 13:27:32
  • C#实现异步编程的方法

    2022-03-01 22:43:27
  • Java的Swing编程中使用SwingWorker线程模式及顶层容器

    2021-09-09 08:45:06
  • SpringBoot中使用 RabbitMQ的教程详解

    2023-10-22 01:02:42
  • C#构造函数详解

    2023-02-25 22:38:50
  • JAVA面试题之Forward与Redirect的区别详解

    2023-11-25 02:27:15
  • SpringBoot 集成 activiti的示例代码

    2023-01-22 10:22:03
  • SpringBoot Actuator潜在的OOM问题的解决

    2021-08-26 06:36:43
  • asp之家 软件编程 m.aspxhome.com