Parallel animation in Imageview - android

I want to implement animation like the below image.
I have already used ThreePhaseBottomLibrary and as per my experience animation should go parallel as per above image when I scroll it up!
Below is my Fragment class. It works fine except this Image parallel animation as per the screen:
Myfragment.java
public class MyFragment extends BottomSheetFragment {
private BottomSheetLayout mBottomSheetLayout;
private ImageView mBottomSheetBackgroundImageView;
private int mBottomSheetHeight;
private ImageView movingIconImageView;
private AppBarLayout mAppBarLayout;
private int mMStartMarginBottom;
private int mMStartMarginLeft;
private Toolbar mToolbar;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_my, container, false);
mBottomSheetHeight = getResources().getDimensionPixelSize(R.dimen.header_height);
mAppBarLayout = (AppBarLayout) view.findViewById(R.id.appbar);
view.setMinimumHeight(getResources().getDisplayMetrics().heightPixels);
CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) view.findViewById(R.id.collapsing_toolbar);
//collapsingToolbar.setTitle("Title");
collapsingToolbar.setTitleEnabled(false);
mToolbar = (Toolbar) view.findViewById(R.id.toolbar);
//final AppCompatActivity activity = (AppCompatActivity) getActivity();
//activity.setSupportActionBar(toolbar);
//final ActionBar actionBar = activity.getSupportActionBar();
//actionBar.setDisplayHomeAsUpEnabled(true);
//actionBar.setTitle(null);
mToolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mBottomSheetLayout.dismissSheet();
}
});
mToolbar.setAlpha(0);
mBottomSheetBackgroundImageView = (ImageView) view.findViewById(R.id.backdrop);
mBottomSheetBackgroundImageView.setAlpha(0.0f);
movingIconImageView = (ImageView) view.findViewById(R.id.movingIconImageView);
Glide.with(this).load(R.drawable.cheese_1).centerCrop().into(mBottomSheetBackgroundImageView);
if (mBottomSheetLayout != null)
mBottomSheetLayout.setAppBarLayout(mAppBarLayout);
final int actionBarHeight = getActionBarHeight(getActivity());
mMStartMarginBottom = getResources().getDimensionPixelSize(R.dimen.header_view_start_margin_bottom);
mMStartMarginLeft = getResources().getDimensionPixelSize(R.dimen.header_view_start_margin_left);
movingIconImageView.setPivotX(0);
final float actionBarIconPadding = getResources().getDimensionPixelSize(R.dimen.action_bar_icon_padding);
mAppBarLayout.addOnOffsetChangedListener(new OnOffsetChangedListener() {
float startY = 0;
float scaleDiff = 0;
#Override
public void onOffsetChanged(final AppBarLayout appBarLayout, final int verticalOffset) {
if (mBottomSheetLayout != null && mBottomSheetLayout.isSheetShowing() && mBottomSheetLayout.getState() == State.EXPANDED) {
float progress = (float) -verticalOffset / mAppBarLayout.getTotalScrollRange();
movingIconImageView.setX(mMStartMarginLeft + (progress * (actionBarHeight - mMStartMarginLeft)));
if (startY == 0)
startY = movingIconImageView.getY();
if (scaleDiff == 0) {
scaleDiff = 1 - (actionBarHeight - actionBarIconPadding) / movingIconImageView.getHeight();
movingIconImageView.setPivotY(movingIconImageView.getHeight());
}
movingIconImageView.setScaleX(1f - progress * scaleDiff);
movingIconImageView.setScaleY(1f - progress * scaleDiff);
movingIconImageView.setY(startY - progress * actionBarIconPadding / 2 + mMStartMarginBottom * progress);
}
}
});
return view;
}
/**
* returns the height of the action bar
*/
public static int getActionBarHeight(final Context context) {
// based on http://stackoverflow.com/questions/12301510/how-to-get-the-actionbar-height
final TypedValue tv = new TypedValue();
int actionBarHeight = 0;
if (context.getTheme().resolveAttribute(R.attr.actionBarSize, tv, true))
actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, context.getResources()
.getDisplayMetrics());
return actionBarHeight;
}
public void setBottomSheetLayout(final BottomSheetLayout bottomSheetLayout) {
mBottomSheetLayout = bottomSheetLayout;
if (mBottomSheetLayout != null && mAppBarLayout != null)
mBottomSheetLayout.setAppBarLayout(mAppBarLayout);
mBottomSheetLayout.addOnSheetStateChangeListener(new OnSheetStateChangeListener() {
private ViewPropertyAnimator mToolbarAnimation;
State lastState;
#Override
public void onSheetStateChanged(final State state) {
if (lastState == state)
return;
lastState = state;
if (state != State.EXPANDED) {
if (mToolbarAnimation != null)
mToolbarAnimation.cancel();
mToolbarAnimation = null;
mToolbar.setAlpha(0);
mToolbar.setVisibility(View.INVISIBLE);
} else if (mToolbarAnimation == null) {
mToolbar.setVisibility(View.VISIBLE);
mToolbar.setTranslationY(-mToolbar.getHeight() / 3);
mToolbarAnimation = mToolbar.animate().setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
mToolbarAnimation.alpha(1).translationY(0).start();
}
}
});
}
#Override
public ViewTransformer getViewTransformer() {
return new BaseViewTransformer() {
private ViewPropertyAnimator mBottomSheetBackgroundImageViewFadeInAnimation, mBottomSheetBackgroundImageViewFadeOutAnimation;
private Float mOriginalContactPhotoXCoordinate = null;
private final float mOriginalBottomSheetBackgroundImageViewTranslationY = mBottomSheetBackgroundImageView.getTranslationY();
#Override
public void transformView(final float translation, final float maxTranslation, final float peekedTranslation, final BottomSheetLayout parent, final View view) {
if (mOriginalContactPhotoXCoordinate == null)
mOriginalContactPhotoXCoordinate = movingIconImageView.getX();
if (translation < mBottomSheetHeight)
return;
if (translation == mBottomSheetHeight) {
if (mBottomSheetBackgroundImageViewFadeInAnimation != null)
mBottomSheetBackgroundImageViewFadeInAnimation.cancel();
mBottomSheetBackgroundImageViewFadeInAnimation = null;
if (mBottomSheetBackgroundImageViewFadeOutAnimation == null)
mBottomSheetBackgroundImageViewFadeOutAnimation = mBottomSheetBackgroundImageView.animate().alpha(0);
} else {
if (mBottomSheetBackgroundImageViewFadeOutAnimation != null)
mBottomSheetBackgroundImageViewFadeOutAnimation.cancel();
mBottomSheetBackgroundImageViewFadeOutAnimation = null;
if (mBottomSheetBackgroundImageViewFadeInAnimation == null) {
mBottomSheetBackgroundImageViewFadeInAnimation = mBottomSheetBackgroundImageView.animate().alpha(1);
}
}
float progress = (translation - mBottomSheetHeight) / (maxTranslation - mBottomSheetHeight);
//Log.d("AppLog", "translation:" + translation + " maxTranslation:" + maxTranslation + " progress:" + progress);
//movingIconImageView.setY(progress * (mBottomSheetHeight - movingIconImageView.getHeight()));
movingIconImageView.setY(progress * (mBottomSheetHeight - movingIconImageView.getHeight() - mMStartMarginBottom));
movingIconImageView.setX(mOriginalContactPhotoXCoordinate - progress * (mOriginalContactPhotoXCoordinate - mMStartMarginLeft));
//mBottomSheetBackgroundImageView.setAlpha(progress);
mBottomSheetBackgroundImageView.setTranslationY(mOriginalBottomSheetBackgroundImageViewTranslationY - progress * mOriginalBottomSheetBackgroundImageViewTranslationY);
}
};
}
}
Here is my xml:-
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:id="#+id/main_content"
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/appbar"
android:layout_width="match_parent"
android:layout_height="#dimen/header_height"
android:background="#null">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="168dp"
android:layout_marginTop="40dp"
android:background="#eee">
</FrameLayout>
<ImageView
android:id="#+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:translationY="40dp"
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:theme="#style/ToolbarColoredBackArrow"/>
<ImageView
android:id="#+id/movingIconImageView"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center_horizontal" android:background="#f00"
android:src="#drawable/test"/>
</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"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/window_color"
android:orientation="vertical"
android:paddingTop="24dp">
<include layout="#layout/junk_cardview"/>
<include layout="#layout/junk_cardview"/>
<include layout="#layout/junk_cardview"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<!--<android.support.design.widget.FloatingActionButton-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_margin="#dimen/fab_margin"-->
<!--android:clickable="true"-->
<!--android:src="#android:drawable/ic_menu_send"-->
<!--app:layout_anchor="#id/appbar"-->
<!--app:layout_anchorGravity="bottom|right|end"/>-->
I want my backdrop image to slide up which only fading out with slide!
Note: In the library sample I am getting ImageView alpha from 0 to 1 but I want to slide my imageUp not just animate as like alpha animation!

