fitsSystemWindows="true" doesn't work after calling setContentView() - android

For an app I'm developing I wanted to reload the UI after a user input (basically resetting it completely after they made changes to it). I wanted to try avoiding destroying/recreating the activity and use setContentView() instead because it's a lot faster.
However when I do that I'm having an issue: the newly created UI doesn't respect the fitsSystemWindows="true" and part of it ends up behind the android's status bar.
I managed to boil it down to that code example to test it :
layout.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:id="#+id/mainContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<Button
android:text="Button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/button" />
</LinearLayout>
MainActivity.java
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
reloadUI();
}
});
}
public void reloadUI() {
setContentView(R.layout.layout);
}
}
When I load the app, I get the expected layout, that is a simple button on top of the screen, right below the status bar:
However, once I click the button which calls setContentView a second time (showing the same XML), the Button gets behind the status bar:
Calling mainContainer.getMeasuredHeight() to check what happens gives me 1848px on the first start of the app (on a screen that is 1920px tall, so its height is 72px less than the whole screen, 72px being the height of the status bar), but once I call setContentView again the mainContainer.getMeasuredHeight() gives me 1920px.
Am I missing something here? I could force the mainContainer to stick to a 1848px height with a 72px top padding, but I'd prefer to avoid an ugly hack like this.

So, what you want is to ask the framework to dispatch once more WindowInsets to your root view. That's precisely what ViewCompat.requestApplyInsets(View) will do:
Ask that a new dispatch of View.onApplyWindowInsets(WindowInsets) be performed. This falls back to View.requestFitSystemWindows() where available.
Applying just one line should resolve all your concerns:
public void reloadUI() {
setContentView(R.layout.layout);
// `R.id.mainContainer` is the id of the root view in `R.layout.layout`
ViewCompat.requestApplyInsets(findViewById(R.id.mainContainer));
}

I had the same problem. My solution is to change the rootView marginTop or paddingTop to adapt to the View manually.

Related

Cardview wont turn invisible after set visible

I really didn't want to post it here because this problem sounds really stupid and is difficult to describe, but after hiting my head 2 hours for a stupid layout problem i decided to try
I've one activity with several layout components...
During on create all components are set to be invisible just one keeps visible.
When user presses the button, all components turn visible
when presses button again, all components SHOULD turn invisible again
ALL COMPONENTS VISIBILITY IS ADJUST IN ONLY ONE METHOD
so the activity looks like:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.new_giveaway, R.id.mainView);
/*lots of stuff*/
//last thing
makeVisible(View.INVISIBLE);
}
private void makeVisible(int visi) {
findViewById(R.id.cardView).setVisibility(visi);
((ViewGroup) findViewById(R.id.influencerLayout)).setVisibility(visi);
this.recyclerView.setVisibility(visi);
}
the problem: is on second click all components get invisible but one keeps on screen
the component which stays on is a cardview
Mainlayout:
<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="com.tomatedigital.giveawaymaster.activity.NewGiveawayActivity">
//lots of stuff//
<include layout="#layout/giveaway" />
layout/giveaway is:
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/cardView"
android:layout_width="match_parent"
android:layout_height="#dimen/giveawayCardHeight"
card_view:cardCornerRadius="4dp"
card_view:cardElevation="4dp"
card_view:cardUseCompatPadding="true">
//lots of other stuf
</cardview>
It's the first thing i set visible on controller method but the only which doesn't go back invisible
REPEATING: there is no other calls to setVisibility other than these, all visibitilities are controled just under that method
I didn't post the whole activity code here because is way long
==========UPDATE==========
Just to clarify:
1- the cardview is one separated layout file re-used several places
2- there is only one cardview in the mainlayout
3
if i remove makeVisible(View.INVISIBLE)from onCreate(), all stuff stays visible,
if i call makeVisible(View.INVISIBLE) and never call makeVisible(View.VISIBLE) all stuff stays invisible
but if I invisble->visible->invisible everything goes invisible but cardview keeps visible
When you want to set the whole layout to Invisibility state, you need to do it in your include #layout/giveaway.xml. Becouse it is a view too.
Like we talk in comments...

