一:动态添加View出现的问题
最近遇到一个很让人头疼的问题,使用viewpager动态添加页面或者删除页面时出现了问题(java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first),在stackoverflow上找到了解决办法。(http://stackoverflow.com/questions/22936886/java-lang-illegalstateexception-while-using-viewpager-in-android)
原文是:
the problem is that in your adapters method instantiateItem
you call container.addView(v);
but every View
can have only one parent,
so it can be added only one time to a container via addView(...)
.
When you open the popup the first time, everything works, because v
doesn't have a parent that time. But when you open your popupwinow the second time,
it adds the view again to the container. That cerates the error.
Try to destroy the view if you close the popup view or remove all children views from it with
container.removeAllViews()
解决办法是在instantiateItem中使用如下方式:
ViewGroup parent = (ViewGroup) v.getParent();
if (parent != null) {
parent.removeAllViews();
}
container.addView(v);中间很多次尝试已经接近答案,但是习惯性的去把v.getParent()强制转化为view,view没有removeView()方法,以至于放弃了这种方法,以后要多思考。
二:ViewPager 实现 “轮播”图片
- private class ImageAdapter extends PagerAdapter{
- private ArrayList<ImageView> viewlist;
- public ImageAdapter(ArrayList<ImageView> viewlist) {
- this.viewlist = viewlist;
- }
- @Override
- public int getCount() {
- //设置成最大,使用户看不到边界
- return Integer.MAX_VALUE;
- }
- @Override
- public boolean isViewFromObject(View arg0, Object arg1) {
- return arg0==arg1;
- }
- @Override
- public void destroyItem(ViewGroup container, int position,
- Object object) {
- //Warning:不要在这里调用removeView
- }
- @Override
- public Object instantiateItem(ViewGroup container, int position) {
- //对ViewPager页号求模取出View列表中要显示的项
- position %= viewlist.size();
- if (position<0){
- position = viewlist.size()+position;
- }
- ImageView view = viewlist.get(position);
- //如果View已经在之前添加到了一个父组件,则必须先remove,否则会抛出IllegalStateException。
- ViewParent vp =view.getParent();
- if (vp!=null){
- ViewGroup parent = (ViewGroup)vp;
- parent.removeView(view);
- }
- container.addView(view);
- //add listeners here if necessary
- return view;
- }
- }
-
getCount() 方法的返回值:这个值直接关系到ViewPager的“边界”,因此当我们把它设置为Integer.MAX_VALUE之后,用户基本就看不到这个边界了(估计滑到这里的时候电池已经挂了吧o_O)。当然,通常情况下设置为100倍实际内容个数也是可以的,之前看的某个实现就是这么干的。
-
instantiateItem() 方法position的处理:由于我们设置了count为 Integer.MAX_VALUE,因此这个position的取值范围很大很大,但我们实际要显示的内容肯定没这么多(往往只有几项),所以这里肯定会有求模操作。但是,简单的求模会出现问题:考虑用户向左滑的情形,则position可能会出现负值。所以我们需要对负值再处理一次,使其落在正确的区间内。
-
instantiateItem() 方法父组件的处理:通常我们会直接addView,但这里如果直接这样写,则会抛出IllegalStateException。假设一共有三个view,则当用户滑到第四个的时候就会触发这个异常,原因是我们试图把一个有父组件的View添加到另一个组件。但是,如果直接写成下面这样:
- (ViewGroup)view.getParent().removeView(view);
-
destroyItem() 方法:由于我们在instantiateItem()方法中已经处理了remove的逻辑,因此这里并不需要处理。实际上,实验表明这里如果加上了remove的调用,则会出现ViewPager的内容为空的情况。
- private static class ImageHandler extends Handler{
- /**
- * 请求更新显示的View。
- */
- protected static final int MSG_UPDATE_IMAGE = 1;
- /**
- * 请求暂停轮播。
- */
- protected static final int MSG_KEEP_SILENT = 2;
- /**
- * 请求恢复轮播。
- */
- protected static final int MSG_BREAK_SILENT = 3;
- /**
- * 记录最新的页号,当用户手动滑动时需要记录新页号,否则会使轮播的页面出错。
- * 例如当前如果在第一页,本来准备播放的是第二页,而这时候用户滑动到了末页,
- * 则应该播放的是第一页,如果继续按照原来的第二页播放,则逻辑上有问题。
- */
- protected static final int MSG_PAGE_CHANGED = 4;
- //轮播间隔时间
- protected static final long MSG_DELAY = 3000;
- //使用弱引用避免Handler泄露.这里的泛型参数可以不是Activity,也可以是Fragment等
- private WeakReference<MainActivity> weakReference;
- private int currentItem = 0;
- protected ImageHandler(WeakReference<MainActivity> wk){
- weakReference = wk;
- }
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- Log.d(LOG_TAG, "receive message " + msg.what);
- MainActivity activity = weakReference.get();
- if (activity==null){
- //Activity已经回收,无需再处理UI了
- return ;
- }
- //检查消息队列并移除未发送的消息,这主要是避免在复杂环境下消息出现重复等问题。
- if (activity.handler.hasMessages(MSG_UPDATE_IMAGE)){
- activity.handler.removeMessages(MSG_UPDATE_IMAGE);
- }
- switch (msg.what) {
- case MSG_UPDATE_IMAGE:
- currentItem++;
- activity.viewPager.setCurrentItem(currentItem);
- //准备下次播放
- activity.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY);
- break;
- case MSG_KEEP_SILENT:
- //只要不发送消息就暂停了
- break;
- case MSG_BREAK_SILENT:
- activity.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY);
- break;
- case MSG_PAGE_CHANGED:
- //记录当前的页号,避免播放的时候页面显示不正确。
- currentItem = msg.arg1;
- break;
- default:
- break;
- }
- }
- }
- public class MainActivity extends Activity {
- private static final String LOG_TAG = "MainActivity";
- private ImageHandler handler = new ImageHandler(new WeakReference<MainActivity>(this));
- private ViewPager viewPager;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- //初始化iewPager的内容
- viewPager = (ViewPager) findViewById(R.id.main_viewpager);
- LayoutInflater inflater = LayoutInflater.from(this);
- ImageView view1 = (ImageView) inflater.inflate(R.layout.item, null);
- ImageView view2 = (ImageView) inflater.inflate(R.layout.item, null);
- ImageView view3 = (ImageView) inflater.inflate(R.layout.item, null);
- view1.setImageResource(R.drawable.ics);
- view2.setImageResource(R.drawable.jellybean);
- view3.setImageResource(R.drawable.kitkat);
- ArrayList<ImageView> views = new ArrayList<ImageView>();
- views.add(view1);
- views.add(view2);
- views.add(view3);
- viewPager.setAdapter(new ImageAdapter(views));
- viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
- //配合Adapter的currentItem字段进行设置。
- @Override
- public void onPageSelected(int arg0) {
- handler.sendMessage(Message.obtain(handler, ImageHandler.MSG_PAGE_CHANGED, arg0, 0));
- }
- @Override
- public void onPageScrolled(int arg0, float arg1, int arg2) {
- }
- //覆写该方法实现轮播效果的暂停和恢复
- @Override
- public void onPageScrollStateChanged(int arg0) {
- switch (arg0) {
- case ViewPager.SCROLL_STATE_DRAGGING:
- handler.sendEmptyMessage(ImageHandler.MSG_KEEP_SILENT);
- break;
- case ViewPager.SCROLL_STATE_IDLE:
- handler.sendEmptyMessageDelayed(ImageHandler.MSG_UPDATE_IMAGE, ImageHandler.MSG_DELAY);
- break;
- default:
- break;
- }
- }
- });
- viewPager.setCurrentItem(Integer.MAX_VALUE/2);//默认在中间,使用户看不到边界
- //开始轮播效果
- handler.sendEmptyMessageDelayed(ImageHandler.MSG_UPDATE_IMAGE, ImageHandler.MSG_DELAY);
- }//end of onCreate
- }//end of MainActivity