The image you posted is originally from a post about the design of the Google I/O app in 2014. A corresponding image showed what this motion would actually look like in practice [on the right]:
As stated in the article, the source for this app was made public on GitHub. I suggest you take a look at that code in order to get your answer. Though the source currently available is the 2015 version of the app, not the 2014 version mentioned in the article.

Related

Drawer handle change Background off the handle

As this question is many times but I am not getting the answer regarding how the change the handle background.
Till now what i got is :
Close Drawer
Open Drawer
When Drawer is Open the handle come inside the drawer and I have kept the drawer full screen.
Now the problem is When drawer is open handle should change its layout from circular android logo to square android logo.
Here is code :
MainActivty :
public class MainActivity extends AppCompatActivity {
DrawerLayout mDrawerLayout;
ListView mDrawerList;
LinearLayout mDrawer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
mDrawerLayout = (DrawerLayout) findViewById( R.id.drawer_layout );
mDrawer = findViewById( R.id.drawer );
mDrawerList = (ListView) findViewById( R.id.drawer_list );
String[] list = {"hello", "One"};
if (mDrawerLayout.isDrawerOpen( GravityCompat.END )){
DrawerHandle.attach( mDrawer, R.layout.layout_handle, 0.5f );
}else {
DrawerHandle.attach( mDrawer, R.layout.layout_handle_2, 0.5f );
}
}
}
DrawerHandle.class :
public class DrawerHandle implements DrawerLayout.DrawerListener {
public static final String TAG = "DrawerHandle";
private ViewGroup mRootView;
private DrawerLayout mDrawerLayout;
private View mHandle;
private View mDrawer;
private float mVerticalOffset;
private int mGravity;
private WindowManager mWM;
private Display mDisplay;
private Point mScreenDimensions = new Point();
private OnClickListener mHandleClickListener = new OnClickListener(){
#Override
public void onClick(View v) {
if(!mDrawerLayout.isDrawerOpen(mGravity)) mDrawerLayout.openDrawer(mGravity);
else mDrawerLayout.closeDrawer(mGravity);
}
};
private OnTouchListener mHandleTouchListener = new OnTouchListener() {
private static final int MAX_CLICK_DURATION = 200;
private long startClickTime;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
startClickTime = System.currentTimeMillis();
break;
}
case MotionEvent.ACTION_UP: {
if(System.currentTimeMillis() - startClickTime < MAX_CLICK_DURATION) {
v.performClick();
return true;
}
}
}
MotionEvent copy = MotionEvent.obtain(event);
copy.setEdgeFlags(ViewDragHelper.EDGE_ALL);
copy.setLocation(event.getRawX() + (mGravity == Gravity.LEFT || mGravity == GravityCompat.START ? -mHandle.getWidth()/2 : mHandle.getWidth() / 2), event.getRawY());
mDrawerLayout.onTouchEvent(copy);
copy.recycle();
return true;
}
};
private int getDrawerViewGravity(View drawerView) {
final int gravity = ((DrawerLayout.LayoutParams) drawerView.getLayoutParams()).gravity;
return GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(drawerView));
}
private float getTranslation(float slideOffset){
return (mGravity == GravityCompat.START || mGravity == Gravity.LEFT) ? slideOffset*mDrawer.getWidth() : -slideOffset*mDrawer.getWidth();
}
private void updateScreenDimensions() {
if (Build.VERSION.SDK_INT >= 13) {
mDisplay.getSize(mScreenDimensions);
} else {
mScreenDimensions.x = mDisplay.getWidth();
mScreenDimensions.y = mDisplay.getHeight();
}
}
public DrawerHandle(DrawerLayout drawerLayout, View drawer, int handleLayout, float handleVerticalOffset) {
mDrawer = drawer;
mGravity = getDrawerViewGravity(mDrawer);
mDrawerLayout = drawerLayout;
mRootView = (ViewGroup)mDrawerLayout.getRootView();
LayoutInflater inflater = (LayoutInflater) mDrawerLayout.getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE );
mHandle = inflater.inflate(handleLayout, mRootView, false);
mWM = (WindowManager) mDrawerLayout.getContext().getSystemService(Context.WINDOW_SERVICE);
mDisplay = mWM.getDefaultDisplay();
mHandle.setOnClickListener(mHandleClickListener);
mHandle.setOnTouchListener(mHandleTouchListener);
mRootView.addView(mHandle, new FrameLayout.LayoutParams(mHandle.getLayoutParams().width, mHandle.getLayoutParams().height, mGravity));
setVerticalOffset(handleVerticalOffset);
mDrawerLayout.setDrawerListener(this);
}
public static DrawerHandle attach(View drawer, int handleLayout, float verticalOffset) {
if (!(drawer.getParent() instanceof DrawerLayout)) throw new IllegalArgumentException("Argument drawer must be direct child of a DrawerLayout");
return new DrawerHandle((DrawerLayout)drawer.getParent(), drawer, handleLayout, verticalOffset);
}
public static DrawerHandle attach(View drawer, int handleLayout) {
return attach(drawer, handleLayout, 0);
}
#Override
public void onDrawerClosed(View arg0) {
}
#Override
public void onDrawerOpened(View arg0) {
mHandle.setX( 10 );
}
#Override
public void onDrawerSlide(View arg0, float slideOffset) {
float translationX = getTranslation(slideOffset);
mHandle.setTranslationX(translationX);
}
#Override
public void onDrawerStateChanged(int arg0) {
}
public View getView(){
return mHandle;
}
public View getDrawer() {
return mDrawer;
}
public void setVerticalOffset(float offset) {
updateScreenDimensions();
mVerticalOffset = offset;
mHandle.setY(mVerticalOffset*mScreenDimensions.y);
}
}
activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
android:id="#+id/drawer_layout"
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:context=".MainActivity">
<LinearLayout
android:id="#+id/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="right"
android:orientation="vertical"
android:padding="20dp"
android:layout_marginRight="-67dp"
android:background="#CCFF0000"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Collection"
android:paddingBottom="20dp"
/>
<ListView
android:id="#+id/drawer_list"
android:layout_width="match_parent"
android:layout_height="0dip"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"
android:layout_weight="1"
/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.v4.widget.DrawerLayout>
layout_handle.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#mipmap/ic_launcher_square" />
</android.support.constraint.ConstraintLayout>
layout_handle_2.xml :
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="#mipmap/ic_launcher_round" />
</android.support.constraint.ConstraintLayout>
I'm stuck with this problem just help me out.
Kindly let me know your suggestion.
Thank's in advance.

