brainfree
brainfree

Reputation: 827

WRAP_CONTENT not working after dynamically adding views

I'm trying to create a fragment that lays out a series of custom views dynamically. The main content for this layout is a RelativeLayout nested in a LinearLayout (to center it horizontally), nested in a ScrollView.

The RelativeLayout has a few TextViews and a 9 patch ImageView that is meant to scale with the dynamically added custom views. However, the image (achievements_bgImageView below) is ending up as the size of the screen, and is not respecting the size of its parent RelativeLayout even after I've added the appropriate amount of custom views. The image scales fine when I manually set the size of achievements_mainLayout (see the commented out lines below), but does nothing if I try to let that RelativeLayout's wrap_content handle its own sizing.

The ScrollView IS respecting the size of the RelativeLayout, as all the content is present, it's simply the imageView that isn't stretching to match the content at this point.

Any help would be appreciated... My manual calculations don't seem to be good enough to account for different devices, despite the fact I'm accounting for screen density and I'm manually forcing the RelativeLayout to a constant width.

It's worth noting that the measured size of the RelativeLayout is always equal to the height of the screen, regardless of whether or not the sum of its content is greater or less than that height. So, essentially, WRAP_CONTENT is simply not doing what it's supposed to be doing. I have nothing referencing any edge of of the RelativeLayout, so circular dependencies shouldn't be a problem.

fragment_achievements.xml

<?xml version="1.0" encoding="utf-8"?>

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fillViewport="true" >

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal">

        <RelativeLayout
                android:layout_width="320dp"
                android:layout_height="wrap_content"
                android:id="@+id/achievements_mainLayout">

            <ImageView
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent"
                    android:id="@+id/achievements_bgImageView"
                    android:src="@drawable/bkg_achievements9"
                    android:adjustViewBounds="true"
                    android:layout_marginLeft="8dp"
                    android:layout_marginTop="8dp"
                    android:layout_marginRight="8dp"
                    android:layout_centerHorizontal="true"
                    android:scaleType="fitXY"/>

            <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Name Field"
                    android:id="@+id/achievements_nameTextView"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentLeft="true"
                    android:layout_marginLeft="28dp"
                    android:layout_marginTop="30dp"/>

            <ImageView
                    android:layout_width="52dp"
                    android:layout_height="52dp"
                    android:id="@+id/achievements_avatarImageView"
                    android:layout_below="@+id/achievements_nameTextView"
                    android:layout_alignLeft="@+id/achievements_nameTextView"
                    android:src="@drawable/achieve_avatar"
                    android:layout_marginTop="5dp"/>

            <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textAppearance="?android:attr/textAppearanceSmall"
                    android:text="Top Moment:"
                    android:id="@+id/textView2"
                    android:layout_alignBottom="@+id/achievements_avatarImageView"
                    android:layout_toRightOf="@+id/achievements_avatarImageView"
                    android:layout_marginBottom="16dp"
                    android:layout_marginLeft="4dp"
                    android:textSize="12dp"/>

            <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textAppearance="?android:attr/textAppearanceSmall"
                    android:text="Me Overall:"
                    android:id="@+id/textView3"
                    android:layout_alignTop="@+id/textView2"
                    android:layout_alignLeft="@+id/textView2"
                    android:layout_marginTop="16dp"
                    android:textSize="12dp"/>

            <TextView
                    android:layout_width="52dp"
                    android:layout_height="wrap_content"
                    android:textAppearance="?android:attr/textAppearanceSmall"
                    android:text="153"
                    android:id="@+id/achievements_totalPointsTextView"
                    android:gravity="center"
                    android:layout_alignTop="@+id/achievements_avatarImageView"
                    android:layout_alignRight="@+id/achievements_bgImageView"
                    android:layout_alignEnd="@+id/achievements_bgImageView"
                    android:layout_marginRight="31dp"
                    android:textColor="#f7a033"/>

            <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Moment"
                    android:id="@+id/achievements_topMomentTextView"
                    android:layout_alignTop="@+id/textView2"
                    android:layout_toRightOf="@+id/textView2"
                    android:layout_marginLeft="5dp"
                    android:textSize="12dp"/>

            <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="153"
                    android:id="@+id/achievements_overallTextView"
                    android:layout_alignTop="@+id/textView3"
                    android:layout_toRightOf="@+id/textView3"
                    android:layout_marginLeft="5dp"
                    android:textSize="12dp"/>

        </RelativeLayout>
    </LinearLayout>
