开发者

Inflating Views and adding them to a LinearLayout within a HorizontalScrollView - Odd Behaviour

开发者 https://www.devze.com 2023-02-03 18:28 出处:网络
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.

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.

On the Nexus One this generally seems to work fine, however on the G1 I get some odd behaviour.

So the Layout File for the Activity 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">

 <include android:layout_width="fill_parent"
  android:layout_height="wrap_content" android:id="@+id/ActionBarInclude"
  layout="@layout/action_bar"></include>

  <LinearLayout android:layout_width="fill_parent"
   android:layout_height="fill_parent" android:orientation="vertical"
   android:background="@drawable/action_bar_drop_shadow">

   <TextView android:layout_height="wrap_content"
    android:layout_width="fill_parent" android:gravity="center"
    android:id="@+id/AccountSummaryAppTitleTextView" android:text=""
    style="@style/titleTextView"></TextView>

   <co.uk.gauntface.android.admob.views.TabBarScrollView
    android:id="@+id/AccountSummaryTabBarScrollView"
    android:layout_width="fill_parent" android:layout_height="wrap_content"
    android:scrollbars="none">

   </co.uk.gauntface.android.admob.views.TabBarScrollView>

   <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">
            </co.uk.gauntface.android.admob.views.SlidingLayout>

  </LinearLayout>

</LinearLayout>

Now the reason I've set FillViewPort to true is because of a similar issue here: LinearLayout not expanding inside a ScrollView although this hasn't fixed my issue.

The source code for the Activity is:

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<SiteStat> 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<SiteStat>) 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<SiteStat>) 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;
  }
 }
}

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.

The Source code for the SlidingLayout is:

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 >= 0 && whichScreen < 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 < 0) {
    mNewSelectedPosition = 0;
   } else if(mNewSelectedPosition >= 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 < 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();
  }
 }
}

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).

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.

The log on android shows no errors what so ever.

I'm at a loss as to what it could be / what I can do to force the layouts to update / inflate.

Any ideas / suggestions would be very welcome.

Cheers, Matt

p.s. congratulations if you made it this far in the post :)


The solution in the end was to measure the child views and pass in a measure spec forcing them to be an exact width / height:

v.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));

The original code can be found on github https://github.com/gauntface/SideSwipeSnapViewLibrary

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号