title: Inflating Views and adding them to a LinearLayout within a HorizontalScrollView - Odd Behaviour
body: <p>I'm trying to create a class that can be added to a Layout file, have an adapter set and then update from a DataSetObserver.</p>
  
  <p>On the Nexus One this generally seems to work fine, however on the G1 I get some odd behaviour.</p>
  
  <p>So the Layout File for the Activity is:</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical" android:layout_width="fill_parent"
   android:layout_height="fill_parent"&gt;
  
   &lt;include android:layout_width="fill_parent"
    android:layout_height="wrap_content" android:id="@+id/ActionBarInclude"
    layout="@layout/action_bar"&gt;&lt;/include&gt;
  
    &lt;LinearLayout android:layout_width="fill_parent"
     android:layout_height="fill_parent" android:orientation="vertical"
     android:background="@drawable/action_bar_drop_shadow"&gt;
  
     &lt;TextView android:layout_height="wrap_content"
      android:layout_width="fill_parent" android:gravity="center"
      android:id="@+id/AccountSummaryAppTitleTextView" android:text=""
      style="@style/titleTextView"&gt;&lt;/TextView&gt;
  
     &lt;co.uk.gauntface.android.admob.views.TabBarScrollView
      android:id="@+id/AccountSummaryTabBarScrollView"
      android:layout_width="fill_parent" android:layout_height="wrap_content"
      android:scrollbars="none"&gt;
  
     &lt;/co.uk.gauntface.android.admob.views.TabBarScrollView&gt;
  
     &lt;co.uk.gauntface.android.admob.views.SlidingLayout android:layout_width="fill_parent"
      android:id="@+id/AccountSummarySlidingLayout" android:layout_weight="1"
      android:layout_height="wrap_content" android:scrollbars="none" android:fadingEdge="none"
      android:fillViewport="true"&gt;
              &lt;/co.uk.gauntface.android.admob.views.SlidingLayout&gt;
  
    &lt;/LinearLayout&gt;
  
  &lt;/LinearLayout&gt;
  </code></pre>
  
  <p>Now the reason I've set FillViewPort to true is because of a similar issue here: <a href="https://stackoverflow.com/questions/2599837/linearlayout-not-expanding-inside-a-scrollview">LinearLayout not expanding inside a ScrollView</a>
  although this hasn't fixed my issue.</p>
  
  <p>The source code for the Activity is:</p>
  
  <pre><code>package co.uk.gauntface.android.admob;
  
  import java.util.ArrayList;
  import co.uk.gauntface.android.admob.models.RawSite;
  import co.uk.gauntface.android.admob.models.SiteStat;
  import co.uk.gauntface.android.admob.networking.AdmobDownloadListener;
  import co.uk.gauntface.android.admob.networking.SiteStatService;
  import co.uk.gauntface.android.admob.views.AccountSummaryAdapter;
  import co.uk.gauntface.android.admob.views.SlidingLayout;
  import co.uk.gauntface.android.admob.views.TabBarScrollView;
  import android.os.Bundle;
  import android.os.Handler;
  import android.os.Message;
  import android.util.Log;
  import android.view.View;
  import android.widget.TextView;
  
  public class AccountSummary extends AdmobActivity
  { 
   private final static String TAG = "admob";
  
   public static final String ACCOUNT_SUMMARY_INTENT_EXTRA_ID = "AccountSummaryIntentExtraID";
  
   private String mAccountID;
   private RawSite mRawSiteData;
  
   private Handler mHandler;
   private SiteStatService mSiteStatService;
   private ArrayList&lt;SiteStat&gt; mSiteStatData;
  
   private ActionBarHelper mActionBarHelper;
  
   private TextView mAppTitleTextView;
   private TabBarScrollView mTabBarScrollView;
   private SlidingLayout mSlidingLayout;
   private AccountSummaryAdapter mAdapter;
  
   /** Called when the activity is first created. */
   public void onCreate(Bundle savedInstanceState)
   {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.account_summary);
  
    mAccountID = getIntent().getExtras().getString(ACCOUNT_SUMMARY_INTENT_EXTRA_ID);
    mSiteStatService = new SiteStatService(getApplicationContext());
    //mChartAdapter = new PointPositionAdapter(getApplicationContext());
  
    initActivity();
  
    setUpUI();
  
    if(getLastNonConfigurationInstance() != null) {
     mSiteStatData = (ArrayList&lt;SiteStat&gt;) getLastNonConfigurationInstance();
     mAdapter.setSiteStatData(mSiteStatData);
    } else {
     updateSiteStatData();
    }
   }
  
   /**public void onConfigurationChanged (Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    setContentView(R.layout.account_summary);
  
    setUpUI();
   }**/
  
   @Override
   public Object onRetainNonConfigurationInstance() {
    return mSiteStatData;
   }
  
   private void initActivity() {
    mHandler = new Handler() {
     public void handleMessage(Message msg) {
      switch(msg.arg1) {
      default:
       break;
      }
     }
    };
  
    mAdapter = new AccountSummaryAdapter(getApplicationContext(), new Integer[]{
     R.string.revenue,
     R.string.ecpm,
     R.string.requests,
     R.string.impressions,
     R.string.fill_rate,
     R.string.ctr
    });
  
    DatabaseManager dbManager = new DatabaseManager(getApplicationContext());
    dbManager.open();
    mRawSiteData = dbManager.getSite(mAccountID);
    dbManager.close();
  
   }
  
   private void setUpUI() {
    mActionBarHelper = new ActionBarHelper(this);
  
    mAppTitleTextView = (TextView) findViewById(R.id.AccountSummaryAppTitleTextView);
    mAppTitleTextView.setText(mRawSiteData.getSiteName());
  
    mTabBarScrollView = (TabBarScrollView) findViewById(R.id.AccountSummaryTabBarScrollView);
    mTabBarScrollView.setAdapter(mAdapter.getTabBarAdapter());
    mTabBarScrollView.setOnItemOnClickListener(mAdapter);
  
    mSlidingLayout = (SlidingLayout) findViewById(R.id.AccountSummarySlidingLayout);
    mSlidingLayout.setAdapter(mAdapter.getSlidingLayoutAdapter());
    mSlidingLayout.setOnDisplayChangeListener(mAdapter);
   }
  
   private void updateSiteStatData() {
    mActionBarHelper.incrementRequests();
    int daysToGet = getResources().getInteger(R.integer.account_summary_days);
    mSiteStatService.downloadDailySiteStats(daysToGet, mRawSiteData.getSiteID(), new AdmobDownloadListener() {
  
     public void onDownloadSuccess(Object obj) {
      mActionBarHelper.decrementRequests();
      mSiteStatData = (ArrayList&lt;SiteStat&gt;) obj;
  
      //mAdapter.setSiteStatData(mSiteStatData);
     }
  
     public void onDownloadError(int errorCode, String errorMsg) {
      mActionBarHelper.decrementRequests();
      displayErrorMessage(errorCode, errorMsg);
     }
    });
   }
  
   public void onClick(View v) {
    switch(v.getId())
    {
    case R.id.ActionBarRefreshImageButton:
     updateSiteStatData();
     break;
    }
   }
  }
  </code></pre>
  
  <p>Which will display the views fine, however the SlidingLayout will actually appear blank (i.e it will not inflate the views contained in the LinearLayout.</p>
  
  <p>The Source code for the SlidingLayout is:</p>
  
  <pre><code>package co.uk.gauntface.android.admob.views;
  
  import co.uk.gauntface.android.admob.R;
  import android.content.Context;
  import android.database.DataSetObserver;
  import android.util.AttributeSet;
  import android.util.Log;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.ViewConfiguration;
  import android.widget.Adapter;
  import android.widget.HorizontalScrollView;
  import android.widget.LinearLayout;
  
  public class SlidingLayout extends HorizontalScrollView {
  
   private static final String TAG = "admob";
  
   private int mSelectedPosition;
   private int mNewSelectedPosition;
   private LinearLayout mContainerLinearLayout;
  
   private int mScaledTouchSlop;
  
   private float mPrevX;
  
   private int mItemCount;
   private boolean mIsTouchEventScroll;
  
   private SlidingLayoutAdapter mAdapter;
   private DataSetObserver mDataSetObserver;
   private OnDisplayChangeListener mDisplayChangeListener;
  
   public SlidingLayout(Context context) {
    super(context);
  
    constructor();
   }
  
   public SlidingLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
  
    constructor();
   }
  
   public SlidingLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
  
    constructor();
   }
  
   private void constructor() {
    Log.v(TAG, "SlidingLayout: constructor()");
    mSelectedPosition = 0;
    mNewSelectedPosition = 0;
  
    mItemCount = 0;
  
    mScaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
   }
  
   @Override
   public boolean onTouchEvent(MotionEvent event) {
    Log.v(TAG, "SlidingLayout: onTouchEvent()");
    int action = event.getAction();
  
    switch (action) {
    case MotionEvent.ACTION_DOWN:
     break;
    case MotionEvent.ACTION_MOVE:
     int deltaX = (int) (mPrevX - event.getX());
     scrollBy(deltaX, 0);
     break;
    case MotionEvent.ACTION_UP:
     animateToPosition();
    }
  
    mPrevX = event.getX();
  
    return true;
   }
  
   private void animateToPosition() {
    Log.v(TAG, "SlidingLayout: animateToPosition()");
    double scrollX = getScrollX();
    int screenWidth = getWidth();
  
    double ratio = scrollX / (double) screenWidth;
    int whichScreen = (int) Math.round(ratio);
  
    if(whichScreen &gt;= 0 &amp;&amp; whichScreen &lt; mItemCount) {
     int newX = whichScreen * screenWidth;
     //int screenDiff = whichScreen - mDisplayedView;
     int screenDiff = whichScreen - mSelectedPosition;
     switch(screenDiff) {
     case -1:
      mNewSelectedPosition = mSelectedPosition - 1;
      break;
     case 0:
      mNewSelectedPosition = mSelectedPosition;
      break;
     case 1:
      mNewSelectedPosition = mSelectedPosition + 1;
      break;
     }
  
     if(mNewSelectedPosition &lt; 0) {
      mNewSelectedPosition = 0;
     } else if(mNewSelectedPosition &gt;= mItemCount) {
      mNewSelectedPosition = mItemCount - 1;
     }
  
     mIsTouchEventScroll = true;
     smoothScrollTo(newX, 0);
    }
   }
  
   public void setOnDisplayChangeListener(OnDisplayChangeListener changeListener) {
    mDisplayChangeListener = changeListener;
   }
  
   public void setAdapter(SlidingLayoutAdapter adapter) {
    Log.v(TAG, "SlidingLayout: setAdapter()");
    if(mContainerLinearLayout == null) {
     mContainerLinearLayout = new LinearLayout(getContext());
     mContainerLinearLayout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
     mContainerLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
     mContainerLinearLayout.setBackgroundColor(getContext().getResources().getColor(R.color.admob_color));
  
     addView(mContainerLinearLayout);
    }
  
    if(mAdapter != null) {
     mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }
  
    mAdapter = adapter;
  
    if(mAdapter != null) {
     mDataSetObserver = new SlidingLayoutDataSetObserver();
     mAdapter.registerDataSetObserver(mDataSetObserver);
  
     mItemCount = mAdapter.getCount();
     mSelectedPosition = mAdapter.getSelectedPosition();
    } else {
     mItemCount = 0;
     mSelectedPosition = 0;
  
     return;
    }
  
    populateLayout();
   }
  
   // l is horizontal scroll origin, t is vertical
   /**protected void onScrollChanged (int l, int t, int oldl, int oldt) {
    super.onScrollChanged(l, t, oldl, oldt);
    if(mIsTouchEventScroll) {
     if(l % getWidth() == 0) {
      mIsTouchEventScroll = false;
      mSelectedPosition = mNewSelectedPosition;
      if(mDisplayChangeListener != null) {
       mDisplayChangeListener.onDisplayChange(l/getWidth());
      }
     }
    }
   }**/
  
   private void populateLayout() {
    Log.v(TAG, "SlidingLayout: populateLayout() - itemCount = "+mItemCount);
    if(mContainerLinearLayout != null) {
     mContainerLinearLayout.removeAllViews();
    }
  
    for(int i = 0; i &lt; mItemCount; i++) {
     View v = mAdapter.getView(i, null, null);
     v.setMinimumWidth(getWidth());
     mContainerLinearLayout.addView(v);
    }
  
    invalidate();
  
    scrollTo(mSelectedPosition * getWidth(), 0);
  
    //View childView = mContainerLinearLayout.getChildAt(mSelectedPosition);
    //mContainerLinearLayout.invalidate();
    //childView.invalidate();
    Log.v(TAG, "SlidingLayout: populateLayout() scrollX - "+getScrollX());
    //Log.v(TAG, "SlidingLayout: populateLayout() childView.width() - "+childView.getWidth());
    Log.v(TAG, "SlidingLayout: populateLayout() getWidth() - "+getWidth());
    Log.v(TAG, "SlidingLayout: populateLayout() getHeight() - "+getHeight());
    //Log.v(TAG, "SlidingLayout: populateLayout() container.getWidth() - "+mContainerLinearLayout.getWidth());
    //Log.v(TAG, "SlidingLayout: populateLayout() container.getHeight() - "+mContainerLinearLayout.getHeight());
   }
  
   protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    if(mAdapter != null) {
     mItemCount = mAdapter.getCount();
     mSelectedPosition = mAdapter.getSelectedPosition();
    }
    populateLayout();
   }
  
   public interface OnDisplayChangeListener {
    abstract void onDisplayChange(int position);
   }
  
   public interface SlidingLayoutAdapter extends Adapter {
    abstract int getSelectedPosition();
   }
  
   public class SlidingLayoutDataSetObserver extends DataSetObserver {
    public SlidingLayoutDataSetObserver() {
     super();
    }
  
    @Override
    public void onChanged() {
     Log.v(TAG, "SlidingLayout: onChanged()");
     mItemCount = mAdapter.getCount();
     mSelectedPosition = mAdapter.getSelectedPosition();
     populateLayout();
    }
  
    @Override
    public void onInvalidated() {
     // Data is invalid so we should reset our state
     //mItemCount = 0;
     //mSelectedPosition = 0;//mAdapter.getSelectedPosition();
     //Log.v(TAG, "SlidingLayout: onInvalidated() removeAllViews");
     //mContainerLinearLayout.removeAllViews();
     //invalidate();
     //populateLayout();
     //requestLayout();
     Log.v(TAG, "SlidingLayout: onInvalidated()");
  
     mItemCount = mAdapter.getCount();
     mSelectedPosition = mAdapter.getSelectedPosition();
     populateLayout();
    }
   }
  }
  </code></pre>
  
  <p>Now what actually happens is the LinearLayout (Colored red) fills the entire view, but any calls to getWidth() and getHeight() on the LinearLayout return 0 (odd enough in it's own right).</p>
  
  <p>But if I touch a separate HorizontalScrollView (the mTabBarScrollView in the main activity), the SlidingLayout will actually resize / redraw / layout itself out. The same also occurs  when a web service updates the adapter with data, which calls notifyDataSetChanged() (causing the resize / redraw / layout), however calling notifydatasetchanged() in the onCreate method of the activity doesn't have the same effect. </p>
  
  <p>The log on android shows no errors what so ever.</p>
  
  <p>I'm at a loss as to what it could be / what I can do to force the layouts to update / inflate.</p>
  
  <p>Any ideas / suggestions would be very welcome.</p>
  
  <p>Cheers,
  Matt</p>
  
  <p>p.s. congratulations if you made it this far in the post :)</p>