仿CoordinatorLayout的behavior动画
创建BehaviorRelativeLayout 继承 RelativeLayout 定义ViewGroup。
BehaviorRelativeLayout的作用
1、是记录子View中标记为Behavior的对象,将标记的VIew添加到List集合;
2、当监听的将标记的子View循环出发进行动画。/** 和系统自带的behavior没有关系 纯粹的思想相近 */ public class BehaviorRelativeLayout extends RelativeLayout { List<View> behaviorView = new ArrayList<>(); public BehaviorRelativeLayout(Context context) { super(context); } public BehaviorRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onFinishInflate() { super.onFinishInflate(); for (int i = 0; i < getChildCount(); i++) { View childAt = getChildAt(i); LayoutParams lp = (LayoutParams) childAt.getLayoutParams(); if(lp.withBehavior){ behaviorView.add(childAt); } } } public void allTranslationY(float transilationY){ for (View view : behaviorView) { view.setTranslationY(transilationY); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(),attrs); } static class LayoutParams extends RelativeLayout.LayoutParams{ boolean withBehavior; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.BehaviorRelativeLayout); withBehavior = a.getBoolean(R.styleable.BehaviorRelativeLayout_withBehavior,false); a.recycle(); } } }
自定义属性
<resources> <declare-styleable name="BehaviorRelativeLayout"> <attr name="withBehavior" format="boolean" /> </declare-styleable> </resources>
添加需要监测的View, BottomPlaneChooseView,
BottomPlaneChooseView包裹在BehaviorRelativeLayout 中,这里她是一个自定义VIewGroup在是一个底部的布局容器,我们onlayout()方法中得到父布局BehaviorRelativeLayout 对象,当我们点击BottomPlaneChooseView将其向Y正方向隐藏时调用BehaviorRelativeLayout 的mParent.allTranslationY(translationY)方法。BottomPlaneChooseView 类似NestedSrollView,被观察的View;
作用:1. 根据BottomPlaneChooseView 的移动,来改变其他标记的子View的移动。public class BottomPlaneChooseView extends LinearLayout implements CompoundButton.OnCheckedChangeListener { private CheckBox mCbShowMore; private CheckBox mCbRotation; private TextView tvAddr; private TextView tvStatus; private Button btnChoosePlane; private int firstChildHei; private BehaviorRelativeLayout mParent; private PlaneAdapter mAdapter; private IBottomChooseViewListener mIChooseViewListener; private IBottomSwitchListener mBottomSwitchListener; public void setChooseViewListener(IBottomChooseViewListener listener) { mIChooseViewListener = listener; } public void setBottomSwitchListener(IBottomSwitchListener bottomSwitchListener) { if (bottomSwitchListener != null) { mBottomSwitchListener = bottomSwitchListener; } } public BottomPlaneChooseView(Context context) { super(context); init(context, null); } public BottomPlaneChooseView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } public void hideMore() { ObjectAnimator translationY = ObjectAnimator.ofFloat(this, "translationY", getTranslationY() , getHeight()); translationY.setDuration(200); translationY.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); mBottomSwitchListener.onColseBottomView(); } }); translationY.start(); mCbShowMore.setChecked(false); } public void showMore() { ObjectAnimator translationY = ObjectAnimator.ofFloat(this, "translationY", getTranslationY(), 0); translationY.setDuration(200); translationY.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); mBottomSwitchListener.onOpenBottomView(); } }); translationY.start(); mCbShowMore.setChecked(true); } @Override public void setTranslationY(float translationY) { super.setTranslationY(translationY); if (mParent != null) { mParent.allTranslationY(translationY); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); ViewParent parent = getParent(); for (; ; ) { if (parent == null) break; if (parent instanceof BehaviorRelativeLayout) { mParent = (BehaviorRelativeLayout) parent; break; } parent = parent.getParent(); } } public void init(Context context, AttributeSet attributeSet) { setOrientation(VERTICAL); LayoutInflater.from(context).inflate(R.layout.item_plane_choose, this, true); GridView gridView = findViewById(R.id.gv_plane_selected_list); ///gridView.setChoiceMode(GridView.CHOICE_MODE_SINGLE); mAdapter = new PlaneAdapter(); gridView.setAdapter(mAdapter); mCbShowMore = (CheckBox) findViewById(R.id.cb_show_more); mCbRotation = (CheckBox) findViewById(R.id.cb_rotation); tvAddr = findViewById(R.id.tvAddr); tvStatus = findViewById(R.id.tvStatus); btnChoosePlane = findViewById(R.id.bt_choose_plane); setBtnChoosePlaneText(0); mCbShowMore.setOnCheckedChangeListener(this); mCbRotation.setOnCheckedChangeListener(this); } @Override public void setOnClickListener(@Nullable OnClickListener l) { super.setOnClickListener(l); setChildClickListener(this, l); } private void setChildClickListener(ViewGroup parent, OnClickListener l) { for (int i = 0; i < parent.getChildCount(); i++) { View childAt = parent.getChildAt(i); if (!(childAt instanceof AdapterView)) { childAt.setOnClickListener(l); if (childAt.isClickable() && childAt instanceof ViewGroup) { setChildClickListener((ViewGroup) childAt, l); } } } } @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (buttonView == mCbShowMore) { if (isChecked) { showMore(); } else { hideMore(); } } else if (buttonView == mCbRotation) { if (isChecked) { for (int i = 0; i < mPlaneList.size(); i++) { mPlaneList.get(i).isChoice = false; } mAdapter.notifyDataSetChanged(); mIChooseViewListener.onChooseChangeLoop(true); } else { mIChooseViewListener.onChooseChangeLoop(false); } } } public void setChooseInfoView(PlaneChooseBean chooseBean) { tvAddr.setText(chooseBean.addr); tvStatus.setText(chooseBean.status); mPlaneList.clear(); if (chooseBean.planeList.size() > 1) { mCbRotation.setChecked(true); for (ChoicePlaneInfo.PlaneInfo info : chooseBean.planeList) { // 将传递过来选中的标志位置为未选择(默认不选中) info.isChoice = false; } } else if (chooseBean.planeList.size() == 1) { // 只有一个默认选中 mCbRotation.setChecked(false); } mPlaneList.addAll(chooseBean.planeList); setBtnChoosePlaneText(chooseBean.planeList.size()); mAdapter.notifyDataSetChanged(); } private void setBtnChoosePlaneText(int num) { String f_text = String.format(getContext().getString(R.string.choose_plane_format_num), num + ""); btnChoosePlane.setText(f_text); } private List<ChoicePlaneInfo.PlaneInfo> mPlaneList = new ArrayList<>(); class PlaneAdapter extends BaseAdapter { @Override public int getCount() { return mPlaneList.size(); } @Override public Object getItem(int position) { return mPlaneList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { VH vh; if (convertView == null) { vh = new VH(); convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_bottom_plane_name, parent, false); vh.mTvPlaneName = (CheckedTextView) convertView.findViewById(R.id.tv_plane_name); vh.mTvGroupName = (CheckedTextView) convertView.findViewById(R.id.tv_group_name); convertView.setTag(vh); } else { vh = (VH) convertView.getTag(); } vh.mTvPlaneName.setText(mPlaneList.get(position).sn); vh.mTvGroupName.setText(mPlaneList.get(position).group); ((CheckLayout) convertView).setChecked(mPlaneList.get(position).isChoice); convertView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mIChooseViewListener != null) { mPlaneList.get(position).isChoice = !mPlaneList.get(position).isChoice; if (mPlaneList.get(position).isChoice) { mIChooseViewListener.OnChoosePlaneClick(mPlaneList.get(position)); // 只选一架飞机,即当前飞机选择后其它飞机变不选择 for (int i = 0; i < mPlaneList.size(); i++) { if (i != position) { mPlaneList.get(i).isChoice = false; } } mCbRotation.setChecked(false); } else { mCbRotation.setChecked(true); } notifyDataSetChanged(); } } }); return convertView; } class VH { private CheckedTextView mTvPlaneName; private CheckedTextView mTvGroupName; } } public interface IBottomChooseViewListener { void OnChoosePlaneClick(ChoicePlaneInfo.PlaneInfo planeInfo); void onChooseChangeLoop(boolean isLoop); } public interface IBottomSwitchListener { void onOpenBottomView(); void onColseBottomView(); } }
BottomPlaneChooseView的xml布局
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:paddingEnd="@dimen/dp_8"
android:paddingStart="@dimen/dp_12">
<LinearLayout
android:id="@+id/lay_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:gravity="center_vertical"
android:orientation="horizontal">
<CheckBox
android:id="@+id/cb_show_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:button="@drawable/full_screen_selector"
android:checked="true"
android:gravity="center"
android:paddingTop="@dimen/dp_8"
android:paddingBottom="@dimen/dp_8"
android:paddingEnd="@dimen/dp_12"/>
<View
android:layout_width="@dimen/normal_deliver_dimen"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/dp_8"
android:layout_marginTop="@dimen/dp_8"
android:layout_marginEnd="@dimen/dp_4"
android:background="@color/white"/>
</LinearLayout>
<Button
android:id="@+id/bt_choose_plane"
android:layout_width="@dimen/dp_70"
android:layout_height="@dimen/dp_20"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginStart="@dimen/dp_12"
android:background="@drawable/select_choose_plane"
android:gravity="center"
android:stateListAnimator="@null"
android:text="@string/choose_plane_format_num"
android:textColor="@color/colorPrimary"
android:textSize="@dimen/sp_7"
/>
<!--android:theme="@style/button_style"-->
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/bt_choose_plane"
android:layout_toRightOf="@id/lay_left"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="@dimen/dp_6">
<TextView
android:id="@+id/tvAddr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_4"
android:drawableEnd="@mipmap/icon_arrow_white"
android:drawableLeft="@mipmap/icon_region"
android:drawablePadding="@dimen/dp_4"
android:drawableStart="@mipmap/icon_region"
android:padding="@dimen/dp_6"
android:text="地区"
android:textColor="@color/white"
android:textSize="@dimen/sp_9"/>
<TextView
android:id="@+id/tvStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_6"
android:drawableEnd="@mipmap/icon_arrow_white"
android:drawableLeft="@mipmap/icon_state"
android:drawablePadding="@dimen/dp_4"
android:drawableStart="@mipmap/icon_state"
android:padding="@dimen/dp_6"
android:text="状态"
android:textColor="@color/white"
android:textSize="@dimen/sp_9"/>
<CheckBox
android:id="@+id/cb_rotation"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:button="@drawable/rotation_selector"
android:checked="true"
android:paddingEnd="@dimen/dp_4"
android:paddingStart="@dimen/dp_4"
android:layout_marginLeft="@dimen/dp_2"
android:text="@string/rotation"
android:textColor="@color/white"
android:textSize="@dimen/sp_8"/>
<ImageView
android:id="@+id/v_set_loop_time"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingEnd="@dimen/dp_12"
android:scaleType="centerInside"
android:src="@mipmap/btn_edit_1"/>
</LinearLayout>
</HorizontalScrollView>
</RelativeLayout>
<GridView
android:id="@+id/gv_plane_selected_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnWidth="@dimen/dp_80"
android:gravity="center"
android:horizontalSpacing="@dimen/dp_8"
android:maxHeight="@dimen/dp_62"
android:numColumns="5"
android:padding="@dimen/dp_8"
android:scrollbars="none"
android:verticalSpacing="@dimen/dp_8"
android:visibility="visible"/>
</merge>
BehaviorRelativeLayou 在 xml布局使用
将BehaviorRelativeLayout自定义ViewGroup放在布局的最外层,然后在需要移动的子View上添加withBehavior自定义属性。<com.cfuas.info.widget.BehaviorRelativeLayout 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:id="@+id/root" android:layout_width="match_parent" android:layout_height="match_parent"> <com.cfuas.info.widget.BottomPlaneChooseView android:id="@+id/bpcv_plane_choose_view" android:layout_width="match_parent" android:layout_height="@dimen/bottom_plane_choose_height" android:background="@color/bottom_view_bg_color" android:layout_alignParentBottom="true" android:layout_alignParentStart="true"/> <ImageView android:id="@+id/iv_switch_bottom" android:layout_width="@dimen/dp_24" android:layout_height="@dimen/dp_24" android:src="@drawable/select_bottom_switch" android:layout_above="@+id/bpcv_plane_choose_view" android:layout_alignParentStart="true" android:layout_marginBottom="@dimen/dp_8" android:layout_marginEnd="@dimen/dp_8" android:layout_marginStart="@dimen/dp_8" app:withBehavior="true" android:visibility="gone" /> </com.cfuas.info.widget.BehaviorRelativeLayout>