OnOffsetChanged is not working

Trying to make the avatar pic get smaller until disappear by using OnOffsetChanged but it is not working in the fragment. I tried the same way with an activity it worked.
<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:background="#android:color/white"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
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"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleTextAppearance="#style/TextAppearance.AppCompat.Title"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:titleEnabled="false">
<ImageView
android:layout_width="match_parent"
android:layout_height="220dp"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:src="#drawable/bg_polygon"
app:layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:contentInsetStartWithNavigation="0dp"
app:layout_collapseMode="pin"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView>
**some code**
</android.support.v4.widget.NestedScrollView>
<com.mikhaellopez.circularimageview.CircularImageView
android:id="#+id/profileImage"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="#drawable/photo_female_6"
app:civ_border="true"
app:civ_border_width="2dp"
app:layout_anchor="#id/app_bar_layout"
app:layout_anchorGravity="bottom|center" />
I tried to use OnOffsetChangedListener using two ways non of them worked
the first is calling the (AppBarLayout) v.findViewById(R.id.app_bar_layout)).addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener()) to override the method
and the second is by implementing the appbarlayout in the fragmet class
public class ProfileFragment extends Fragment implements
AppBarLayout.OnOffsetChangedListener {
public ProfileFragment() {
// Required empty public constructor
}
public static ProfileFragment newInstance() {
ProfileFragment fragment = new ProfileFragment();
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppBarLayout appBarLayout = (AppBarLayout) getActivity().findViewById(R.id.app_bar_layout);
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_profile, container, false);
return inflater.inflate(R.layout.fragment_profile, container, false);
}
private void initComponent(View v) {
final CircularImageView image = v.findViewById(R.id.profileImage);
final CollapsingToolbarLayout collapsing_toolbar = v.findViewById(R.id.collapsing_toolbar);
((AppBarLayout) v.findViewById(R.id.app_bar_layout)).addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
int min_height = ViewCompat.getMinimumHeight(collapsing_toolbar) * 2;
float scale = (float) (min_height + verticalOffset) / min_height;
image.setScaleX(scale >= 0 ? scale : 0);
image.setScaleY(scale >= 0 ? scale : 0);
}
});
}
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
final CircularImageView image;
image = (CircularImageView) getActivity().findViewById(R.id.profileImage);
final CollapsingToolbarLayout collapsing_toolbar = (CollapsingToolbarLayout) getActivity().findViewById(R.id.collapsing_toolbar);
int min_height = ViewCompat.getMinimumHeight(collapsing_toolbar) * 2;
float scale = (float) (min_height + verticalOffset) / min_height;
image.setScaleX(scale >= 0 ? scale : 0);
image.setScaleY(scale >= 0 ? scale : 0);
}
}
and when I debugged the code I found that the method was never called even when I used the CollapsingToolbarLayout.
i solved this problem by adding an "onactivitycreated" method so when i try to get the view i will exist
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
AppBarLayout mAppBarLayout = getView().findViewById(R.id.app_bar_layout);
final CollapsingToolbarLayout collapsing_toolbar = getView().findViewById(R.id.collapsing_toolbar);
final CircularImageView image = getView().findViewById(R.id.profileImage);
mAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
int min_height = ViewCompat.getMinimumHeight(collapsing_toolbar) * 2;
float scale = (float) (min_height + verticalOffset) / min_height;
image.setScaleX(scale >= 0 ? scale : 0);
image.setScaleY(scale >= 0 ? scale : 0);
}
});
}

