Android 仿火螢視訊桌面 神奇的LiveWallPaper

鴻洋發表於2017-05-16

本文已在我的公眾號hongyangAndroid原創首發。

一、概述

上週我的微信公眾號推送了一篇Android 實現"透明螢幕,當時我看到之後就覺得特別感興趣,也立即聯絡作者要了授權~~

感興趣的原因是,我是內涵段子的資深使用者,前段時間基本被一款叫火螢視訊桌面的軟體(就是將視訊作為桌面)給刷屏了,所以看了下作者的程式碼,看到了SurfaceHolder,立刻想到了,肯定可以用來播放視訊實現視訊桌面的效果,於是週末嘗試了下,果然很簡單。

所以本篇文章無限感謝Android 實現"透明螢幕一文,程式碼也部分參考自其提供的透明相機。

github.com/songixan/Wa…

效果圖是這樣的:

Android 仿火螢視訊桌面 神奇的LiveWallPaper

注:本文的測試機為小米5s ,可能不同手機會有一些相容性問題,嘗試解決下。

二、實現

(1) 配置相關

首先編寫一個xml檔案,用於描述wallpaper的thumbnaildescriptionsettingsActivity等,這裡為了簡單,僅設定了thumbnail。

<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
    android:thumbnail="@mipmap/ic_launcher" />複製程式碼

(2)編寫程式碼

Wallpaper需要在螢幕上一直顯示,其背後其實是一個Service,所以實現一個Wallpaper需要繼承自WallpaperService,實現其抽象方法onCreateEngine,如下:

public class VideoLiveWallpaper extends WallpaperService {
    public Engine onCreateEngine() {
        return new VideoEngine();
    }
    //...
}複製程式碼

可以看到返回值是一個Engine,Engine為WallpaperService的內部類,其內部包含onSurfaceCreatedonSurfaceChangedonSurfaceDestroyedonTouchEvent等方法,看到這些方法,立刻想到了SurfaceView,關於SurfaceView相關知識可以參考:

此外,大家還記得在Android播放視訊嗎?

常規的做法有通過VideoView,除此以外還有通過MediaPlayer配合SurfaceView配合來實現,今天這個例子類似後者。

我們只需要通過MediaPlayer將解碼的資料不斷的輸送到傳入的Surface中即可。

class VideoEngine extends Engine {

    private MediaPlayer mMediaPlayer;

    @Override
    public void onSurfaceCreated(SurfaceHolder holder) {
        L.d("VideoEngine#onSurfaceCreated ");
        super.onSurfaceCreated(holder);
        mMediaPlayer = new MediaPlayer();
        mMediaPlayer.setSurface(holder.getSurface());
        try {
            AssetManager assetMg = getApplicationContext().getAssets();
            AssetFileDescriptor fileDescriptor = assetMg.openFd("test1.mp4");
            mMediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),
                    fileDescriptor.getStartOffset(), fileDescriptor.getLength());
            mMediaPlayer.setLooping(true);
            mMediaPlayer.setVolume(0, 0);
            mMediaPlayer.prepare();
            mMediaPlayer.start();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

     @Override
    public void onVisibilityChanged(boolean visible) {
        L.d("VideoEngine#onVisibilityChanged visible = " + visible);
        if (visible) {
            mMediaPlayer.start();
        } else {
            mMediaPlayer.pause();
        }
    }

    @Override
    public void onSurfaceDestroyed(SurfaceHolder holder) {
        L.d("VideoEngine#onSurfaceDestroyed ");
        super.onSurfaceDestroyed(holder);
        mMediaPlayer.release();
        mMediaPlayer = null;

    }複製程式碼

程式碼非常簡單,在onSurfaceCreated中去初始化mMediaPlayer,核心程式碼即為設定setSurface方法,這裡我預設設定了靜音。

onVisibilityChanged,即當桌面不可見時,我們要暫停播放,等回到桌面繼續。

當onSurfaceDestroyed時釋放資源~~

這樣我們的VideoLiveWallpaper就寫好了,別忘了他是個Service,需要我們去註冊。

<service
    android:name=".VideoLiveWallpaper"
    android:label="@string/app_name"
    android:permission="android.permission.BIND_WALLPAPER"
    android:process=":wallpaper">
    <!-- 配置intent-filter -->
    <intent-filter>
        <action android:name="android.service.wallpaper.WallpaperService" />
    </intent-filter>
    <!-- 配置meta-data -->
    <meta-data
        android:name="android.service.wallpaper"
        android:resource="@xml/livewallpaper" />
</service>複製程式碼

(3)設定為桌布

註冊完成後,我們在MainActivity中新增一個按鈕點選設定為桌面背景,呼叫程式碼如下

public static void setToWallPaper(Context context) {
    final Intent intent = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
    intent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
            new ComponentName(context, VideoLiveWallpaper.class));
    context.startActivity(intent);
}複製程式碼

