1. 程式人生 > >原創安卓手機QQ7.0登入介面動態背景視訊實現方案

原創安卓手機QQ7.0登入介面動態背景視訊實現方案

qq7.0登入介面動態背景實現 qq7.0登入介面動態視訊背景實現 android動態視訊背景 android動態背景

分析qq7.0:

視訊在開啟登入介面就開始播放 了,而且期間無黑屏
而且是迴圈播放的

畫質問題這裡就不說了,這個看視訊源了。

可以讓不規則的寬高各種寬高不定的視訊比例 以及視訊大小都能 適應任意安卓手機的寬高 包括平板,且不留任何縫隙

播放器控制元件選取:解決的是手機適配的問題,另外是播放器控制元件,這裡選擇系統播放器比較好. 因為有些播放器不支援讀取asset資料夾的Uri比如七牛的

視訊載入速度比較慢第一幀用圖片代替且需要耦合視訊的第一幀

圖片的第一幀擷取我用的是一個比較專業的adobe premiere的開發工具 這個你們也可以讓ps等後期的去做,這種事情對我來說的話還是小kiss,

技術點:
如何讀取資原始檔視訊
如何測量
如何根據視訊大小計算應該縮放的比例大小 解決任意尺寸視訊手機不留黑邊
如何讓圖片的封面縮放大小和視訊的縮放大小吻合
如何呼叫onStart短暫黑屏問題

架構搭建

資源的讀取


   String VIDEO_PATH = "android.resource://" + BuildConfig.APPLICATION_ID + "/" + R.raw.login;
videoView.setVideoURI(Uri.parse(Constants.VIDEO_PATH));

建立一個自定義視訊類 自定義圖片類 圖片在視訊的上面因為視訊不是馬上播放 載入有一定時間這裡也會存在一個黑屏

關於讀取視訊的問題,之前嘗試過讀取assests裡面的視訊失敗了,在stackoverflow照的方案也不行,最後還是把視訊放到和res/raw資料夾裡面了,

具體實現之視訊控制元件

1. 拿到視訊的寬高度才能進行測量重新佈局

在繼承的VideoView裡新增setOnPreparedListener方法獲取視訊寬高度設定給成員變數就可以拿到了

 super.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared
(final MediaPlayer mp) { SystemVideoView.this.videoWidth = mp.getVideoWidth(); SystemVideoView.this.videoHeight = mp.getVideoHeight(); } }

2. 繼承VideoView重寫onMeasure測量方法

需要一個完美的演算法來解決寬高都鋪滿螢幕問題

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        MeasureUtil.Size measure = MeasureUtil.measure(displayAspectRatio, widthMeasureSpec, heightMeasureSpec, videoWidth, videoHeight);

        setMeasuredDimension(measure.width, measure.height);
    }

這裡的演算法比較麻煩,不懂的同學搬用模版程式碼
測量工具類MeasureUtil.measure方法抽出來
的大致程式碼是

    public static MeasureUtil.Size measure(int displayAspectRatio, int widthMeasureSpec, int heightMeasureSpec, int videoWidth, int videoHeight) {


 if (widthMode == View.MeasureSpec.EXACTLY && heightMode == View.MeasureSpec.EXACTLY) {  

  if (percentVideo > percentView) {
                            width = widthSize;
                            height = (int) ((float) widthSize / percentVideo);
                        } else {
                            height = heightSize;
                            width = (int) ((float) heightSize * percentVideo);
                        }

    }else if (widthMode == View.MeasureSpec.EXACTLY) {
                width = widthSize;
                height = widthSize * videoHeight / videoWidth;
                if (heightMode == View.MeasureSpec.AT_MOST && height > heightSize) {
                    height = heightSize;
                }
            } else if (heightMode == View.MeasureSpec.EXACTLY) {
                height = heightSize;
                width = heightSize * videoWidth / videoHeight;
                if (widthMode == View.MeasureSpec.AT_MOST && width > widthSize) {
                    width = widthSize;
                }
            } else {
                width = videoWidth;
                height = videoHeight;
                if (heightMode == View.MeasureSpec.AT_MOST && videoHeight > heightSize) {
                    height = heightSize;
                    width = heightSize * videoWidth / videoHeight;
                }

                if (widthMode == View.MeasureSpec.AT_MOST && width > widthSize) {
                    width = widthSize;
                    height = widthSize * videoHeight / videoWidth;
                }
            }

}


public static class Size {
        public final int width;
        public final int height;

        public Size(int width, int height) {
            this.width = width;
            this.height = height;
        }
    }

3. 黑屏問題解決探討

