有个场景就是一个页面里有多个VideoView播放视频,然后每个视频都有一个音量值,但是VideoView并不支持直接设置音量,而是要通过调节系统音量来实现,那么这样的话,就不能实现为每个视频独立调节音量了我们知道MediaPlayer+SurfaceView也能实现视频的播放,并且MediaPlayer是可以直接通过setVolume来调节视频音量的,但是因为这里已经用了VideoView实现了播放,再改的话就太浪费时间和人力了。想到VideoView是继承SurfaceView的,那会不会VideoView的视频播放控制是由MediaPlayer实现的呢,查看VideoView源码:
try { mMediaPlayer = new MediaPlayer(); // TODO: create SubtitleController in MediaPlayer, but we need // a context for the subtitle renderers final Context context = getContext(); final SubtitleController controller = new SubtitleController( context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer); controller.registerRenderer(new WebVttRenderer(context)); controller.registerRenderer(new TtmlRenderer(context)); controller.registerRenderer(new Cea708CaptionRenderer(context)); controller.registerRenderer(new ClosedCaptionRenderer(context)); mMediaPlayer.setSubtitleAnchor(controller, this); if (mAudioSession != 0) { mMediaPlayer.setAudioSessionId(mAudioSession); } else { mAudioSession = mMediaPlayer.getAudioSessionId(); } mMediaPlayer.setOnPreparedListener(mPreparedListener); mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); mMediaPlayer.setOnCompletionListener(mCompletionListener); mMediaPlayer.setOnErrorListener(mErrorListener); mMediaPlayer.setOnInfoListener(mInfoListener); mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); mCurrentBufferPercentage = 0; mMediaPlayer.setDataSource(mContext, mUri, mHeaders); mMediaPlayer.setDisplay(mSurfaceHolder); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setScreenOnWhilePlaying(true); mMediaPlayer.prepareAsync();果然如此,VideoView仅仅只是封装了MediaPlayer和SurfaceView。那么既然这样的话就好办了,只需要获取到VideoView里面的MediaPlayer就可以单独修改这个视频的声音大小了,但是发现MediaPlayer是私有的并且VideoView并没有留出方法给外层调用,那么只能通过反射了。
/** * @param volume 音量大小 * @param object VideoView实例 * */ public void setVolume(float volume,Object object) { try { Class<?> forName = Class.forName("android.widget.VideoView"); Field field = forName.getDeclaredField("mMediaPlayer"); field.setAccessible(true); MediaPlayer mMediaPlayer = (MediaPlayer) field.get(object); mMediaPlayer.setVolume(volume, volume); } catch (Exception e) { } }据此思想,当遇到系统Api限制的问题时,我们不妨另辟蹊径,通过反射解除限制,从而解决问题,比如下面这个例子:
某些情况下我们想在ViewPager滑动的时候才创建页面,并不希望ViewPager给我们缓存页面,所以我们先找到ViewPager里面设置缓存个数相关的参数和方法。
private static final int DEFAULT_OFFSCREEN_PAGES = 1; private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES; public void setOffscreenPageLimit(int limit) { if (limit < DEFAULT_OFFSCREEN_PAGES) { Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + DEFAULT_OFFSCREEN_PAGES); limit = DEFAULT_OFFSCREEN_PAGES; } if (limit != mOffscreenPageLimit) { mOffscreenPageLimit = limit; populate(); } }可以看到ViewPager限制了传入缓存页数不能小于1,所以无论如何ViewPager都会给我们缓存一个以上的页面。没关系,同样的道理,我们可以修改这个限制,这里我们直接通过反射修改mOffscreenPageLimit的值来实现。
try { Class<?> forName = Class.forName("android.support.v4.view.ViewPager"); Field defauleField = forName.getDeclaredField("mOffscreenPageLimit"); defauleField.setAccessible(true); defauleField.set(0,viewPager); } catch (Exception e) { }通过这两个例子主要是想告诉大家不要被系统Api所限制,当发现SDK里没提供你所需要的方法时候,别放弃,细心看看源码,也许会柳暗花明又一村,代码是死的,人是活的,为达目的,可以不择手段,正常路径走不通,那么可以通过一些非正常的方式来达到目的。