Container in CoordinatorLayout with CollapsingToolbarLayout larger than the system window

How do I prevent Framelayout container from expanding past the system window. As you can see below in the image, the framelayout is expanding past the system window. This is preventing the FloatingActionButton in the fragment to be cut off. I am not looking to put the Fab inside of the activity because of shared transitions between changing fragments.
View
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.design.widget.CoordinatorLayout
android:id="#+id/parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorBackground"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar"
android:layout_width="match_parent"
android:layout_height="160dp"
android:background="#color/colorBackground"
android:fitsSystemWindows="true"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginStart="64dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:toolbarId="#+id/toolbar">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:navigationIcon="#drawable/ic_arrow_back"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
</layout>
Update Working Solution
public class MainActivity extends AppCompatActivity {
ActivityAddPlanBinding binding;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_add_plan);
setSupportActionBar(binding.toolbar);
initContainerHeights();
}
int appBarHeight;
int screenHeight;
private void initContainerHeights() {
appBarHeight = binding.appBar.getLayoutParams().height;
screenHeight = screenHeight();
binding.appBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
binding.container.requestLayout();
binding.container.getLayoutParams().height = screenHeight - (appBarHeight + verticalOffset);
}
});
}
private int screenHeight() {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.heightPixels - statusBarHeight();
}
public int statusBarHeight() {
final Resources resources = getResources();
final int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
return resources.getDimensionPixelSize(resourceId);
} else {
return (int) Math.ceil((Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? 24 : 25) * resources.getDisplayMetrics().density);
}
}
}
I have found a solution by calculating the screen height - the verticalOffset of the appbar.
Sulution
public class MainActivity extends AppCompatActivity {
ActivityAddPlanBinding binding;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_add_plan);
setSupportActionBar(binding.toolbar);
initContainerHeights();
}
int appBarHeight;
int screenHeight;
private void initContainerHeights() {
appBarHeight = binding.appBar.getLayoutParams().height;
screenHeight = screenHeight();
binding.appBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
binding.container.requestLayout();
binding.container.getLayoutParams().height = screenHeight - (appBarHeight + verticalOffset);
}
});
}
private int screenHeight() {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.heightPixels - statusBarHeight();
}
public int statusBarHeight() {
final Resources resources = getResources();
final int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
return resources.getDimensionPixelSize(resourceId);
} else {
return (int) Math.ceil((Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? 24 : 25) * resources.getDisplayMetrics().density);
}
}
}
remove "#string/appbar_scrolling_view_behavior" in framelayout..collapsing toolbar is designed for that purpose...else there is no need of colapsing toolbar..Its a waste to take collapsing toolbar if u dont want ur framelayout to be scrollable..

