Get height of child layout (including child elements) in onCreateView of fragment - android

The size of a layout i'm using in a fragment is always 0.
I have a fragment that includes a child-layout that looks like this:
<RelativeLayout android:id="#+id/animationLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="30dp"
android:background="#color/colorPrimary"
android:elevation="4dp">
This layout has a child-layout that is defined like this:
<TableLayout android:id="#+id/searchForm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="0"
android:background="#color/colorPrimary"
android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true">
[Some Child elements]
</TableLayout>
In the onCreateView of the parent fragment, the height of the layout 'searchForm' is always 0.
Even if i measure the size in
searchForm.post(new Runnable() {
#Override
public void run() {
searchFormHeight = searchForm.getHeight(); //always 0
}
}
the size is 0.
How can i get the size of this layout in onCreateView?

You need to use a ViewTreeListener for a callback when the entire view is inflated. If I recall android waits for the entire view to be inflated at once, so even if a view is instaniated, it may not be inflated returning a height of 0.
searchForm.getViewTreeObserver()
.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw () {
searchFormHeight = searchForm.getHeight();
return true;
});

Ok, i don't know what i've done wrong before, but finally following worked for me:
searchForm.post(new Runnable() {
#Override
public void run() {
searchFormHeight = searchForm.getHeight();
}
});

Actually onCreateView() process the creation of view that's why it return 0
Try following to get height, override function into fragment class
#Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
searchFormHeight = searchForm.getHeight();
}

Related

Exception in Scrollview

In my android application, I'm facing a IllegalStateException. I can't reproduce this exception later. This is the stacktrace
Non-fatal Exception: java.lang.IllegalStateException: ScrollView can host only one direct child
at android.widget.ScrollView.addView(ScrollView.java:397)
at android.support.design.widget.BaseTransientBottomBar.showView(BaseTransientBottomBar.java:436)
at android.support.design.widget.BaseTransientBottomBar$1.handleMessage(BaseTransientBottomBar.java:178)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:146)
at android.app.ActivityThread.main(ActivityThread.java:5679)
at java.lang.reflect.Method.invokeNative(Method.java)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
at dalvik.system.NativeStart.main(NativeStart.java)
Anyone please help me!
I examined the question carefully, and I logically found that it is related to showing Snackbar on a fragment or activity, taken as a reference in another class as global object, when it is invalidated in onStop(), onDestroy() life cycle callback stage respectively:
https://stackoverflow.com/a/52019719/787399
Inside your ScrollView you have to host a child (e.g. a Linear Layout) which at it's time will host all the UI elements from that scrollview. You can't have, for example, 2 textviews added directly to a ScrollView. You need to have something to hold those UI elements inside the scrollview.
In ScrollView you can have only one View (in View I mean TextView, Button and cetera, but ViewGroup is child of View too) or ViewGroup. So if you have multiple Views put them in a proper ViewGroup and it will work fine.
Scrollview can only have one direct child
example:
This is valid-->
ScrollView
LinearLayout
Other Views
....
....
LinearLayout
ScrollView
This is not-->
ScrollView
LinearLayout
Other Views
....
....
LinearLayout
LinearLayout
Other Views
....
....
LinearLayout
ScrollView
I got the same issue. and I have reproduced using the below steps.
Step 1: make you Fragment Or Activity Layout with a parent of Scrollview.
Step 2: show Snackbar in onPause(), onStop(), onDestroy() like belove.
#Override
public void onPause() {
super.onPause();
Snackbar.make(button, "onPause", Snackbar.LENGTH_LONG).show();
}
#Override
public void onStop() {
super.onPause();
Snackbar.make(button, "onStop", Snackbar.LENGTH_LONG).show();
}
#Override
public void onDestroy() {
super.onDestroy();
Snackbar.make(button, "onDestroy", Snackbar.LENGTH_LONG).show();
}
Now run an app and check in logcat. when you click on back button you will get same error mention in question.
Solution:
make a common Snackbar like below.
#Override
public void onPause() {
super.onPause();
showSnackbar("onPause");
}
#Override
public void onStop() {
super.onPause();
showSnackbar("onStop");
}
#Override
public void onDestroy() {
super.onDestroy();
showSnackbar("onDestroy");
}
private void showSnackbar(String message) {
if (isValidContext(getActivity())) {
Snackbar.make(btnConsume, message, Snackbar.LENGTH_LONG).show();
}
}
public static boolean isValidContext(final Context context) {
if (context == null) {
return false;
}
if (context instanceof Activity) {
final Activity activity = (Activity) context;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return !activity.isDestroyed() && !activity.isFinishing();
} else {
return !activity.isFinishing();
}
}
return true;
}
ScrollView will not accept many child's, Use only one Layout inside ScrollView like LinearLayout, RelativeLayout,FrameLayout or ConstraintLayout(Based on your needs)and then add all your child's.
See this for your reference
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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="wrap_content">
<android.support.constraint.ConstraintLayout 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="wrap_content">
//Add your Views here
</android.support.constraint.ConstraintLayout>
</ScrollView>
Scroll view allow only one child directly like below :
<?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">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<!--Here scroll view allow only one direct child-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--Here your other xml code-->
</LinearLayout>
</ScrollView>
</LinearLayout>

