1. 程式人生 > >Android AppWidget(桌面小部件-音樂播放動畫)

Android AppWidget(桌面小部件-音樂播放動畫)

桌面小部件基礎篇:Android AppWidget (桌面小部件)

音樂播放 (動畫實現)

   

一個音樂播放的柱狀圖(不會上傳動圖,自行腦補)

思路方案:

1,自定義View,widget 僅支援部分控制元件,自定義沒用,我把自定義弄完了,才想起來。所以這個方案pass

2,幀動畫,直接使用ImageView,也不行,無法獲取子控制元件屬性,幀動畫執行不了

3,LayoutAnimation,這個我還沒試怎麼實現複雜動畫,不過應該可以實現簡單的 縮放,透明度,等屬性動畫(自己測試吧)

4,執行緒實現幀動畫

5,handler 實現幀動畫(比較好控制)

自定義View(記錄,不是最終選擇方案)

package cn.sh.changxing.onlineradio.view;

import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.RemoteViews;

import java.util.Random;

/**
 * 仿柱形頻譜 動畫播放
 */
public class PlayerView extends View {

    private static final String TAG = "PlayerView2";

    /**
     * 畫筆
     */
    private Paint paint;

    /**
     * 整個控制元件寬,高,最終取值
     */
    private int width, height, min;

    /**
     * 小矩形邊長
     */
    private float border;

    /**
     * 間隔
     */
    private float interval;

    /**
     * 柱狀圖 X*X(預設 7*7)
     */
    private int line = 7;

    private ValueAnimator valueAnimator;
    private int current = -1;
    private int random = 0;

    public PlayerView(Context context) {
        this(context, null);
    }

    public PlayerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    /**
     * 初始化
     */
    private void init() {

        Log.e(TAG, "init: ");

        paint = new Paint();
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        paint.setColor(Color.WHITE);

        initAnimation();
    }


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

        if (width == 0 || height == 0) {
            width = getMeasuredWidth();
            height = getMeasuredHeight();

            min = Math.min(width, height);
            interval = (float) (min * 0.025);
            border = (min - (line + 1) * interval) / line;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //繪製 多列
        for (int i = 0; i < line; i++) {
            drawColumnRect(canvas, (i + 1) * interval + i * border, random);
        }

    }


    /**
     * 繪製一列
     *
     * @param canvas
     * @param startX X軸的起始位置
     * @param random 為了動畫效果,設定額外增加的隨機數個數(每列生成個數 = 隨機生成的個數+額外統一增加個數) 達到上下抖動效果
     */
    private void drawColumnRect(Canvas canvas, float startX, int random) {

        //隨機生成個數
        //根據 間隔,小方塊邊長,個數,確定要繪製的小方塊區域

        int size = new Random().nextInt(4);//0-3

        for (int i = 0; i < random + size; i++) {
            RectF rectF = new RectF();
            rectF.left = startX;
            rectF.top = min - (i + 1) * (border + interval);
            rectF.right = startX + border;
            rectF.bottom = min - (i + 1) * interval - i * border;

            //透明度變化
            paint.setAlpha(50 + i * 30);
            canvas.drawRect(rectF, paint);
        }
    }


    /**
     * 開始播放
     */
    public void startPlay() {

        if (null != valueAnimator && !valueAnimator.isRunning()) {
            valueAnimator.start();
        } else {
            initAnimation();
            startPlay();
        }
    }

    /**
     * 停止播放
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public void stop() {
        if (null != valueAnimator && valueAnimator.isRunning()) {
            valueAnimator.pause();
        }
    }

    /**
     * 銷燬 回收
     */
    public void destroy() {

    }

    /**
     * 初始化動畫 (暫時寫死)
     */
    private void initAnimation() {

        valueAnimator = ValueAnimator.ofInt(0, 5);
        valueAnimator.setDuration(850);
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int progress = (int) animation.getAnimatedValue();
                if (progress != current) {
                    postInvalidate();
                    random = new Random().nextInt(5);//額外增加個數 0-4
                    current = progress;
                }
            }
        });
    }

}

