I am trying to implement auto-hiding feature for my toolbar using CoordinateLayout. But I also want to tweaks it a bit, so that the hide/show only works after I scroll past my imageview.
I am currently able to "turn on/off" the hide/show availability for the toolbar via AppBarLayout.LayoutParams. So right now it won't hide the toolbar if I scroll within the imageview's height and will hide if I scroll pass the imageview. The only problem is, if I scroll the layout very fast and instantly pull my finger before it pass the imageview's height the toolbar won't hide. In my opinion it is because the scrolling listener's function is called before the parameter of the toolbar's show/hide is changed to enable.
Here's the code:
ArticleActivity.java
public class ArticleActivity extends AppCompatActivity {
private Toolbar toolbar;
private ImageView imageView;
private NestedScrollView scrollView;
private Matrix matrix;
private int imageHeight = 0;
private float scale = 0;
private boolean hideable = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_article);
imageView = (ImageView) findViewById(R.id.imageView);
scrollView = (NestedScrollView) findViewById(R.id.scrollView);
toolbar = (Toolbar) findViewById(R.id.toolbar);
matrix = new Matrix();
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
scale = (float) displayMetrics.widthPixels / ((float) imageView.getDrawable().getIntrinsicWidth());
matrix.postScale(scale, scale);
imageView.setImageMatrix(matrix);
if(imageView.getViewTreeObserver().isAlive()) {
imageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
imageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
imageHeight = imageView.getHeight();
}
});
}
if(scrollView.getViewTreeObserver().isAlive()) {
scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
int scrollY = scrollView.getScrollY();
if(scrollY <= imageHeight) {
matrix.setTranslate(0, scrollY / 4);
matrix.postScale(scale, scale);
imageView.setImageMatrix(matrix);
if(hideable) {
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
params.setScrollFlags(0);
toolbar.setLayoutParams(params);
hideable = false;
}
} else {
if(!hideable) {
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
// 1 = scroll, 4 = enterAlways, 16 = snap
params.setScrollFlags(21);
toolbar.setLayoutParams(params);
hideable = true;
}
}
}
});
}
}}
activity_article.xml:
<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/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="matrix"
android:src="#drawable/download"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/imageView"
android:text="What is Lorem Ipsum?"/>
</RelativeLayout>
</android.support.v4.widget.NestedScrollView>
What should I do to fix this? Or is there another way to achieve this? Thank you
Related
I have a View and a RecyclerView housed in a LinearLayout. What I want to achieve is something like this:
https://material.google.com/patterns/scrolling-techniques.html#scrolling-techniques-behavior
Basically when I scroll the RecyclerView up, the View collapses. It expands if I scroll the RecyclerView down.
I've tried various methods but the animation stutters if the finger jerks around a scroll position. It only animates well if the finger does a deliberate scroll movement in one direction. How do I do this correctly?
You have to use Coordinator Layout with the CollapsingToolbarLayout
<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:clipToPadding="false">
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="210dp"
android:stateListAnimator="#animator/appbar_always_elevated" //I put this here because I want to have shadow when is open, but you have to create the xml file.
android:background="#color/WHITE">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:collapsedTitleTextAppearance="#style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
app:expandedTitleMarginStart="72dp"
app:expandedTitleTextAppearance="#style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
app:layout_scrollFlags="scroll|exitUntilCollapsed"> //HERE you should take a look what you want your collapse bar do.
<Here you put the content for you collapse bar, like a ImageView>
<android.support.v7.widget.Toolbar //This is the size of your fixed bar when you collapse, even here you can put a back button, for example
android:id="#+id/app_bar"
android:layout_width="match_parent"
android:layout_height="40dp"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/main_home_list_swipe"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" >
<android.support.v7.widget.RecyclerView
android:id="#+id/main_home_list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.design.widget.CoordinatorLayout >
Obs: the comments will give errors if you put on a xml file. Is on propose so you will remember to read hahahah
Try this:-
I also want this kind of animation on custom view and i have achieved it this way.
public class TestActivity extends AppCompatActivity {
private static final int HIDE_THRESHOLD = 20;
//this is you custom layout it is any thing.
LinearLayout customLayout;
private int scrolledDistance = 0;
private boolean controlsVisible = true;
private RecyclerView recyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
scrolledDistance = dy;
if (scrolledDistance > HIDE_THRESHOLD && controlsVisible) {
hideViews();
controlsVisible = false;
} else if (scrolledDistance < -HIDE_THRESHOLD && !controlsVisible) {
showViews();
controlsVisible = true;
}
}
});
}
private void hideViews() {
customLayout.animate().translationY(-customLayout.getHeight()).setInterpolator(new AccelerateInterpolator(2));
}
private void showViews() {
customLayout.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2));
}
}
Edit - 1
for ScrollView try this listener
scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
if (scrolledDistance > HIDE_THRESHOLD && controlsVisible) {
hideViews();
controlsVisible = false;
scrolledDistance = 0;
} else if (scrolledDistance < -HIDE_THRESHOLD && !controlsVisible) {
showViews();
controlsVisible = true;
scrolledDistance = 0;
}
}
});
Hope it also helps you...
To achieve toolbar to expand and collapse smoothly you can apply translate animation or use CoordinatorLayout with AppBarLayout and Toolbar.
Animation : First you have to detect scroll up and scroll down on your RecyclerView. To do so you can set “setOnScrollListener” on your RecyclerView. Once you have both scroll up and scroll down, simply apply animation.
Code:
rvHomeList.setOnScrollListener(new RecyclerView.OnScrollListener() {
int verticalOffset;
boolean scrollingUp;
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (scrollingUp) {
Log.e("onScrollStateChanged", "UP");
if (verticalOffset > llTop.getHeight()) {
toolbarAnimateHide();
}
} else {
Log.e("onScrollStateChanged", "down");
if (llTop.getTranslationY() < llTop.getHeight() * -0.6 && verticalOffset > llTop.getHeight()) {
toolbarAnimateHide();
} else {
toolbarAnimateShow(verticalOffset);
}
}
}
}
#Override
public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
verticalOffset += dy;
scrollingUp = dy > 0;
int toolbarYOffset = (int) (dy - llTop.getTranslationY());
llTop.animate().cancel();
if (scrollingUp) {
Log.e("onScrolled", "UP");
if (toolbarYOffset < llTop.getHeight()) {
llTop.setTranslationY(-toolbarYOffset);
} else {
llTop.setTranslationY(-llTop.getHeight());
}
} else {
Log.e("onScrolled", "down");
if (toolbarYOffset < 0) {
llTop.setTranslationY(0);
} else {
llTop.setTranslationY(-toolbarYOffset);
}
}
}
});
Animation Methods:
private void toolbarAnimateShow(final int verticalOffset) {
if (!isShowing) {
isShowing = true;
llTop.animate()
.translationY(0)
.setInterpolator(new LinearInterpolator())
.setDuration(180)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
llTop.setVisibility(View.VISIBLE);
isShowing = false;
}
});
}
}
private void toolbarAnimateHide() {
if (!isHidding) {
isHidding = true;
llTop.animate()
.translationY(-llTop.getHeight())
.setInterpolator(new LinearInterpolator())
.setDuration(180)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
llTop.setVisibility(View.GONE);
isHidding = false;
}
});
}
}
CoordinatorLayout with AppBarLayout and Toolbar: By using coordinatorLayout with appBarLayout and toolbar, and set the scroll flag used within the attribute app:layout_scrollFlags to achieve the scroll effect. It must be enabled for any scroll effects to take into effect. This flag must be enabled along with enterAlways, enterAlwaysCollapsed, exitUntilCollapsed, or snap.
enterAlways: The view will become visible when scrolling up. This flag is useful in cases when scrolling from the bottom of a list and wanting to expose the Toolbar as soon as scrolling up takes place.
enterAlwaysCollapsed: Normally, when only enterAlways is used, the Toolbar will continue to expand as you scroll down.Assuming enterAlways is declared and you have specified a minHeight, you can also specify enterAlwaysCollapsed. When this setting is used, your view will only appear at this minimum height. Only when scrolling reaches to the top will the view expand to its full height
exitUntilCollapsed: When the scroll flag is set, scrolling down will normally cause the entire content to move.By specifying a minHeight and exitUntilCollapsed, the minimum height of the Toolbar will be reached before the rest of the content begins to scroll and exit from the screen
snap: Using this option will determine what to do when a view only has been partially reduced. If scrolling ends and the view size has been reduced to less than 50% of its original, then this view to return to its original size. If the size is greater than 50% of its sized, it will disappear completely.
Code:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/llBase"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:orientation="vertical">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbarContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="#dimen/_40sdp"
android:gravity="center"
android:theme="#style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways">
<include
layout="#layout/top_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<RelativeLayout
android:id="#+id/rlMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
</LinearLayout>
You need to use CoordinatorLayout to achieve what you want.
You could find all needed information in this tutorial.
I have this layout (code is at the bottom) which contains a CollapsingToolBarLayout at the top and a NestedScrollView at the bottom.
When I scroll up, the collapsing toolbar will start to collapse, then the scroll view will scroll up with the collapsing toolbar at first and then goes behind the collapsed tool bar.
I want to have some animations (image slides left when scrolling up and slides back when scrolling down) in the collapsing toolbar. The issue now is: sometimes, when I scroll up, the image doesn't slide left. When it slid left, and I scroll down, it doesn't slide back.
I trigger these animations through onOffsetChanged for the AppBarLayout and OnTouchListener for the NestedScrollView.
// People image slide left when user scrolls up on the scroll view
mScrollView.setOnTouchListener(scrollViewTouchListener);
// People image slide back when app bar is almost expanded
mAppBar.addOnOffsetChangedListener(appBarOffsetChangedListener);
// OnOffsetChangedListener for the AppBarLayout
private AppBarLayout.OnOffsetChangedListener appBarOffsetChangedListener = new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int offset) {
// If the app bar is almost expanded and people image slided left, make it slide back
if ((offset > -20 || offset == 0) && mPeopleSlidedLeft) {
mPeopleImage.animate().setDuration(animationTime)
.translationX(originalPeoplePosition[0]);
mPeopleSlidedLeft = false;
}
}
};
// Touch listener for the scroll view
private View.OnTouchListener scrollViewTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
float y = event.getY();
if (event.getAction() == MotionEvent.ACTION_MOVE) {
float dy = y - mPreviousY;
// if user scrolls up and people image hasn't slided left,
if (dy < -1 && mPeopleSlidedLeft == false) {
DisplayMetrics dm = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
int xDest = dm.widthPixels / 2;
xDest += mPeopleImage.getMeasuredWidth() / 2;
mPeopleImage.animate().setDuration(animationTime)
.translationX(originalPeoplePosition[0] - xDest);
}
}
mPeopleSlidedLeft = true;
mPreviousY = y;
return false;
}
};
Just note that the scrollview's setOnScrollChangeListener won't work as it's not triggered when the toolbar is collapsing.
A simplified version of the layout is below:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:elevation="0dp">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="#dimen/collapsing_toolbar_margin"
android:fitsSystemWindows="true"
android:minHeight="120dp"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<LinearLayout
android:id="#+id/backdrop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:fitsSystemWindows="true"
android:orientation="vertical"
app:layout_collapseMode="parallax">
</LinearLayout>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="#dimen/toolbar_height"
android:layout_gravity="center_horizontal"
app:contentInsetEnd="16dp"
app:contentInsetStart="16dp"
app:elevation="0dp"
app:layout_collapseMode="pin"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:background="#color/white"
android:orientation="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<android.support.v4.widget.NestedScrollView
android:id="#+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
<include
layout="#layout/notification"
android:layout_width="match_parent"
android:layout_height="#dimen/active_inactive_time_height"
android:layout_gravity="bottom"
app:layout_anchorGravity="bottom|right"
android:layout_marginBottom="#dimen/bottom_navigation_bar_offset" />
</android.support.design.widget.CoordinatorLayout>
Can someone please have a look? I will really appreciate it!
I'm not sure why you're using a touch listener on the NestedScrollView, if I understand correctly you want there to be 2 states:
Toolbar is expanded and people image is visible
Toolbar is collapsed and people image is hidden
And the transition between these 2 states should be to slide the people image off the left of the screen?
This should be achievable with the AppBarLayout.OnOffsetChangedListener alone, and you can use the change of offset to "animate" the view moving instead of setting up trigger points which can result in a much smoother implementation.
Something like:
private AppBarLayout.OnOffsetChangedListener appBarOffsetChangedListener = new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
float fraction = ((float) Math.abs(verticalOffset)) / appBarLayout.getTotalScrollRange();
int peopleRange = mPeopleImage.getRight();
mPeopleImage.setTranslationX(fraction * peopleRange * -1);
}
};
I've added some configuration of the timing and speed of the slid. In this case it waits until the header is collapsed 25% before sliding and the image moves left twice as fast. You could play with these numbers to get what you're looking for.
private AppBarLayout.OnOffsetChangedListener appBarOffsetChangedListener = new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
float fraction = ((float) Math.abs(verticalOffset)) / appBarLayout.getTotalScrollRange();
int peopleRange = mPeopleImage.getRight();
float delay = 0.25f;
float speed = 2f;
fraction = Math.max(fraction - delay, 0f) * speed;
mPeopleImage.setTranslationX(fraction * peopleRange * -1);
}
};
I've been reading around all day without any success on this.
Basically want to be able to set an ImageView inside a android.support.design.widget.CollapsingToolbarLayout to change it's height depending on the onOffsetChanged change detected so that it will "zoom-out" when collapsed to fit the whole image width and "zoom-in" when expanded to do normal centerCrop behavior.
I tried setting the ImageView height in the onOffsetChanged but that causes other issues assuming due to the CollapsingToolbarLayout is also repositioning it.
Sample functionality I've seen in ParallaxListView project but wish to use the CollapsingToolbarLayout.
Anyone able to give sample code (if it is possible)?
Also seen this project but again similar limitation. Other projects as well.
You can try using android:scaleType="matrix"for the collapsing image's layout definition.
In the code,
store the initial ImageMatrix in a Matrix using matrix.set(imageView.getImageMatrix());
And depending upon the scroll of collapsing toolbar, you can use matrix.postScale() to scale the image and finally set it back to the ImageView using imageView.setImageMatrix(matrix). That can give you the zoom in / out effect.
I managed to do it in the end with the following code for anyone else out there that it may help. The code will fit to width when expanded and fit to height when collapsed. It can be changed to scale (zoom) further in if needed.
Not sure if optimal code is written, suggestions welcome. To measure the bitmap and the view and calculate the min/max scale I use the first call to onOffsetChanged which seems to work fine.
public class MyActivity extends AppCompatActivity implements AppBarLayout.OnOffsetChangedListener {
private float collapsedScale;
private float expandedScale;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity_layout);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setTitle(entry.label);
photoView = (ImageView) findViewById(R.id.photo_image);
AppBarLayout mAppBarLayout = (AppBarLayout) findViewById(R.id.appbar);
mAppBarLayout.addOnOffsetChangedListener(this);
}
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
int maxScroll = appBarLayout.getTotalScrollRange();
float scrollPercent = (float) Math.abs(verticalOffset) / (float) maxScroll;
if (collapsedScale == 0) {
Drawable photo = photoView.getDrawable();
int bitmapWidth = photo.getIntrinsicWidth();
int bitmapHeight = photo.getIntrinsicHeight();
collapsedScale = (float)photoView.getWidth()/(float)bitmapWidth;
expandedScale = (float)photoView.getHeight()/(float)bitmapHeight;
scalePhotoImage(photoView, expandedScale);
} else {
scalePhotoImage(photoView, collapsedScale + (expandedScale - collapsedScale) * (1f - scrollPercent));
}
}
private static void scalePhotoImage(ImageView photoView, float scale) {
Drawable photo = photoView.getDrawable();
int bitmapWidth = photo.getIntrinsicWidth();
int bitmapHeight = photo.getIntrinsicHeight();
float offsetX = (photoView.getWidth() - bitmapWidth) / 2F;
float offsetY = (photoView.getHeight() - bitmapHeight) / 2F;
float centerX = photoView.getWidth() / 2F;
float centerY = photoView.getHeight() / 2F;
Matrix imageMatrix = new Matrix();
imageMatrix.setScale(scale, scale, centerX, centerY);
imageMatrix.preTranslate(offsetX, offsetY);
photoView.setImageMatrix(imageMatrix);
}
}
My layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/menu_background_color"
tools:context="style.donkey.android.EntryDetailsActivity">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:elevation="6dp"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
app:contentScrim="#android:color/transparent">
<ImageView
android:id="#+id/photo_image"
android:layout_width="match_parent"
android:layout_height="300dp"
android:src="#drawable/demo_photo"
android:fitsSystemWindows="true"
android:scaleType="matrix"
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="100dp"
android:theme = "#style/ToolBarStyle"
app:layout_collapseMode="pin">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView 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"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<include layout="#layout/content_layout" />
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
I am trying to achieve a similar behavior to that of Telegram, on the settings page, that is, there is a CircleImage that when scrolling up goes to the left of the Topbar title, and when scrolling down goes to the middle of the expanded AppBarLayout.
I was basing my work on this example:
https://github.com/saulmm/CoordinatorBehaviorExample
But in this case the original coder is recreating the Topbar twice. I dont want to do that, the default behavior of the topbar is what I need and also I want to take advantage of the hamburger menu and the options menu that come out of the box.
This is my view hierarchy:
DrawerLayout
|
|---CoordinatorLayout
|--AppBarLayout
| |-CollapsingToolbarLayout
| |-ImageView (backdrop image)
| |-Toolbar
|--NestedScrollView
|--ImageView (circleimage avatar)
As you can see I cannot make the Toolbar layout a sibling of my CircleImage so I cannot bind them together on the layoutDependsOn method. I tried binding to the AppBarLayout basing my code off the one on the github repo but to be honest I cannot make much sense of what's happening in the original code.
My behavior was implemented in much the same manner as Saul's. The biggest difference is that I like to put a non-visible view like a Space where I wanted the circle image to end up, then use that view's bounds to determine how to move & size the circle image.
public class CollapsingImageBehavior extends CoordinatorLayout.Behavior<View> {
private final static int X = 0;
private final static int Y = 1;
private final static int WIDTH = 2;
private final static int HEIGHT = 3;
private int mTargetId;
private int[] mView;
private int[] mTarget;
public CollapsingImageBehavior() {
}
public CollapsingImageBehavior(Context context, AttributeSet attrs) {
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CollapsingImageBehavior);
mTargetId = a.getResourceId(R.styleable.CollapsingImageBehavior_collapsedTarget, 0);
a.recycle();
}
if (mTargetId == 0) {
throw new IllegalStateException("collapsedTarget attribute not specified on view for behavior");
}
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof AppBarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
setup(parent, child);
AppBarLayout appBarLayout = (AppBarLayout) dependency;
int range = appBarLayout.getTotalScrollRange();
float factor = -appBarLayout.getY() / range;
int left = mView[X] + (int) (factor * (mTarget[X] - mView[X]));
int top = mView[Y] + (int) (factor * (mTarget[Y] - mView[Y]));
int width = mView[WIDTH] + (int) (factor * (mTarget[WIDTH] - mView[WIDTH]));
int height = mView[HEIGHT] + (int) (factor * (mTarget[HEIGHT] - mView[HEIGHT]));
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
lp.width = width;
lp.height = height;
child.setLayoutParams(lp);
child.setX(left);
child.setY(top);
return true;
}
private void setup(CoordinatorLayout parent, View child) {
if (mView != null) return;
mView = new int[4];
mTarget = new int[4];
mView[X] = (int) child.getX();
mView[Y] = (int) child.getY();
mView[WIDTH] = child.getWidth();
mView[HEIGHT] = child.getHeight();
View target = parent.findViewById(mTargetId);
if (target == null) {
throw new IllegalStateException("target view not found");
}
mTarget[WIDTH] += target.getWidth();
mTarget[HEIGHT] += target.getHeight();
View view = target;
while (view != parent) {
mTarget[X] += (int) view.getX();
mTarget[Y] += (int) view.getY();
view = (View) view.getParent();
}
}
}
And here's the layout. One important thing I found out is that the circle image view needed to have an elevation set so that it would lay out atop the toolbar in collapsed mode, otherwise it would be behind the toolbar and not shown.
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.krislarson.customcoordinatorlayoutbehavior.ScrollingActivity">
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
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="280dp"
android:minHeight="108dp"
android:fitsSystemWindows="true"
app:title="Abby"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleGravity="center_horizontal"
app:expandedTitleMarginTop="140dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="#+id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/sunset"
app:layout_collapseMode="parallax"
android:scaleType="centerCrop"/>
<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/AppTheme.PopupOverlay">
<Space
android:id="#+id/circle_collapsed_target"
android:layout_width="40dp"
android:layout_height="40dp"/>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_scrolling"/>
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/circle_image_view"
android:layout_width="120dp"
android:layout_height="120dp"
android:src="#drawable/abby"
android:layout_marginTop="220dp"
android:layout_gravity="top|center_horizontal"
android:elevation="8dp"
app:border_color="#android:color/black"
app:border_width="2dp"
app:collapsedTarget="#id/circle_collapsed_target"
app:layout_behavior="com.krislarson.customcoordinatorlayoutbehavior.CollapsingImageBehavior"/>
</android.support.design.widget.CoordinatorLayout>
You can see the entire demo project at https://github.com/klarson2/CustomCoordinatorLayoutBehavior
One possibility would be to create a custom view for your ToolBar and hide the red dot in the ToolBar if it is expanded and show an ImageView with the red dot instead (which is hidden when the toolbar is collapsed).
You can see how to add a custom view to a ToolBar at this answer: https://stackoverflow.com/a/27859966/5052976
After doing this just create a ImageView that is visible when the ToolBar is expanded.
final CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsingToolbarLayout);
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appBarLayout);
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
boolean isShow = false;
int scrollRange = -1;
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (scrollRange == -1) {
scrollRange = appBarLayout.getTotalScrollRange();
}
if (scrollRange + verticalOffset == 0) {
//show toolbar dot and hide imageview dot
isShow = true;
} else if(isShow) {
//hide toolbar dot and show imageview dot
isShow = false;
}
}
});
Unfortunately I can't test this right now but I think it should work ;-)
I am writing an application using the camera on the textureview.
First, the camera is in a small window, and when you click on the button should be stretched to full screen.
When textureview in fullscreen mode all right.
But when I set to textureview fixed height, picture from the camera is compresses.
How to make that the image scaled correctly?
Button onClick listener
ViewGroup.LayoutParams params = mTextureView.getLayoutParams();
if(!isFullScreen) {
isFullScreen = true;
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
params.width = dm.widthPixels;
params.height = dm.heightPixels;
mTextureView.setLayoutParams(params);
}else{
isFullScreen = false;
params.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics());
mTextureView.setLayoutParams(params);
}
I cretaed it by using nice library https://github.com/umano/AndroidSlidingUpPanel
<com.sothree.slidinguppanel.SlidingUpPanelLayout xmlns:sothree="http://schemas.android.com/apk/res-auto"
android:id="#+id/sliding_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom"
sothree:umanoDragView="#+id/dragView"
sothree:umanoInitialState="collapsed"
sothree:umanoPanelHeight="200dp"
sothree:umanoParalaxOffset="0dp">
<!-- MAIN CONTENT -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- SLIDING LAYOUT -->
<LinearLayout
android:id="#+id/dragView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/black"
android:clickable="true"
android:focusable="true"
android:orientation="vertical">
<TextureView
android:id="#+id/texture_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</com.sothree.slidinguppanel.SlidingUpPanelLayout>
and added paralax to camera scroll
slidingPanel.setPanelSlideListener(new SlidingUpPanelLayout.PanelSlideListener() {
#Override
public void onPanelSlide(View panel, float slideOffset) {
dragView.setPadding(0, (int) (parallaxOffset * slideOffset - parallaxOffset), 0, 0);
}
#Override
public void onPanelExpanded(View panel) {
Log.i(TAG, "onPanelExpanded");
resizeBtn.setSelected(true);
}
#Override
public void onPanelCollapsed(View panel) {
Log.i(TAG, "onPanelCollapsed");
resizeBtn.setSelected(false);
}
#Override
public void onPanelAnchored(View panel) {
Log.i(TAG, "onPanelAnchored");
}
#Override
public void onPanelHidden(View panel) {
Log.i(TAG, "onPanelHidden");
}
});
}