這樣就完成了程式碼的初步編寫啦~~

(4)增加一些引數的支援

剛才我們設定了預設是靜音,可能有時候我們會希望能夠動態去控制視訊桌面的引數,正常應該嘗試去使用settingsActivity,不過我覺得其實廣播也挺合適的,無非就是Service(可能在獨立的程式)和Activity等通訊嘛~~

這裡我們增加一個核取方塊,支援設定開啟聲音or關閉聲音。

public static final String VIDEO_PARAMS_CONTROL_ACTION = "com.zhy.livewallpaper";
public static final String KEY_ACTION = "action";
public static final int ACTION_VOICE_SILENCE = 110;
public static final int ACTION_VOICE_NORMAL = 111;

class VideoEngine extends Engine {
    // 省略其他程式碼
    private BroadcastReceiver mVideoParamsControlReceiver;

    @Override
    public void onCreate(SurfaceHolder surfaceHolder) {
        super.onCreate(surfaceHolder);
        IntentFilter intentFilter = new IntentFilter(VIDEO_PARAMS_CONTROL_ACTION);
        registerReceiver(mVideoParamsControlReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                L.d("onReceive");
                int action = intent.getIntExtra(KEY_ACTION, -1);

                switch (action) {
                    case ACTION_VOICE_NORMAL:
                        mMediaPlayer.setVolume(1.0f, 1.0f);
                        break;
                    case ACTION_VOICE_SILENCE:
                        mMediaPlayer.setVolume(0, 0);
                        break;
                }
            }
        }, intentFilter);
    }
    @Override
    public void onDestroy() {
        unregisterReceiver(mVideoParamsControlReceiver);
        super.onDestroy();

    }
}複製程式碼

Engine還有onCreate和onDestroy宣告週期方法,可以在onCreate中註冊動態廣播,當接受到傳送的action為ACTION_VOICE_NORMAL則開啟聲音;接收到傳送的ACTION_VOICE_SILENCE則為靜音狀態。

最後直接在VideoLiveWallpaper中新增兩個靜態方法用於傳送廣播即可:

public static void voiceSilence(Context context) {
    Intent intent = new Intent(VideoLiveWallpaper.VIDEO_PARAMS_CONTROL_ACTION);
    intent.putExtra(VideoLiveWallpaper.KEY_ACTION, VideoLiveWallpaper.ACTION_VOICE_SILENCE);
    context.sendBroadcast(intent);
}

public static void voiceNormal(Context context) {
    Intent intent = new Intent(VideoLiveWallpaper.VIDEO_PARAMS_CONTROL_ACTION);
    intent.putExtra(VideoLiveWallpaper.KEY_ACTION, VideoLiveWallpaper.ACTION_VOICE_NORMAL);
    context.sendBroadcast(intent);
}複製程式碼

在Actiivty中:

public class MainActivity extends AppCompatActivity {
    private CheckBox mCbVoice;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mCbVoice = (CheckBox) findViewById(R.id.id_cb_voice);

        mCbVoice.setOnCheckedChangeListener(
                new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(
                            CompoundButton buttonView, boolean isChecked) {
                        if (isChecked) {
                            // 靜音
                            VideoLiveWallpaper.voiceSilence(getApplicationContext());
                        } else {
                            VideoLiveWallpaper.voiceNormal(getApplicationContext());
                        }
                    }
                });
    }
}複製程式碼

監聽一下CheckBox狀態,傳送廣播即可。

ok,這樣一個簡單的視訊桌面就完成啦~~

原始碼地址:

直接將這個目錄以專案形式匯入。


支援我的話可以關注下我的公眾號,每天都會推送新知識~

歡迎關注我的微信公眾號:hongyangAndroid
(可以給我留言你想學習的文章,支援投稿)
Android 仿火螢視訊桌面 神奇的LiveWallPaper

參考

相關文章