Android鬼点子-Coordinatorlayout与它的关系户们


       Coordinatorlayout是Material风格的重要组件,是Design Support Library中重要一部分,它的作用是协调(Coordinate)其他组件, 实现联动.

       使用Coordinatorlayout可很容易的实现这样的动画。
来自http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0717/3196.html

       Coordinatorlayout其实就是一个强化版的FrameLayout。使用Coordinatorlayout需要引入

compile 'com.android.support:design:22.2.1'

       我们都知道,当一个View响应了触摸事件之后,这个事件就不会被和它同行级别的其他View接受到。也就是说像上面那张图那样,如果ListView接收到了事件,那么和它同级别的TitleBar就不会收到这个事件了。但是Coordinatorlayout就会很好的解决这个问题。所有的事件都是首先由Coordinatorlayout来进行处理和分发,然后再由其他子View做出响应。下面我用一个例子来详细的说明。

tu

上布局!!!!

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
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:textFloatingActionButton="http://schemas.android.com/apk/res-auto"
android:id="@+id/img2"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/transparent"
app:layout_behavior="ui.FilmInfoBehavior"
>
<RelativeLayout
android:id="@+id/top"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/topR"
android:layout_width="match_parent"
android:layout_height="350dp">
...
</RelativeLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/topR"
android:background="@color/transparent"
android:paddingBottom="10dp"
android:paddingTop="10dp"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
...
</RelativeLayout>
</RelativeLayout>
</android.support.v4.widget.NestedScrollView>
</RelativeLayout>
</android.support.design.widget.AppBarLayout>
<ui.TextFloatingActionButton
android:id="@+id/play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:clickable="true"
app:backgroundTint="@color/DarkFontColor"
app:elevation="6dp"
app:layout_anchor="@id/img1"
app:layout_anchorGravity="bottom|right"
app:pressedTranslationZ="12dp"
app:rippleColor="#33728dff"
textFloatingActionButton:textFloatingActionButtonText="@string/播放"
textFloatingActionButton:textFloatingActionButtonTextColor="@color/FontColor"
/>
</android.support.design.widget.CoordinatorLayout>

       最外层是CoordinatorLayout,让后CoordinatorLayout的两个子View是AppBarLayout和FloatingActionButton,这里的TextFloatingActionButton是我自己优化了一下,可以显示文字。AppBarLayout里面有个很重要的是NestedScrollView。

关系户一:FloatingActionButton简称FAB

就是那个圆圆的播放键。FloatingActionButton已经要是CoordinatorLayout的直接子View,layout_anchor这个锚点信息才会有作用。anchor可以控制FAB的位置。

1、app:borderWidth=””——————边框宽度,通常设置为0 ,用于解决Android 5.X设备上阴影无法正常显示的问题

2、app:backgroundTint=””—————按钮的背景颜色,不设置,默认使用theme中colorAccent的颜色

3、app:rippleColor=””——————–点击的边缘阴影颜色

4、app:elevation=””———————-边缘阴影的宽度

5、app:pressedTranslationZ=”16dp”—–点击按钮时,按钮边缘阴影的宽度,通常设置比elevation的数值大

对了,FloatingActionButton的原形是ImageView。下面是我自己改的可以显示文字的FloatingActionButton。因为我只显示一个字,所以文字位置的计算可能不具有通用性,使用的时候需要修改一下。

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
public class TextFloatingActionButton extends FloatingActionButton {
Typeface mtypeface;
String text;
int textcolor;
public TextFloatingActionButton(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs , R.styleable.TextFloatingActionButton);
text = a.getString(R.styleable.TextFloatingActionButton_textFloatingActionButtonText);
a.recycle();
mtypeface = Typeface.createFromAsset(context.getAssets(), "iconfont/iconfont.ttf");
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint=new Paint();
// paint.setTextAlign(Paint.Align.CENTER);
paint.setColor(Color.WHITE);
paint.setTypeface(mtypeface);
paint.setTextSize(getWidth() * 0.6f);
Rect rect = new Rect();
//返回包围整个字符串的最小的一个Rect区域
paint.getTextBounds(text, 0, 1, rect);
//拿到字符串的宽度
float stringWidth = rect.width();
float stringHeight = rect.height();
float x =(getWidth()* 0.9f-stringWidth)/2;
canvas.drawText(text, x ,(getHeight()* 0.9f + stringHeight)/2 ,paint);
}
}

attr.xml

1
2
3
4
5
6
<resources>
<declare-styleable name="TextFloatingActionButton">
<attr name="textFloatingActionButtonText" format="string"></attr>
<attr name="textFloatingActionButtonTextColor" format="color"></attr>
</declare-styleable>
</resources>

关系户二:Behavior

       当A做了XX,B就XX。这里B做的动作,就是在Behavior中实现的。Behavior中有两个很重要的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 判断child的布局是否依赖dependency
*/
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, T child, View dependency) {
boolean rs;
//根据逻辑判断rs的取值
//返回false表示child不依赖dependency,ture表示依赖
return rs;
}
/**
* 当dependency发生改变时(位置、宽高等),执行这个函数
* 返回true表示child的位置或者是宽高要发生改变,否则就返回false
*/
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, T child, View dependency) {
//child要执行的具体动作
return true;
}

dependency就是A,child就B。注意,这里的Child一定要是Coordinatorlayout的子View。Behavior写好了,是这样使用的。使用这个属性的View是Child。

app:layout_behavior="ui.FilmInfoBehavior"

关系户三:NestedScrollView

NestedScrollView收到了触摸事件,然后让它的父控件滚动,这是我上面那张图的效果。做出响应的是Child,需要设置Behavior,就是布局中的AppBarLayout。下面是我写的Behavior。

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
public class FilmInfoBehavior extends AppBarLayout.ScrollingViewBehavior {
final AlphaAnimation InAnimation = new AlphaAnimation(0, 1);
int totle;
int sum = 0;
public FilmInfoBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
totle = target.getTop() * 6 / 7;
Log.d("FilmInfoBehavior", "target.getTop()/7:" + (target.getTop() * 6 / 7));
return true;
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
View back = coordinatorLayout.findViewById(R.id.backBt);
View top = coordinatorLayout.findViewById(R.id.top);
View cover = coordinatorLayout.findViewById(R.id.cover);
if(dyUnconsumed > 0 && sum + dyUnconsumed < totle ){
top.offsetTopAndBottom(- dyUnconsumed);
back.offsetTopAndBottom(dyUnconsumed);
sum = sum + dyUnconsumed;
}
if(dyUnconsumed < 0 && sum + dyUnconsumed > 0 ){
sum = sum + dyUnconsumed;
top.offsetTopAndBottom(- dyUnconsumed);
back.offsetTopAndBottom(dyUnconsumed);
}
cover.setAlpha(((float)(sum)/totle));
}
}

NestedScrollView实现了NestedScrollingParent,NestedScrollingChild接口,才会在事件响应之前被拦截,然后在onStartNestedScroll和 onNestedScroll中做出响应。dyUnconsumed是手指滑动的距离。

完整源码:Github

文章目录
|