Android 支援多個VideoView聲音調節
阿新 • • 發佈:2019-01-08
有個場景就是一個頁面裡有多個VideoView播放視訊,然後每個視訊都有一個音量值,但是VideoView並不支援直接設定音量,而是要通過調節系統音量來實現,那麼這樣的話,就不能實現為每個視訊獨立調節音量了我們知道MediaPlayer+SurfaceView也能實現視訊的播放,並且MediaPlayer是可以直接通過setVolume來調節視訊音量的,但是因為這裡已經用了VideoView實現了播放,再改的話就太浪費時間和人力了。想到VideoView是繼承SurfaceView的,那會不會VideoView的視訊播放控制是由MediaPlayer實現的呢,檢視VideoView原始碼:
try果然如此,VideoView僅僅只是封裝了MediaPlayer和SurfaceView。那麼既然這樣的話就好辦了,只需要獲取到VideoView裡面的MediaPlayer就可以單獨修改這個視訊的聲音大小了,但是發現MediaPlayer是私有的並且VideoView並沒有留出方法給外層呼叫,那麼只能通過反射了。{ 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();
/** * @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裡沒提供你所需要的方法時候,別放棄,細心看看原始碼,也許會柳暗花明又一村,程式碼是死的,人是活的,為達目的,可以不擇手段,正常路徑走不通,那麼可以通過一些非正常的方式來達到目的。