Android - Toolbar goes above Statusbar on Listview scroll

I am trying to hide the toolbar when a listview is scrolled, using Coordinator Layout. I've used app:layout_scrollFlags="scroll|enterAlways" on my toolbar, and android:nestedScrollingEnabled="true" in my listview.
The toolbar hides correctly when I scroll down my listview, but it goes 'above' the statusbar. In web development terminology, it looks like the 'Z-Index' of the toolbar is higher than the statusbar. This is how it looks -
Here is my activity_main.xml
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" android:id="#+id/main_content"
android:layout_width="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true" tools:context=".MainActivity"
android:background="#FFFFFF" >
<android.support.design.widget.AppBarLayout android:id="#+id/appbar"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar android:id="#+id/toolbar"
android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary" app:popupTheme="#style/AppTheme.PopupOverlay"
app:layout_scrollFlags="scroll|enterAlways" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager android:id="#+id/container"
android:layout_width="match_parent" android:layout_height="match_parent"
android:background="#FFFFFF"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
Here is my fragment_main.xml code
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context=".MainActivity$PlaceholderFragment">
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollingCache="false"
android:nestedScrollingEnabled="true" />
</RelativeLayout>
How do I fix this problem? Thanks in advance!
UPDATE: I've posted my answer below! Check for the accepted answer.
I finally fixed this. If anyone else if stuck here, this is how I fixed it. No need to change your layouts to Recyclerview and all! Here are the steps.
1) Set the fitsSystemWindows to false for the CoordinatorLayout.
android:fitsSystemWindows="false"
2) Now, my status bar turned white. To fix this, add the following lines to your onCreate method in MainActivity.java
Window window = getWindow();
// clear FLAG_TRANSLUCENT_STATUS flag:
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
// finally change the color
window.setStatusBarColor(getResources().getColor(R.color.colorPrimaryDark));
Voila! Now everything works perfect.
Here is my activity_main.xml contents, in case I missed something.
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay"
app:contentScrim="#color/colorPrimaryDark"
app:layout_scrollFlags="scroll|enterAlways">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="#style/AppTheme.PopupOverlay"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout android:id="#+id/tabs"
android:layout_width="match_parent" android:layout_height="wrap_content"
app:tabIndicatorColor="#android:color/white"
app:tabIndicatorHeight="3dp"
android:textStyle="bold"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
Why do you have AppBarlayout and toolbar layout if you do not want a toolbar that expands and contracts? basically that AppBarlayout is used when one wants it to be expanding and contracting and it used with a android.support.v4.widget.NestedScrollView so chances are the issue is coming up from there, they are not compatible, you could use a android.support.v7.widget.RecyclerViewin place of the listview and the viewpager has the attribute appbar_scrolling_view_behavior which triggers the appbarlayout to start listening for scroll inputs, I archieve having a normal behaving toolbar by not having AppBarlayout but only toolbar so you could remove the AppBarlayout, looking at things it is not really needed, since you happen to want it to expand and contract below is a sample of how your code should look like
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
android:layout_width="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/scrollableview"
android:layout_width="match_parent"
android:visibility="gone"
android:background="#FFFFFF"
android:paddingTop="?attr/actionBarSize"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<!-- Your content, maybe a ListView? -->
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/fab_margin"
android:clickable="true"
android:id="#+id/fabAddItem"
android:src="#drawable/ic_action_add"
app:backgroundTint="#color/google_lightblue"
app:layout_anchor="#+id/toolbar"
app:layout_anchorGravity="bottom|right|end" />
<aubry.chromio.com.dressup.view.CollapsingTitleLayout
android:id="#+id/backdrop_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="#style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
app:expandedTextSize="40dp"
app:expandedMargin="16dp">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_height="?attr/actionBarSize"
android:layout_width="match_parent" />
</aubry.chromio.com.dressup.view.CollapsingTitleLayout>
</FrameLayout>
and here is my CollapingsTitleLayout
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.Toolbar;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import aubry.chromio.com.dressup.R;
public class CollapsingTitleLayout extends FrameLayout {
// Pre-JB-MR2 doesn't support HW accelerated canvas scaled text so we will workaround it
// by using our own texture
private static final boolean USE_SCALING_TEXTURE = Build.VERSION.SDK_INT < 18;
private static final boolean DEBUG_DRAW = false;
private static final Paint DEBUG_DRAW_PAINT;
static {
DEBUG_DRAW_PAINT = DEBUG_DRAW ? new Paint() : null;
if (DEBUG_DRAW_PAINT != null) {
DEBUG_DRAW_PAINT.setAntiAlias(true);
DEBUG_DRAW_PAINT.setColor(Color.MAGENTA);
}
}
private Toolbar mToolbar;
private View mDummyView;
private float mScrollOffset;
private final Rect mToolbarContentBounds;
private float mExpandedMarginLeft;
private float mExpandedMarginRight;
private float mExpandedMarginBottom;
private int mRequestedExpandedTitleTextSize;
private int mExpandedTitleTextSize;
private int mCollapsedTitleTextSize;
private float mExpandedTop;
private float mCollapsedTop;
private String mTitle;
private String mTitleToDraw;
private boolean mUseTexture;
private Bitmap mExpandedTitleTexture;
private float mTextLeft;
private float mTextRight;
private float mTextTop;
private float mScale;
private final TextPaint mTextPaint;
private Paint mTexturePaint;
private Interpolator mTextSizeInterpolator;
public CollapsingTitleLayout(Context context) {
this(context, null);
}
public CollapsingTitleLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CollapsingTitleLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mTextPaint = new TextPaint();
mTextPaint.setAntiAlias(true);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CollapsingTitleLayout);
mExpandedMarginLeft = mExpandedMarginRight = mExpandedMarginBottom =
a.getDimensionPixelSize(R.styleable.CollapsingTitleLayout_expandedMargin, 0);
final boolean isRtl = ViewCompat.getLayoutDirection(this)
== ViewCompat.LAYOUT_DIRECTION_RTL;
if (a.hasValue(R.styleable.CollapsingTitleLayout_expandedMarginStart)) {
final int marginStart = a.getDimensionPixelSize(
R.styleable.CollapsingTitleLayout_expandedMarginStart, 0);
if (isRtl) {
mExpandedMarginRight = marginStart;
} else {
mExpandedMarginLeft = marginStart;
}
}
if (a.hasValue(R.styleable.CollapsingTitleLayout_expandedMarginEnd)) {
final int marginEnd = a.getDimensionPixelSize(
R.styleable.CollapsingTitleLayout_expandedMarginEnd, 0);
if (isRtl) {
mExpandedMarginLeft = marginEnd;
} else {
mExpandedMarginRight = marginEnd;
}
}
if (a.hasValue(R.styleable.CollapsingTitleLayout_expandedMarginBottom)) {
mExpandedMarginBottom = a.getDimensionPixelSize(
R.styleable.CollapsingTitleLayout_expandedMarginBottom, 0);
}
final int tp = a.getResourceId(R.styleable.CollapsingTitleLayout_android_textAppearance,
android.R.style.TextAppearance);
setTextAppearance(tp);
if (a.hasValue(R.styleable.CollapsingTitleLayout_collapsedTextSize)) {
mCollapsedTitleTextSize = a.getDimensionPixelSize(
R.styleable.CollapsingTitleLayout_collapsedTextSize, 0);
}
mRequestedExpandedTitleTextSize = a.getDimensionPixelSize(
R.styleable.CollapsingTitleLayout_expandedTextSize, mCollapsedTitleTextSize);
final int interpolatorId = a
.getResourceId(R.styleable.CollapsingTitleLayout_textSizeInterpolator,
android.R.anim.accelerate_interpolator);
mTextSizeInterpolator = AnimationUtils.loadInterpolator(context, interpolatorId);
a.recycle();
mToolbarContentBounds = new Rect();
setWillNotDraw(false);
}
public void setTextAppearance(int resId) {
TypedArray atp = getContext().obtainStyledAttributes(resId,
R.styleable.CollapsingTextAppearance);
mTextPaint.setColor(atp.getColor(
R.styleable.CollapsingTextAppearance_android_textColor, Color.WHITE));
mCollapsedTitleTextSize = atp.getDimensionPixelSize(
R.styleable.CollapsingTextAppearance_android_textSize, 0);
atp.recycle();
recalculate();
}
#Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
super.addView(child, index, params);
if (child instanceof Toolbar) {
mToolbar = (Toolbar) child;
mDummyView = new View(getContext());
mToolbar.addView(mDummyView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
}
/**
* Set the value indicating the current scroll value. This decides how much of the
* background will be displayed, as well as the title metrics/positioning.
*
* A value of {#code 0.0} indicates that the layout is fully expanded.
* A value of {#code 1.0} indicates that the layout is fully collapsed.
*/
public void setScrollOffset(float offset) {
if (offset != mScrollOffset) {
mScrollOffset = offset;
calculateOffsets();
}
}
private void calculateOffsets() {
final float offset = mScrollOffset;
final float textSizeOffset = mTextSizeInterpolator != null
? mTextSizeInterpolator.getInterpolation(mScrollOffset)
: offset;
mTextLeft = interpolate(mExpandedMarginLeft, mToolbarContentBounds.left, offset);
mTextTop = interpolate(mExpandedTop, mCollapsedTop, offset);
mTextRight = interpolate(getWidth() - mExpandedMarginRight, mToolbarContentBounds.right, offset);
setInterpolatedTextSize(
interpolate(mExpandedTitleTextSize, mCollapsedTitleTextSize, textSizeOffset));
ViewCompat.postInvalidateOnAnimation(this);
}
private void calculateTextBounds() {
final DisplayMetrics metrics = getResources().getDisplayMetrics();
// We then calculate the collapsed text size, using the same logic
mTextPaint.setTextSize(mCollapsedTitleTextSize);
float textHeight = mTextPaint.descent() - mTextPaint.ascent();
float textOffset = (textHeight / 2) - mTextPaint.descent();
mCollapsedTop = mToolbarContentBounds.centerY() + textOffset;
// First, let's calculate the expanded text size so that it fit within the bounds
// We make sure this value is at least our minimum text size
mExpandedTitleTextSize = (int) Math.max(mCollapsedTitleTextSize,
getSingleLineTextSize(mTitle, mTextPaint,
getWidth() - mExpandedMarginLeft -mExpandedMarginRight, 0f,
mRequestedExpandedTitleTextSize, 0.5f, metrics));
mExpandedTop = getHeight() - mExpandedMarginBottom;
// The bounds have changed so we need to clear the texture
clearTexture();
}
#Override
public void draw(Canvas canvas) {
final int saveCount = canvas.save();
final int toolbarHeight = mToolbar.getHeight();
canvas.clipRect(0, 0, canvas.getWidth(),
interpolate(canvas.getHeight(), toolbarHeight, mScrollOffset));
// Now call super and let it draw the background, etc
super.draw(canvas);
if (mTitleToDraw != null) {
float x = mTextLeft;
float y = mTextTop;
final float ascent = mTextPaint.ascent() * mScale;
final float descent = mTextPaint.descent() * mScale;
final float h = descent - ascent;
if (DEBUG_DRAW) {
// Just a debug tool, which drawn a Magneta rect in the text bounds
canvas.drawRect(mTextLeft,
y - h + descent,
mTextRight,
y + descent,
DEBUG_DRAW_PAINT);
}
if (mUseTexture) {
y = y - h + descent;
}
if (mScale != 1f) {
canvas.scale(mScale, mScale, x, y);
}
if (mUseTexture && mExpandedTitleTexture != null) {
// If we should use a texture, draw it instead of text
canvas.drawBitmap(mExpandedTitleTexture, x, y, mTexturePaint);
} else {
canvas.drawText(mTitleToDraw, x, y, mTextPaint);
}
}
canvas.restoreToCount(saveCount);
}
private void setInterpolatedTextSize(final float textSize) {
if (mTitle == null) return;
if (isClose(textSize, mCollapsedTitleTextSize) || isClose(textSize, mExpandedTitleTextSize)
|| mTitleToDraw == null) {
// If the text size is 'close' to being a decimal, then we use this as a sync-point.
// We disable our manual scaling and set the paint's text size.
mTextPaint.setTextSize(textSize);
mScale = 1f;
// We also use this as an opportunity to ellipsize the string
final CharSequence title = TextUtils.ellipsize(mTitle, mTextPaint,
mTextRight - mTextLeft,
TextUtils.TruncateAt.END);
if (title != mTitleToDraw) {
// If the title has changed, turn it into a string
mTitleToDraw = title.toString();
}
if (USE_SCALING_TEXTURE && isClose(textSize, mExpandedTitleTextSize)) {
ensureExpandedTexture();
}
mUseTexture = false;
} else {
// We're not close to a decimal so use our canvas scaling method
if (mExpandedTitleTexture != null) {
mScale = textSize / mExpandedTitleTextSize;
} else {
mScale = textSize / mTextPaint.getTextSize();
}
mUseTexture = USE_SCALING_TEXTURE;
}
ViewCompat.postInvalidateOnAnimation(this);
}
private void ensureExpandedTexture() {
if (mExpandedTitleTexture != null) return;
int w = (int) (getWidth() - mExpandedMarginLeft - mExpandedMarginRight);
int h = (int) (mTextPaint.descent() - mTextPaint.ascent());
mExpandedTitleTexture = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(mExpandedTitleTexture);
c.drawText(mTitleToDraw, 0, h - mTextPaint.descent(), mTextPaint);
if (mTexturePaint == null) {
// Make sure we have a paint
mTexturePaint = new Paint();
mTexturePaint.setAntiAlias(true);
mTexturePaint.setFilterBitmap(true);
}
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mToolbarContentBounds.left = mDummyView.getLeft();
mToolbarContentBounds.top = mDummyView.getTop();
mToolbarContentBounds.right = mDummyView.getRight();
mToolbarContentBounds.bottom = mDummyView.getBottom();
if (changed && mTitle != null) {
// If we've changed and we have a title, re-calculate everything!
recalculate();
}
}
private void recalculate() {
if (getHeight() > 0) {
calculateTextBounds();
calculateOffsets();
}
}
/**
* Set the title to display
*
* #param title
*/
public void setTitle(String title) {
if (title == null || !title.equals(mTitle)) {
mTitle = title;
clearTexture();
if (getHeight() > 0) {
// If we've already been laid out, calculate everything now otherwise we'll wait
// until a layout
recalculate();
}
}
}
private void clearTexture() {
if (mExpandedTitleTexture != null) {
mExpandedTitleTexture.recycle();
mExpandedTitleTexture = null;
}
}
/**
* Recursive binary search to find the best size for the text
*
* Adapted from https://github.com/grantland/android-autofittextview
*/
private static float getSingleLineTextSize(String text, TextPaint paint, float targetWidth,
float low, float high, float precision, DisplayMetrics metrics) {
final float mid = (low + high) / 2.0f;
paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mid, metrics));
final float maxLineWidth = paint.measureText(text);
if ((high - low) < precision) {
return low;
} else if (maxLineWidth > targetWidth) {
return getSingleLineTextSize(text, paint, targetWidth, low, mid, precision, metrics);
} else if (maxLineWidth < targetWidth) {
return getSingleLineTextSize(text, paint, targetWidth, mid, high, precision, metrics);
} else {
return mid;
}
}
/**
* Returns true if {#code value} is 'close' to it's closest decimal value. Close is currently
* defined as it's difference being < 0.01.
*/
private static boolean isClose(float value, float targetValue) {
return Math.abs(value - targetValue) < 0.01f;
}
/**
* Interpolate between {#code startValue} and {#code endValue}, using {#code progress}.
*/
private static float interpolate(float startValue, float endValue, float progress) {
return startValue + ((endValue - startValue) * progress);
}
}

