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 点击ImageButton时有“按下”的效果的实现1为ImageButton添加图片后,有边框,看起来像是图片贴
- 在使用各类App的时候,尤其是在发布朋友圈、微博的时候,都会选择配图,进入手机相册,选择自己想要的照片,作为发布内容的一部分,这里就简单介绍
- 本次为了记录开发一个基于webservice接口,去解析对方传送过来的xml字符串。实际使用时遇到的一些问题。传输过来的xml格式大致如下:
- 本文实例讲述了WinForm判断关闭事件来源于用户点击右上角“关闭”按钮的方法。分享给大家供大家参考。具体如下:protected over
- @Cacheable在同一类中方法调用无效上述图片中,同一个类中genLiveBullets()方法调用同类中的queryLiveByRoo
- 本文实例讲述了Android通过json向MySQL中写入数据的方法。分享给大家供大家参考,具体如下:先说一下如何通过json将Androi
- 前言我们一说到spring,可能第一个想到的是 IOC(控制反转) 和 AOP(面向切面编程)。没错,它们是spring的基石,得益于它们的
- 如何在WinForm中请求发送HTTP手工发送HTTP请求主要是调用 System.Net的HttpWebResponse方法手工发送HTT
- 在讲述这个模式之前,我们先看一个案例:游戏回档游戏的某个场景,一游戏角色有生命力、攻击力、防御力等数据,在打Boss前和后会不一样,我们允许
- 一、前言程序中经常会用到TabControl控件,默认的控件样式很普通。而且样式或功能不一定符合我们的要求。比如:我们需要TabContro
- 前言在这个系列博客的第二篇的最后部分提到了预布局,在预布局阶段,计算剩余空间时会把将要移除的 ViewHolder 忽略,从而计算出递补的
- 使用版本:spring-boot: 2.1.6.RELEASEsping: 5.1.8.RELEASEjava: openjdk 11.0.
- 本文通过JavaMailSender实现邮箱注册验证中遇到的问题开始着手,给大家详细分析了其原理以及问题的解决办法。使用邮箱注册验证,我们需
- 整理文档,搜刮出一个Android图片实现压缩处理的实例代码,稍微整理精简一下做下分享。详解:1.获取本地图片File文件 获取Bitma
- 一.介绍观察者模式(Observer Pattern)属于行为型模式。定义了对象之间的一对多依赖,让多个观察者同时监听某一个主题对象,类似于
- 当遇到以下场景:其他人写的单元测试影响统计结果一些需要调用外部接口的测试暂不运行需要在非本机环境上运行一些不回滚的单元测试则有必要选择以下方
- Spring的七个核心模块,供大家参考,具体内容如下1、Spring core:核心容器核心容器提供spring框架的基本功能。Spring
- 在工作中,如果需要跟XML打交道,难免会遇到需要把一个类型集合转换成XML格式的情况。之前的方法比较笨拙,需要给不同的类型,各自写一个转换的
- 开篇我们还是和原来一样,讲一讲做爬虫的思路以及需要准备的知识吧,高手们请直接忽略。首先我们来缕一缕思绪,想想到底要做什么,列个简单的需求。需
- Android用SharedPreferences实现登录注册注销功能前言本文用SharedPreferences本地缓存账号信息来实现登录