</ScrollView>

AchievementFragment.java

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View fragmentView = null;

    fragmentView = inflater.inflate(R.layout.fragment_achievements, container, false);

    ImageView avatarImageView = (ImageView)fragmentView.findViewById(R.id.achievements_avatarImageView);

    ...

    // Basic Achievement List Setup
    RelativeLayout mainLayout = (RelativeLayout)fragmentView.findViewById(R.id.achievements_mainLayout);
    AchievementRow currentRow = null;

    List achievementTypeList = CampaignManager.sharedManager().sortedAchievementTypeList();

    int achievementCount = achievementTypeList.size();

    for (int i = 0; i < achievementCount; i++) {
        AchievementType achievementType = (AchievementType)achievementTypeList.get(i);

        // Every third achievement creates a new row.
        if ((i % 3) == 0) {
            AchievementRow row = (AchievementRow)inflater.inflate(R.layout.widget_achievementrow, null);

            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);

            if (currentRow == null) {
                layoutParams.addRule(RelativeLayout.BELOW, avatarImageView.getId());
                layoutParams.setMargins(10, 70, 10, 0);
            } else {
                layoutParams.addRule(RelativeLayout.BELOW, currentRow.getId());
                layoutParams.setMargins(10, 10, 10, 0);
            }

            layoutParams.addRule(RelativeLayout.ALIGN_LEFT, backgroundImageView.getId());
            layoutParams.addRule(RelativeLayout.ALIGN_RIGHT, backgroundImageView.getId());

            row.setLayoutParams(layoutParams);
            row.setId(i+1);
            mainLayout.addView(row);

            currentRow = row;
        }

        // Now setup the Button
        AchievementButton achievementButton = currentRow.buttonForIndex(i % 3);
        achievementButton.achievementType = achievementType;
        achievementButton.setOnClickListener(achievementButtonListener);
        achievementButton.setVisibility(View.VISIBLE);

        CacheManager.sharedManager().fetchAchievementThumbnail(getActivity(), achievementButton, achievementType);
    }

    // This is the manual scaling of mainLayout
    // float scale = getResources().getDisplayMetrics().density;
    // float headerHeight = scale * 150.0f;
    // float rowHeight = scale * 78.0f;
    // ViewGroup.LayoutParams mainLayoutParams = mainLayout.getLayoutParams();
    // mainLayoutParams.height = (int)(headerHeight + (Math.ceil(achievementCount / 3.0) * rowHeight));

    return fragmentView;
}

Upvotes: 48

Views: 36634

Answers (9)

Ultimo_m
Ultimo_m

Reputation: 4897

I encountered same issue when working with LinearLayout which has wrap_content and one child as TextView match_parent.

To fix this I did this:

Remove the TextView programatically and then add it again.

linearLayout.removeView(textView)
linearLayout.addView(textView)

I know it sounds stupid but it works. In my case calling invalidate didn't work, only this worked.

Depending on your implementation you need to take care of view index inside its parent

Upvotes: 1

Jakub S.
Jakub S.

Reputation: 6080

My problem was fixed by seting layout_width to some specific dp.

So changing from "wrap content" or "match parent" to

android:layout_width="300dp"

will fix it, but i know it's not solution for all cases. But maybe you have some parent width, so you can apply the width to the textview. height leave with wrap_content, and it will work.

Upvotes: 0

Jairo Correa
Jairo Correa

Reputation: 636

A simple way to update the size of a View with WRAP_CONTENT is change the visibility to GONE and back to the old visibility.

int visibility = view.getVisibility();
view.setVisibility(View.GONE);
view.setVisibility(visibility);

TESTED ON ANDROID JELLY BEAN IN 2014

MAY NOT WORK ON NEWER ANDROID VERSIONS

Upvotes: 9

kc ochibili
kc ochibili

Reputation: 3131

Set the layout parameters again(width and height), right after adding the view. This worked for me.

if the parent View is a FrameLayout, then do something like this:

    ImageView view = (ImageView) LayoutInflater.from(activity).inflate(R.layout.image_object_view, null);

    imageObjectsHolder.addView(view);
    FrameLayout.LayoutParams param = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    view.setLayoutParams(param);

