CoordinatorLayout是在desgin包下的一個(gè)用于協(xié)調(diào)子控件的組件,可以解決絕大部分滑動(dòng)聯(lián)動(dòng)問(wèn)題,使用方法也很簡(jiǎn)單,為觀察者注冊(cè)一個(gè)Behavior,在Behavior指定要監(jiān)聽(tīng)的控件(可以多個(gè))
這邊實(shí)現(xiàn)一個(gè)TextView隨著另一個(gè)TextView的移動(dòng)而移動(dòng)
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_obed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="被觀察者"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_ob"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="觀察者"
android:textSize="18sp"
app:layout_behavior="com.aruba.coordinatorlayoutapplication.MyBehavior" />
</android.support.design.widget.CoordinatorLayout>
public class MyBehavior extends CoordinatorLayout.Behavior {
/**
* 注意:一定要寫(xiě)這個(gè)構(gòu)造方法,因?yàn)槭荂oordinatorLayout反射要用到
*
* @param context
* @param attrs
*/
public MyBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 確定要監(jiān)聽(tīng)哪個(gè)控件
*
* @param parent
* @param child
* @param dependency
* @return
*/
@Override
public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
if (dependency.getId() == R.id.tv_obed)//監(jiān)聽(tīng)我們的被觀察者
return true;
return false;
}
/**
* 被監(jiān)聽(tīng)的控件發(fā)生的改變,則會(huì)回調(diào)此方法
*
* @param parent
* @param child
* @param dependency
* @return
*/
@Override
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
child.setY(dependency.getY() + dependency.getHeight());//將觀察者view放在被監(jiān)聽(tīng)的控件的下面
child.setX(dependency.getX());
return true;
}
}
public class MainActivity extends AppCompatActivity {
private TextView tvObed;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvObed = findViewById(R.id.tv_obed);
//被觀察者隨著手指的移動(dòng)而移動(dòng)
tvObed.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
tvObed.setX(event.getRawX());
tvObed.setY(event.getRawY());
return true;
}
});
}
}
最后效果:

CoordinatorLayout.gif
此外FloatingActionButton擁有自己封裝好的Behavior,下面結(jié)合RecyclerView進(jìn)行使用
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FloatingActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:src="@drawable/ic_add_24dp"
app:borderWidth="0dp"
app:layout_behavior=".FloatingBehavior" />
</android.support.design.widget.CoordinatorLayout>
public class FloatingBehavior extends FloatingActionButton.Behavior {
private ValueAnimator valueAnimator;
private boolean isReverse;
public FloatingBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 設(shè)置我們需要監(jiān)聽(tīng)的滑動(dòng),橫向還是豎直
*
* @param coordinatorLayout
* @param child
* @param directTargetChild
* @param target
* @param axes
* @param type
* @return
*/
@Override
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
//只關(guān)注豎直滑動(dòng)
return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
/**
* 滑動(dòng)控件有滑動(dòng)的時(shí)候,會(huì)回調(diào)這個(gè)方法
*
* @param coordinatorLayout
* @param child
* @param target
* @param dxConsumed
* @param dyConsumed
* @param dxUnconsumed
* @param dyUnconsumed
* @param type
*/
@Override
public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
createValueAnimator(child);
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {//滑動(dòng)控件往下滾動(dòng)
animeOut();
} else if (dyConsumed < 0 && child.getVisibility() == View.INVISIBLE) {
child.setVisibility(View.VISIBLE);
animeIn();
}
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
}
private void animeIn() {
if (valueAnimator == null || valueAnimator.isRunning()) {
return;
}
valueAnimator.reverse();
isReverse = true;
}
private void animeOut() {
if (valueAnimator == null || valueAnimator.isRunning()) {
return;
}
valueAnimator.start();
isReverse = false;
}
public void createValueAnimator(final FloatingActionButton child) {
if (valueAnimator == null) {
valueAnimator = ValueAnimator.ofFloat(1f);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float fraction = animation.getAnimatedFraction();
child.setScaleX(1 - fraction);
child.setScaleY(1 - fraction);
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (!isReverse) {
child.setVisibility(View.INVISIBLE);
}
super.onAnimationEnd(animation);
}
});
valueAnimator.setDuration(1000);
}
}
}
public class FloatingActivity extends AppCompatActivity {
private RecyclerView recyclerview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_floating);
recyclerview = findViewById(R.id.recyclerview);
recyclerview.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
MyAdapter myAdapter = new MyAdapter();
recyclerview.setAdapter(myAdapter);
}
class MyAdapter extends RecyclerView.Adapter {
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
TextView textView = new TextView(viewGroup.getContext());
textView.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.MATCH_PARENT));
textView.setTextSize(30);
textView.setGravity(Gravity.CENTER);
RecyclerView.ViewHolder viewHolder = new ViewHolder(textView);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
((TextView) viewHolder.itemView).setText(String.valueOf(i + 1));
}
@Override
public int getItemCount() {
return 20;
}
class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}
}
效果如下:

CoordinatorLayout.gif