DirectionalViewPager常见问题及解决方法小结

来源:互联网 发布:数据库系统实现第三版 编辑:程序博客网 时间:2024/05/24 04:08

前些时候在网上找到了一个DirectionalViewPager的控件,经过多次修改和优化后,实现了比较复杂、且灵活的,有足够稳定性的广告展示控件。

DirectionalViewPager这个控件网上已经有很多了,这里提供GC上的ViewPager的 API地址:
Android 5.1.1_r1/ android.support.v4.view.ViewPager
  下面总结下我遇到的DirectionalViewPager的常见错误:
  1. ViewPager.draw,有可能出现,至于为什么如下那样改,你可以换编译环境和支持包多试试

NullPointerException   at android.support.v4.view.ViewPager.draw(ViewPager.java:2289)       at android.view.View.buildDrawingCache(View.java:13678)       at android.view.View.getDisplayList(View.java:13338)       at android.view.View.getDisplayList(View.java:13404)       at android.view.View.draw(View.java:14182)       at android.view.ViewGroup.drawChild(ViewGroup.java:3103)       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2940)       at android.view.View.getDisplayList(View.java:13357)       at android.view.View.getDisplayList(View.java:13404)       at android.view.View.draw(View.java:14182)       at android.view.ViewGroup.drawChild(ViewGroup.java:3103)       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2940)       at android.view.View.draw(View.java:14468)       at android.widget.FrameLayout.draw(FrameLayout.java:472)       at android.view.View.getDisplayList(View.java:13362)       at android.view.View.getDisplayList(View.java:13404)       at android.view.View.draw(View.java:14182)       at android.view.ViewGroup.drawChild(ViewGroup.java:3103)       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2940)       at android.view.View.getDisplayList(View.java:13357)       at android.view.View.getDisplayList(View.java:13404)       at android.view.View.draw(View.java:14182)       at android.view.ViewGroup.drawChild(ViewGroup.java:3103)       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2940)       at android.view.View.draw(View.java:14468)       at android.widget.FrameLayout.draw(FrameLayout.java:472)       at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2334)       at android.view.View.getDisplayList(View.java:13362)       at android.view.View.getDisplayList(View.java:13404)       at android.view.View.draw(View.java:14182)       at android.view.ViewGroup.drawChild(ViewGroup.java:3103)       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2940)       at android.view.View.draw(View.java:14468)       at android.widget.FrameLayout.draw(FrameLayout.java:472)       at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2334)       at android.view.View.getDisplayList(View.java:13362)       at android.view.View.getDisplayList(View.java:13404)       at android.view.HardwareRenderer$GlRenderer.buildDisplayList(HardwareRenderer.java:1570)       at android.view.HardwareRenderer$GlRenderer.draw(HardwareRenderer.java:1449)       at android.view.ViewRootImpl.draw(ViewRootImpl.java:2381)       at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2253)       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1883)       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1000)       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5670)       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)       at android.view.Choreographer.doCallbacks(Choreographer.java:574)       at android.view.Choreographer.doFrame(Choreographer.java:544)       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)       at android.os.Handler.handleCallback(Handler.java:733)       at android.os.Handler.dispatchMessage(Handler.java:95)       at android.os.Looper.loop(Looper.java:136)       at android.app.ActivityThread.main(ActivityThread.java:5017)       at java.lang.reflect.Method.invokeNative(Native Method)       at java.lang.reflect.Method.invoke(Method.java:515)       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:815)       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:631)       at dalvik.system.NativeStart.main(Native Method)                                                            

  解决方法
  在DirectionalViewPager类方法initViewPager()中添加super.initViewPager()进行初始化,其中做了什么可以查看ViewPager的类方法initViewPager():
  a. 问题代码(git上的旧版本DirectionalViewPager代码):
  