只要呼叫start就會有一定概率的黑屏毫秒
先不管測量鋪滿問題,我們發現會存在一個坑,就是視訊黑屏問題,進入這個介面肯定要讓它不黑屏的.
1.嘗試過在onPrepared裡面再在讓VideoView顯示隱藏結果沒卵用
1.直接隱藏控制元件在方案1的基礎上延長几秒,start過程中依然隱藏(不同手機需要的延長時間不同,)但是如果0秒到1秒的過程中如果沒有畫面動還好,如果動了,延長超過1秒後在顯示此控制元件那麼視訊就需要留長 不然首幀和此時videoview顯示的時間不一致,後面發現這種死辦法又沒法解決迴圈播放問題

最後的解決方法通過百度找到 是根據info的視訊第一幀來判斷:

  mp.setOnInfoListener(new MediaPlayer.OnInfoListener() {
                    @Override
                    public boolean onInfo(MediaPlayer mp, int what, int extra) {
                        if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
                            if (onCorveHideListener != null) {
                                onCorveHideListener.requestHide();
                            }
                        }
                        if (onInfoListener != null) {
                            onInfoListener.onInfo(mp, what, extra);
                        }
                        return false;
                    }
                });

圖片的解決方案和視訊一樣,你這都需要程式碼得話打賞一個吧,哈哈,

隱藏的方法在外面了。叫 setOnCorveHideListener ,實際上進入介面就應該馬上顯示畫面的隱藏視訊的話是一個白屏,所以這裡需要

最後介面activity或者fragment程式碼

    String VIDEO_PATH = "android.resource://" + BuildConfig.APPLICATION_ID + "/" + R.raw.login;

    loginActivityBinding.videoView.setDisplayAspectRatio(MeasureUtil.ASPECT_RATIO_PAVED_PARENT);
        loginActivityBinding.videoView.setOnCorveHideListener(new SystemVideoView.OnCorveHideListener() {
            @Override
            public void requestHide() {
                loginActivityBinding.corver.setVisibility(View.GONE);
            }
        });
        loginActivityBinding.videoView.setVideoURI(Uri.parse(Constants.VIDEO_PATH));
        loginActivityBinding.videoView.start();
        loginActivityBinding.videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                loginActivityBinding.videoView.seekTo(0);
                loginActivityBinding.videoView.start();
            }
        });




 @Override
    public void onPause() {
        super.onPause();
        loginActivityBinding.videoView.pause();
    }

    @Override
    public void onResume() {
        super.onResume();
        loginActivityBinding.videoView.start();
    }

完整SystemVideoView程式碼

public class SystemVideoView extends VideoView {


    private int videoWidth;//width
    private int videoHeight;
    private int displayAspectRatio;

    public SystemVideoView(Context context) {
        super(context);
    }

    public SystemVideoView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public SystemVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);

    }

    protected void init(Context context) {
        this.videoHeight = context.getResources().getDisplayMetrics().heightPixels;
        this.videoWidth = context.getResources().getDisplayMetrics().widthPixels;

        super.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(final MediaPlayer mp) {

                SystemVideoView.this.videoWidth = mp.getVideoWidth();
                SystemVideoView.this.videoHeight = mp.getVideoHeight();
                mp.setOnInfoListener(new MediaPlayer.OnInfoListener() {
                    @Override
                    public boolean onInfo(MediaPlayer mp, int what, int extra) {
                        if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
                            if (onCorveHideListener != null) {
                                onCorveHideListener.requestHide();
                            }
                        }
                        if (onInfoListener != null) {
                            onInfoListener.onInfo(mp, what, extra);
                        }
                        return false;
                    }
                });
            }
        });
    }

    MediaPlayer.OnPreparedListener onPreparedListener = null;

    public interface OnCorveHideListener {
        void requestHide();
    }

    @Override
    public void setOnInfoListener(MediaPlayer.OnInfoListener onInfoListener) {
        this.onInfoListener = onInfoListener;
    }

    MediaPlayer.OnInfoListener onInfoListener;

    public void setOnCorveHideListener(OnCorveHideListener onCorveHideListener) {
        this.onCorveHideListener = onCorveHideListener;
    }

    OnCorveHideListener onCorveHideListener;

    @Override
    public void setOnPreparedListener(MediaPlayer.OnPreparedListener l) {
        this.onPreparedListener = l;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);


        MeasureUtil.Size measure = MeasureUtil.measure(displayAspectRatio, widthMeasureSpec, heightMeasureSpec, videoWidth, videoHeight);
        setMeasuredDimension(measure.width, measure.height);





    public void setDisplayAspectRatio(int var1) {
        displayAspectRatio = var1;
        this.requestLayout();

    }


    @Override
    public boolean isPlaying() {
        return false;
    }

    public int getDisplayAspectRatio() {
        return displayAspectRatio;
    }

    public void setCorver(int resource) {
        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), resource, opts);

    }

我的部落格
我的簡書