CoordinatorLayout:视图随自定义行为

时间:2017-09-25 19:52:23

标签: java android android-layout android-view android-coordinatorlayout

我是CoordinatorLayout的新手,这是我在CoordinatorLayout中遇到的一种非常奇怪的行为。我有一个ImageView(或者更具体地说是ImageView的子类名为CircleImageView (它将中心的个人资料图片存放在此处))作为CoordinatorLayout的子项之一。我已将此CircleImageView锚定到AppbarLayout(这是CoordinatorLayout的另一个孩子)。这是我的整个布局:

到目前为止一切顺利。我目前能够滚动AppbarLayoutNestedScrollView移动。但是,当我们向上滚动并决定依赖自定义CoordinatorLayour.Behavior时,我想到动画配置文件图片向右移动。我最终得到了一个尝试翻译CircleImageView的自定义行为。它还没有完成,但是应该将视图大致翻译一些,除了现在,CircleImageView已经完全消失,引入了自定义行为。

这可能是什么原因?

注意 :我尝试用ImageView替换CircleImageView,行为保持不变。

以下是参考布局:

<?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"
    android:fitsSystemWindows="false">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/main.appbar"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:fitsSystemWindows="false"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light">

            </android.support.v7.widget.Toolbar>

            <android.support.v7.widget.AppCompatImageView
                android:id="@+id/imageView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom|end"
                android:layout_marginBottom="24dp"
                android:layout_marginEnd="24dp"
                android:layout_marginRight="24dp"
                android:layout_weight="1"
                android:tint="@color/white"
                app:srcCompat="@drawable/ic_settings_24px" />

        </android.support.design.widget.CollapsingToolbarLayout>


    </android.support.design.widget.AppBarLayout>

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/profile_pic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:elevation="4dp"
           app:layout_behavior="com.learncity.learner.account.profile.ProfilePicBehavior"
        android:src="@drawable/avatar_boy_2"
        app:layout_anchor="@id/main.appbar"
        app:layout_anchorGravity="bottom|center" />

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">


            <TextView
                android:id="@+id/user_name"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginEnd="8dp"
                android:layout_marginLeft="8dp"
                android:layout_marginRight="8dp"
                android:layout_marginStart="8dp"
                android:layout_marginTop="64dp"
                android:lineSpacingExtra="8dp"
                android:padding="@dimen/activity_horizontal_margin"
                android:text="@string/account_person_name_label"
                android:textAlignment="center"
                android:textSize="40sp"
                app:layout_constraintHorizontal_bias="0.0"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <android.support.v7.widget.AppCompatImageView
                android:id="@+id/id_phone_icon"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="24dp"
                android:layout_marginStart="24dp"
                android:layout_marginTop="8dp"
                android:tint="@color/colorPrimary"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/user_name"
                app:srcCompat="@drawable/ic_phone_black_24dp" />

            <TextView
                android:id="@+id/id_phone_no"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="16dp"
                android:layout_marginStart="16dp"
                android:text="Phone No"
                app:layout_constraintBottom_toBottomOf="@+id/id_phone_icon"
                app:layout_constraintLeft_toRightOf="@+id/id_phone_icon"
                app:layout_constraintTop_toTopOf="@+id/id_phone_icon"
                app:layout_constraintVertical_bias="0.571" />


            <android.support.v7.widget.AppCompatImageView
                android:id="@+id/id_email_icon"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="24dp"
                android:layout_marginStart="24dp"
                android:layout_marginTop="8dp"
                android:tint="@color/colorPrimary"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/id_phone_icon"
                app:srcCompat="@drawable/ic_email_black_24dp" />

            <TextView
                android:id="@+id/id_email_id"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="16dp"
                android:layout_marginStart="16dp"
                android:text="Email Id"
                app:layout_constraintBottom_toBottomOf="@+id/id_email_icon"
                app:layout_constraintLeft_toRightOf="@+id/id_email_icon"
                app:layout_constraintTop_toTopOf="@+id/id_email_icon" />

            <View
                style="@style/Divider"
                android:layout_marginEnd="8dp"
                android:layout_marginLeft="8dp"
                android:layout_marginRight="8dp"
                android:layout_marginStart="8dp"
                android:layout_marginTop="8dp"
                app:layout_constraintTop_toBottomOf="@+id/id_email_icon" />

        </android.support.constraint.ConstraintLayout>

    </android.support.v4.widget.NestedScrollView>


</android.support.design.widget.CoordinatorLayout>

自定义行为:(我知道计算可能不合理,但我正在尝试初始粗略动画

public class ProfilePicBehavior extends CoordinatorLayout.Behavior<CircleImageView>{


    public ProfilePicBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, CircleImageView child, View dependency) {
        return dependency instanceof AppBarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, CircleImageView child, View dependency) {
        // Translate the CircleImageView to the right
        // Calculate first, what fraction the AppBarLayout has shrunk by
        float proportion = dependency.getHeight() / 200f;
        // Translate the child by this proportion
        float translationX = parent.getWidth() * proportion;
        child.setTranslationX(translationX);

        return true;
    }
}

4 个答案:

答案 0 :(得分:2)