Android snackbar action not right aligned on tablets

Note: I've seen the existing question titled "Align Snackbar's Action on the right", however this does not address my particular question.
According to the material design spec the action in a snackbar should be right/end aligned. The default behaviour on smaller screens seems to work as expected (full width of screen, action is aligned to the right of the screen), but on tablets I'm seeing the action immediately next to the description/title text with some padding. Example here:
Snackbar action position example
I've seen this on a real Nexus 9 (api 25), and 3 emulators (api 22, 23, and 24) in both portrait and landscape.
The layout xml for the snackbar contents in the Android source (design_layout_snackbar_include.xml) seems to suggest the action button should be right/end aligned so I'm a bit baffled as to why I'm not seeing this.
I first saw this occurring in one of the apps my company is developing, and I've now made a test app to reproduce the issue (which I used to produce the example image linked earlier). All the app does is display "Hello World!" in an activity, and when the back button is pressed a snackbar is shown to confirm the action. My test activity looks like this:
package au.com.test.snackbartest;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity
{
#Override
protected void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
}
#Override
public void onBackPressed()
{
Snackbar snackbar = Snackbar.make( findViewById( R.id.activity_main ), "Exit?", Snackbar.LENGTH_LONG );
snackbar.setAction( "EXIT", new View.OnClickListener()
{
#Override
public void onClick( View v )
{
finish();
}
} );
snackbar.show();
}
}
The layout xml looks like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="#+id/activity_main"
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:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="au.com.test.snackbartest.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"/>
</RelativeLayout>
Using a coordinator layout instead of relative layout doesn't affect this behaviour. Also, extending Activity instead of AppCompatActivity makes no difference either.
So my question is, am I using the snackbar wrong somehow, is this a bug in Android, or is the design spec simply left up to the developer to fully implement with a custom snackbar layout?
Any help or clarification on this would be greatly appreciated!
This is a bug introduced with the 25.1.0 support library. Reverting back to 25.0.1 results in the correct behaviour. Turns out a bug report had already been filed here:
Issue 231850: Snackbar Action Text Not Right-Aligned in 25.1.0
Thanks ianhanniballake for the suggestion of it being a regression in the support library update.

View.layout() works until next UI update

My launching activity consists of Linear layout with two buttons. Both buttons have listeners: the first one (b) moves itself upon click: 30px to the left and 30px back upon the next click.
The second one (b2) changes its text upon click. Here is the code:
public class TestActivity extends Activity {
public final String TAG="TestActivity";
boolean toTop=true;
boolean setInitialText=false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button b=(Button)findViewById(R.id.button);
Button b2=(Button)findViewById(R.id.button2);
b.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
int modifier;
if(toTop) modifier=-30;
else modifier=30;
v.layout(v.getLeft()+modifier,v.getTop(),v.getRight()+modifier,v.getBottom());
toTop=!toTop;
}
});
b2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
String currentText;
if(setInitialText)currentText="Press to change text";
else currentText="Press to change back";
((Button)v).setText(currentText);
setInitialText=!setInitialText;
}
});
}
}
XML layout-file:
<?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:gravity="center"
android:orientation="vertical" >
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Press to begin animation" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Press to change text" />
My problem: when b is moved to the left and I press b2, b moves to its initial position. Why? I don't want it to move back and didn't specify it anywhere.
It looks like View.layout looses its effect. Why it happens? I tested that in other situations and it seems that any UI update makes all invoked View.layout methods loose their effect.
In my main project there is a ListView which is populated with images from background - all view moves loose effect when new image appears. Besides, if I add EditText and try to enter something (as user), view moves loose their effect as well. Can anyone explain to me what's going on and why do views move back?
Looks like parent layout repositions it's siblings after you set a new text for the button2, because as describe in xml, button2 wraps it's content by width and height.
As you changed button's content, it requests it's parent layout to get a new position for it. In this case parent layout will recalculate position values for all of it's siblings. That's why button1 also comes back to it's previous position.
Keep in mind, that you also set the gravity value for parent layout as center, that means that when layout will position it's siblings it will position them in it's center.
Try to experiment with some ohter layout classes like FrameLayout, which has absolute manner of positioning it's siblings and RelativeLayout which and also try your case getting rid of layout's gravity.
Here says that this issue may be solved, using view.setLayoutParams() instead of view.layout()