一個隨機生成小方塊,也能做到類似音訊播放時的效果,部分直接寫死的引數,記錄一下,並不能用在 Widget上。

Handler實現

package cn.sh.changxing.onlineradio.widget;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.widget.RemoteViews;

import com.ximalaya.ting.android.opensdk.player.XmPlayerManager;

import cn.sh.changxing.applib.constant.SysBroadcastConstant;
import cn.sh.changxing.onlineradio.OnlineRadioApplication;
import cn.sh.changxing.onlineradio.R;
import cn.sh.changxing.onlineradio.activity.MainActivity;
import cn.sh.changxing.onlineradio.service.aidl.IMediaPlayerService;

import static cn.sh.changxing.applib.constant.SysBroadcastConstant.ACTION_NOTIFY_MPLAYER_STATE_CHANGED;
import static cn.sh.changxing.applib.constant.SysBroadcastConstant.EXTRA_VALUE_NOTIFY_MPLAYER_PLAYING;

public class RadioWidgetProvider2 extends AppWidgetProvider {

    private static final String TAG = "RadioWidgetProvider2";
    public static int REQUEST_GO_ACTIVITY_CODE = 112;
    private static final int START_ANIMATION = 0;
    private static final int END_ANIMATION = 1;

    private static Context mContext;
    private static RemoteViews remoteViews;

    private IMediaPlayerService mediaPlayerService;

    private int[] bitmapId = new int[]{R.drawable.radio_play_gif_2, R.drawable.radio_play_gif_3, R.drawable.radio_play_gif_4, R.drawable.radio_play_gif_5,
            R.drawable.radio_play_gif_6, R.drawable.radio_play_gif_8, R.drawable.radio_play_gif_9, R.drawable.radio_play_gif_10, R.drawable.radio_play_gif_11,
            R.drawable.radio_play_gif_12, R.drawable.radio_play_gif_13, R.drawable.radio_play_gif_14, R.drawable.radio_play_gif_13, R.drawable.radio_play_gif_12,
            R.drawable.radio_play_gif_11, R.drawable.radio_play_gif_10, R.drawable.radio_play_gif_9, R.drawable.radio_play_gif_8, R.drawable.radio_play_gif_6,
            R.drawable.radio_play_gif_5, R.drawable.radio_play_gif_4, R.drawable.radio_play_gif_3};

    //圖片資源
//    private int[] bitmapId = new int[]{R.drawable.radio_play_gif_2, R.drawable.radio_play_gif_4, R.drawable.radio_play_gif_6, R.drawable.radio_play_gif_8,
//            R.drawable.radio_play_gif_10, R.drawable.radio_play_gif_12, R.drawable.radio_play_gif_14};

    //動畫間隔時間/ms
    private long sleep = 100;

