Reputation: 1182
I need this type of behavior to implement.Image should be scroll and set into center with text like wtsapp. but in wtsapp it set into left alignment, i need to set into center. how can i achieve this?
after scrolled image will show like that with text in toolbar.(mentioned)
Upvotes: 0
Views: 1113
Reputation: 134
1. Behavior for CoordinatorLayout and AppBarLayout
public class AvatarImageBehavior extends CoordinatorLayout.Behavior<ImageView> {
// 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;
private boolean onlyVerticalMove;
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);
onlyVerticalMove = a.getBoolean(R.styleable.AvatarImageBehavior_onlyVerticalMove, false);
a.recycle();
}
}
@Override
public boolean layoutDependsOn(@NotNull final CoordinatorLayout parent, @NotNull final ImageView child, @NotNull final View dependency) {
return dependency instanceof AppBarLayout; // change if you want another sibling to depend on
}
@Override
public boolean onDependentViewChanged(@NotNull final CoordinatorLayout parent, @NotNull final ImageView child, @NotNull 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 = Math.max(currentToolbarHeight, finalToolbarHeight);
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
if (!onlyVerticalMove) {
child.setX(newXPosition);
}
child.setY(startYPositionImage - distanceYToSubtract);
return true;
}
private void initProperties(
final ImageView 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;
}
}
}
```java
public class AppBarScrollWatcher implements AppBarLayout.OnOffsetChangedListener {
private int scrollRange = -1;
private OffsetListener listener;
public AppBarScrollWatcher(OffsetListener listener) {
this.listener = listener;
}
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (scrollRange == -1) {
scrollRange = appBarLayout.getTotalScrollRange();
}
int appbarHeight = scrollRange + verticalOffset;
float alpha = (float) appbarHeight / scrollRange;
if (alpha < 0) {
alpha = 0;
}
float alphaZeroOnCollapsed = shrinkAlpha(alpha);
float alphaZeroOnExpanded = Math.abs(alphaZeroOnCollapsed - 1);
int argbZeroOnExpanded = (int) Math.abs((alphaZeroOnCollapsed * 255) - 255);
int argbZeroOnCollapsed = (int) Math.abs(alphaZeroOnCollapsed * 255);
listener.onAppBarExpanding(alphaZeroOnExpanded <= 0, alphaZeroOnCollapsed <= 0, argbZeroOnExpanded, argbZeroOnCollapsed, alphaZeroOnCollapsed, alphaZeroOnExpanded);
}
private float shrinkAlpha(float alpha) {
NumberFormat formatter = NumberFormat.getInstance(Locale.getDefault());
formatter.setMaximumFractionDigits(2);
formatter.setMinimumFractionDigits(2);
formatter.setRoundingMode(RoundingMode.HALF_DOWN);
return Float.parseFloat(formatter.format(alpha));
}
public interface OffsetListener {
void onAppBarExpanding(boolean expanded, boolean collapsed, int argbZeroOnExpanded, int argbZeroOnCollapsed, float alphaZeroOnCollapsed, float alphaZeroOnExpanded);
}
}
res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="AvatarImageBehavior">
<attr name="finalXPosition" format="dimension" />
<attr name="finalYPosition" format="dimension" />
<attr name="finalHeight" format="dimension" />
<attr name="finalToolbarHeight" format="dimension" />
<attr name="onlyVerticalMove" format="boolean" />
</declare-styleable>
</resources>
2. Implementation in Activity/Fragment
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:contentScrim="@color/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:titleEnabled="false">
<LinearLayout
android:id="@+id/header_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingStart="24dp"
android:paddingTop="160dp"
android:paddingEnd="24dp"
android:paddingBottom="56dp">
</LinearLayout>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:layout_gravity="bottom"
app:contentInsetStart="0dp"
app:layout_collapseMode="pin"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:titleMarginStart="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<ImageView
android:id="@+id/still_photo"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:contentDescription="@string/app_name"
android:scaleType="fitCenter"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_ph_person_male_80dp" />
<ImageView
android:id="@+id/ic_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.7"
android:clickable="true"
android:contentDescription="@string/app_name"
android:focusable="true"
android:padding="8dp"
android:tint="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_more_vert_black_24dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/v_sections"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:behavior_overlapTop="24dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp">
<View
android:layout_width="match_parent"
android:layout_height="1000dp" />
</androidx.cardview.widget.CardView>
</androidx.core.widget.NestedScrollView>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/moving_photo"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_gravity="top|center_horizontal"
android:layout_marginTop="64dp"
android:contentDescription="@string/app_name"
android:scaleType="fitCenter"
app:finalHeight="48dp"
app:finalToolbarHeight="?android:attr/actionBarSize"
app:finalYPosition="4dp"
app:layout_behavior=".custom.AvatarImageBehavior"
app:onlyVerticalMove="true"
app:srcCompat="@drawable/ic_ph_person_male_80dp" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
private lateinit var appBarScrollListener: AppBarScrollWatcher
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_launcher)
setupAppBar()
}
private fun setupAppBar() {
appBarScrollListener =
AppBarScrollWatcher(AppBarScrollWatcher.OffsetListener { _, collapsed, _, _, _, _ ->
still_photo.visibility = if (collapsed) View.VISIBLE else View.INVISIBLE
})
app_bar.addOnOffsetChangedListener(appBarScrollListener)
}
override fun onDestroy() {
app_bar.removeOnOffsetChangedListener(appBarScrollListener)
super.onDestroy()
}
Note that you should put two ImageView in the layout.
AppCompatImageView
directly inside the CoordinatorLayout so that we can
use CoordinatorLayout.Behavior on it, it would be the moving photo.
The important prop here is app:onlyVerticalMove="true"
, that make
your moving photo scrolled vertically. I made the default value to
false, it will move the photo to the start point of CoordinatorLayout
(top left).If you want to exclude Toolbar
from moving elements, just remove android:layout_gravity="bottom"
Upvotes: 1