几个月前我和你有同样的问题。我通过下面的自定义# Deny user1 from all hosts but host1 DenyUsers user1@!host1,* # Allow all users from any host that are not denied yet AllowUsers *@* 解决它(我不记得相同问题的链接):

CoordinatorLayout.Behavior

您必须添加自定义样式public class CollapsingImageBehavior extends CoordinatorLayout.Behavior<View> { private final static int X = 0; private final static int Y = 1; private final static int WIDTH = 2; private final static int HEIGHT = 3; private int mTargetId; private int[] mView; private int[] mTarget; public CollapsingImageBehavior() { } public CollapsingImageBehavior(Context context, AttributeSet attrs) { if (attrs != null) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CollapsingImageBehavior); mTargetId = a.getResourceId(R.styleable.CollapsingImageBehavior_collapsedTarget, 0); a.recycle(); } if (mTargetId == 0) { throw new IllegalStateException("collapsedTarget attribute not specified on view for behavior"); } } @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { return dependency instanceof AppBarLayout; } @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { setup(parent, child); AppBarLayout appBarLayout = (AppBarLayout) dependency; int range = appBarLayout.getTotalScrollRange(); float factor = -appBarLayout.getY() / range; int left = mView[X] + (int) (factor * (mTarget[X] - mView[X])); int top = mView[Y] + (int) (factor * (mTarget[Y] - mView[Y])); int width = mView[WIDTH] + (int) (factor * (mTarget[WIDTH] - mView[WIDTH])); int height = mView[HEIGHT] + (int) (factor * (mTarget[HEIGHT] - mView[HEIGHT])); CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams(); lp.width = width; lp.height = height; child.setLayoutParams(lp); child.setX(left); child.setY(top); return true; } private void setup(CoordinatorLayout parent, View child) { if (mView != null) return; mView = new int[4]; mTarget = new int[4]; mView[X] = (int) child.getX(); mView[Y] = (int) child.getY(); mView[WIDTH] = child.getWidth(); mView[HEIGHT] = child.getHeight(); View target = parent.findViewById(mTargetId); if (target == null) { throw new IllegalStateException("target view not found"); } mTarget[WIDTH] += target.getWidth(); mTarget[HEIGHT] += target.getHeight(); View view = target; while (view != parent) { mTarget[X] += (int) view.getX(); mTarget[Y] += (int) view.getY(); view = (View) view.getParent(); } } }

attrs.xml

之后,您可以按如下方式定义xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CollapsingImageBehavior">
    <attr name="collapsedTarget" format="integer"></attr>
</declare-styleable>
</resources>

<?xml version="1.0" encoding="utf-8"?>

答案 1 :(得分:1)

根据这个答案:https://stackoverflow.com/a/40023161/6248491

声明包含 CollapsingToolbarLayout 与父母提升相关的小提琴

尝试将AppBarLayout提升为0dp。此外,CircleImageView应保持更高(更高)的高度,以便不会在顶部“提升”。

希望这会有所帮助。如果有效,请告诉我。

答案 2 :(得分:1)

去年我不得不做类似的事情。

AvatarImageBehavior基于the GitHub project by Saul Molinero (saulmm)

您可以注意到,CircleImageView是布局的最后一个元素。也许这是你的问题? 希望它有所帮助。

片段的布局

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:custom="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:ignore="RtlHardcoded">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/main_appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/main.collapsing"
            android:layout_width="match_parent"
            android:layout_height="550dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

            <ImageView
                android:id="@+id/iv_product_background"
                android:layout_width="match_parent"
                android:layout_height="400dp"
                android:scaleType="centerCrop"
                android:tint="#11000000"
                app:layout_collapseMode="parallax"/>

            <FrameLayout
                android:id="@+id/main_framelayout_title"
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:layout_gravity="bottom|center_horizontal"
                android:background="@color/white"
                android:orientation="vertical"
                app:layout_collapseMode="parallax">

                <LinearLayout
                    android:id="@+id/main_linearlayout_title"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:orientation="vertical">

                    <TextView
                        android:id="@+id/tv_product_title_open"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_horizontal"
                        android:gravity="bottom|center"
                        tools:text="Title"
                        android:textColor="@android:color/white"
                        android:textSize="30sp"/>

                    <TextView
                        android:id="@+id/tv_product_tagline"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_horizontal"
                        android:layout_marginTop="4dp"
                        android:gravity="center"
                        android:paddingEnd="@dimen/standard_margin_space"
                        android:paddingStart="@dimen/standard_margin_space"
                        tools:text="Tagline"
                        android:textColor="@android:color/white"/>

                </LinearLayout>
            </FrameLayout>
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>


    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:scrollbars="none"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:id="@+id/products_view_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"/>
    </android.support.v4.widget.NestedScrollView>

    <android.support.v7.widget.Toolbar
        android:id="@+id/main_toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        app:layout_anchor="@id/main_framelayout_title"
        app:theme="@style/ThemeOverlay.AppCompat.Dark"
        app:title="">

        <include layout="@layout/toolbar_buttons"/>

        <TextView
            android:id="@+id/tv_product_title_closed"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="71dp"
            android:gravity="center_vertical"
            tools:text="Title"
            android:textColor="@android:color/white"
            android:textSize="26sp"/>

        <!--</LinearLayout>-->
    </android.support.v7.widget.Toolbar>

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/iv_product_avatar"
        android:layout_width="@dimen/product_avatar_width"
        android:layout_height="@dimen/product_avatar_width"
        android:layout_gravity="top|center_horizontal"
        android:layout_marginTop="235dp"
        android:src="@drawable/img_products_total16_avatar"
        app:border_color="@color/grey"
        app:border_width="0dp"
        app:layout_behavior="com.myname.AvatarImageBehavior"/>