Can't manage to requestFocus a Spinner

I've got an annoying issue with a screen. The screen consists of a bunch of Spinners one under the other, and then underneath the spinner, an EditText.
The problem is that when the screen starts, the EditText has focus, meaning that some Spinners are off the top of the screen. Try as I might, I cannot make the top Spinner start focused, either by using <requestFocus/> in the screen XML, or by using requestFocus() in code. I've attempted to do what requestFocus skipping next EditText suggests, and if I'm following the suggestion correctly, it doesn't work either.
To reproduce the issue, create a new Android project in Eclipse. main.xml is
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Spinner
android:id="#+id/spinner"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<requestFocus />
</Spinner>
<EditText
android:id="#+id/edittext"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
The activity code is
package nz.co.kb.testspinner;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
public class TestSpinner extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.main);
View view = getLayoutInflater().inflate(R.layout.main, null);
final View spinner = view.findViewById(R.id.spinner);
view.post(new Runnable() {
public void run() {
spinner.requestFocus();
}
});
setContentView(view);
spinner.requestFocus();
}
}
Note multiple styles of requestFocus attempted.
Is this a platform bug, or am I doing something wrong?
Thanks.
I had a similar problem, I solved by doing two things:
1) I set the Spinner object on top (Within the onCreate method) just to make sure that my code gets executed first.
2) I used the following:
Spinner s1 = (Spinner) findViewById(R.id.spinner1);
s1.setFocusable(true);
s1.setFocusableInTouchMode(true);
Let me know if this helps or you need any further help.
From online documentation:
A view will not actually take focus if it is not focusable (isFocusable() returns false), or if it is focusable and it is not focusable in touch mode (isFocusableInTouchMode()) while the device is in touch mode.
In my case,this worked out.
Spinner s1 = (Spinner) findViewById(R.id.spinner1);
s1.requestFocusFromTouch();
s1.performClick();

WebView displays black screen

I'm a little embarrassed to post this but I can't seem to figure out
where I'm going wrong. I've looked at every example and every
tutorial and everything looks right to me. Here's what I'm doing. I
have a listview that when you click on an item it will take you to a
WebView that displays some static formatted text associated with that
list entry.
I had it all working with a TextView but I wanted to be able to use
HTML formatting for the text and figured the WebView was the way to
go. Right now it is just supposed to display a generic link for
testing purposes but when the viewContent intent starts it just goes
to a black screen. I can go back and pick another entry and it also
just shows the black screen.
I'm not sure what code you are going to want to see so here's the
viewSection class file and the viewsection.xml layout.
viewSection.java:
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
public class viewSection extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WebView wv;
setContentView(R.layout.viewsection);
wv = (WebView) findViewById(R.id.wv1);
wv.loadData("<a href='x'>Hello World! - 1</a>",
"text/html",
"utf-8");
}
}
viewsection.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<WebView android:id="#+id/wv1"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
/>
</LinearLayout>
You probably want to set android:layout_height on the WebView to fill_parent. I'm not sure if WebView supports wrap_content.
EDIT: You'll want to set the LinearLayout width and height to fill_parent as well.
Also, if you're using very light HTML styling, you can still use a TextView; there are samples in the API Demos sample app on how to do this (i.e. StyledText.java and Link.java).

Categories

Resources