app直播源码,列表越界后自动弹回原样的效果
app直播源码,列表越界后自动弹回原样的效果实现的相关代码
一、越界回弹效果的例子
二、效果拆分
下拉、上拉越界回弹:已到达列表顶部,执行下拉操作,此时会进行越界回弹
惯性滑动 越界回弹:快速滑动列表,列表已到达底部,但由于惯性会继续前进一部分,此时会根据速度决定越界的距离以及回弹的速度
三、下拉、上拉越界回弹实现方式及代码
在实际项目中可能在多个页面都需要回弹的效果,所以需要尽可能的减少与需要回弹view的耦合代码。所以可以自定义一个ViewGroup来包裹需要回弹的view。
public class PullOverLayout extends RelativeLayout {
//允许的越界回弹的高度
protected float mOverScrollHeight;
//被包裹的子View
private View mChildView;
//~~~~~
public PullOverLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mOverScrollHeight = DensityUtil.dp2px(context, 240);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
//获得子控件,也就是被包裹的recycleview等
mChildView = getChildAt(0);
}
}
重写事件拦截方法onInterceptTouchEvent()
通过竖直和水平方向Move的距离来判断是否拦截该事件
dy大于0为手指向下滑动,dy小于0为手指向上滑动,通过setStatePTD、setStatePBU方法记录Down或者Up
当被包裹的子view无法滑动时即发生越界时拦截并响应该Move事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mTouchX = ev.getX();
mTouchY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float dx = ev.getX() - mTouchX;
float dy = ev.getY() - mTouchY;
if (Math.abs(dx) <= Math.abs(dy)) {
if (dy > 0 && !ScrollingUtil.canChildScrollUp(mChildView)) {
//1、下拉操作
LogUtil.d(TAG, "setStatePTD");
cp.setStatePTD();
return true;
} else if (dy < 0 && !ScrollingUtil.canChildScrollDown(cp.getContent())) {
//2、上拉操作
LogUtil.d(TAG, "setStatePBU");
cp.setStatePBU();
return true;
}
}
break;
}
return super.onInterceptTouchEvent(ev);
}
重写onTouchEvent()方法响应拦截的事件
onTouchEvent用来响应事件,当move和up时,做相应的“越界”操作和“回弹”操作
下拉和上拉操作分别执行对应的越界和回弹操作即可
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
//当发生越界时由viewGroup响应Move事件
case MotionEvent.ACTION_MOVE:
float dy = e.getY() - mTouchY;
if (cp.isStatePTD()) {
//1、如果是下拉操作的move事件,则执行顶部越界操作
dy = Math.min(cp.getOsHeight() * 2, dy);
dy = Math.max(0, dy);
cp.getAnimProcessor().scrollHeadByMove(dy);
} else if (cp.isStatePBU()) {
//2、如果是上拉操作的move事件,则执行底部越界的操作
dy = Math.min(cp.getOsHeight() * 2, Math.abs(dy));
dy = Math.max(0, dy);
cp.getAnimProcessor().scrollBottomByMove(dy);
}
return true;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (cp.isStatePTD()) {
//3、如果是下拉操作,当收到Up事件时,进行释放操作下拉操作,即执行下拉回弹操作
cp.getAnimProcessor().dealPullDownRelease();
} else if (cp.isStatePBU()) {
//4、如果是上拉操作,当收到up事件时,进行释放上拉操作,执行上拉回弹操作
cp.getAnimProcessor().dealPullUpRelease();
}
return true;
}
return super.onTouchEvent(e);
}
第三步onTouchEvent中的下拉越界方法scrollHeadByMove(float moveY)和下拉释放的回弹方法dealPullDownRelease()
public void scrollHeadByMove(float moveY) {
float offsetY = decelerateInterpolator.getInterpolation(moveY / cp.getOsHeight() / 2) * moveY / 2;
LogUtil.d(TAG, "scrollHeadByMove ", "offsetY:", String.valueOf(offsetY));
cp.getHeader().setVisibility(GONE);
cp.getHeader().getLayoutParams().height = (int) Math.abs(offsetY);
cp.getHeader().requestLayout();
cp.getContent().setTranslationY(offsetY);
}
public void dealPullDownRelease() {
int start = getVisibleHeadHeight();
int end = 0;
LogUtil.d(TAG, "dealPullDownRelease ", "start:", String.valueOf(start), "end:", String.valueOf(end));
ValueAnimator va = ValueAnimator.ofInt(start, end);
va.setInterpolator(new DecelerateInterpolator());
va.addUpdateListener(animHeadUpListener);
va.setDuration((int) (Math.abs(start - end) * animFraction));
va.start();
}
四、惯性滑动 越界回弹实现方式及代码
给被包裹的子view设置监听
给定一个最低速度的阈值,当滑动速度超过这个值时根据惯性执行越界回弹操作
下方代码只实现了recycleview的越界回弹,listview类似。即被包裹的子view必须是recycleview
public void initChildViewFlingListener() {
final View mChildView = cp.getContent();
//1、给被包裹的子view设置监听事件,监听fling操作,并记录速度
final GestureDetector gestureDetector = new GestureDetector(cp.getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
LogUtil.d(TAG, "onScroll ", "distanceY:", String.valueOf(distanceY));
return super.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
LogUtil.d(TAG, "onFling ", "velocityY:", String.valueOf(velocityY));
mVelocityY = velocityY;
return false;
}
});
mChildView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//手势监听的两个任务:1.监听fling动作,获取速度 2.监听滚动状态变化
return gestureDetector.onTouchEvent(event);
}
});
if (mChildView instanceof RecyclerView) {
((RecyclerView) mChildView).addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
//2、到达越界速度阈值后,执行fling的顶部越界操作
if (mVelocityY >= OVER_SCROLL_MIN_VX && ScrollingUtil.isRecyclerViewToTop((RecyclerView) mChildView)) {
cp.getAnimProcessor().animOverScrollTop(mVelocityY, cur_delay_times);
mVelocityY = 0;
cur_delay_times = ALL_DELAY_TIMES;
}
if (mVelocityY <= -OVER_SCROLL_MIN_VX && ScrollingUtil.isRecyclerViewToBottom((RecyclerView) mChildView)) {
cp.getAnimProcessor().animOverScrollBottom(mVelocityY, cur_delay_times);
mVelocityY = 0;
cur_delay_times = ALL_DELAY_TIMES;
}
}
super.onScrollStateChanged(recyclerView, newState);
}
});
}
}
执行越界动画
底部越界和顶部越界代码类似,此处只讲解顶部越界即可
/**
* 执行顶部越界
* 越界高度height ∝ vy/computeTimes,此处采用的模型是height=A*(vy + B)/computeTimes
*/
public void animOverScrollTop(float vy, int computeTimes) {
LogUtil.d(TAG, "animOverScrollTop ", "vy:", String.valueOf(vy), "computeTimes:", String.valueOf(computeTimes));
if (cp.isOsTopLocked()) return;
cp.lockOsTop();
cp.setStatePTD();
//1、计算越界高度,最大不超过设定的越界高度
int oh = (int) Math.abs(vy / computeTimes / 2);
final int overHeight = Math.min(oh, cp.getOsHeight());
//2、计算越界时间
final int time = overHeight <= 50 ? 115 : (int) (0.3 * overHeight + 100);
//3、执行越界操作
animLayoutByTime(0, overHeight, time, overScrollTopUpListener, new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//4、执行回弹操作
animLayoutByTime(overHeight, 0, 2 * time, overScrollTopUpListener, new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
cp.releaseOsTopLock();
}
});
}
});
}
//最终执行的就是个属性动画
public void animLayoutByTime(int start, int end, long time, AnimatorUpdateListener listener, AnimatorListener animatorListener) {
ValueAnimator va = ValueAnimator.ofInt(start, end);
va.setInterpolator(new DecelerateInterpolator());
va.addUpdateListener(listener);
va.addListener(animatorListener);
va.setDuration(time);
va.start();
}
以上就是 app直播源码,列表越界后自动弹回原样的效果实现的相关代码,更多内容欢迎关注之后的文章