</android.support.design.widget.CoordinatorLayout>

<强> AvatarImageBehavior

public class AvatarImageBehavior extends CoordinatorLayout.Behavior<CircleImageView> {

    private final static String TAG = AvatarImageBehavior.class.getSimpleName();
    private final Context mContext;

    private boolean isInitialized = false;

    private float mStartX;
    private float mMaxXMove;

    private float mStartY;
    private float mMaxYMove;

    private float mMaxScroll;

    private float mStartHeight;
    private float mMaxHeightChange;

    private float mFinalHeight;
    private float mFinalX;
    private float mFinalY;

    public AvatarImageBehavior(Context context, AttributeSet attrs) {
        mContext = context;
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, CircleImageView child, View dependency) {
        return dependency instanceof Toolbar;
    }

    private void initProperties(CircleImageView child, View dependency) {
        mMaxScroll = dependency.getY();

        mStartHeight = child.getHeight();
        mFinalHeight = mContext.getResources().getDimensionPixelOffset(R.dimen.product_avatar_final_width);
        mMaxHeightChange = mStartHeight - mFinalHeight;

        mStartX = child.getX();
        mFinalX = mContext.getResources().getDimensionPixelOffset(R.dimen.product_avatar_margin_left);
        mMaxXMove = mStartX - mFinalX;

        mStartY = child.getY();
        mFinalY = (dependency.getHeight() - mFinalHeight) / 2f;
        mMaxYMove = mStartY - mFinalY;

        isInitialized = true;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, CircleImageView child, View dependency) {

        if (!isInitialized)
            initProperties(child, dependency);

        final float currScrollDist = dependency.getY();

        if (currScrollDist == 0) {
            setParams(child, (int) mFinalX, (int) mFinalY, (int) mFinalHeight);
        } else if (currScrollDist == mMaxScroll) { // reset the values if the scroll is at the max
            setParams(child, (int) mStartX, (int) mStartY, (int) mStartHeight);
        } else {
            float scrollFactor = currScrollDist / mMaxScroll;
            float factor = 1f - scrollFactor;

            float currX = mStartX - (mMaxXMove * factor);
            float currY = mStartY - (mMaxYMove * factor);
            float currHeight = mStartHeight - (mMaxHeightChange * factor);

            setParams(child, (int) currX, (int) currY, (int) currHeight);
        }

        return true;
    }

    private void setParams(CircleImageView view, int xPos, int yPos, int height) {
        view.setX(xPos);
        view.setY(yPos);
        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) view.getLayoutParams();
        lp.width = height;
        lp.height = height;
        view.setLayoutParams(lp);
    }
}

答案 3 :(得分:1)

我终于弄明白了。我将问题缩小到自定义行为(显然),问题是子视图(CircleImageView)的翻译异常值。

以下是微调值后的自定义行为:

public class ProfilePicBehavior extends CoordinatorLayout.Behavior<CircleImageView>{

    private int mDependencyHeight;
    private int mProfilePicMargin;
    private int mActionBarHeight;

    public ProfilePicBehavior(Context context) {
        init(context);
    }

    public ProfilePicBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context){

        mDependencyHeight = (int)context.getResources()
                .getDimension(R.dimen.appbarlayout_learner_home_height);

        mProfilePicMargin = (int)ViewUtils.dpToPx(context, 8f);

        mActionBarHeight = (int)ActivityUtils.getActionBarHeight(context);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, CircleImageView child, View dependency) {

        if(dependency instanceof AppBarLayout){
            return true;
        }
        return false;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, CircleImageView child, View dependency) {
        // Translate the CircleImageView to the right
        // Calculate first, what fraction the AppBarLayout has shrunk by
        int bottom = dependency.getBottom();
        int top = dependency.getTop();
        int viewHeight = bottom;

        float proportion = Math.min(1, 1 - ((viewHeight - mActionBarHeight) / (float)(mDependencyHeight - mActionBarHeight)));
        // Translate the child by this proportion
        float translationX = (parent.getWidth()/2 - child.getWidth()/2 - mProfilePicMargin) * proportion;
        float translationY = (child.getHeight()/2 - mProfilePicMargin) * proportion;

        child.setTranslationX(translationX);
        child.setTranslationY(translationY);

        return true;
    }
}

你可以看到它比通过LayoutParams操纵属性的 @ Eselfar 答案更简单。(我还没有测试过他的答案,因为我的答案动画有点不同)

逻辑非常简单:CircleImageView收缩的比例翻译AppBarLayout