具有自定义视图的 CollapsingToolbarLayout
CollapsingToolbarLayout with a custom view
我正在尝试使用自定义视图实现 CollapsingToolbarLayout,但我做不到:
我想做什么(抱歉我不能 post 图片所以它在 imgur 上):
已展开,header 是带有图片和标题的个人资料屏幕
不展开(滚动),图像和标题将在工具栏上
但我看到的一切都没有像我预期的那样工作
我是这个动画和棒棒糖动画的新手,所以如果有人能帮助我,我将不胜感激!
(我没有 post 示例代码,因为我没有与 post 相关的东西)
我的解决方案
我有相同的场景要实现,所以我从狗的例子开始,并做了一些改变,使其完全像你描述的那样工作。我的代码可以作为该项目的分支找到,请参阅 https://github.com/hanscappelle/CoordinatorBehaviorExample
布局中最重要的变化:
<android.support.design.widget.CoordinatorLayout
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.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="@dimen/expanded_toolbar_height"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
>
<FrameLayout
android:id="@+id/main.framelayout.title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
>
<LinearLayout
android:id="@+id/main.linearlayout.title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/spacing_small"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="bottom|center_horizontal"
android:text="@string/tequila_name"
android:textColor="@android:color/white"
android:textSize="@dimen/textsize_xlarge"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/spacing_xxsmall"
android:text="@string/tequila_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:scrollbars="none"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="@dimen/spacing_xsmall"
android:padding="@dimen/spacing_normal"
android:text="@string/lorem"
android:textSize="@dimen/textsize_medium"
/>
</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:layout_anchor="@id/main.collapsing"
app:theme="@style/ThemeOverlay.AppCompat.Dark"
app:title=""
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
>
<Space
android:layout_width="@dimen/image_final_width"
android:layout_height="@dimen/image_final_width"
/>
<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/tequila_title"
android:textColor="@android:color/white"
android:textSize="@dimen/textsize_large"
/>
</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="top|center_horizontal"
android:layout_marginTop="@dimen/spacing_normal"
android:src="@drawable/ninja"
app:border_color="@android:color/white"
app:border_width="@dimen/border_width"
app:finalHeight="@dimen/image_final_width"
app:finalXPosition="@dimen/spacing_small"
app:finalYPosition="@dimen/spacing_small"
app:finalToolbarHeight="?attr/actionBarSize"
app:layout_behavior="saulmm.myapplication.AvatarImageBehavior"
/>
</android.support.design.widget.CoordinatorLayout>
并且在我优化的 AvatarImageBehaviour class 中,仅将头像从原始位置移动到属性中配置的位置。因此,如果您希望图像从另一个位置移动,只需在布局内移动它即可。当您这样做时,请确保 AppBarLayout 仍然是它的兄弟,否则它不会在代码中找到。
package saulmm.myapplication;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.util.AttributeSet;
import android.view.View;
import de.hdodenhof.circleimageview.CircleImageView;
public class AvatarImageBehavior extends CoordinatorLayout.Behavior<CircleImageView> {
// calculated from given layout
private int startXPositionImage;
private int startYPositionImage;
private int startHeight;
private int startToolbarHeight;
private boolean initialised = false;
private float amountOfToolbarToMove;
private float amountOfImageToReduce;
private float amountToMoveXPosition;
private float amountToMoveYPosition;
// user configured params
private float finalToolbarHeight, finalXPosition, finalYPosition, finalHeight;
public AvatarImageBehavior(
final Context context,
final AttributeSet attrs) {
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AvatarImageBehavior);
finalXPosition = a.getDimension(R.styleable.AvatarImageBehavior_finalXPosition, 0);
finalYPosition = a.getDimension(R.styleable.AvatarImageBehavior_finalYPosition, 0);
finalHeight = a.getDimension(R.styleable.AvatarImageBehavior_finalHeight, 0);
finalToolbarHeight = a.getDimension(R.styleable.AvatarImageBehavior_finalToolbarHeight, 0);
a.recycle();
}
}
@Override
public boolean layoutDependsOn(
final CoordinatorLayout parent,
final CircleImageView child,
final View dependency) {
return dependency instanceof AppBarLayout; // change if you want another sibling to depend on
}
@Override
public boolean onDependentViewChanged(
final CoordinatorLayout parent,
final CircleImageView child,
final View dependency) {
// make child (avatar) change in relation to dependency (toolbar) in both size and position, init with properties from layout
initProperties(child, dependency);
// calculate progress of movement of dependency
float currentToolbarHeight = startToolbarHeight + dependency.getY(); // current expanded height of toolbar
// don't go below configured min height for calculations (it does go passed the toolbar)
currentToolbarHeight = currentToolbarHeight < finalToolbarHeight ? finalToolbarHeight : currentToolbarHeight;
final float amountAlreadyMoved = startToolbarHeight - currentToolbarHeight;
final float progress = 100 * amountAlreadyMoved / amountOfToolbarToMove; // how much % of expand we reached
// update image size
final float heightToSubtract = progress * amountOfImageToReduce / 100;
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
lp.width = (int) (startHeight - heightToSubtract);
lp.height = (int) (startHeight - heightToSubtract);
child.setLayoutParams(lp);
// update image position
final float distanceXToSubtract = progress * amountToMoveXPosition / 100;
final float distanceYToSubtract = progress * amountToMoveYPosition / 100;
float newXPosition = startXPositionImage - distanceXToSubtract;
//newXPosition = newXPosition < endXPosition ? endXPosition : newXPosition; // don't go passed end position
child.setX(newXPosition);
child.setY(startYPositionImage - distanceYToSubtract);
return true;
}
private void initProperties(
final CircleImageView child,
final View dependency) {
if (!initialised) {
// form initial layout
startHeight = child.getHeight();
startXPositionImage = (int) child.getX();
startYPositionImage = (int) child.getY();
startToolbarHeight = dependency.getHeight();
// some calculated fields
amountOfToolbarToMove = startToolbarHeight - finalToolbarHeight;
amountOfImageToReduce = startHeight - finalHeight;
amountToMoveXPosition = startXPositionImage - finalXPosition;
amountToMoveYPosition = startYPositionImage - finalYPosition;
initialised = true;
}
}
}
来源
最常见的例子是 https://github.com/saulmm/CoordinatorBehaviorExample 列出的狗。这是一个很好的例子,但确实在展开视图的中间有一个工具栏,背景图像也会移动。在我的示例中删除了所有内容。
另一个解释可以在 http://www.devexchanges.info/2016/03/android-tip-custom-coordinatorlayout.html 找到,但是由于引用的 cloud/sea 背景图像在狗的例子中也可以找到,所以一个显然是建立在另一个之上的。
我也发现了这个 SO 问题并获得了赏金,但无法真正找出最终解决方案是什么
最后,这应该是一个可以完成工作的工作库。我已经检查过了,但初始图像没有居中,我宁愿处理我之前看过的狗示例。参见 https://github.com/datalink747/CollapsingAvatarToolbar
更多阅读内容
http://saulmm.github.io/mastering-coordinator
http://www.androidauthority.com/using-coordinatorlayout-android-apps-703720/
https://developer.android.com/reference/android/support/design/widget/CoordinatorLayout.html
https://guides.codepath.com/android/handling-scrolls-with-coordinatorlayout
我正在尝试使用自定义视图实现 CollapsingToolbarLayout,但我做不到:
我想做什么(抱歉我不能 post 图片所以它在 imgur 上):
已展开,header 是带有图片和标题的个人资料屏幕
不展开(滚动),图像和标题将在工具栏上
但我看到的一切都没有像我预期的那样工作
我是这个动画和棒棒糖动画的新手,所以如果有人能帮助我,我将不胜感激!
(我没有 post 示例代码,因为我没有与 post 相关的东西)
我的解决方案
我有相同的场景要实现,所以我从狗的例子开始,并做了一些改变,使其完全像你描述的那样工作。我的代码可以作为该项目的分支找到,请参阅 https://github.com/hanscappelle/CoordinatorBehaviorExample
布局中最重要的变化:
<android.support.design.widget.CoordinatorLayout
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.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="@dimen/expanded_toolbar_height"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
>
<FrameLayout
android:id="@+id/main.framelayout.title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
>
<LinearLayout
android:id="@+id/main.linearlayout.title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/spacing_small"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="bottom|center_horizontal"
android:text="@string/tequila_name"
android:textColor="@android:color/white"
android:textSize="@dimen/textsize_xlarge"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/spacing_xxsmall"
android:text="@string/tequila_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:scrollbars="none"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="@dimen/spacing_xsmall"
android:padding="@dimen/spacing_normal"
android:text="@string/lorem"
android:textSize="@dimen/textsize_medium"
/>
</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:layout_anchor="@id/main.collapsing"
app:theme="@style/ThemeOverlay.AppCompat.Dark"
app:title=""
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
>
<Space
android:layout_width="@dimen/image_final_width"
android:layout_height="@dimen/image_final_width"
/>
<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/tequila_title"
android:textColor="@android:color/white"
android:textSize="@dimen/textsize_large"
/>
</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="top|center_horizontal"
android:layout_marginTop="@dimen/spacing_normal"
android:src="@drawable/ninja"
app:border_color="@android:color/white"
app:border_width="@dimen/border_width"
app:finalHeight="@dimen/image_final_width"
app:finalXPosition="@dimen/spacing_small"
app:finalYPosition="@dimen/spacing_small"
app:finalToolbarHeight="?attr/actionBarSize"
app:layout_behavior="saulmm.myapplication.AvatarImageBehavior"
/>
</android.support.design.widget.CoordinatorLayout>
并且在我优化的 AvatarImageBehaviour class 中,仅将头像从原始位置移动到属性中配置的位置。因此,如果您希望图像从另一个位置移动,只需在布局内移动它即可。当您这样做时,请确保 AppBarLayout 仍然是它的兄弟,否则它不会在代码中找到。
package saulmm.myapplication;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.util.AttributeSet;
import android.view.View;
import de.hdodenhof.circleimageview.CircleImageView;
public class AvatarImageBehavior extends CoordinatorLayout.Behavior<CircleImageView> {
// calculated from given layout
private int startXPositionImage;
private int startYPositionImage;
private int startHeight;
private int startToolbarHeight;
private boolean initialised = false;
private float amountOfToolbarToMove;
private float amountOfImageToReduce;
private float amountToMoveXPosition;
private float amountToMoveYPosition;
// user configured params
private float finalToolbarHeight, finalXPosition, finalYPosition, finalHeight;
public AvatarImageBehavior(
final Context context,
final AttributeSet attrs) {
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AvatarImageBehavior);
finalXPosition = a.getDimension(R.styleable.AvatarImageBehavior_finalXPosition, 0);
finalYPosition = a.getDimension(R.styleable.AvatarImageBehavior_finalYPosition, 0);
finalHeight = a.getDimension(R.styleable.AvatarImageBehavior_finalHeight, 0);
finalToolbarHeight = a.getDimension(R.styleable.AvatarImageBehavior_finalToolbarHeight, 0);
a.recycle();
}
}
@Override
public boolean layoutDependsOn(
final CoordinatorLayout parent,
final CircleImageView child,
final View dependency) {
return dependency instanceof AppBarLayout; // change if you want another sibling to depend on
}
@Override
public boolean onDependentViewChanged(
final CoordinatorLayout parent,
final CircleImageView child,
final View dependency) {
// make child (avatar) change in relation to dependency (toolbar) in both size and position, init with properties from layout
initProperties(child, dependency);
// calculate progress of movement of dependency
float currentToolbarHeight = startToolbarHeight + dependency.getY(); // current expanded height of toolbar
// don't go below configured min height for calculations (it does go passed the toolbar)
currentToolbarHeight = currentToolbarHeight < finalToolbarHeight ? finalToolbarHeight : currentToolbarHeight;
final float amountAlreadyMoved = startToolbarHeight - currentToolbarHeight;
final float progress = 100 * amountAlreadyMoved / amountOfToolbarToMove; // how much % of expand we reached
// update image size
final float heightToSubtract = progress * amountOfImageToReduce / 100;
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
lp.width = (int) (startHeight - heightToSubtract);
lp.height = (int) (startHeight - heightToSubtract);
child.setLayoutParams(lp);
// update image position
final float distanceXToSubtract = progress * amountToMoveXPosition / 100;
final float distanceYToSubtract = progress * amountToMoveYPosition / 100;
float newXPosition = startXPositionImage - distanceXToSubtract;
//newXPosition = newXPosition < endXPosition ? endXPosition : newXPosition; // don't go passed end position
child.setX(newXPosition);
child.setY(startYPositionImage - distanceYToSubtract);
return true;
}
private void initProperties(
final CircleImageView child,
final View dependency) {
if (!initialised) {
// form initial layout
startHeight = child.getHeight();
startXPositionImage = (int) child.getX();
startYPositionImage = (int) child.getY();
startToolbarHeight = dependency.getHeight();
// some calculated fields
amountOfToolbarToMove = startToolbarHeight - finalToolbarHeight;
amountOfImageToReduce = startHeight - finalHeight;
amountToMoveXPosition = startXPositionImage - finalXPosition;
amountToMoveYPosition = startYPositionImage - finalYPosition;
initialised = true;
}
}
}
来源
最常见的例子是 https://github.com/saulmm/CoordinatorBehaviorExample 列出的狗。这是一个很好的例子,但确实在展开视图的中间有一个工具栏,背景图像也会移动。在我的示例中删除了所有内容。
另一个解释可以在 http://www.devexchanges.info/2016/03/android-tip-custom-coordinatorlayout.html 找到,但是由于引用的 cloud/sea 背景图像在狗的例子中也可以找到,所以一个显然是建立在另一个之上的。
我也发现了这个 SO 问题并获得了赏金,但无法真正找出最终解决方案是什么
最后,这应该是一个可以完成工作的工作库。我已经检查过了,但初始图像没有居中,我宁愿处理我之前看过的狗示例。参见 https://github.com/datalink747/CollapsingAvatarToolbar
更多阅读内容
http://saulmm.github.io/mastering-coordinator http://www.androidauthority.com/using-coordinatorlayout-android-apps-703720/ https://developer.android.com/reference/android/support/design/widget/CoordinatorLayout.html https://guides.codepath.com/android/handling-scrolls-with-coordinatorlayout