详解Android ViewCompat的作用
详解AndroidViewCompat的作用
ViewCompat类主要是用来提供兼容性的,比如我最近看的比较的多的canScrollVertically方法,在ViewCompat里面针对几个版本有不同的实现,原理上还是根据版本判断,有时甚至还要判断传入参数的类型.但是要注意的是,ViewCompat仅仅让你调用不崩溃,并不保证你调用的结果在不同版本的机器上一致.
关于如何优雅的组织代码,ViewCompat类的结构非常适合我们参考.
ViewCompat里面定义了一个接口,这个接口列出了所有它支持的方法
interfaceViewCompatImpl{ publicbooleancanScrollHorizontally(Viewv,intdirection); publicbooleancanScrollVertically(Viewv,intdirection); publicintgetOverScrollMode(Viewv); publicvoidsetOverScrollMode(Viewv,intmode); ...... }
ViewCompat类并非是在方法层面进行版本判断然后调用不同的方法,而是在类的层面上做的,也就是说在调用方法时并没有判断版本的调用,因为一台手机的版本在开机到关机期间是不可能发生变化的,所以只需要判断一次,而这次判断放在了类的静态初始化块里.
staticfinalViewCompatImplIMPL; static{ finalintversion=android.os.Build.VERSION.SDK_INT; if(version>=21){ IMPL=newLollipopViewCompatImpl(); }elseif(version>=19){ IMPL=newKitKatViewCompatImpl(); }elseif(version>=17){ IMPL=newJbMr1ViewCompatImpl(); }elseif(version>=16){ IMPL=newJBViewCompatImpl(); }elseif(version>=14){ IMPL=newICSViewCompatImpl(); }elseif(version>=11){ IMPL=newHCViewCompatImpl(); }elseif(version>=9){ IMPL=newGBViewCompatImpl(); }elseif(version>=7){ IMPL=newEclairMr1ViewCompatImpl(); }else{ IMPL=newBaseViewCompatImpl(); } }
这样我们就得到了针对各个版本的不同实现.
但是有些方法的实现在跨越几个版本的时候是不变的,有些方法又有可能每次都变,如何实现高效的代码复用呢?那就是继承+重写.
比如BaseViewCompatImpl这个类是基类,实现ViewCompatImpl接口,把所有的方法都实现一次
staticclassBaseViewCompatImplimplementsViewCompatImpl{ ...... publicbooleancanScrollHorizontally(Viewv,intdirection){ return(vinstanceofScrollingView)&& canScrollingViewScrollHorizontally((ScrollingView)v,direction); } publicbooleancanScrollVertically(Viewv,intdirection){ return(vinstanceofScrollingView)&& canScrollingViewScrollVertically((ScrollingView)v,direction); } ...... @Override publicbooleanisOpaque(Viewview){ finalDrawablebg=view.getBackground(); if(bg!=null){ returnbg.getOpacity()==PixelFormat.OPAQUE; } returnfalse; } ...... }
但是这些实现基本上都是空的,或者无效的,或者是一些workaround,这也很正常,因为确实不可能让每个方法都做到兼容,只能尽量让他的版本支持多一点,兼容性方法本来就有很多问题.以上面这三个方法为例,前两个方法都是api14出现的方法,在14以下基本上等于是直接返回了false(这里低版本是仅对ScollingView提供了支持,ScollingView有三个基类,其中一个是RecyclerView),google显然没有想到什么好的方法在低版本提供对这个方法的支持,所以干脆就在api小于14时一直使用这个实现,而isOpaque则是类似workaround的方法,在api7时,isOpaque被正式添加到View类中,所以在api7我们可以直接调View的isOpaque,那么应该怎么写代码呢?应当新建一个类,继承BaseViewCompatImpl,重写isOpaque方法,也就是下面这样:
staticclassEclairMr1ViewCompatImplextendsBaseViewCompatImpl{ @Override publicbooleanisOpaque(Viewview){ returnViewCompatEclairMr1.isOpaque(view); } ...... }
而其他没有更好兼容方案的方法我们都不管,那么api9如果某些方法又有了更好的实现,或者可以直接调用系统的api了,就再新建一个类GBViewCompatImpl,这个类需要继承EclairMr1ViewCompatImpl.
同理,我们在api14对应的类ICSViewCompatImpl中自然就会看到canScrollHorizontally和canScrollVertically的新的实现,而ICSViewCompatImpl必然继承自HCViewCompatImpl.
就这样慢慢的演化,像串铜钱一样,每一个新的类对应一个新的版本(版本之间不需要连续),同时继承自前一个版本的类,在实现类的继承树上越接近叶子,这个实现类的能力就越强.
最后看一下我们在代码里面使用这个类时的调用代码,比如我要调用canScrollVertically方法,那么我的代码一定是ViewCompat.canScrollVertically(v,dy),看看这个方法对应的代码
publicstaticbooleancanScrollHorizontally(Viewv,intdirection){ returnIMPL.canScrollHorizontally(v,direction); }
ViewCompat相当于是一个中介,它自己其实什么都不懂,但是它认识一个懂的人IMPL,它将所有的调用都交给了IMPL,而IMPL在ViewCompat这个类加载时就已经根据当前系统版本实例化了,不需要再判断版本了.
关于具体的使用请查看官方文档
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!