Upvotes: 0

Md. Taiful Islam
Md. Taiful Islam

Reputation: 51

You should use NestedScrollView Instead of simple scrollview. here is my sample Activity Layout code

<?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:background="#F0ECE6"
    >

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />


        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabMode="fixed"
            app:tabIndicatorHeight="6dp"
            android:layout_marginTop="-10dp"
            app:tabGravity="fill"/>
    </android.support.design.widget.AppBarLayout>

    <wsit.rentguru.utility.CustomViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"  />
</android.support.design.widget.CoordinatorLayout>

here is code for custom viewpager

public class CustomViewPager extends ViewPager {

        private boolean enabled;

        public CustomViewPager(Context context, AttributeSet attrs) {
            super(context, attrs);
            this.enabled = false;
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (this.enabled) {
                return super.onTouchEvent(event);
            }

            return false;
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            if (this.enabled) {
                return super.onInterceptTouchEvent(event);
            }

            return false;
        }

        public void setPagingEnabled(boolean enabled) {
            this.enabled = enabled;
        }
    }

setup function for viewpager in the activity

private void setupViewPager(ViewPager viewPager) {
        ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
        adapter.addFragment(new SampleFragment(), " ");

        viewPager.setAdapter(adapter);
    }

class ViewPagerAdapter extends FragmentPagerAdapter {
        private final List<Fragment> mFragmentList = new ArrayList<>();
        private final List<String> mFragmentTitleList = new ArrayList<>();

        public ViewPagerAdapter(FragmentManager manager) {
            super(manager);
        }

        @Override
        public Fragment getItem(int position) {
            return mFragmentList.get(position);
        }

        @Override
        public int getCount() {
            return mFragmentList.size();
        }

        public void addFragment(Fragment fragment, String title) {
            mFragmentList.add(fragment);
            mFragmentTitleList.add(title);
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return mFragmentTitleList.get(position);
        }
    }

here is the sample SampleFragment layout code

