自定义layout
首先我们把需要的title和Recyclerview放入一个自定义布局里面。这是为了简单我们的代码。所以把功能放到自定义layout里面。代码如下:
private void initial(Context context, AttributeSet attrs) {
LayoutInflater.from(context).inflate(R.layout.cardlayout, this);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SWCardLayout);
TextView title = (TextView) findViewById(R.id.title);
title.setText(array.getText(R.styleable.SWCardLayout_text));
title.setBackgroundColor(array.getColor(R.styleable.SWCardLayout_backgroundcolor,Color.BLACK));
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler);
recyclerView.setLayoutManager(new LinearLayoutManager(context));
NumberAdapter numberAdapter = new NumberAdapter(context);
recyclerView.setAdapter(numberAdapter);
array.recycle();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != oldw || h != oldh) {
titleheight = findViewById(R.id.title).getMeasuredHeight();
}
}
public int getTitleheight() {
return titleheight;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
这边代码我就不多说了。返回title的高度为了后面的计算。
随手鲁一个behavior
我们先自定义一个behavior放入我们刚刚写的自定义layout。代码如下:
public class CardBehavior extends Behavior<SWCardLayout> {
private int childheight = 0;
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, SWCardLayout child,
View directTargetChild, View target, int nestedScrollAxes) {
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 && directTargetChild == child;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
至于nestedscrolling的这个方法我就不多少了。前面有几篇讲到这个的。
现在我们需要思考的是如何摆放他们的位置。其实behavior都帮我们处理好了。那就是onLayoutChild和OnmesureChild。控制他们的宽高和布局显示都在这2个方法里面。接下来我们看下这块的代码如何去写:
//控制子控件的onlayout和onmesure
@Override
public boolean onLayoutChild(CoordinatorLayout parent, SWCardLayout child, int layoutDirection) {
//按照默认的情况控制,会导致child重叠
parent.onLayoutChild(child, layoutDirection);
//控制顶部的偏移量(上一个距离顶部的边距+自身title高度)
SWCardLayout frontChild = getFrontChild(parent, child);
if (frontChild != null) {
int offset = frontChild.getTop() + frontChild.getTitleheight();
child.offsetTopAndBottom(offset);
}
childheight = child.getTop();
return true;
}
@Override
public boolean onMeasureChild(CoordinatorLayout parent, SWCardLayout child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
//防止布局的重绘
int offset = getChildOffset(parent, child);
int height = View.MeasureSpec.getSize(parentHeightMeasureSpec) - offset;
child.measure(parentWidthMeasureSpec, View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
return true;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
我们来分析下,先从onmesure说起。我们需要得到他的偏移量,也就是上一个的title的高度。如果是若干个布局,那就是若干的title的高度。计算title的偏移量的代码很简单,就是叠加title的高度:
private int getChildOffset(CoordinatorLayout parent, SWCardLayout child) {
int offset = 0;
for (int i = 0; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
if (view != child) {
if (view instanceof SWCardLayout) {
offset += ((SWCardLayout) view).getTitleheight();
}
}
}
return offset;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
得到它的偏移量之后我们直接通过子view的onmesure布局扔进去。所以这个不是很难。
现在我们继续看onlayoutchild方法。我注释也写的比较明确了。如果单独通过layout放置的话,child会重叠在一起。那么计算方法就是计算前一个child顶部的距离加上他自身的title的高度。那么我们来看看如何得到前一个child。其他代码很简单,遍历父view就可以。代码如下:
//得到前一个child
private SWCardLayout getFrontChild(CoordinatorLayout parent, SWCardLayout child) {
int index = parent.indexOfChild(child);
for (int i = index - 1; i >= 0; i--) {
View view = parent.getChildAt(i);
if (view instanceof SWCardLayout) {
return (SWCardLayout) view;
}
}
return null;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
我们先来看下效果。
我们会发现现在是滚动不了的。那么既然显示都显示出来 了。还怕他滚不了。看老夫随手一段代码分分钟让他滚起来。
那么滚动的处理在那边呢。当然是在onnestprescroll里面。nestedscrolling的一个方法里面处理。这个我前面有介绍过。不了解nestedscrolling可以先翻翻之前的文章。
那么我们应该怎么处理呢?还是老套路,计算偏移量。我们需要先处理自己本身的滑动,在处理其他的滑动来实现联动效果。具体代码如下:
//为了处理联动
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, SWCardLayout child,
View target, int dx, int dy, int[] consumed) {
//先处理自己的
int min = childheight;
int max = childheight + child.getHeight() - child.getTitleheight();
int top = child.getTop();
int offset = Math.min(Math.max(top - dy, min), max) - top;
child.offsetTopAndBottom(offset);
consumed[1] = -offset;
Log.i("----------", "onNestedPreScroll: " + consumed[1]);
//再处理其他的
if (consumed[1] == 0) {
return;
} else if (consumed[1] > 0) {//上滑
SWCardLayout current = child;
SWCardLayout cardLayout = getFrontChild(coordinatorLayout, child);
while (cardLayout != null) {
int layoutoffset = getHeightOffset(cardLayout, current);
cardLayout.offsetTopAndBottom(-layoutoffset);
current = cardLayout;
cardLayout = getFrontChild(coordinatorLayout, current);
}
} else if (consumed[1] < 0) {//下滑
SWCardLayout current = child;
SWCardLayout cardLayout = getNextChild(coordinatorLayout, child);
while (cardLayout != null) {
int layoutoffset = getHeightOffset(current, cardLayout);
cardLayout.offsetTopAndBottom(layoutoffset);
current = cardLayout;
cardLayout = getNextChild(coordinatorLayout, current);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
上滑的时候我们需要得到前一个的swcardlayout带着他滚动,下滑的时候我们需要得到下一个child进行滚动,得到下一个child的方法其实他之前得到前一个的方法是差不多的。代码如下:
private SWCardLayout getNextChild(CoordinatorLayout parent, SWCardLayout child) {
int index = parent.indexOfChild(child);
for (int i = index + 1; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
if (view instanceof SWCardLayout) {
return (SWCardLayout) view;
}
}
return null;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
那么就这么写完了。我们先来看下效果:
额。。。似乎翻车了。不过就看着图,我们知道应该是偏移量的问题搞错了。我们的偏移量必须要大于0应该。那么我们修改下代码。改成如下的:
else if (consumed[1] > 0) {//上滑
SWCardLayout current = child;
SWCardLayout cardLayout = getFrontChild(coordinatorLayout, child);
while (cardLayout != null) {
int layoutoffset = getHeightOffset(cardLayout, current);
if (layoutoffset > 0) {
cardLayout.offsetTopAndBottom(-layoutoffset);
}
current = cardLayout;
cardLayout = getFrontChild(coordinatorLayout, current);
}
} else if (consumed[1] < 0) {//下滑
SWCardLayout current = child;
SWCardLayout cardLayout = getNextChild(coordinatorLayout, child);
while (cardLayout != null) {
int layoutoffset = getHeightOffset(current, cardLayout);
if (layoutoffset > 0) {
cardLayout.offsetTopAndBottom(layoutoffset);
}
current = cardLayout;
cardLayout = getNextChild(coordinatorLayout, current);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
判断下偏移量的值在做child的offset设置。我们在来看一下效果。
代码
一个成功的联动效果就这么出来了。最后我放出自定义behavior的整体代码:
public class CardBehavior extends Behavior<SWCardLayout> {
private int childheight = 0;
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, SWCardLayout child,
View directTargetChild, View target, int nestedScrollAxes) {
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 && directTargetChild == child;
}
//为了处理联动
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, SWCardLayout child,
View target, int dx, int dy, int[] consumed) {
//先处理自己的
int min = childheight;
int max = childheight + child.getHeight() - child.getTitleheight();
int top = child.getTop();
int offset = Math.min(Math.max(top - dy, min), max) - top;
child.offsetTopAndBottom(offset);
consumed[1] = -offset;
Log.i("----------", "onNestedPreScroll: " + consumed[1]);
//再处理其他的
if (consumed[1] == 0) {
return;
} else if (consumed[1] > 0) {//上滑
SWCardLayout current = child;
SWCardLayout cardLayout = getFrontChild(coordinatorLayout, child);
while (cardLayout != null) {
int layoutoffset = getHeightOffset(cardLayout, current);
if (layoutoffset > 0) {
cardLayout.offsetTopAndBottom(-layoutoffset);
}
current = cardLayout;
cardLayout = getFrontChild(coordinatorLayout, current);
}
} else if (consumed[1] < 0) {//下滑
SWCardLayout current = child;
SWCardLayout cardLayout = getNextChild(coordinatorLayout, child);
while (cardLayout != null) {
int layoutoffset = getHeightOffset(current, cardLayout);
if (layoutoffset > 0) {
cardLayout.offsetTopAndBottom(layoutoffset);
}
current = cardLayout;
cardLayout = getNextChild(coordinatorLayout, current);
}
}
}
private SWCardLayout getNextChild(CoordinatorLayout parent, SWCardLayout child) {
int index = parent.indexOfChild(child);
for (int i = index + 1; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
if (view instanceof SWCardLayout) {
return (SWCardLayout) view;
}
}
return null;
}
private int getHeightOffset(SWCardLayout top, SWCardLayout bottom) {
return top.getTop() + top.getTitleheight() - bottom.getTop();
}
//控制子控件的onlayout和onmesure
@Override
public boolean onLayoutChild(CoordinatorLayout parent, SWCardLayout child, int layoutDirection) {
//按照默认的情况控制,会导致child重叠
parent.onLayoutChild(child, layoutDirection);
//控制顶部的偏移量(上一个距离顶部的边距+自身title高度)
SWCardLayout frontChild = getFrontChild(parent, child);
if (frontChild != null) {
int offset = frontChild.getTop() + frontChild.getTitleheight();
child.offsetTopAndBottom(offset);
}
childheight = child.getTop();
return true;
}
//得到前一个child
private SWCardLayout getFrontChild(CoordinatorLayout parent, SWCardLayout child) {
int index = parent.indexOfChild(child);
for (int i = index - 1; i >= 0; i--) {
View view = parent.getChildAt(i);
if (view instanceof SWCardLayout) {
return (SWCardLayout) view;
}
}
return null;
}
@Override
public boolean onMeasureChild(CoordinatorLayout parent, SWCardLayout child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
//防止布局的重绘
int offset = getChildOffset(parent, child);
int height = View.MeasureSpec.getSize(parentHeightMeasureSpec) - offset;
child.measure(parentWidthMeasureSpec, View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
return true;
}
private int getChildOffset(CoordinatorLayout parent, SWCardLayout child) {
int offset = 0;
for (int i = 0; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
if (view != child) {
if (view instanceof SWCardLayout) {
offset += ((SWCardLayout) view).getTitleheight();
}
}
}
return offset;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
当然那个SWCardLayout你们可以自己进行封装。想怎么玩怎么玩~。就是这么6。