天天看點

【Android】完美解決HorizontalScrollView和SwipeRefreshLayout放在一起報空指針的問題

這類問題如果細化到ViewPager和SwipeRefreshLayout方面,解決方法詳見http://blog.csdn.net/u010386612/article/details/50548977

一般來說,HorizontalScrollView會選用一個Adapter,而且一般會用自定義的Adapter

我這裡用的是我自己定義的HorizontalScrollAdapter

public abstract class BaseHorizontalAdapter
{
	private HorizontalScrollMenu mHorizontalScrollMenu;
	
	public abstract List<String> getMenuItems();

	public abstract List<View> getContentViews();
	
	public abstract void onPageChanged(int position,boolean visitStatus);
	
	public void setHorizontalScrollMenu(HorizontalScrollMenu horizontalScrollMenu)
	{
		mHorizontalScrollMenu=horizontalScrollMenu;
	}
	
	public void notifyDataSetChanged()
	{
		mHorizontalScrollMenu.notifyDataSetChanged(this);
	}
}
           

自定義的HorizontalScrollviewMenu,附上代碼

public class HorizontalScrollMenu extends LinearLayout
{
	private BaseHorizontalAdapter mAdapter;
	private RadioGroup rg_items;
	private List<RadioButton> rb_items = new ArrayList<RadioButton>();
	private HorizontalViewPager vp_content;
	private Context mContext;
	private ColorStateList mColors;
	private int mBackgroundResId;
	private int mPaddingLeft = 20;
	private int mPaddingTop = 20;
	private int mPaddingRight = 20;
	private int mPaddingBottom = 20;
	private HorizontalScrollView hsv_menu;
	private boolean[] mVisitStatus; // 菜單通路狀态
	private List<String> mItems; // 菜單名
	private List<View> mPagers; // 内容頁
	private boolean mSwiped = true; // 是否可滑動

	public HorizontalScrollMenu(Context context)
	{
		this(context, null);
		// TODO Auto-generated constructor stub
	}

	public HorizontalScrollMenu(Context context, AttributeSet attrs)
	{
		this(context, attrs, 0);
		// TODO Auto-generated constructor stub
	}

	public HorizontalScrollMenu(Context context, AttributeSet attrs,
			int defStyle)
	{
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
		mContext = context;
		View v = LayoutInflater.from(context).inflate(
				R.layout.view_horizontal_scroll_menu, this, true);
		rg_items = (RadioGroup) v.findViewById(R.id.rg_items);
		vp_content = (HorizontalViewPager) v.findViewById(R.id.vp_content);
		
		mColors = getResources().getColorStateList(
				R.drawable.selector_menu_item_text);
		hsv_menu = (HorizontalScrollView) v.findViewById(R.id.hsv_menu);
		mBackgroundResId = R.drawable.bg_rb_checked;
	}

	public void setAdapter(BaseHorizontalAdapter adapter)
	{
		if (null != adapter)
		{
			adapter.setHorizontalScrollMenu(this);
			mAdapter = adapter;
			initView(adapter);
		}
	}

	/**
	 * 初始化視圖
	 * 
	 * @param adapter
	 */
	private void initView(BaseHorizontalAdapter adapter)
	{
		if (null == adapter)
		{
			return;
		}
		mItems = mAdapter.getMenuItems();
		mVisitStatus = new boolean[mItems.size()];
		initMenuItems(mItems);
		mPagers = mAdapter.getContentViews();
		initContentViews(mPagers);
	}

	/**
	 * 當資料集改變通知視圖重繪
	 * 
	 * @param adapter
	 */
	public void notifyDataSetChanged(BaseHorizontalAdapter adapter)
	{
		rg_items.removeAllViews();
		rb_items.clear();
		initView(adapter);
	}

	/**
	 * 初始化菜單項
	 * 
	 * @param items
	 */
	@SuppressLint("InflateParams")
	private void initMenuItems(List<String> items)
	{
		if (null != items && 0 != items.size())
		{
			rg_items.setOnCheckedChangeListener(mItemListener);
			for (String str : items)
			{
				RadioButton rb_item = (RadioButton) LayoutInflater.from(
						mContext).inflate(R.layout.view_horizontal_scroll_menu_item, null);
				rb_item.setTextColor(mColors);
				rb_item.setText(str);
				rb_item.setTextSize(16);
				rb_item.setGravity(Gravity.CENTER);
				rb_item.setPadding(mPaddingLeft, mPaddingTop, mPaddingRight,
						mPaddingBottom);
				rg_items.addView(rb_item);
				rb_items.add(rb_item);
			}
			rb_items.get(0).setChecked(true);
		}

	}

	/**
	 * 初始化内容
	 * 
	 * @param contentViews
	 */
	private void initContentViews(List<View> contentViews)
	{
		if (null == contentViews || 0 == contentViews.size())
		{
			return;
		}
		Log.i("list", ""+contentViews.size());
		vp_content.setAdapter(new MyViewPagerAdapter(contentViews));
		vp_content.setOnPageChangeListener(mPageListener);
	}

	/**
	 * 設定顔色變化清單
	 * 
	 * @param colorListId
	 */
	public void setColorList(int colorListId)
	{
		mColors = getResources().getColorStateList(colorListId);
	}

