I have a working CollapsingToolbarLayout the only problem I am seeing is that the content being shown when expanded dissapears too quickly when scrolling down and was wondering if it possible to define at what point of the road the content should dissapear and the appbar appear. It seems like after collalsing ~25% it already hides the content and only shows the color of the appbar. In the Google Play App you can see that this happens really late when almost collapsing all.
i would give you and example of how i achieved what you need.
Since you want the content to start dessapearing after for example 60%, first, declare a variable like this
private static final int PERCENTAGE_TO_SHOW_ELEMENT = 60;
Then you have to declare a variable to know if your element have been hidden already and a variable to track the scroll percentage
private int mMaxScrollSize;
private boolean mIsElementHidden;
After that, you have to add to your appBarLayout the listener called addOnOffsetChangedListener like this and override the method onOffsetChanged:
yourAppBar.addOnOffsetChangedListener(this);
Inside the method onOffsetChanged, use this code:
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
if (mMaxScrollSize == 0)
mMaxScrollSize = appBarLayout.getTotalScrollRange();
int currentScrollPercentage = (Math.abs(verticalOffset)) * 100
/ mMaxScrollSize;
if (currentScrollPercentage >= PERCENTAGE_TO_SHOW_ELEMENT) {
if (!mIsElementHidden) {
mIsElementHidden= true;
ViewCompat.animate(yourView).scaleY(0).scaleX(0).start();
ViewCompat.animate(anotherView).scaleY(0).scaleX(0).start();
}
}
if (currentScrollPercentage < PERCENTAGE_TO_SHOW_ELEMENT) {
if (mIsElementHidden) {
mIsElementHidden= false;
ViewCompat.animate(yourView).scaleY(1).scaleX(1).start();
ViewCompat.animate(anotherView).scaleY(1).scaleX(1).start();
}
}
} else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
//You can change something if your layout is different in LANDSCAPE
}
}
You can play with the variable PERCENTAGE_TO_SHOW_ELEMENT to decide when to make your view(s) appear or dissapear.
I found the solution. You don't obligatory need java code for this, you can define this with the style property scrimVisibleHeightTrigger but take in mind that the used value should be major than the size of the actionbar, sadly using ?acionBarSize did not work, so you must take into account the height of the appbar and add some DPs to it.
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
style="#style/Widget.MyCustomWidget.CollapsingHeader"
<style name="Widget.MyCustomWidget.CollapsingHeader" parent="#style/Widget.Design.CollapsingToolbar">
<item name="scrimAnimationDuration">200</item>
<item name="scrimVisibleHeightTrigger">1dp</item>
</style>
Create a class which implements AppBarLayout.OnOffsetChangedListener and override the onOffsetChanged method
<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"
tools:ignore="RtlHardcoded"
>
<android.support.design.widget.AppBarLayout
android:id="#+id/main.appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
>
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/main.collapsing"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
>
<ImageView
android:id="#+id/main.imageview.placeholder"
android:layout_width="match_parent"
android:layout_height="300dp"
android:scaleType="centerCrop"
android:src="#drawable/quila2"
android:tint="#11000000"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.9"
/>
<FrameLayout
android:id="#+id/main.framelayout.title"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_gravity="bottom|center_horizontal"
android:background="#color/primary"
android:orientation="vertical"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.3"
>
<LinearLayout
android:id="#+id/main.linearlayout.title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="bottom|center"
android:text="#string/quila_name"
android:textColor="#android:color/white"
android:textSize="30sp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="4dp"
android:text="#string/quila_tagline"
android:textColor="#android:color/white"
/>
</LinearLayout>
</FrameLayout>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.Toolbar
android:id="#+id/main.toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/primary"
app:layout_anchor="#id/main.framelayout.title"
app:theme="#style/ThemeOverlay.AppCompat.Dark"
app:title=""
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
>
<Space
android:layout_width="#dimen/image_final_width"
android:layout_height="#dimen/image_final_width"
/>
<TextView
android:id="#+id/main.textview.title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:gravity="center_vertical"
android:text="#string/quila_name2"
android:textColor="#android:color/white"
android:textSize="20sp"
/>
</LinearLayout>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CoordinatorLayout>
Change the PERCENTAGE_TO_SHOW_TITLE_AT_TOOLBAR and PERCENTAGE_TO_HIDE_TITLE_DETAILS to get the desired effect.
public class MainActivity extends AppCompatActivity implements AppBarLayout.OnOffsetChangedListener {
private static final float PERCENTAGE_TO_SHOW_TITLE_AT_TOOLBAR = 0.9f;
private static final float PERCENTAGE_TO_HIDE_TITLE_DETAILS = 0.3f;
private static final int ALPHA_ANIMATIONS_DURATION = 200;
private boolean mIsTheTitleVisible = false;
private boolean mIsTheTitleContainerVisible = true;
private LinearLayout mTitleContainer;
private TextView mTitle;
private AppBarLayout mAppBarLayout;
private Toolbar mToolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindActivity();
mAppBarLayout.addOnOffsetChangedListener(this);
mToolbar.inflateMenu(R.menu.menu_main);
startAlphaAnimation(mTitle, 0, View.INVISIBLE);
}
private void bindActivity() {
mToolbar = (Toolbar) findViewById(R.id.main_toolbar);
mTitle = (TextView) findViewById(R.id.toolbar_title);
mTitleContainer = (LinearLayout) findViewById(R.id.linearlayout_title);
mAppBarLayout = (AppBarLayout) findViewById(R.id.appbar);
}
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int offset) {
int maxScroll = appBarLayout.getTotalScrollRange();
float percentage = (float) Math.abs(offset) / (float) maxScroll;
handleAlphaOnTitle(percentage);
handleToolbarTitleVisibility(percentage);
}
private void handleAlphaOnTitle(float percentage) {
if (percentage >= PERCENTAGE_TO_HIDE_TITLE_DETAILS) {
if(mIsTheTitleContainerVisible) {
startAlphaAnimation(mTitleContainer, ALPHA_ANIMATIONS_DURATION, View.INVISIBLE);
mIsTheTitleContainerVisible = false;
}
} else {
if (!mIsTheTitleContainerVisible) {
startAlphaAnimation(mTitleContainer, ALPHA_ANIMATIONS_DURATION, View.VISIBLE);
mIsTheTitleContainerVisible = true;
}
}
}
private void handleToolbarTitleVisibility(float percentage) {
if (percentage >= PERCENTAGE_TO_SHOW_TITLE_AT_TOOLBAR) {
if(!mIsTheTitleVisible) {
startAlphaAnimation(mTitle, ALPHA_ANIMATIONS_DURATION, View.VISIBLE);
mIsTheTitleVisible = true;
}
} else {
if (mIsTheTitleVisible) {
startAlphaAnimation(mTitle, ALPHA_ANIMATIONS_DURATION, View.INVISIBLE);
mIsTheTitleVisible = false;
}
}
}
public static void startAlphaAnimation (View v, long duration, int visibility) {
AlphaAnimation alphaAnimation = (visibility == View.VISIBLE) ? new AlphaAnimation(0f, 1f) : new AlphaAnimation(1f, 0f);
alphaAnimation.setDuration(duration);
alphaAnimation.setFillAfter(true);
v.startAnimation(alphaAnimation);
}
}
Related
I defined a textview with auto-scrolling to bottom enabled and it works.
But when i start the app, the textview content automatically scrolls to the top on the first touch, which appears very strange to me. From then on, it behaves normally, after I scrolls it manually to bottom. I mean, no auto-scrolling to top happens again regardless of the subsequent touchs. And it always scroll to bottom as defined when texts are appended.
How may I identify the cause oh this behavior, in order to circumvent it?.
Here is the the textview layout:
<LinearLayout
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/colorScreenBackground"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="xxx.xxxxx.xxxx.MainActivity"
tools:showIn="#layout/activity_main"
android:orientation="vertical"
android:weightSum="100">
<TextView
android:id="#+id/result"
android:scrollbars = "vertical"
android:textSize="16sp"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="40"
android:elevation="8dp"
android:textIsSelectable="true"
android:gravity="bottom"
android:background="#color/white"
android:padding="5dp"
android:textAppearance="#style/Base.TextAppearance.AppCompat.Medium" />
<androidx.core.widget.NestedScrollView
android:id="#+id/nested_scroll"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="60"
android:background="#color/gray_background"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp"
tools:listitem="#layout/recyclerview_item"
android:background="#color/gray_background"
/>
</androidx.core.widget.NestedScrollView>
</LinearLayout>
And this is how I use it in code
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedPref = getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE);
setContentView(R.layout.activity_main);
final Runnable r = new Runnable() {
#Override
public void run() {
doubleClick = false;
}
};
final TextView textView = findViewById(R.id.result);
textView.setMovementMethod(new ScrollingMovementMethod());
textView.setTextIsSelectable(true);
textView.setText(deviceLog.getResult());
textView.setOnLongClickListener(this);
recyclerView.setOnDragListener(this);
final NestedScrollView nestedScrollView = findViewById(R.id.nested_scroll);
if (textView.getText().length() == 0) {
LinearLayout.LayoutParams nestedScrolliewLayoutParams = (LinearLayout.LayoutParams) nestedScrollView.getLayoutParams();
nestedScrolliewLayoutParams.weight = 100;
textView.setVisibility(View.GONE);
nestedScrollView.setLayoutParams(nestedScrolliewLayoutParams);
}
textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (doubleClick) {
LinearLayout.LayoutParams nestedScrolliewLayoutParams = (LinearLayout.LayoutParams) nestedScrollView.getLayoutParams();
LinearLayout.LayoutParams linearLayoutParams = (LinearLayout.LayoutParams) textView.getLayoutParams();
if (textViewEnlarged) {
nestedScrolliewLayoutParams.weight = 60;
linearLayoutParams.weight = 40;
textViewEnlarged = false;
} else {
nestedScrolliewLayoutParams.weight = 20;
linearLayoutParams.weight = 80;
textViewEnlarged = true;
}
nestedScrollView.setLayoutParams(nestedScrolliewLayoutParams);
textView.setLayoutParams(linearLayoutParams);
doubleClick = false;
} else {
doubleClick = true;
Handler doubleClikHandler = new Handler();
doubleClikHandler.postDelayed(r, 500);
}
}
});
}
I have a solution that have done on my Project.
Try to call textView.requestFocus() after set the text value:
itemView.text_view_item_description?.text = "..."
itemView.text_view_item_description?.requestFocus()
Hope It works for you.
I have a "details" fragment which have a lot of textviews, relativelayouts etc. These are wrapped inside a NestedScrollView:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:orientation="vertical"
android:paddingBottom="10dp">
<android.support.v4.widget.NestedScrollView
android:id="#+id/movie_details_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent">
// Here are the textviews, relativelayouts etc...
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:id="#+id/edit_movies_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_edit_white_24dp"
/>
</android.support.design.widget.CoordinatorLayout>
Now i want to to add a FAB at the bottom of the screen (not at the bottom of the nestedscrollview) which also scroll down when i scroll through the nestedscrollview. But with my code the FAB is always at the bottom of the nestedscrollview. So when i scroll all the way down the FAB appears. I want that the FAB is always visible in the right bottom corner...
EDIT
I forgot to mention that i use fading action bar (https://github.com/ManuelPeinado/FadingActionBar) but a bit edited.
Relevant code:
m_FadingActionBarHelper.createView(getContext()); // this will create the view with header content etc.
The createView:
public final View createView(LayoutInflater inflater) {
//
// Prepare everything
mInflater = inflater;
if (mContentView == null) {
mContentView = inflater.inflate(mContentLayoutResId, null); // this will load my view which i already posted.
}
if (mHeaderView == null) {
mHeaderView = inflater.inflate(mHeaderLayoutResId, null, false);
}
// See if we are in a ListView, WebView or ScrollView scenario
ListView listView = (ListView) mContentView.findViewById(android.R.id.list);
View root;
if (listView != null) {
root = createListView(listView);
} else if (mContentView instanceof CDMObservableWebViewWithHeader){
root = createWebView();
} else {
root = createScrollView(); // this will be called in my example
}
if (mHeaderOverlayView == null && mHeaderOverlayLayoutResId != 0) {
mHeaderOverlayView = inflater.inflate(mHeaderOverlayLayoutResId, mMarginView, false);
}
if (mHeaderOverlayView != null) {
mMarginView.addView(mHeaderOverlayView);
}
// Use measured height here as an estimate of the header height, later on after the layout is complete
// we'll use the actual height
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
mHeaderView.measure(widthMeasureSpec, heightMeasureSpec);
updateHeaderHeight(mHeaderView.getMeasuredHeight());
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int headerHeight = mHeaderContainer.getHeight();
if ((!mFirstGlobalLayoutPerformed || (headerHeight != mLastHeaderHeight)) && headerHeight != 0) {
updateHeaderHeight(headerHeight);
mFirstGlobalLayoutPerformed = true;
}
}
});
return root;
}
The createScrollView:
private View createScrollView() {
ViewGroup scrollViewContainer = (ViewGroup) mInflater.inflate(R.layout.fab__scrollview_container, null);
CDMObservableScrollView scrollView = (CDMObservableScrollView) scrollViewContainer.findViewById(R.id.fab__scroll_view);
scrollView.setOnScrollChangedCallback(mOnScrollChangedListener);
ViewGroup contentContainer = (ViewGroup) scrollViewContainer.findViewById(R.id.fab__container);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mContentView.setLayoutParams(layoutParams);
contentContainer.addView(mContentView);
mHeaderContainer = (FrameLayout) scrollViewContainer.findViewById(R.id.fab__header_container);
initializeGradient(mHeaderContainer);
mHeaderContainer.addView(mHeaderView, 0);
mMarginView = (FrameLayout) contentContainer.findViewById(R.id.fab__content_top_margin);
return scrollViewContainer;
}
The xml which will be loaded:
<denis.de.meperdia.fadingactionbar.CDMRootLayout
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">
<include layout="#layout/fab__header_container"/>
<denis.de.meperdia.fadingactionbar.CDMObservableScrollView
android:id="#+id/fab__scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/fab__container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="#+id/fab__content_top_margin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"/>
</LinearLayout>
</denis.de.meperdia.fadingactionbar.CDMObservableScrollView>
</denis.de.meperdia.fadingactionbar.CDMRootLayout>
The class CDMRootLayout:
public class CDMRootLayout extends CoordinatorLayout {
private View mHeaderContainer;
private View mListViewBackground;
private boolean mInitialized = false;
public CDMRootLayout(Context context) {
super(context);
}
public CDMRootLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CDMRootLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//at first find headerViewContainer and listViewBackground
if(mHeaderContainer == null)
mHeaderContainer = findViewById(R.id.fab__header_container);
if(mListViewBackground == null)
mListViewBackground = findViewById(R.id.fab__listview_background);
//if there's no headerViewContainer then fallback to standard FrameLayout
if(mHeaderContainer == null) {
super.onLayout(changed, left, top, right, bottom);
return;
}
if(!mInitialized) {
super.onLayout(changed, left, top, right, bottom);
//if mListViewBackground not exists or mListViewBackground exists
//and its top is at headercontainer height then view is initialized
if(mListViewBackground == null || mListViewBackground.getTop() == mHeaderContainer.getHeight())
mInitialized = true;
return;
}
//get last header and listViewBackground position
int headerTopPrevious = mHeaderContainer.getTop();
int listViewBackgroundTopPrevious = mListViewBackground != null ? mListViewBackground.getTop() : 0;
//relayout
super.onLayout(changed, left, top, right, bottom);
//revert header top position
int headerTopCurrent = mHeaderContainer.getTop();
if(headerTopCurrent != headerTopPrevious) {
mHeaderContainer.offsetTopAndBottom(headerTopPrevious - headerTopCurrent);
}
//revert listViewBackground top position
int listViewBackgroundTopCurrent = mListViewBackground != null ? mListViewBackground.getTop() : 0;
if(listViewBackgroundTopCurrent != listViewBackgroundTopPrevious) {
mListViewBackground.offsetTopAndBottom(listViewBackgroundTopPrevious - listViewBackgroundTopCurrent);
}
}
}
And the class CDMObservableScrollView:
public class CDMObservableScrollView extends ScrollView implements CDMObservableScrollable {
// Edge-effects don't mix well with the translucent action bar in Android 2.X
private boolean mDisableEdgeEffects = true;
private CDMOnScrollChangedCallback mOnScrollChangedListener;
public CDMObservableScrollView(Context context) {
super(context);
}
public CDMObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CDMObservableScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mOnScrollChangedListener != null) {
mOnScrollChangedListener.onScroll(l, t);
}
}
#Override
protected float getTopFadingEdgeStrength() {
// http://stackoverflow.com/a/6894270/244576
if (mDisableEdgeEffects && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return 0.0f;
}
return super.getTopFadingEdgeStrength();
}
#Override
protected float getBottomFadingEdgeStrength() {
// http://stackoverflow.com/a/6894270/244576
if (mDisableEdgeEffects && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return 0.0f;
}
return super.getBottomFadingEdgeStrength();
}
#Override
public void setOnScrollChangedCallback(CDMOnScrollChangedCallback callback) {
mOnScrollChangedListener = callback;
}
}
EDIT 2
I can delimit the problem now:
If i these lines the FAB works as i want:
<denis.de.meperdia.fadingactionbar.CDMObservableScrollView
android:id="#+id/fab__scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
But then the synchonization of my fading action bar is destroyed...
Sorry for that much code but it's really complicated to understand without this.
Import the app namespace, surround the NestedScrollView with a RelativeLayout and then set the RelativeLayout as anchor for FAB.
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" //Import app namespace
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:orientation="vertical"
android:paddingBottom="10dp">
<android.support.v4.widget.NestedScrollView
android:id="#+id/mainview"
android:layout_width="match_parent"
android:layout_height="match_parent">
// Here are the textviews, relativelayouts etc...
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:id="#+id/edit_movies_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_edit_white_24dp"
app:layout_anchor="#id/mainview" //attributes of app namespace
app:layout_anchorGravity="bottom|end"
/>
</android.support.design.widget.CoordinatorLayout>
Replace android:layout_gravity="bottom|end"
with this android:layout_gravity="bottom|right"
Add below line in NestedScrollView.
app:layout_behavior="#string/appbar_scrolling_view_behavior"
You can easily do it with Coordinate Layout anchor property to attach your fab to any of your view node
CoordinatorLayout and use the layout_anchor and layout_anchorGravity attributes.
<ListView
android:id="#+id/lvToDoList"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:src="#drawable/ic_done"
app:layout_anchor="#id/lvToDoList"
app:layout_anchorGravity="bottom|right|end" />
This will make FAB to aatch to list view at bottom. You can do same with any other layout/View.
Finally i solved this problem by moving the FloatingActionButton from the content layout to the outside.
My container layout looks like this:
<?xml version="1.0" encoding="utf-8"?>
<denis.de.meperdia.fadingactionbar.CDMRootLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/fab__header_container"/>
<denis.de.meperdia.fadingactionbar.CDMObservableScrollView
android:id="#+id/fab__scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/fab__container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="#+id/fab__content_top_margin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"/>
</LinearLayout>
</denis.de.meperdia.fadingactionbar.CDMObservableScrollView>
<android.support.design.widget.CoordinatorLayout
android:id="#+id/fab__floating_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.design.widget.CoordinatorLayout>
</denis.de.meperdia.fadingactionbar.CDMRootLayout>
I added a container for the FloatingActionButton which i fill dynamically by loading it from another file. The moving problem of the FloatingActionButton is solved now. There's a little other problem but i opened a new question for this.
EDIT
Changed my solution. I had the problem that if i want to show a snackbar, the FloatingActionButton didn't scroll correctly. I add the FloatingActionButton now programmatically to the root view. Now it works correctly.
try this example:
activity_main.xml
<?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"
tools:context="com.internet.Main2Activity">
<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>
<include
layout="#layout/content_main2"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<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"
app:srcCompat="#android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
content_main2.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="com.internet.Main2Activity"
tools:showIn="#layout/activity_main2">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
I am adding action menu items to the action bar dynamically and because I want a "badge" effect, I am setting views to the items instead of just drawable icons. My problem is that when I have items which have their views set, they are also aligned to the right edge of the action bar. I want to have some sort of inset, but nothing seems to achieve that. I tried setting margins on the action view layouts, setting insets in the XML and programmatically, but nothing is changing. Does anyone know how I can make it so that the actions don't appear right at the edge?
This is what I see now:
And I want to have some sort of padding/margin between the icon and the right edge of the action bar, like on the left side between the left edge and the title.
Here is some code:
View of the action menu items (res/layout/navbar_action.xml):
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="53dp"
android:layout_height="53dp"
android:layout_gravity="center_vertical"
android:layout_marginRight="12dp">
<ImageView
android:id="#+id/navbar_icon"
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_gravity="bottom|left"/>
<TextView
android:id="#+id/navbar_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|right"
android:background="#drawable/navbar_badge_circle"
android:gravity="center"
android:textSize="#dimen/textsize_caption"
android:visibility="gone"
/>
</FrameLayout>
Setting up the action bar and the action menu in the base activity:
public abstract class BaseActivity extends AppCompatActivity {
public static final int MENU_PRIMARY = 0;
public static final int MENU_SECONDARY = 1;
protected ActionBar actionBar;
boolean hasPrimaryAction = false;
String primaryActionTitle;
MenuItem.OnMenuItemClickListener primaryActionListener;
View primaryActionView;
boolean hasSecondaryAction = false;
String secondaryActionTitle;
MenuItem.OnMenuItemClickListener secondaryActionListener;
View secondaryActionView;
...
protected boolean initToolbar() {
if (getSupportActionBar() != null) {
actionBar = getSupportActionBar();
return true;
}
Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar);
if (toolbar != null) {
toolbar.setVisibility(View.VISIBLE);
setSupportActionBar(toolbar);
actionBar = getSupportActionBar();
return true;
}
return false;
}
...
public void setAction(String title, Drawable icon, int actionNumber) {
if (actionNumber == MENU_PRIMARY) {
hasPrimaryAction = true;
primaryActionTitle = title;
primaryActionView = LayoutInflater.from(this).inflate(R.layout.navbar_action, null);
ImageView iconView = (ImageView) primaryActionView.findViewById(R.id.navbar_icon);
iconView.setImageDrawable(icon);
primaryIcon = icon;
} else if (actionNumber == MENU_SECONDARY) {
hasSecondaryAction = true;
secondaryActionTitle = title;
secondaryActionIcon = icon;
secondaryActionView = LayoutInflater.from(this).inflate(R.layout.navbar_action, null);
ImageView iconView = (ImageView) secondaryActionView.findViewById(R.id.navbar_icon);
iconView.setImageDrawable(icon);
}
supportInvalidateOptionsMenu();
}
public void setActionBadge(int actionNumber, int badgeCount) {
if (actionNumber == MENU_PRIMARY) {
if (primaryActionView != null) {
TextView badgeView = (TextView) primaryActionView.findViewById(R.id.navbar_badge);
if (badgeCount > 0){
badgeView.setVisibility(View.VISIBLE);
badgeView.setText(String.valueOf(badgeCount));
} else {
badgeView.setVisibility(View.INVISIBLE);
}
}
} else if (actionNumber == MENU_SECONDARY) {
if (secondaryActionView != null) {
TextView badgeView = (TextView) secondaryActionView.findViewById(R.id.navbar_badge);
if (badgeCount > 0) {
badgeView.setVisibility(View.VISIBLE);
badgeView.setText(String.valueOf(badgeCount));
} else {
badgeView.setVisibility(View.INVISIBLE);
}
}
}
supportInvalidateOptionsMenu();
}
public void removeAction(int actionNumber) {
if (actionNumber == MENU_PRIMARY) {
hasPrimaryAction = false;
} else if (actionNumber == MENU_SECONDARY) {
hasSecondaryAction = false;
}
supportInvalidateOptionsMenu();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if (hasPrimaryAction) {
menu.add(Menu.NONE, 0, Menu.NONE, primaryActionTitle).setActionView(primaryActionView).setOnMenuItemClickListener(primaryActionListener).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
if (hasSecondaryAction) {
menu.add(Menu.NONE, 1, Menu.NONE, secondaryActionTitle).setActionView(secondaryActionView).setOnMenuItemClickListener(secondaryActionListener).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
return true;
}
}
Layout of the action bar:
<android.support.design.widget.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/actionBarBg"
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:elevation="4dp"
android:fitsSystemWindows="true"
app:contentScrim="?attr/actionBarBg"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:titleEnabled="false">
<FrameLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar
android:id="#+id/action_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:elevation="4dp"
app:layout_collapseMode="pin"
android:visibility="gone">
<TextView
android:id="#+id/action_bar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/TextAppearance.Widget.AppCompat.Toolbar.Title"
android:layout_gravity="left"
android:visibility="gone"/>
<ImageView
android:id="#+id/search_icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="right"
android:layout_marginLeft="#dimen/navigation_icons_margin_left"
android:layout_marginRight="#dimen/navigation_icons_margin_right"
android:background="#drawable/search_icon_dark_bg"
android:visibility="gone" />
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
Try this :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parent_cart"
android:layout_width="55dp"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:paddingRight="5dp"
android:layout_gravity="right">
<ImageView
android:id="#+id/navbar_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/home"
android:paddingBottom="10dp"
android:paddingLeft="5dp"
android:layout_marginTop="20dp"
android:paddingTop="15dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp" />
<TextView
android:id="#+id/nom_product"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#drawable/red_circle"
android:gravity="center"
android:layout_margin="10dp"
android:text="0"
android:textColor="#color/white"
android:layout_centerHorizontal="true" />
</RelativeLayout>
Red Circle :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<solid android:color="#color/colorPrimaryDark" />
<size
android:height="20dp"
android:width="20dp" />
</shape>
I'm using android:windowSoftInputMode="stateHidden|adjustResize" to push the EditText and RecyclerView up when keyboard appears, but it only works on the EditText. RecyclerView stays in the same position (thus some elements are not visible). What am I doing wrong?
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context="co.bla.bla.ChatActivity"
android:id="#+id/chat_root">
<include android:id="#+id/toolbar" layout="#layout/toolbar"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/chat_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/message_input_wrapper"
android:scrollbars="vertical"
android:paddingBottom="20dp"
android:layout_below="#id/toolbar"
/>
<LinearLayout
android:id="#+id/message_input_wrapper"
android:layout_width="match_parent"
android:layout_height="60dp"
android:padding="6dp"
android:orientation="horizontal"
android:weightSum="4"
android:background="#android:color/white"
android:layout_alignParentBottom="true">
<EditText
android:id="#+id/message_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="#string/chat_input_hint"
android:layout_gravity="center_vertical"
android:layout_weight="3"
android:background="#android:color/transparent"/>
<Button
android:id="#+id/message_send_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#string/chat_button_text"
android:layout_weight="1"
android:onClick="onButtonClicked"/>
</LinearLayout>
</RelativeLayout>
I don't know how adjustResize exactly works, but I assume when using android:layout_above in this case, the RecyclerView and LinearLayout "stick" together and should be pushed up together?
In your activity manifest:
android:windowSoftInputMode="adjustResize"
Then add an OnLayoutChangeListener to yout recyclerView:
recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right,int bottom, int oldLeft, int oldTop,int oldRight, int oldBottom)
{
recyclerView.scrollToPosition(recyclerView.getAdapter().getItemCount()-1);
}
});
This works for me :)
set a soft keyboard listener:
private boolean mLastKeyBoardVisible = false;
private void addOnSoftKeyBoardVisibleListener(final OnSoftKeyBoardVisibleListener listener) {
final View decorView = getWindow().getDecorView();
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect rect = new Rect();
decorView.getWindowVisibleDisplayFrame(rect);
int displayHight = rect.bottom - rect.top;
int hight = decorView.getHeight();
boolean visible = (double) displayHight / hight < 0.8;
if(visible != mLastKeyBoardVisible){
listener.onSoftKeyBoardVisible(visible);
}
mLastKeyBoardVisible = visible;
}
});
}
private OnSoftKeyBoardVisibleListener onSoftKeyBoardVisibleListener = new OnSoftKeyBoardVisibleListener() {
#Override
public void onSoftKeyBoardVisible(boolean visible) {
scrollToBottom(visible);
}
};
synchronized private void scrollToBottom(boolean visible) {
if (visible) {
if (mMessageList.size() != messageAdapter.getItemCount() && mMessageList.size() > 0) {
messageAdapter.notifyDataSetChanged();
messageListView.scrollToPosition(mMessageList.size() - 1);
} else if (mMessageList.size() > 0) {
messageListView.scrollToPosition(mMessageList.size() - 1);
}
}
}
I am looking to slide/up down a nested layout on its parent layout click.
Ie the parent layout will have a hidden child. On click I would like the parents height to animate down (slide down) to fit the child layout. On click again I would like the child to animate up (slide up). Basically just animating the parents height to show/hide the child.
I have found this which looks to work but seems like a lot of code:
http://gmariotti.blogspot.com/2013/09/expand-and-collapse-animation.html
I have seen a lot of things using 'animateLayoutChanges' to animate things however I cannot get that to work.
I have tried this:
<LinearLayout android:id="#+id/parent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout android:id="#+id/child"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:animateLayoutChanges="true">
<TextView android:id="#+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some text to show/hide"/>
</LinearLayout>
</LinearLayout>
Then in code:
LinearLayout parent = (LinearLayout)findViewById(R.id.parent);
parent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LinearLayout child = (LinearLayout)findViewById(R.id.child);
child.setVisibility(child.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE);
}
});
That sets the visibility of the child view correctly but there is absolutely no animation.
Well, first of all android:animateLayoutChanges effects the child elements. So, obviously, if you are changing the properties of the element itself, it will not be animated.
I think you can accomplish what you are trying to in API 16 by enabling the LayoutTransition.CHANGING animation.
<LinearLayout android:id="#+id/parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:animateLayoutChanges="true">
<TextView android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title"/>
<TextView android:id="#+id/message"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="Some text to show/hide"/>
</LinearLayout>
LinearLayout parent = (LinearLayout)findViewById(R.id.parent);
parent.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
View text = findViewById(R.id.text);
text.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
View message = findViewById(R.id.message);
ViewGroup.LayoutParams params = message.getLayoutParams();
params.height = params.height == 0 ? ViewGroup.LayoutParams.WRAP_CONTENT : 0;
message.setLayoutParams(params);
}
});
Try to use SlidingDrawer
use this code
xml layout sideupdown.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/bopen"
android:layout_width="90sp"
android:layout_height="wrap_content"
android:text="open" />
<Button
android:id="#+id/btogg"
android:layout_width="90sp"
android:layout_height="wrap_content"
android:text="change" />
<Button
android:id="#+id/bclose"
android:layout_width="90sp"
android:layout_height="wrap_content"
android:text="close" />
</LinearLayout>
<SlidingDrawer
android:id="#+id/slidingDrawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:content="#+id/content"
android:handle="#+id/handle" >
<Button
android:id="#+id/handle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Handle" />
<LinearLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<CheckBox
android:id="#+id/chkbok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="On/Off" />
</LinearLayout>
</SlidingDrawer>
</FrameLayout>
java class SlideUpDown.java
public class SlideUpDown extends Activity implements OnClickListener,
OnCheckedChangeListener, OnDrawerOpenListener, OnDrawerCloseListener {
Button opn, cloz, chng, hndl;
CheckBox chkbox;
SlidingDrawer sd;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sideupdown);
refference();
opn.setOnClickListener(this);
cloz.setOnClickListener(this);
chng.setOnClickListener(this);
chkbox.setOnCheckedChangeListener(this);
sd.setOnDrawerOpenListener(this);
sd.setOnDrawerCloseListener(this);
}
private void refference() {
opn = (Button) findViewById(R.id.bopen);
cloz = (Button) findViewById(R.id.bclose);
chng = (Button) findViewById(R.id.btogg);
chkbox = (CheckBox) findViewById(R.id.chkbok);
sd = (SlidingDrawer) findViewById(R.id.slidingDrawer);
hndl = (Button) findViewById(R.id.handle);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bopen:
sd.open();
break;
case R.id.bclose:
sd.close();
break;
case R.id.btogg:
sd.toggle();
break;
}
}
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (chkbox.isChecked()) {
sd.lock();
hndl.setEnabled(false);
} else {
sd.unlock();
hndl.setEnabled(true);
}
}
#Override
public void onDrawerOpened() {
}
#Override
public void onDrawerClosed() {
}
#Override
protected void onDestroy() {
super.onDestroy();
}
}
In one of our applications we hide and show a header and footer. We do this by changing the height and visibility of the view.
It looks something like this
ValueAnimator va;
if (show)
va = ValueAnimator.ofInt(0, px);
else
va = ValueAnimator.ofInt(px, 0);
va.setDuration(400);
va.setInterpolator(new DecelerateInterpolator(2));
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
topicNavBar.getLayoutParams().height = value;
topicNavBar.requestLayout();
if (value == 0) {
if (show)
topicNavBar.setVisibility(View.VISIBLE);
else
topicNavBar.setVisibility(View.GONE);
}
if (show && value == px) {
animationInProgress = false;
}
if (!show && value == 0) {
animationInProgress = false;
}
}
});
va.start();
Hope this helps