具有自定义行为的 CollapsingToolbarLayout
CollapsingToolbarLayout with a customised behavior
我正在关注这个流行的 link for cordinator behaviour, following this 。
我想要一个理想的行为,即图像应该浮动到右侧而不是左侧,为此,我修改了
AvatarImageBehavior.java -> maybeInitProperties() method ->
如下:
if (mFinalXPosition == 0)
mFinalXPosition= getScreenWidth()-70;
现在我希望工具栏固定在顶部,只有图像可以移动并粘在上面。
据我了解,只有 Behavior 文件是负责此过渡。
我尝试了太多东西,但没有得到正确的方法/回调以继续。
欢迎任何解决方案/方法/不同的示例。
因此,首先您应该做的是调整布局文件 - 通过删除其 layout_anchor
属性使 Toolbar
固定在顶部。
<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: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="wrap_content"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
>
<ImageView
android:id="@+id/main.imageview.placeholder"
android:layout_width="match_parent"
android:layout_height="300dp"
android:scaleType="centerCrop"
android:src="@drawable/quila2"
android:tint="#11000000"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.9"
/>
</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:scrollbars="none"
app:behavior_overlapTop="24dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<android.support.v7.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardElevation="8dp"
app:contentPadding="16dp"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="8dp"
android:text="@string/lorem"
android:textSize="18sp"
/>
</android.support.v7.widget.CardView>
</android.support.v4.widget.NestedScrollView>
<android.support.v7.widget.Toolbar
android:id="@+id/main.toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/primary"
app:theme="@style/ThemeOverlay.AppCompat.Dark"
app:title="">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/main.textview.title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:gravity="center_vertical"
android:text="@string/quila_name2"
android:textColor="@android:color/white"
android:textSize="20sp"/>
</LinearLayout>
</android.support.v7.widget.Toolbar>
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="@dimen/image_width"
android:layout_height="@dimen/image_width"
android:layout_gravity="center_horizontal"
android:src="@drawable/quila"
app:border_color="@android:color/white"
app:border_width="2dp"
app:finalHeight="@dimen/image_final_width"
app:layout_behavior="saulmm.myapplication.AvatarImageBehavior"
/>
</android.support.design.widget.CoordinatorLayout>
注意,我还删除了 main.framelayout.title
部分,因为我认为它是 Toolbar
还有。我还删除了 onOffsetChanged()
以及 MainActivity
中的所有相关方法,并使 Toolbar
的标题始终可见。
其次,当然要在AvatarImageBehavior
class中进行修改。这里最主要的是现在你的 ImageView
依赖于 NestedScrollView
而不是 Toolbar
。但是由于我们不再依赖 Toolbar
,我们仍然需要它来定义 mFinalYPosition
,所以我也添加了查找 Toolbar
的方法。这是行为:
public class AvatarImageBehavior extends CoordinatorLayout.Behavior<CircleImageView> {
private final static float MIN_AVATAR_PERCENTAGE_SIZE = 0.3f;
private final static int EXTRA_FINAL_AVATAR_PADDING = 80;
private final static String TAG = "behavior";
private Context mContext;
private float mCustomFinalYPosition;
private float mCustomStartXPosition;
private float mCustomStartToolbarPosition;
private float mCustomStartHeight;
private float mCustomFinalHeight;
private float mAvatarMaxSize;
private float mStartPosition;
private int mStartXPosition;
private float mStartToolbarPosition;
private int mStartYPosition;
private int mFinalYPosition;
private int mStartHeight;
private int mFinalXPosition;
private float mChangeBehaviorPoint;
public AvatarImageBehavior(Context context, AttributeSet attrs) {
mContext = context;
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AvatarImageBehavior);
mCustomFinalYPosition = a.getDimension(R.styleable.AvatarImageBehavior_finalYPosition, 0);
mCustomStartXPosition = a.getDimension(R.styleable.AvatarImageBehavior_startXPosition, 0);
mCustomStartToolbarPosition = a.getDimension(R.styleable.AvatarImageBehavior_startToolbarPosition, 0);
mCustomStartHeight = a.getDimension(R.styleable.AvatarImageBehavior_startHeight, 0);
mCustomFinalHeight = a.getDimension(R.styleable.AvatarImageBehavior_finalHeight, 0);
a.recycle();
}
init();
}
private void init() {
bindDimensions();
}
private void bindDimensions() {
mAvatarMaxSize = mContext.getResources().getDimension(R.dimen.image_width);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, CircleImageView child, View dependency) {
return dependency instanceof NestedScrollView;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, CircleImageView child, View dependency) {
maybeInitProperties(parent, child, dependency);
final int maxScrollDistance = (int) (mStartToolbarPosition);
float expandedPercentageFactor = dependency.getY() / maxScrollDistance;
if (expandedPercentageFactor < mChangeBehaviorPoint) {
float heightFactor = (mChangeBehaviorPoint - expandedPercentageFactor) / mChangeBehaviorPoint;
float distanceXToSubtract = ((mStartXPosition - mFinalXPosition)
* heightFactor) + (child.getHeight()/2);
float distanceYToSubtract = ((mStartYPosition - mFinalYPosition)
* (1f - expandedPercentageFactor)) + (child.getHeight()/2);
child.setX(mStartXPosition - distanceXToSubtract);
child.setY(mStartYPosition - distanceYToSubtract);
float heightToSubtract = ((mStartHeight - mCustomFinalHeight) * heightFactor);
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
lp.width = (int) (mStartHeight - heightToSubtract);
lp.height = (int) (mStartHeight - heightToSubtract);
child.setLayoutParams(lp);
} else {
float distanceYToSubtract = ((mStartYPosition - mFinalYPosition)
* (1f - expandedPercentageFactor)) + (mStartHeight/2);
child.setX(mStartXPosition - child.getWidth()/2);
child.setY(mStartYPosition - distanceYToSubtract);
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
lp.width = (int) (mStartHeight);
lp.height = (int) (mStartHeight);
child.setLayoutParams(lp);
}
return true;
}
private void maybeInitProperties(CoordinatorLayout parent, CircleImageView child, View dependency) {
if (mStartYPosition == 0)
mStartYPosition = (int) (dependency.getY());
View toolbar = tryFindToolbarInLayout(parent);
if (mFinalYPosition == 0 && toolbar != null) {
mFinalYPosition = (toolbar.getHeight() / 2);
}
if (mStartHeight == 0)
mStartHeight = child.getHeight();
if (mStartXPosition == 0)
mStartXPosition = (int) (child.getX() + (child.getWidth() / 2));
if (mFinalXPosition == 0)
mFinalXPosition = dependency.getContext().getResources().getDisplayMetrics().widthPixels - mContext.getResources().getDimensionPixelOffset(R.dimen.abc_action_bar_content_inset_material)
- ((int) mCustomFinalHeight / 2);
if (mStartToolbarPosition == 0)
mStartToolbarPosition = dependency.getY();
if (mChangeBehaviorPoint == 0 && toolbar != null) {
mChangeBehaviorPoint = (child.getHeight() - mCustomFinalHeight) / (mStartYPosition - mFinalYPosition - toolbar.getHeight());
}
}
private View tryFindToolbarInLayout(CoordinatorLayout parent) {
for (int i = 0; i < parent.getChildCount(); i++) {
View child = parent.getChildAt(i);
if (child instanceof Toolbar)
return child;
}
return null;
}
public int getStatusBarHeight() {
int result = 0;
int resourceId = mContext.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = mContext.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
}
这是我设法做到的:
我正在关注这个流行的 link for cordinator behaviour, following this
我想要一个理想的行为,即图像应该浮动到右侧而不是左侧,为此,我修改了
AvatarImageBehavior.java -> maybeInitProperties() method ->
如下:
if (mFinalXPosition == 0)
mFinalXPosition= getScreenWidth()-70;
现在我希望工具栏固定在顶部,只有图像可以移动并粘在上面。
据我了解,只有 Behavior 文件是负责此过渡。
我尝试了太多东西,但没有得到正确的方法/回调以继续。
欢迎任何解决方案/方法/不同的示例。
因此,首先您应该做的是调整布局文件 - 通过删除其 layout_anchor
属性使 Toolbar
固定在顶部。
<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: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="wrap_content"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
>
<ImageView
android:id="@+id/main.imageview.placeholder"
android:layout_width="match_parent"
android:layout_height="300dp"
android:scaleType="centerCrop"
android:src="@drawable/quila2"
android:tint="#11000000"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.9"
/>
</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:scrollbars="none"
app:behavior_overlapTop="24dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<android.support.v7.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardElevation="8dp"
app:contentPadding="16dp"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="8dp"
android:text="@string/lorem"
android:textSize="18sp"
/>
</android.support.v7.widget.CardView>
</android.support.v4.widget.NestedScrollView>
<android.support.v7.widget.Toolbar
android:id="@+id/main.toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/primary"
app:theme="@style/ThemeOverlay.AppCompat.Dark"
app:title="">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/main.textview.title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:gravity="center_vertical"
android:text="@string/quila_name2"
android:textColor="@android:color/white"
android:textSize="20sp"/>
</LinearLayout>
</android.support.v7.widget.Toolbar>
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="@dimen/image_width"
android:layout_height="@dimen/image_width"
android:layout_gravity="center_horizontal"
android:src="@drawable/quila"
app:border_color="@android:color/white"
app:border_width="2dp"
app:finalHeight="@dimen/image_final_width"
app:layout_behavior="saulmm.myapplication.AvatarImageBehavior"
/>
</android.support.design.widget.CoordinatorLayout>
注意,我还删除了 main.framelayout.title
部分,因为我认为它是 Toolbar
还有。我还删除了 onOffsetChanged()
以及 MainActivity
中的所有相关方法,并使 Toolbar
的标题始终可见。
其次,当然要在AvatarImageBehavior
class中进行修改。这里最主要的是现在你的 ImageView
依赖于 NestedScrollView
而不是 Toolbar
。但是由于我们不再依赖 Toolbar
,我们仍然需要它来定义 mFinalYPosition
,所以我也添加了查找 Toolbar
的方法。这是行为:
public class AvatarImageBehavior extends CoordinatorLayout.Behavior<CircleImageView> {
private final static float MIN_AVATAR_PERCENTAGE_SIZE = 0.3f;
private final static int EXTRA_FINAL_AVATAR_PADDING = 80;
private final static String TAG = "behavior";
private Context mContext;
private float mCustomFinalYPosition;
private float mCustomStartXPosition;
private float mCustomStartToolbarPosition;
private float mCustomStartHeight;
private float mCustomFinalHeight;
private float mAvatarMaxSize;
private float mStartPosition;
private int mStartXPosition;
private float mStartToolbarPosition;
private int mStartYPosition;
private int mFinalYPosition;
private int mStartHeight;
private int mFinalXPosition;
private float mChangeBehaviorPoint;
public AvatarImageBehavior(Context context, AttributeSet attrs) {
mContext = context;
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AvatarImageBehavior);
mCustomFinalYPosition = a.getDimension(R.styleable.AvatarImageBehavior_finalYPosition, 0);
mCustomStartXPosition = a.getDimension(R.styleable.AvatarImageBehavior_startXPosition, 0);
mCustomStartToolbarPosition = a.getDimension(R.styleable.AvatarImageBehavior_startToolbarPosition, 0);
mCustomStartHeight = a.getDimension(R.styleable.AvatarImageBehavior_startHeight, 0);
mCustomFinalHeight = a.getDimension(R.styleable.AvatarImageBehavior_finalHeight, 0);
a.recycle();
}
init();
}
private void init() {
bindDimensions();
}
private void bindDimensions() {
mAvatarMaxSize = mContext.getResources().getDimension(R.dimen.image_width);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, CircleImageView child, View dependency) {
return dependency instanceof NestedScrollView;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, CircleImageView child, View dependency) {
maybeInitProperties(parent, child, dependency);
final int maxScrollDistance = (int) (mStartToolbarPosition);
float expandedPercentageFactor = dependency.getY() / maxScrollDistance;
if (expandedPercentageFactor < mChangeBehaviorPoint) {
float heightFactor = (mChangeBehaviorPoint - expandedPercentageFactor) / mChangeBehaviorPoint;
float distanceXToSubtract = ((mStartXPosition - mFinalXPosition)
* heightFactor) + (child.getHeight()/2);
float distanceYToSubtract = ((mStartYPosition - mFinalYPosition)
* (1f - expandedPercentageFactor)) + (child.getHeight()/2);
child.setX(mStartXPosition - distanceXToSubtract);
child.setY(mStartYPosition - distanceYToSubtract);
float heightToSubtract = ((mStartHeight - mCustomFinalHeight) * heightFactor);
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
lp.width = (int) (mStartHeight - heightToSubtract);
lp.height = (int) (mStartHeight - heightToSubtract);
child.setLayoutParams(lp);
} else {
float distanceYToSubtract = ((mStartYPosition - mFinalYPosition)
* (1f - expandedPercentageFactor)) + (mStartHeight/2);
child.setX(mStartXPosition - child.getWidth()/2);
child.setY(mStartYPosition - distanceYToSubtract);
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
lp.width = (int) (mStartHeight);
lp.height = (int) (mStartHeight);
child.setLayoutParams(lp);
}
return true;
}
private void maybeInitProperties(CoordinatorLayout parent, CircleImageView child, View dependency) {
if (mStartYPosition == 0)
mStartYPosition = (int) (dependency.getY());
View toolbar = tryFindToolbarInLayout(parent);
if (mFinalYPosition == 0 && toolbar != null) {
mFinalYPosition = (toolbar.getHeight() / 2);
}
if (mStartHeight == 0)
mStartHeight = child.getHeight();
if (mStartXPosition == 0)
mStartXPosition = (int) (child.getX() + (child.getWidth() / 2));
if (mFinalXPosition == 0)
mFinalXPosition = dependency.getContext().getResources().getDisplayMetrics().widthPixels - mContext.getResources().getDimensionPixelOffset(R.dimen.abc_action_bar_content_inset_material)
- ((int) mCustomFinalHeight / 2);
if (mStartToolbarPosition == 0)
mStartToolbarPosition = dependency.getY();
if (mChangeBehaviorPoint == 0 && toolbar != null) {
mChangeBehaviorPoint = (child.getHeight() - mCustomFinalHeight) / (mStartYPosition - mFinalYPosition - toolbar.getHeight());
}
}
private View tryFindToolbarInLayout(CoordinatorLayout parent) {
for (int i = 0; i < parent.getChildCount(); i++) {
View child = parent.getChildAt(i);
if (child instanceof Toolbar)
return child;
}
return null;
}
public int getStatusBarHeight() {
int result = 0;
int resourceId = mContext.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = mContext.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
}
这是我设法做到的: