Android AppWidget(桌面小部件-音樂播放動畫)
阿新 • • 發佈:2018-12-07
桌面小部件基礎篇: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中,判斷是否播放,停止。