简介:
- Android5.0后Google为Android的滑动机制提供了NestedScrolling特性,可以使我们对嵌套View更简单事件拦截处理。本篇就对NestedScrolling实际应用做一些讲解。
</br>
NestedScrolling与传统事件分发机制作对比:
- 比如某个外部的ViewGroup拦截掉内部View的事件,那本次事件会被ViewGroup消费并且不会向下传递,如果子view也想处理只能等待下一次手指再按下。
- NestedScrolling可以很好的解决传统事件拦截的缺点,内部View在滚动的时候通过NestedScrollingChild将dx,dy交给NestedScrollingParent,NestedScrollingParent可对其进行部分消耗,剩余的部分还给内部View。
而NestedScrolling也是support.v4提供的支持类,在老版本也可以很好的兼容。
</br>
描述:
通过NestedScrolling可以实现哪些效果?
- 菜单悬停效果
- 下拉刷新、上拉加载更多
- 拖拽回弹
实现嵌套分发主要通过以下两个接口:
NestedScrollingParent
NestedScrollingChild
要使用 NestedScrolling机制,父View 需要实现NestedScrollingParent接口,而子View需要实现NestedScrollingChild接口。RecyclerView已经默认实现了NestedScrollingChild,如果RecyclerView不满足你的业务需求,那需要去实现NestedScrollingChild,这里我只对NestedScrollingParent实现。
</br>
实现:
public class DZStickyNavLayouts extends LinearLayout implements NestedScrollingParent {
private View mHeaderView;
private View mFooterView;
private static final int MAX_WIDTH = 500;
private View mChildView;
// 解决多点触控问题
private boolean isRunAnim;
private int mDrag = 2;
public DZStickyNavLayouts(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(LinearLayout.HORIZONTAL);
mHeaderView = new View(context);
mHeaderView.setBackgroundColor(0xffFFC125);
mFooterView = new View(context);
mFooterView.setBackgroundColor(0xffFF3E96);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mChildView = getChildAt(0);
LayoutParams layoutParams = new LayoutParams(MAX_WIDTH, LayoutParams.MATCH_PARENT);
addView(mHeaderView, 0, layoutParams);
addView(mFooterView, getChildCount(), layoutParams);
// 左移
scrollBy(MAX_WIDTH, 0);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
ViewGroup.LayoutParams params = mChildView.getLayoutParams();
params.width = getMeasuredWidth();
}
/**
* 必须要复写 onStartNestedScroll后调用
*/
@Override
public void onNestedScrollAccepted(View child, View target, int axes) {
}
/**
* 返回true代表处理本次事件
*/
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
if (target instanceof RecyclerView && !isRunAnim) {
return true;
}
return false;
}
/**
* 复位初始位置
*/
@Override
public void onStopNestedScroll(View target) {
startAnimation(new ProgressAnimation());
}
/**
* 回弹动画
*/
private class ProgressAnimation extends Animation {
// 预留
private float startProgress = 0;
private float endProgress = 1;
private ProgressAnimation(){
isRunAnim = true;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float progress = ((endProgress - startProgress) * interpolatedTime) + startProgress;
scrollBy((int) ((MAX_WIDTH - getScrollX()) * progress), 0);
if (progress == 1) {
isRunAnim = false;
}
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
setDuration(260);
setInterpolator(new AccelerateInterpolator());
}
}
@Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
}
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
// 如果在自定义ViewGroup之上还有父View交给我来处理
getParent().requestDisallowInterceptTouchEvent(true);
// dx>0 往左滑动 dx<0往右滑动
boolean hiddenLeft = dx > 0 && getScrollX() < MAX_WIDTH && !ViewCompat.canScrollHorizontally(target, -1);
boolean showLeft = dx < 0 && !ViewCompat.canScrollHorizontally(target, -1);
boolean hiddenRight = dx < 0 && getScrollX() > MAX_WIDTH && !ViewCompat.canScrollHorizontally(target, 1);
boolean showRight = dx > 0 && !ViewCompat.canScrollHorizontally(target, 1);
if (hiddenLeft || showLeft || hiddenRight || showRight) {
scrollBy(dx / mDrag, 0);
consumed[0] = dx;
}
// 限制错位问题
if (dx > 0 && getScrollX() > MAX_WIDTH && !ViewCompat.canScrollHorizontally(target, -1)) {
scrollTo(MAX_WIDTH, 0);
}
if (dx < 0 && getScrollX() < MAX_WIDTH && !ViewCompat.canScrollHorizontally(target, 1)) {
scrollTo(MAX_WIDTH, 0);
}
}
@Override
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
return false;
}
@Override
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
// 当RecyclerView在界面之内交给它自己惯性滑动
if (getScrollX() == MAX_WIDTH) {
return false;
}
return true;
}
@Override
public int getNestedScrollAxes() {
return 0;
}
/**
* 限制滑动 移动x轴不能超出最大范围
*/
@Override
public void scrollTo(int x, int y) {
if (x < 0) {
x = 0;
} else if (x > MAX_WIDTH * 2) {
x = MAX_WIDTH * 2;
}
super.scrollTo(x, y);
}
}
具体实现思路就是给RecyclerView添加头和尾,并控制滑动距离然后通过NestedScrolling进行分发,最后在手指离开界面开启还原动画。
文章参考:
http://blog.csdn.net/lmj623565791/article/details/52204039
网友评论