void initViewPager() {        setPersistentDrawingCache(PERSISTENT_ALL_CACHES);        setWillNotDraw(false);        //VerticalViewPager类中设置viewpager每个页卡的间距,与gallery的spacing属性类似        // mViewPager.setPageMargin((int)getResources().getDimensionPixelOffset(R.dimen.ui_5_dip));        mScroller = new Scroller(getContext());        final ViewConfiguration configuration = ViewConfiguration                .get(getContext());        mTouchSlop = ViewConfigurationCompat                .getScaledPagingTouchSlop(configuration);        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();        if (this.mmodel != -2)            setAdapter(this.adapters);    }

  b. 正确代码(最新版代码):

void initViewPager() {        super.initViewPager();        //为动画滑动动作而声明的线程池(静态对象)初始化,给主线程减轻压力。根据需求添加上去的        synchronized (DirectionalViewPager.class){            if(executorService == null)                executorService = Executors.newFixedThreadPool(7);        }        setWillNotDraw(false);        mScroller = new Scroller(getContext());        final ViewConfiguration configuration = ViewConfiguration.get(getContext());        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();//        setPersistentDrawingCache(PERSISTENT_ALL_CACHES);        if (this.mmodel != -2)            setAdapter(this.adapters);    }      

  另外,看过stackflow等上面的解决方法有大概以下不能解决实际问题但被很多人转载的解决方法,如:
  
     a.DirectionalViewPager中覆写getChildDrawingOrder,此函数在ViewPager类中默认就“return i ”了,新旧版本都不需要覆写:
  
  旧版Android 4.4 - ViewPager.getChildDrawingOrder:

    @Override    protected int getChildDrawingOrder(int childCount, int i) {        return i;    }

  新版Android 5.1 - ViewPager.getChildDrawingOrder:

@Override    protected int getChildDrawingOrder(int childCount, int i) {        final int index = mDrawingOrder == DRAW_ORDER_REVERSE ? childCount - 1 - i : i;        final int result = ((LayoutParams) mDrawingOrderedChildren.get(index).getLayoutParams()).childIndex;        return result;    }

    b.DirectionalViewPager中覆写draw,通过try-catch处理空指针错误,如果每个孩子节点都存在空指针,即使此控件出现空指针不会造成崩溃,但控件什么都没有绘制和显示。所以以下代码只是预防程序崩溃,并不能解决实际错误,但建议这样写:

    @Override    public void draw(Canvas canvas) {        try {            super.draw(canvas);        } catch (NullPointerException e) {            Log.d("ViewPager", "Nullpointer skipped");        }    }

  
  
  2.

NullPointerException:   mLeftEdge.finish()  at android.support.v4.view.ViewPager.draw() (ViewPager.java:2289)

  
  这个错误没去详细记录,是mLeftEdge造成的空指针错误。

  问题原因及解决方法
  这个错误造成的原因是我给DirectionalViewPager设置了定时滑动的Timer(或者有人会用ScheduledExecutorService),而滑动动作函数smoothScrollTo或者computeScroll都会执行invalidate(),继而触发了ViewPager.draw(),而如果此时DirectionalViewPager还未进行初始化,就会造成此问题。
  如果没有设置定时滑动接口的同学们就不需要关注这个问题了。否则,要注意在每次DirectionalViewPager的初始化和销毁同时对Timer进行初始化启动和停止销毁。

3.
偶现 Adapter类的destroyItem()方法中报NullPointerException :destroyItem() 错误。代码如下:

@Override    public void destroyItem(View arg0, int arg1, Object arg2) {            ((DirectionalViewPager) arg0).removeView((View) arg2);    }

解决方法:造成这个的原因,是短时间内控件page 多次动态更新时(增或删),动态更新这个动作还未完成的时候监察者类最终调用了populate(),继而执行了mAdapter.finishUpdate(this),而mAdapter.finishUpdate(this)必须在增删这个动作结束后调用。否则,这时增删的对象为空【子节点还未真正 加入到当前视图/从当前视图中删除】。需要在动态更新【增删页】的动作上使用同步(Synchronized):
注意:这个函数在高版本(API19还未被替代,现在我用的API 23)的supprt.v4包中已经过时了,取而代之的是public void destroyItem(ViewGroup container, int position, Object object),不过函数体里也只是调用了destroyItem(View arg0, int arg1, Object arg2)。都可以使用。
解决代码如下:

    void dataSetChanged() {        // This method only gets called if our observer is attached, so mAdapter        // is non-null.        synchronized (DirectionalViewPager.class) {            boolean needPopulate = mItems.isEmpty() && mAdapter.getCount() > 0;            int newCurrItem = -1;            for (int i = 0; i < mItems.size(); i++) {                final ViewPager.ItemInfo ii = mItems.get(i);                final int newPos = mAdapter.getItemPosition(ii.object);                if (newPos == PagerAdapter.POSITION_UNCHANGED) {                    continue;                }                if (newPos == PagerAdapter.POSITION_NONE) {                    mItems.remove(i);                    i--;                    mAdapter.destroyItem(this, ii.position, ii.object);                    needPopulate = true;                    if (mCurItem == ii.position) {                        // Keep the current item in the valid range                        newCurrItem = Math.max(0, Math.min(mCurItem, mAdapter.getCount() - 1));                    }                    continue;                }                if (ii.position != newPos) {                    if (ii.position == mCurItem) {                        // Our current item changed position. Follow it.                        newCurrItem = newPos;                    }                    ii.position = newPos;                    needPopulate = true;                }            }            if (newCurrItem >= 0) {                // TODO This currently causes a jump.                setCurrentItemInternal(newCurrItem, false, true);                needPopulate = true;            }            if (needPopulate) {                populate();                requestLayout();            }        }    }

  
  4.
  如果实在不想使用观察者类来监视数据变化更新视图,可以将adapters.notifyDataSetChanged(); 替换为线程中调用dataSetChanged(),因为它最终会回调DataSetObservable类中的DataSetObserver对象的onChanged()。而onChanged()这个接口是要由我们自己实现的,且调用了dataSetChanged()。

  5.
  在DirectionalViewPager中平滑滑动的函数,在调用时最好放在线程中,这样可以减轻主线程压力。最好的做法是声明一个线程池,容量可以根据需要设置,这样避免手动一次次new Thread(),而由线程池进行管理。

  

  因为继承了ViewPager,所以measure()、layout()、draw()的方法就不需要重写了。大家可以看看 VerticalViewPager 的写法,它是继承了ViewGroup,里面要重写measure()、layout()、draw(),还有比如设置软件格式显示防止绘制出整个子View(如果有多页的话,绘制不会被限制在控件本身大小之内,而是将所有加载的view都绘制出来),对理解ViewPager很有帮助。

0 0