桌面小部件基础篇:Android AppWidget (桌面小部件)
一个音乐播放的柱状图(不会上传动图,自行脑补)
思路方案:
1,自定义View,widget 仅支持部分控件,自定义没用,我把自定义弄完了,才想起来。所以这个方案pass
2,帧动画,直接使用ImageView,也不行,无法获取子控件属性,帧动画运行不了
3,LayoutAnimation,这个我还没试怎么实现复杂动画,不过应该可以实现简单的 缩放,透明度,等属性动画(自己测试吧)
4,线程实现帧动画
5,handler 实现帧动画(比较好控制)
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上。
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中,判断是否播放,停止。