0

tl;dr: How do I get the DIP height of the FrameLayout?

Full: I am trying to design a layout that is consistent across the general resolutions available. I've gone through the developer best practices on Google and that helped my understanding a bit. I've converted icons to 9-patch and am using DIP heights which improved things a lot.

However where I'm stuck is trying to play three rows of buttons that will take up the remainder of the screen, regardless of the resolution. I'll need to make something different for small screens and tablets, but I am only currently worried about the normal screens on most phones.

I have a layout that has a TabWidget as the @android:id/tabs role and a FrameLayout for the @android:id/tabcontent

One of the Tab Activities is simply 3 rows of buttons which I want to fill the entire FrameLayout which I suspect I must calculate the height of the button based on the height of the FrameLayout.

My question then is, how do I get the DIP height of the FrameLayout?

I've tried a GlobalLayoutListener and that just returns 0. I've tried pulling the LayoutParams and that just returned -1 for FILL_PARENT. I need the actual DIP height of the FrameLayout to properly set the height of the area available.

How can I do that, or am I looking at it incorrectly?

Any help is appreciated.

Kirk
  • 16,182
  • 20
  • 80
  • 112

1 Answers1

1

I figured out a way to get the result I wanted, just not the exact way I was trying and never managed to get the height of the tabcontent directly but indirectly.

I found two methods to do this and I'll post them below.

First I did Method 2 but then discovered I preferred Method 1 and decided to go with that since it's more extendable.

Method 1

This way I found from How to size an Android view based on its parent's dimensions and is the most customizable and readable method. In a nut shell, you need to extend FrameLayout and override the onMeasure method.

<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="0dip"
    android:layout_margin="0dip">
    <LinearLayout
        android:id="@+id/topLayout"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:padding="0dip"
        android:layout_margin="0dip">
        <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="fill_parent"
            android:layout_height="50dp"
            android:padding="0dip"
            android:layout_margin="0dip" />
        <view
            class="com.tmcaz.patch.TabContentFrameLayout"
            android:id="@android:id/tabcontent"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:padding="0dip"
            android:layout_margin="0dip" />
    </LinearLayout>
</TabHost>

The major difference is using a custom class for this where you can handle the sizing from the event itself, similar to Method 2 but no need to do any calculations to get the content height.

I did this to give myself access to the event and handle all of the sizing in the content. Someone reading this may very well need to override something else and deal with the onMeasure event totally differently.

The code is below

public class TabContentFrameLayout extends FrameLayout {
        // add constructors, etc
        @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
        // Should turn these in to member variables and reference it throughout.
        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);

        this.setMeasuredDimension(parentWidth, parentHeight);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

Method 2

I assigned an id to the LinearLayout that held the TabWidget and FrameLayout. Here is my main.xml

<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="0dip"
    android:layout_margin="0dip">
    <LinearLayout
        android:id="@+id/topLayout"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:padding="0dip"
        android:layout_margin="0dip">
        <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="fill_parent"
            android:layout_height="50dip"
            android:padding="0dip"
            android:layout_margin="0dip" />
        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:padding="0dip"
            android:layout_margin="0dip" />
    </LinearLayout>
</TabHost>

I assigned a DIP height to the tabs and then grabbed the LayoutParams for the LinearLayout which I simply subtract the height of the tabs from the result. I've added code here for basic illustrative purposes only and can do it a bit more efficiently, it's not my production code :)

One thing to note is that you can't appear to directly pull the height of the layout during the onCreate event where it's most useful. You need to create a GlobalLayoutListener to capture the change in the layout and get the size.

public class MyTabActivity extends TabActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        LinearLayout ll = (LinearLayout)findViewById(R.id.topLayout);
        ll.getViewTreeObserver().addOnGlobalLayoutListener(
                new ViewTreeObserver.OnGlobalLayoutListener() {

                    public void onGlobalLayout() {
                        DisplayLayoutDimensions();
                    }
                }
        );

         // Code here to add activities to the tabs, etc
}

.

public void DisplayLayoutDimensions()
{
        // Put code to calculate the heights you need instead of printing
        // out the values obviously.
        Resources r = getResources();
        LinearLayout topLayout = (LinearLayout)findViewById(R.id.topLayout);
        LayoutParams tabWidgetParams = getTabHost().getTabWidget().getLayoutParams();

        float scale = r.getDisplayMetrics().density;
        float pxTabContent = topLayout.getHeight() - tabWidgetParams.height;

        /*** The commented out DIP calculations didn't work for me in any AVD so
             manually I calculated them with the scale which worked fine         ***/

        //float dipTopLayout = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, topLayout.getHeight(), r.getDisplayMetrics());
        //float dipTabWidget = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tabWidgetParams.height, r.getDisplayMetrics());
        //float dipTabContent = dipTopLayout - dipTabWidget;

        Log.d("MyTabActivity", "LinearLayout (topLayout) Height: " + topLayout.getHeight() + "px / " + (topLayout.getHeight() / scale) + "dp");
        Log.d("MyTabActivity", "TabWidget Height: " + tabWidgetParams.height + "px / " + (tabWidgetParams.height / scale) + "dp");
        Log.d("MyTabActivity", "Calculated (tabcontent) Height: " + pxTabContent + "px / " + (pxTabContent / scale) + "dp");
}

Hope this helps someone at some point. If someone has a better way to do this, please speak up.

Community
  • 1
  • 1
Kirk
  • 16,182
  • 20
  • 80
  • 112