Fragment parent layout transparent

I use circularRevealAnimation to start one of Fragments. Here's its layout.
<?xml version="1.0" encoding="utf-8"?>
<io.codetail.widget.RevealFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main_container"
android:background="#color/red"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/container2"
android:layout_width="match_parent"
android:background="#color/white"
android:layout_height="match_parent"
android:orientation="vertical">
....
</LinearLayout>
</io.codetail.widget.RevealFrameLayout>
In my Fragment class I trigger revealAnimation in onStart() method.
#Override
public void onStart() {
super.onStart();
// SupportAnimator animator = ViewAnimationUtils.createCircularReveal(container2, 720, 0, 0, 1386); ->line from onCreateView
animator.setDuration(500);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
animator.addListener(new SupportAnimator.AnimatorListener() {
#Override
public void onAnimationStart() {
System.out.println("lol222");
}
#Override
public void onAnimationEnd() {
System.out.println("end");
}
#Override
public void onAnimationCancel() {
}
#Override
public void onAnimationRepeat() {
}
});
}
As you see, the parent layout has red color, when its child is white. When I started this Fragment I can see white circularReveal over the red background, when I want to see such animator over previous fragment. How can I achieve this?
I tried to make background="#null" and so on, while reading SOF related topics. Nothing helped me. So what is write way to setFragmentparentlayoutastransparent`?
You can implement using xml like this
android:background="#android:color/transparent"
or programatically you can change like this
yourview.setBackgroundColor(Color.parseColor("#00000000"));
There was my fault. My container was LinearLayout, not FrameLayout. Bacause of that there wasn't transparency.

Try to Understand the behavior of BottomSheet in android support library 23.2.1

I am trying to implement Bottom sheet in one of my activities and I am kind of confused by the way it is behaving!
So here is the problem, I have an activity in which I am trying to show Bottom sheet and I see that:
if we dont set the app:behavior_peekHeight property then the Bottom sheet never works
If you set the PeekHeight to something less than 30dp (basically just to hide it from screen)
If you set app:behavior_peekHeight to more than 30dp in layout file and try to set the state of bottomSheetBehavior to STATE_HIDDEN in you onCreate method your app crashes with this error
caused by:
java.lang.NullPointerException: Attempt to invoke virtual method
'java.lang.Object java.lang.ref.WeakReference.get()' on a null object reference at android.support.design.widget.BottomSheetBehavior.setState(BottomSheetBehavior.jav a:440)
at myapp.activity.SomeActivity.onCreate(SomeActivity.java:75)
I am really confused on why is it not allowing me to hide it in onCreate? or why cant we just set the peekHeight to 0 so that it is not visible on screen unless we call the STATE_EXPANDED or even not setting that property should default it to hide! or atleast I should be able to set it as hidden in my onCreate!
am I missing something? or is the behavior of the BottomSheet rigid?
my layout file for the BottomSheet is something like this:
<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:background="#android:color/white"
android:layout_height="100dp"
android:orientation="vertical"
app:behavior_hideable="true"
app:behavior_peekHeight="40dp" <!-- I cant set this less than 30dp just to hide-->
app:layout_behavior="#string/bottom_sheet_behavior"
tools:context="someActivity"
android:id="#+id/addressbottomSheet"
tools:showIn="#layout/some_activity">
in my activity I am doing something like this:
#InjectView(R.id.addressbottomSheet)
View bottomSheetView;
#Override
protected void onCreate(Bundle savedInstanceState) {
....
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetView);
// only if I have set peek_height to more than 30dp
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
}
In my onclick I am doing this:
#Override
public void onItemClick(View view, int position) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
After working on this issue for few more days I found one alternate solution for this:
Instead of using the Bottom_sheet directly inside your layout, if we create a Bottom_Sheet fragment and then instantiate it in the activity this issue will not occur and the bottom sheet will be hidden and we dont need to specify the peek_height
here is what I did
public class BottomSheetDialog extends BottomSheetDialogFragment implements View.OnClickListener {
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_bottom_sheet, container, false);
}
Then in my activity
bottomSheetDialog = BottomSheetDialog.newInstance(addressList.get(position), position);
bottomSheetDialog.show(getSupportFragmentManager(), AddressActivity.class.getSimpleName());
This actually solved my problem of bottom sheet being not hidden when the activity starts but I am still not able to understand why if bottom_sheet is included directly we face that problem!
(Referring to the question) Suzzi bro the issue with your code is you are trying to call the setState method directly inside onCreate. This is will throw a nullPointer because the WeakReference is not initialized yet. It will get initialized when the Coordinator layout is about to lay its child view.
onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection)
Called when the parent CoordinatorLayout is about the lay out the
given child view.
So the best approach is set the peek height to 0 and show/hide inside the onItemClick listener.
Here is my code:
bottom_sheet.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Bottom sheet"
android:textColor="#android:color/black" />
</LinearLayout>
activity_main.xml
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show hide bottom sheet" />
<include
android:id="#+id/gmail_bottom_sheet"
layout="#layout/bottom_sheet" />
MainActivity.java
public class MainActivity extends AppCompatActivity {
boolean isExpanded;
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CoordinatorLayout coordinatorLayout = (CoordinatorLayout) findViewById(R.id.gmail_coordinator);
final View bottomSheet = coordinatorLayout.findViewById(R.id.gmail_bottom_sheet);
final BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isExpanded) {
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else {
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
isExpanded = !isExpanded;
}
});
}
}
Here initially the bottom sheet is not visible. On clicking the button we will be set the state to STATE_COLLAPSED/STATE_EXPANDED.
The tutorial I followed to make this demo app is listed below:
Bottom Sheet with Android Design Support Library
The reason its crashing is due to the fact that the weak reference is not being set until one of the last lines in onLayoutChild, which gives you your null ptr exception.
What you can do is create a custom BottomSheet Behavior and override onLayoutChild, setting the expanded state there.
An example can be found here:
NullPointerExeption with AppCompat BottomSheets
To avoid the Null pointer exception, set the state to HIDDEN like this in onCreate()
View bottomSheetView = findViewById(R.id.bottomsheet_review_detail_id);
mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheetView);
bottomSheetView.post(new Runnable() {
#Override
public void run() {
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
}
});

Reload a View on Orientation Change

In my Activity I have a View that "depends" from screen orientation. If it's in landscape mode, i use a layout under layout-large-hand, but if it's in portrait mode, I use the layouts under the layouts folder.
The Activity shows also a Map with some markers and informations.
In my AndroidManifest i have
android:configChanges="orientation|screenSize"
But the activity shows only the layout on portrait mode.
How can I fix this?
EDIT 3/12:
I have a layout like this:
<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"
android:orientation="vertical" >
<fragment
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment" />
<include
android:id="#+id/my_view"
layout="#layout/my_view"
android:visibility="gone" />
</RelativeLayout>
and i want that my_view changes the layout, but map remains with all markers, zoom level, position, ecc ecc...
my_view is visible when i click on a Button created dinamically in the activity.
EDIT 2:
Like SweetWisher ツ says, i was trying to setup a custom behaviour for the views. But when i rotate the device, the map disappear. This is part of my code in the activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initUI();
}
private void initUI() {
setContentView(R.layout.my_layout);
if (mp == null) {
mp = MapFragment.newInstance();
}
getFragmentManager().beginTransaction().replace(R.id.placeholder, mp).commit();
initUIStuff()
}
private void initUIStuff(){
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
initUI();
}
#Override
protected void onStart() {
super.onStart();
initGMap();
}
#Override
protected void onResume() {
super.onResume();
initGMap();
}
private void initGMap() {
if (mp != null {
//Initialize Map
}
}
If you use android:configChanges="orientation" in your Manifest you're preventing Android from doing default reset of view hierarchy on screen orientation change. You have to manually change your views in onConfigurationChanged method and by that I mean inflate your desired layout and replace your old layout with it. If you want to take advantage of Android automatic view hierarchy reset don't use android:configChanges="orientation".
Edit:
Use following code to add map fragment through XML:
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
You also don't need now 'onConfigurationChange' method so delete it.

getWidth and getHeight are returning a zero

I am trying to make a simple drawing program for the android.
I have a custom View class to handle the drawing. When I call its getWidth and getHeight metheds, I get a zero.
But, the drawing works fine, I hard code in the width and height so it works. Why is it doing this?
My View class
public class cDrawing extends View{
char BitMap[];
static final short WIDTH=160;
static final short HEIGHT=440;
static final char EMPTY=' ';
int mWidthSize;
int mHeightSize;
static final char RED ='R';
int y;
public cDrawing(Context context) {
super(context);
y=3;
// set up our bitmap
BitMap=new char[WIDTH*HEIGHT];
for(int i=0; i<WIDTH*HEIGHT; i++)
BitMap[i]=EMPTY;
// returns zero why???????
int h=getHeight();
h=400;
int w=getWidth();
w=320;
mWidthSize=w/WIDTH;
mHeightSize=h/HEIGHT;
// TODO Auto-generated constructor stub
}
The Activity class
public class cCanves extends Activity implements OnClickListener {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.canves);
cDrawing board=new cDrawing(this);
LinearLayout layout = (LinearLayout)findViewById(R.id.parent2);
layout.addView(board);
// set up buttons
View mEraser = findViewById(R.id.buteraser);
mEraser.setOnClickListener(this);
View mBlack = findViewById(R.id.butblack);
mBlack.setOnClickListener(this);
View mWhite = findViewById(R.id.butwhite);
mWhite.setOnClickListener(this);
View mRed = findViewById(R.id.butred);
mRed.setOnClickListener(this);
} // end function
public void onClick(View v) {
Intent i;
switch(v.getId())
{
case R.id.buteraser:
break;
case R.id.butblack:
break;
case R.id.butwhite:
break;
case R.id.butred:
break;
} // end switch
} // function
}
the xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="#+id/parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:orientation="vertical"
android:layout_height="fill_parent">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:orientation="horizontal"
android:layout_height="wrap_content">
<ImageButton android:id="#+id/buteraser"
android:src="#drawable/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageButton android:id="#+id/butblack"
android:src="#drawable/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageButton android:id="#+id/butwhite"
android:src="#drawable/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageButton android:id="#+id/butred"
android:src="#drawable/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout android:id="#+id/parent2"
android:layout_width="fill_parent"
android:orientation="vertical"
android:layout_height="fill_parent">
</LinearLayout>
</LinearLayout>
The width and height are not defined until the view is actually rendered to the screen.
Use protected abstract void onLayout (boolean changed, int l, int t, int r, int b) (which you override in your activity) to know when the sizes are ready.
Complementing Mah's answer, I found out that you can get the values from parameters, like the code bellow:
ImageView imageProcess = (ImageView) li.inflate(
R.layout.andamento_sinistro_imageprocess, centerLayout, false);
imageProcess.setBackgroundResource(
(isActive)?(R.drawable.shape_processon):(R.drawable.shape_processoff));
RelativeLayout.LayoutParams imageProcessParams =
(RelativeLayout.LayoutParams)imageProcess.getLayoutParams();
imageProcessParams.leftMargin =
(int) (centerPosition - 0.5*imageProcessParams.width);
imageProcessParams.addRule(RelativeLayout.CENTER_VERTICAL);
centerLayout.addView(imageProcess);
The real catch here is the use of the LayoutParams, that have rules yet not processed by the element.
I'm not certain, but it may have something to do with where the code is in the lifecycle of your activity. If you're calling getWidth() and getHeight() before the View is displayed on screen, you'll get a value of 0. I've had that happen to me, too.
I'm not sure if there's a way around this. I had to rely on getting the hardware screen's width and height, instead of the view's width and height. You might end up having to approximate the width and height of your view and hard coding it.
You should call getWidth() and getHeight() in the overrided method onLayout.
Just Use the getViewTreeObserver() Listener, and inside this just
calculate the height and width.
Follow the code :
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//Do your Stuff calculation , like view.getWidth() ...
});
The view only has dimensions after beeing displayed for the first time.
Other thing:
int h=getHeight();
h=400;
Is useless no ? Is it just for testing ?

Categories

Resources