Cannot make support bottomsheet not-hideable when in Expanded state - android

I'm trying to make a bottomsheet using google support library. The goal is to have a sheet that:
Can be hidden programmatically only
Its height is calculated automatically
Is defined statically in xml
So far so good, simple stuff. There is also this promising isHideable() which defaults to false.
But the bottomsheet seems to ignore the isHideable when the sheet is set to STATE_EXPANDED (although its not going to cover the whole screen). The only way to make it unhideable is to set a peek height (which I don't want). Is there a way to have it expanded and not-hideable without setting the height manually (or via layout change triggers)
Here is the (super slim) code used:
Activity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View bottomSheet = findViewById(R.id.bottomsheet);
BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
behavior.setHideable(false);
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
}
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<LinearLayout
android:id="#+id/bottomsheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:behavior_hideable="false"
app:layout_behavior="#string/bottom_sheet_behavior"
android:background="#android:color/white">
<TextView
android:layout_width="match_parent"
android:layout_height="300dp"
android:text=":) :) :)"/>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
Behavior

The simplest but hackish way I've found so far:
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
bottomSheet.post(new Runnable() {
#Override
public void run() {
behavior.setPeekHeight(bottomSheet.getHeight());
}
});
And of course when there is need to hide it firstly call setHideable(true).
This is just a workaround that might lead to weird behavior.

Related

How to animate height change of views inside a LinearLayout?

I am working on an Android 4+ app which uses a quite simply layout: Multiple views are stacked using a LinearLayout within a ScrollView
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="HardcodedText,UseCompoundDrawables,UselessParent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusableInTouchMode="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
<!-- Top Container -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
<Button... />
</LinearLayout>
<!-- Hidden Container -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
... Some Content ...
</LinearLayout>
<!-- Bottom Container -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
... Some Content ...
</LinearLayout>
</LinearLayout>
</ScrollView>
The HiddenContainer should not be visible when the layout it created. Thus in the beginning the BottomContainer is directly beneath the TopContainer. A click on the Button within the TopContainer toggles the visibility of the HiddenContainer.
Doing this with hiddenContainer.setVisibility(hiddenContainer.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE) works find and without any problem. However it does not look good when the view suddenly appears or disappears. Instead I would like to animate the change.
I was surprised that I was not able to find an easy solution for this:
Using android:animateLayoutChanges="true" does work, however I am not able to control the animation.
While using a ValueAnimator to change hiddenContainer.setScaleY(...) gives me control over the animation setScaleY(0) makes the container invisible without reducing the space it occupies within the layout.
Using the ValueAnimator to change hiddenContainer.setHeight(...) might work, however I don't want to use a fixed height value when showing the container (e.g. hiddenContainer.setHeight(300)) but the height which is determined by the containers content.
So, how to solve this?
For animate your changes of layout (alpha, visibility, height, etc) you can use TransitionManager. For example: I have three static methods and use them when I want to animate layout changes:
public static final int DURATION = 200;
public static void beginAuto(ViewGroup viewGroup) {
AutoTransition transition = new AutoTransition();
transition.setDuration(DURATION);
transition.setOrdering(TransitionSet.ORDERING_TOGETHER);
TransitionManager.beginDelayedTransition(viewGroup, transition);
}
public static void beginFade(ViewGroup viewGroup) {
Fade transition = new Fade();
transition.setDuration(DURATION);
TransitionManager.beginDelayedTransition(viewGroup, transition);
}
public static void beginChangeBounds(ViewGroup viewGroup) {
ChangeBounds transition = new ChangeBounds();
transition.setDuration(DURATION);
TransitionManager.beginDelayedTransition(viewGroup, transition);
}
And when you want to animate layout changes you can just call one of this methods before layout changings:
beginAuto(hiddenContainerParentLayout);
hiddenContainer.setVisibility(hiddenContainer.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE)

Animating views in AppBarLayout