<android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#F0ECE6"
    android:fillViewport="true"
    android:scrollbars="vertical"
    android:animateLayoutChanges="true"
    xmlns:android="http://schemas.android.com/apk/res/android">

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#F0ECE6"
    android:focusableInTouchMode="true"
   >

    <Spinner
        android:id="@+id/product_category"
        android:layout_margin="20dp"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="@drawable/edittext_rectangle_box"
        android:gravity="center|left"
        android:textSize="14sp"
        android:paddingLeft="10dp"
        android:drawableRight="@drawable/ic_down_arrow"
        />

    <Spinner
        android:id="@+id/product_sub_category"
        android:layout_below="@+id/product_category"
        android:layout_marginRight="20dp"
        android:layout_marginLeft="20dp"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:gravity="center|left"
        android:visibility="gone"
        android:paddingLeft="10dp"
        android:background="@android:color/white"
        android:drawableRight="@drawable/ic_down_arrow"
        />
    <EditText
        android:id="@+id/product_title"
        android:layout_below="@+id/product_sub_category"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="15dp"
        android:background="@android:color/white"
        android:hint="PRODUCT TITLE"
        android:singleLine="true"
        android:imeOptions="actionDone"
        android:layout_width="match_parent"
        android:gravity="center|left"
        android:padding="10dp"
        android:textSize="14sp"
        android:textColorHint="#000000"
        android:layout_height="40dp" />

    <LinearLayout
        android:id="@+id/availability_layout"
        android:layout_below="@+id/product_title"
        android:layout_marginRight="20dp"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/availability"
            android:textStyle="bold"
            android:paddingBottom="10dp"
            android:textSize="14sp"
            />

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:weightSum="2">

            <Button
                android:id="@+id/from"
                android:background="@android:color/white"
                android:hint="FROM"
                android:layout_weight="1"
                android:gravity="center|left"
                android:padding="10dp"
                android:textSize="14sp"
                android:textStyle="normal"
                android:textColorHint="#000000"
                android:layout_width="0dp"
                android:layout_height="40dp"
                android:layout_marginRight="10dp"/>

            <Button
                android:id="@+id/to"
                android:background="@android:color/white"
                android:hint="TO"
                android:layout_weight="1"
                android:gravity="center|left"
                android:padding="10dp"
                android:textSize="14sp"
                android:textColorHint="#000000"
                android:layout_width="0dp"
                android:layout_height="40dp"
                android:layout_marginLeft="10dp"/>



        </LinearLayout>


    </LinearLayout>


    <LinearLayout
        android:id="@+id/product_location_layout"
        android:layout_below="@+id/availability_layout"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/product_location"
            android:textStyle="bold"
            android:paddingBottom="10dp"
            android:textSize="14sp"
            />


        <Spinner
            android:id="@+id/state_spinner"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:background="@drawable/edittext_rectangle_box"
            android:gravity="center|left"
            android:textSize="14sp"
            android:layout_marginBottom="10dp"
            android:drawableRight="@drawable/ic_down_arrow"
            android:paddingLeft="10dp"
            />

        <EditText
            android:id="@+id/area"
            android:background="@android:color/white"
            android:hint="Area"
            android:gravity="center|left"
            android:padding="10dp"
            android:textSize="14sp"
            android:textColorHint="#000000"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:singleLine="true"
            android:imeOptions="actionNext"
            android:layout_marginBottom="10dp"
            />


        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:weightSum="2">

            <EditText
                android:id="@+id/zipCode"
                android:background="@android:color/white"
                android:hint="Zip Code"
                android:layout_weight="1"
                android:gravity="center|left"
                android:padding="10dp"
                android:textSize="14sp"
                android:textColorHint="#000000"
                android:layout_width="0dp"
                android:layout_height="40dp"
                android:singleLine="true"
                android:imeOptions="actionNext"
                android:layout_marginRight="10dp"/>

            <EditText
                android:id="@+id/city"
                android:background="@android:color/white"
                android:hint="City"
                android:layout_weight="1"
                android:gravity="center|left"
                android:padding="10dp"
                android:textSize="14sp"
                android:singleLine="true"
                android:imeOptions="actionDone"
                android:textColorHint="#000000"
                android:layout_width="0dp"
                android:layout_height="40dp"
                android:layout_marginLeft="10dp"/>



        </LinearLayout>


    </LinearLayout>

    <Button
        android:id="@+id/tab1_next"
        android:layout_width="150dp"
        android:layout_height="40dp"
        android:text="NEXT"
        android:layout_below="@+id/product_location_layout"
        android:layout_margin="20dp"
        android:layout_alignParentRight="true"
        android:background="@color/next_button"
        android:textColor="@android:color/white"
        android:layout_marginBottom="20dp"
        />

</RelativeLayout>

</android.support.v4.widget.NestedScrollView>

Upvotes: 0

elliotbay
elliotbay

Reputation: 337

Try calling requestLayout on the children.

I recently had a similar problem and was similarly frustrated that things like invalidate and requestLayout seemed to do nothing. What I didn't understand is that requestLayout doesn't propagate down to its children; it propagates up to its parents. To re-measure something that was previously measured, I had to call requestLayout on the View that changed rather than the View I actually wanted to resize.

Upvotes: 22

Eric
Eric

Reputation: 805

Android does NOT refresh layout of views with "wrap_content" once it has been displayed.

So if you add a child view, or modify the content dynamically, you're screwed. I do agree that this is a nightmare and a real flaw in Android UI!

To solve that, I've written a static class that recomputes the sizes and forces the update of the layout for the views with "wrap_content"

The code and instructions to use are available here:

https://github.com/ea167/android-layout-wrap-content-updater

Enjoy!

Upvotes: 15

brainfree
brainfree

Reputation: 827

Ok, I solved this by manually measuring the RelativeLayout immediately after adding all the views and setting the mainLayoutParams height explicitly. I wish I was smarter and knew why it wasn't automatically doing this correctly in the first place, but oh well.

    ...
    mainLayout.measure(0, 0);

    ViewGroup.LayoutParams mainLayoutParams = mainLayout.getLayoutParams();
    mainLayoutParams.height = mainLayout.getMeasuredHeight() + 10;
    ...

Upvotes: 2

Barry Connolly
Barry Connolly

Reputation: 663

You are getting this problem because you set your layout first and then add its content dynamically.

You are telling the layout to wrap to the content that is not yet their. Try using your layout inflater after you have grabbed your content

Upvotes: 0

Related Questions