Android : Restoring recyclerview parallax

I try to code my own parallax effect using a recycler view. It's works pretty well at the moment... until I don't change my device orientation.
At state restoration, I get back my "metrics" variable I use to calculate the header Y translation, no problem with that.
But I have troubles getting height from my different views after restoration.
basically here's what I log :
D/parallax﹕ gridHeight: 0 - toolbar: 0 - poster: 0
I tried using .measure() then getMeasuredHeight() but here's what I get :
D/parallax﹕ gridHeight: 0 - toolbar: 128 - poster: 1108
Maybe I missused Measure. Or maybe I should use my parallax method at another moment ? (clother to the runtime ?) If you got any clue...
This is my first question here after a year of reading. Hope I did this the good way. And thank you for any help ;)
Here's my code :
public class ParallaxActivity extends Activity {
private int metrics = 0;
private int resize = 0;
private int gridHeight = -1;
private int toolbarHeight = -1;
private int posterHeight = -1;
private boolean docked = false;
private Toolbar toolbar;
private ImageView poster;
private LinearLayout header;
private RecyclerView grid;
private RecyclerView.LayoutManager manager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_parallax2);
toolbar = (Toolbar)findViewById(R.id.toolbar_actionbar);
toolbar.setNavigationIcon(R.drawable.ic_launcher);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
}
});
poster = (ImageView)findViewById(R.id.poster);
grid = (RecyclerView)findViewById(R.id.list);
header = (LinearLayout)findViewById(R.id.header);
manager = new GridLayoutManager(this, 2);
grid.setLayoutManager(manager);
DefaultItemAnimator animator = new DefaultItemAnimator();
grid.setItemAnimator(animator);
RecyclerGridAdapter ad = new RecyclerGridAdapter(this);
grid.setAdapter(ad);
ad.update();
grid.setOnScrollListener(new RecyclerView.OnScrollListener()
{
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
metrics += dy / 3;
parallax();
}
});
}
#Override
protected void onSaveInstanceState(Bundle b)
{
super.onSaveInstanceState(b);
b.putInt("metrics", metrics);
}
#Override
protected void onRestoreInstanceState(Bundle b)
{
super.onRestoreInstanceState(b);
metrics = b.getInt("metrics", 0);
}
#Override
public void onAttachedToWindow()
{
if (metrics != 0)
parallax();
}
private void parallax()
{
if (gridHeight == -1)
{
gridHeight = grid.getHeight();
toolbarHeight = toolbar.getHeight();
posterHeight = poster.getHeight();
Log.d("parallax", "gridHeight: " + gridHeight + " - toolbar: " + toolbarHeight + " - poster: " + posterHeight);
}
if (!docked && metrics > posterHeight - toolbarHeight)
{
docked = true;
toolbar.setBackgroundColor(0xff000000);
}
if (docked && metrics < posterHeight - toolbarHeight)
{
docked = false;
toolbar.setBackgroundColor(0x00000000);
}
if (metrics < 0)
resize = 0;
else if ( metrics > posterHeight - toolbarHeight)
resize = posterHeight - toolbarHeight;
else
resize = metrics;
header.setTranslationY(-resize);
grid.setTranslationY(-resize);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) grid.getLayoutParams();
params.height = gridHeight + resize;
grid.setLayoutParams(params);
}
}
and my layout :
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar_actionbar"
android:background="#null"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="5dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:elevation="0dp">
<ImageView
android:id="#+id/poster"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
android:src="#drawable/jpeg"/>
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="SerieKids"
android:textSize="20dp"
android:textColor="#ffffff"
android:background="#000000"
android:paddingStart="100dp"/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"/>
</LinearLayout>
</RelativeLayout>
And finally the way I use measure()
if (gridHeight == -1)
{
toolbar.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
poster.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
grid.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
gridHeight = grid.getMeasuredHeight();
toolbarHeight = toolbar.getMeasuredHeight();
posterHeight = poster.getMeasuredHeight();
Log.d("parallax", "gridHeight: " + gridHeight + " - toolbar: " + toolbarHeight + " - poster: " + posterHeight);
}
Add a onGlobalLayoutListener to your RecyclerView. That way you're making sure your parallax routine is only called after you view has been completly drawn. Also make sure you unregister the globalLayout otherwise it will be called every time you draw your view (if you're using it to control the rotation changes then there is no need to remove the listener I think. That way it will always be called when you rotate because the layout will be drawn again).
An example of how to register and unregister the layout listener:
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
//your parallax routine here
}
});

Categories

Resources