I have an AppBarLayout inside a coordinatorLayout as follows:
<coordinatorLayout>
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TabLayout
android:id="#+id/scrollable_category_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:tabGravity="center"
app:tabMode="scrollable" />
<RelativeLayout
android:id="#+id/parent_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="100dp"
android:background="#android:color/white"
android:visibility="visible">
<few more layout/>
</RelativeLayout>
</android.support.design.widget.AppBarLayout>
<more views>
</coordinatorLayout>
I want the relativeLayout to be hidden by default and if the API gives a particular response then I will show the RelativeLayout with its content. The content can have varying height depending on the API response (eg. Text can be single line or multiline etc.). So I dont know the height of the view in the begining. Now I want to apply a translationY to the view such that it appears to be coming from beneath the tabLayout (showing shadows etc.).
I have tried many solutions, one of which is:
adParentLayout.animate()
.translationY(0)
.setDuration(5000)
.withEndAction(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "Animation Complete", Toast.LENGTH_SHORT).show();
}
})
.withStartAction(new Runnable() {
#Override
public void run() {
adParentLayout.setVisibility(View.VISIBLE);
adParentLayout.setTranslationY(-adParentLayout.getHeight());
}
});
This obviously doesn't work. I dont know much about animations and would like suggestions.
One solution I tried was making the view visible in the layout and then apply translation. But this makes the view translate above the TabLayout covering it completely. It turns out that AppBarLayout extends LinearLayout, so the second Layout RelativeLayout translated above it. I dont understand how this effect can be achieved and any input in the right direction will be helpful.
I got it working with LayoutTransition. Basically I just added android:animateLayoutchanges=true in appBarLayout and it handled animating the visibility of the view.
I didnt know about this. Seems like a great thing to have.

Layout behind status bar - Android Lollipop

I want in my application to be able to implement this effect:
where the status bar is semi transparent and the layout is behind the status bar. Every example that I've read on the subject, was mainly associated with the navigation drawer and mostly used the ScrimInsetScrollView (or ScrimInsetsFrameLayout). I tried implementing this with ScrimInsetsFrameLayout.
Basically I have an activity that holds a fragment, and this is my layout (the fragment is later added to the container in the activity's onCreate method):
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:fitsSystemWindows="true"
>
<com.test.app.widget.ScrimInsetsFrameLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:insetForeground="#4000"
android:id="#+id/container"
></com.test.app.widget.ScrimInsetsFrameLayout>
</FrameLayout>
And also I've set the android:statusBarColor to transparent in themes.
The solution does not work for me. Apparently I am doing something wrong here.
Can someone point out where I am mistaken?
Have you tried
#Override
public void onCreate(Bundle savedInstanceState) {
getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
as described in the Android documentation

Android Form Scrolling with Translucent Status Bar

I am targeting SDK 21, Min SDK 15, Testing on API 19 device (Android 4.4.4)
I have this code in my Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launcher);
setTranslucentStatus();
}
#TargetApi(Build.VERSION_CODES.KITKAT)
private void setTranslucentStatus() {
getWindow()
.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
It works, with one problem. In the layout I have a ScrollView. The ScrollView is not scrolling. However, if I comment out the call to setTranslucentStatus() scrolling works just fine.
By the way, the theme is NoActionBar
Any clue as to why the ScrollView would refuse to scroll when the status bar is translucent?
I don't know if you already found a solution for that, but if you use ConstraintLayout should solve this annoying bug.
A sample layout would look like this
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<ScrollView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
</ScrollView>
Just use the attribute of scroll view
-- scrollbars: none in scroll view it makes the scroll view to scroll with out scrollbars
No need of translucent flag just remove it

RoboFragments in layout.xml

I've been trying to get Roboguice to work with fragments declared in a <fragment> block in the layout file and then injected into the activity, but, although the fragment exists somewhere off screen (an EditText in the fragment takes focus and fires events), It is not visible. Does RoboGuice support what I'm trying to do here or should I go about it a different way?
code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
android:id="#+id/myFragment"
android:name="com.example.MyFragment"
android:layout_height="0dp"
android:layout_width="0dp"
android:layout_weight="1" >
<!-- Preview: layout=#layout/my_fragment -->
</fragment>
</LinearLayout>
Java:
#ContentView(R.layout.participant)
public final class Main extends RoboFragmentActivity {
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#InjectFragment(R.id.myFragment) private MyFragment myFragment;
}
Solved the issue, but for anyone else looking - The issue at hand was completely unrelated to RoboGuice, which allows fragment injection exactly as shown above. Rather the issue was that both of my layout dimensions for the fragment were set to 0dp, ensuring that my fragment would never be rendered.

Categories

Resources