	/**
	 * 設定菜單項狀态背景
	 * 
	 * @param resId
	 */
	public void setCheckedBackground(int resId)
	{
		mBackgroundResId = resId;
	}

	/**
	 * 菜單項切換監聽器
	 */
	private OnCheckedChangeListener mItemListener = new OnCheckedChangeListener()
	{

		@Override
		public void onCheckedChanged(RadioGroup group, int checkedId)
		{
			// TODO Auto-generated method stub
			RadioButton btn = (RadioButton) group.findViewById(checkedId);
			setMenuItemsNullBackground();
			btn.setBackgroundResource(mBackgroundResId);
			btn.setPadding(mPaddingLeft, mPaddingTop, mPaddingRight,
					mPaddingBottom);
			int position = 0;
			for (int i = 0; i < rb_items.size(); i++)
			{
				if (rb_items.get(i) == btn)
				{
					position = i;
				}
			}
			vp_content.setCurrentItem(position, mSwiped);
			moveItemToCenter(btn);
			if (mVisitStatus.length >= position + 1) {
				mAdapter.onPageChanged(position, mVisitStatus[position]);
				mVisitStatus[position] = true;
			}
		}

	};

	/**
	 * 内容頁切換監聽器
	 */
	private OnPageChangeListener mPageListener = new OnPageChangeListener()
	{

		@Override
		public void onPageSelected(int arg0)
		{
			// TODO Auto-generated method stub
			rb_items.get(arg0).setChecked(true);
		}

		@Override
		public void onPageScrolled(int arg0, float arg1, int arg2)
		{
			// TODO Auto-generated method stub

		}

		@Override
		public void onPageScrollStateChanged(int state)
		{

		}
	};

	/**
	 * 将菜單項盡量移至中央位置
	 * 
	 * @param rb
	 */
	private void moveItemToCenter(RadioButton rb)
	{
		DisplayMetrics dm = getResources().getDisplayMetrics();
		int screenWidth = dm.widthPixels;
		int[] locations = new int[2];
		rb.getLocationInWindow(locations);
		int rbWidth = rb.getWidth();
		hsv_menu.smoothScrollBy((locations[0] + rbWidth / 2 - screenWidth / 2),
				0);
	}

	/**
	 * 設定所有菜單項的背景為空
	 */
	private void setMenuItemsNullBackground()
	{
		if (null != rg_items)
			for (int i = 0; i < rg_items.getChildCount(); i++)
			{
				View v = rg_items.getChildAt(i);
				v.setBackgroundResource(android.R.color.transparent);
			}
	}

	public void setMenuItemPaddingLeft(int paddingLeft)
	{
		mPaddingLeft = paddingLeft;
	}

	public void setMenuItemPaddingTop(int paddingTop)
	{
		mPaddingTop = paddingTop;
	}

	public void setMenuItemPaddingRight(int paddingRight)
	{
		mPaddingRight = paddingRight;
	}

	public void setMenuItemPaddingBottom(int paddingBottom)
	{
		mPaddingBottom = paddingBottom;
	}

	/**
	 * 視圖頁的擴充卡
	 * 
	 * @author Administrator
	 * 
	 */
	static class MyViewPagerAdapter extends PagerAdapter
	{
		private List<View> mViews;

		public MyViewPagerAdapter(List<View> views)
		{
			// TODO Auto-generated constructor stub
			mViews = views;
		}

		@Override
		public int getCount()
		{
			// TODO Auto-generated method stub
			return mViews.size();
		}

		@Override
		public boolean isViewFromObject(View arg0, Object arg1)
		{
			// TODO Auto-generated method stub
			return arg0 == arg1;
		}

		@Override
		public void destroyItem(ViewGroup container, int position, Object object)
		{
			// TODO Auto-generated method stub
			container.removeView(mViews.get(position));
		}

		@Override
		public Object instantiateItem(ViewGroup container, int position)
		{
			// TODO Auto-generated method stub
			container.addView(mViews.get(position));
			return mViews.get(position);
		}
	}

	public void setSwiped(boolean swiped)
	{
		mSwiped = swiped;
		vp_content.setSwiped(swiped);
	}

}
           

顯然,onPagedChanged方法用于在HorizontalScrollMenu界面切換的時候調用。一般來講,為了美觀,會在切換(也就是onPageChanged)的時候設定SwipeRefreshLayout的小圓圈開始加載。但是事實上會報空指針,而且會發現空指針居然報在HorizontalMenu中調用Adapter處。

事實上問題所在就是滑動沖突。如果想要開始加載,常用的辦法都是新開一個線程然後設定開始加載

//開始重新整理動畫
swipeRefreshLayout.post(new Runnable() {
					
	@Override
	public void run() {
		swipeRefreshLayout.setRefreshing(true);
		onRefreshListener.onRefresh();
	}
});
           

解決方法:在onPageChanged中使用handler發消息,然後在接收消息之後開始重新整理動畫。如果在onPageChanged中重新整理動畫,會因為滑動沖突報空指針

(注:事實上,在界面初始化的時候,也會調用一次onPageChanged)