I am currently developing an app wherein I need the feature to hide the toolbar on scrolling down (like Google+). I am facing issues with it's rendering of toolbar on scroll. Here is how it looks now -
I want the toolbar to simply hide on scroll down, but here the view is partially covering the toolbar and after that only toolbar is hiding.
This is the layout code -
<FrameLayout 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"
tools:context="org.step.main.MainActivity">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbarone"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize"
android:clipToPadding="false"
tools:context=".MainActivity"
/>
</FrameLayout>
And here is the Activity code -
mRecyclerView = (RecyclerView) findViewById(R.id.list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.setOnScrollListener(new HidingScrollListener() {
#Override
public void onHide() {
hideViews();
}
#Override
public void onShow() {
showViews();
}
});
private void hideViews() {
mToolbar.animate().translationY(-mToolbar.getHeight()).setInterpolator(new AccelerateInterpolator(2));
}
private void showViews() {
mToolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2));
}
Can someone explain why is this happening?
I got it after reading this line on FrameLayout doc -
"Child views are drawn in a stack, with the most recently added child on top."
So, I interchanged the position of Toolbar and RecyclerView and it worked. Here, Toolbar is rendered after RecyclerView, so it is higher in the stack.
<FrameLayout 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"
tools:context="org.step.main.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize"
android:clipToPadding="false"
tools:context=".MainActivity"
/>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbarone"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"/>
</FrameLayout>
Related
In my app, when user clicks on a contact icon, I want to display contact's information in a slide, that appears from bottom and can be swiped up to cover the whole screen, or swiped down to dissapear.
I tried to use the SlidingUpPanel from umano, but I couldn't make it work as I need, because it was not designed for that.
Is there some library for that?
This is called a BottomSheet in Android. You can create your own bottom sheet simply using a view as a Child of a coordinator layout.
Add to your app build.gradle:
dependencies{
compile 'com.android.support:design:24.1.1'
}
And then use these classes to create your own:
BottomSheetDialogFragment
BottomSheetDialog
BottomSheetBehavior
BottomSheetBehavior.BottomSheetCallback
Here is an overly simplified demo to show how to make a FrameLayout act as a bottom sheet, you can replace the contents of the FrameLayout with a Fragment and pass information to it as needed. (in your case whichever contact was clicked):
The 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:fitsSystemWindows="true"
tools:context="com.sample.bottomsheetsample.MainActivity">
<android.support.design.widget.AppBarLayout
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" />
</android.support.design.widget.AppBarLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="com.kdotj.bottomsheetsample.MainActivity"
tools:showIn="#layout/activity_main">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
android:src="#android:drawable/ic_dialog_email" />
<!-- Notice a few things:
app:layout_behavior="#string/bottom_sheet_behavior" required to act as bottom sheet
app:behavior_peekHeight="244dp" this is the collapsed state height
app:behavior_hideable="true" lets you swipe to dismiss the sheet
android:elevation="#dimen/design_appbar_elevation" puts the sheet over the action bar
-->
<FrameLayout
android:id="#+id/fl_bottomSheet"
app:layout_behavior="#string/bottom_sheet_behavior"
app:behavior_peekHeight="244dp"
app:behavior_hideable="true"
android:elevation="#dimen/design_appbar_elevation"
android:background="#777"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.AppCompatTextView
android:layout_gravity="center|top"
android:text="Hello, Bottom Sheet!"
android:padding="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
</android.support.design.widget.CoordinatorLayout>
the Activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FrameLayout frameLayout = (FrameLayout) findViewById(R.id.fl_bottomSheet);
final BottomSheetBehavior behavior = BottomSheetBehavior.from(frameLayout);
behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(#NonNull View bottomSheet, int newState) {
//... Handle the state changes
}
#Override
public void onSlide(#NonNull View bottomSheet, float slideOffset) {
//.. handle sliding if you want to
}
});
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
});
}
}
in my app I am using a viewpager with 3 fragments. In two of those I have recuclerviews. I took advatngage of the new Coordinator layout and made my toolbar hides/shows when scrolling on a recyclerview. My problem is the following Say the user is scrolling on a recyclerview list in fragment A and thus the toolbar is hidden. After that, the user performs a swipe and goes to fragment B which does not have a recycle view to scroll so the toolbar can appear again. Is there a way I can alter the layout_behaviout so that when the user swipes on the view pager the toolbar to be shown?
NOTE: IF it is possible, I want to achieve that only using XML.
This is my main_layput xml code:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:fab="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:fitsSystemWindows="true"
tools:context="com.studentsins.lust.MainActivity">
<android.support.design.widget.AppBarLayout
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.TabLayout
android:id="#+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/NavigationTab"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<include layout="#layout/content_main"/>
<com.getbase.floatingactionbutton.FloatingActionsMenu
android:id="#+id/floatingActionMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
fab:fab_addButtonColorNormal="#color/blood_orange"
fab:fab_addButtonColorPressed="#color/dirtyWhite"
fab:fab_addButtonPlusIconColor="#color/dirtyWhite"
fab:fab_addButtonSize = "normal"
fab:fab_labelStyle="#style/menu_labels_style"
fab:fab_labelsPosition="left"
app:layout_anchor="#id/viewpager"
app:layout_anchorGravity="bottom|end">
<com.getbase.floatingactionbutton.FloatingActionButton
android:id="#+id/createPlanBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_colorNormal="#color/blood_orange"
fab:fab_title="Create a plan"
fab:fab_size="normal"
app:fab_icon="#drawable/ic_event_white_48dp"
fab:fab_colorPressed="#color/dirtyWhite"/>
<com.getbase.floatingactionbutton.FloatingActionButton
android:id="#+id/changeStatusBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_colorNormal="#color/blood_orange"
fab:fab_size="normal"
app:fab_icon="#drawable/ic_textsms_white_48dp"
fab:fab_title="Change status"
fab:fab_colorPressed="#color/dirtyWhite"/>
</com.getbase.floatingactionbutton.FloatingActionsMenu>
</android.support.design.widget.CoordinatorLayout>
You should be able to do this using the ViewPager.OnPageChangedListener and based on the page u are on which u can get from the onPageSelected(int position) method that is part of the listener. Hope this is what u are looking for.
I was able to achieve this by referencing the AppBarLayout and calling the "setExtended" method like that:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//Make sure that the fab is visible when scrolling the pages...
MainActivity.mFloatingActionsMenu.animate()
.setDuration(150)
.translationY(0);
//show the toolbar
expandToolbar();
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
my expandToolbar method:
public void expandToolbar(){
//setExpanded(boolean expanded, boolean animate)
AppBarLayout appBarLayout = (AppBarLayout)findViewById(R.id.appBarLayouy);
appBarLayout.setExpanded(true, true);
}
I'm trying to implement collapsing tollbar with swipe to refresh and recyclerview.
When I'm trying to scroll (when recyclerview has only one item) toolbar collapse,
but when I'm trying to scroll down to show toolbar, it's impossible because swipe down causes swipe to refresh. When recyclerview has more item it works perfectly.
Link to gif with problem
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
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.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="1dp"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="1dp"
android:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/activity_main_swipe_refresh_layout"
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/cities_list"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:elevation="1dp"
android:onClick="addCity"
android:src="#drawable/ic_plus_white_36dp"
app:borderWidth="0dp" />
Update: This bug has been fixed in the version 23.1.1 of support library
You can set onOffsetChanged listener for your AppBarLayout and prevent to swipe refreshing until AppBarLayout layout offset 0.
This is good example :
https://gist.github.com/blackcj/001a90c7775765ad5212
I managed it by adding the following implementation of OnOffsetChangedListener in fragment:
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (collapsingToolbarLayout.getHeight() + verticalOffset < 2 * ViewCompat.getMinimumHeight(collapsingToolbarLayout)) {
swipeRefreshLayout.setEnabled(false);
} else {
swipeRefreshLayout.setEnabled(true);
}
}
#Override
public void onResume() {
super.onResume();
appBarLayout.addOnOffsetChangedListener(this);
}
#Override
public void onPause() {
super.onPause();
appBarLayout.removeOnOffsetChangedListener(this);
}
I started using the new CoordinatorLayout and I ran into this issue:
As you can see when I try to scroll down when the toolbar is partially visible and the recylcerview is at the top position it triggers the refresh event instead of pulling down the toolbar. The user has to scroll up than down again to reveal it.
Layout XML looks something like this:
activity_main.xml:
<android.support.v4.widget.DrawerLayout 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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<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:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
style="#style/customActionBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"/>
</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>
<android.support.design.widget.NavigationView
android:id="#+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#android:color/white"
app:headerLayout="#layout/navigation_drawer_header"
app:menu="#menu/navigation_items" />
</android.support.v4.widget.DrawerLayout>
fragment_main.xml:
<FrameLayout 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.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="8dp"
android:scrollbars="none" />
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
Ther SwipeRefreshLayout is in a FrameLayout becuase there are more elements in the fragment in my app.
Any help would be appreciated.
maybe too late but it might help any new comers, i came up with an easy solution for that. by creating custom class that extends SwipeRefreshLayout you can use it anywhere where AppBaLayout exists follow code below
public class MySwipeLayout extends SwipeRefreshLayout implements AppBarLayout.OnOffsetChangedListener {
private AppBarLayout appBarLayout;
public MySwipeLayout(Context context) {
super(context);
}
public MySwipeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (getContext() instanceof Activity) {
appBarLayout = (AppBarLayout) ((Activity) getContext()).findViewById(R.id.appbar);
appBarLayout.addOnOffsetChangedListener(this);
}
}
#Override
protected void onDetachedFromWindow() {
if(appBarLayout!=null){
appBarLayout.removeOnOffsetChangedListener(this);
appBarLayout = null;
}
super.onDetachedFromWindow();
}
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int i) {
this.setEnabled(i == 0);
}
}
UPDATE: This issue has been fixed in the in the Support Library v23.1.1.
Seems like you have to enable/disable the SwipeRefreshLayout when the AppBarLayout offset changes, using AppBarLayout.OnOffsetChangedListener.
See https://stackoverflow.com/a/30822119/795820
My activitymain.xml file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include android:id="#+id/app_bar" layout="#layout/toolbar"/>
<view
android:id="#+id/recycler_view"
android:layout_below="#id/app_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="android.support.v7.widget.RecyclerView"/>
</RelativeLayout>
Because my main.java file only contains Recyclerview and nothing else, i can't directly add 2 buttons into it.
I tried to add 2 imageButtons in the the recyclerView.
RecyclerView does not properly render itself in AndroidStudio - there is an issue from google.
So i can't see how exactly recycler view and toolbar(app_bar) stand to each other(I assume app_bar toolbar is below recyclerview(layout_below attribute) )
I suspect i should totally change the design of the app to finish it.
MainActivity.java, it only has recyclerview and adapter to it, it sends context into next SearchActivity:
public class MainActivity extends BaseActivity {
private static final String LOG_TAG = "mAIN ACTIVITY";
private List<Book> bookList = new ArrayList<>() ;
private RecyclerView recyclerView;
private BookRecyclerViewAdapter bookRecyclerViewAdapter;
private boolean FIRST_TIME_LAUNCH = false ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
bookRecyclerViewAdapter = new BookRecyclerViewAdapter(new ArrayList<Book>(), MainActivity.this);
recyclerView.setAdapter(bookRecyclerViewAdapter);
}
If i try to add button it stands in upper left corner:
If you want the button to appear at the end of your list after the list has been scrolled down have a look at this question.
If you want the button below your list and be always visible, this is how you do it:
You 'squeeze in' the RecyclerView between the Button and the Toolbar giving it a layout_below and layout_above attribute:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include android:id="#+id/app_bar" layout="#layout/toolbar"/>
<Button
android:id="#+id/button"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<view
android:id="#+id/recycler_view"
android:layout_below="#id/app_bar"
android:layout_above="#id/button"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="android.support.v7.widget.RecyclerView"/>
</RelativeLayout>
Try this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include android:id="#+id/app_bar" layout="#layout/toolbar"/>
<view
android:id="#+id/recycler_view"
android:layout_below="#id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
class="android.support.v7.widget.RecyclerView"/>
<Button
android:id="#+id/button"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_below="#id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
Hope this help!