    private static boolean isRun = false;

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);

        if (null == mContext) {
            mContext = context;
        }
        //設定預設初始顯示圖片
        remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_radio);
        remoteViews.setImageViewResource(R.id.onlineRadio_widget, bitmapId[0]);
        //點選進入應用
        startRadio();
        //多個Widget的情況 重新整理
        for (int i = 0; i < appWidgetIds.length; i++) {
            Log.e(TAG, "onUpdate: ");
            appWidgetManager.updateAppWidget(appWidgetIds[i], remoteViews);
        }
        //初始播放狀態
        initStatus();

    }

    /**
     * 獲取 播放器狀態
     */
    private void initStatus(){
        OnlineRadioApplication application = OnlineRadioApplication.getInstance();
        XmPlayerManager manager = XmPlayerManager.getInstance(application);

        if(manager.isPlaying()){
            if(!isRun){
                Log.e(TAG, "initStatus: isPlaying");
                isRun = true;
                Message msg = mHandler.obtainMessage(START_ANIMATION);
                msg.arg1 = 0;
                mHandler.sendMessage(msg);
            }
        }
    }


    /**
     * 播放器狀態發生變化 接收廣播
     * @param context
     * @param intent
     */
    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        Message msg = mHandler.obtainMessage(START_ANIMATION);
        msg.arg1 = 0;
        String action = intent.getAction();
        if (action.equals(ACTION_NOTIFY_MPLAYER_STATE_CHANGED)) {
            String appPackName = intent.getStringExtra(SysBroadcastConstant.EXTRA_NAME_APP_PACKAGE);
            if (appPackName.equals(OnlineRadioApplication.getInstance().getPackageName())) {
                int state = intent.getIntExtra(SysBroadcastConstant.EXTRA_NAME_NOTIFY_MPLAYER_STATE, 1);
                switch (state) {
                    case EXTRA_VALUE_NOTIFY_MPLAYER_PLAYING:
                        if(!isRun){
                            isRun = true;
                            mHandler.sendMessage(msg);
                            Log.e(TAG, "onReceive: 播放");
                        }
                        break;
                    default:
                        if(isRun){
                            isRun = false;
                            mHandler.sendEmptyMessage(END_ANIMATION);
                            Log.e(TAG, "onReceive: 暫停");
                        }
                        break;
                }
            } else {
                int state = intent.getIntExtra(SysBroadcastConstant.EXTRA_NAME_NOTIFY_MPLAYER_STATE, 1);
                if (state == EXTRA_VALUE_NOTIFY_MPLAYER_PLAYING) {
                    if(!isRun){
                        isRun = true;
                        mHandler.sendMessage(msg);
                        Log.e(TAG, "onReceive: 播放");
                    }
                }
            }
        }
    }


    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            AppWidgetManager appWidgetManger = AppWidgetManager.getInstance(mContext);
            int[] appIds = appWidgetManger.getAppWidgetIds(new ComponentName(mContext, RadioWidgetProvider2.class));

            if (msg.what == START_ANIMATION) {
                if (!isRun) {
                    return;
                }
                if (null != remoteViews) {
                    remoteViews.setImageViewResource(R.id.onlineRadio_widget, bitmapId[msg.arg1]);
                    appWidgetManger.updateAppWidget(appIds, remoteViews);

                    //迴圈切換
                    Message message = mHandler.obtainMessage(START_ANIMATION);
                    message.arg1 = (msg.arg1 + 1) % bitmapId.length;
                    mHandler.sendMessageDelayed(message, sleep);
                }

            } else if (msg.what == END_ANIMATION) {
                //停止時顯示預設圖片
                remoteViews.setImageViewResource(R.id.onlineRadio_widget, bitmapId[0]);
                appWidgetManger.updateAppWidget(appIds, remoteViews);
            }
        }
    };


    /***
     *
     * 點選背景 進入radio
     *
     */
    private void startRadio() {
        Intent intent = new Intent(mContext, MainActivity.class);
        intent.setAction("cn.sh.changxing.action.redio");
        intent.addCategory("android.intent.category.DEFAULT");
        PendingIntent pendingIntent = PendingIntent.getActivity(mContext, REQUEST_GO_ACTIVITY_CODE, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        remoteViews.setOnClickPendingIntent(R.id.widget_layout, pendingIntent);
    }

    /***
     * 檢測電臺服務是否開啟 沒有開啟的話自動開啟
     * @param context
     */
    public void startRadioService(final Context context) {
        mediaPlayerService = OnlineRadioApplication.getInstance().getMediaPlayerService();
        if (mediaPlayerService == null) {
            Intent intent = new Intent();
            intent.setAction("cn.sh.changxing.onlineradio.service.MediaPlayerServiceCTL");
            context.bindService(intent, new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    Log.d(TAG, "服務開啟成功" + name);
                    mediaPlayerService = IMediaPlayerService.Stub.asInterface(service);
                }

                @Override
                public void onServiceDisconnected(ComponentName name) {
                    Log.d(TAG, "服務關閉" + name);
                    context.unbindService(this);
                }
            }, Context.BIND_NOT_FOREGROUND);
        }
    }

    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
        startRadioService(context);
    }

    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
        System.gc();
    }
}

程式碼註釋清晰,準備好圖片陣列,首先判斷播放器狀態,看是否啟動動畫。所有處理在handlerMessage中,判斷是否播放,停止。