原創安卓手機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);
}