文章介绍:
实现一个多布局可滑动可点击的安卓widget组件;
知识点介绍:
- AppWidgetProvider
- AdapterViewFlipper
- RemoteViews
- RemoteViewsService
- PendingIntent
开发步骤
1、新建类继承AppWidgetProvider,重写onUpdate()方法和onReceive()方法;
/**
* 遍历所有的控件,然后更新
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.d(TAG,"onUpdate... appWidgetIds: "+appWidgetIds.length);
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
Log.d(TAG,"onReceive... ");
if(intent == null) {
return;
}
...
}
/**
* 控件的更新和操控
*/
@SuppressLint("ResourceType")
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
Log.d(TAG,"updateAppWidget...appWidgetId: "+appWidgetId);
//获取Widget的组件名
ComponentName componentName = new ComponentName(context, ImageTextWidget.class);
// 创建一个RemoteView
@SuppressLint("RemoteViewLayout") SkinRemoteViews remoteViews = new SkinRemoteViews(context.getPackageName(), R.layout.layout_widget);
// remoteViews.setImageViewResource(R.id.iv_background,R.mipmap.icon_widget_bg);
remoteViews.setImageViewResource(R.id.iv_default,R.mipmap.icon_widget_imagetext);
// 把这个Widget绑定到RemoteViewsService
Intent serviceIntent = new Intent(context, FilpperAdapterService.class);
serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
// 设置适配器
remoteViews.setRemoteAdapter(R.id.avfilpper, serviceIntent);
// 设置当显示的widget_list为空显示的View
remoteViews.setEmptyView(R.id.avfilpper, R.id.iv_default);
// 点击列表触发事件
Intent clickIntent = new Intent(context, ImageTextWidget.class);
// 设置Action,方便在onReceive中区别点击事件
clickIntent.setAction(Constant.ACTION_CLICK_FILPPER);
clickIntent.setData(Uri.parse(clickIntent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent pendingIntentTemplate = PendingIntent.getBroadcast(
MyApplication.myApp, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setPendingIntentTemplate(R.id.avfilpper,
pendingIntentTemplate);
//默认图点击事件,重新发起网络请求更新数据
Intent reqIntent = new Intent(context, ImageTextWidget.class);
reqIntent.setAction(Constant.ACTION_CLICK_DEFAULTVIEW);
PendingIntent reqPanding = PendingIntent.getBroadcast(
MyApplication.myApp,0,reqIntent,PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.iv_default,reqPanding);
//由AppWidgetManager处理Wiget。
appWidgetManager.updateAppWidget(componentName, remoteViews);
}
2、新建服务类继承RemoteViewsService
public class FilpperAdapterService extends RemoteViewsService {
private static final String TAG = "FilpperAdapterService";
private Gson gson = new Gson();
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
Log.d(TAG,"onGetViewFactory... ");
return new FilpperRemoteViewsFactory(this.getApplicationContext(),intent);
}
新建factory类实现RemoteViewsFactory;
class FilpperRemoteViewsFactory implements RemoteViewsFactory {
private Context context;
private List<BannerAttachParentBean> listBean2s;
private Handler handler;
//图片缓存
private HashMap<String,Bitmap> imageMap;
...
}
3、重写factory类中的getViewAt方法
@Override
public RemoteViews getViewAt(int position) {
Log.d(TAG,"getViewAt...111...position: "+position);
Constant.currentWidgetPage = position;
if(position<0 || listBean2s == null || position>=listBean2s.size()) {
return null;
}
BannerAttachParentBean parentBean = listBean2s.get(position);
if(parentBean == null) {
return null;
}
int type = parentBean.getType();
RemoteViews rv = null;
//多布局
if(type == 0) { //图文类型
rv = new RemoteViews(context.getPackageName(), R.layout.image_text_widget);
imageTextLayout(rv,parentBean,position);
} else if(type == 2){ //商铺类型
rv = new RemoteViews(context.getPackageName(), R.layout.layout_miniprogram_recommend);
miniProgramLayout(rv,parentBean,position);
} else if(type == 100 && Constant.underTakeList != null) { //小程序兜底数据
//fliper里面不能再嵌套gridview,只能使用基础view实现
rv = new RemoteViews(context.getPackageName(), R.layout.layout_miniprogram_default4);
miniProgramDefaultLayout(rv,position);
}
return rv;
}
/**
* 图文板块
*/
private void imageTextLayout(RemoteViews rv,BannerAttachParentBean parentBean,int position) {
BannerAttachBean bannerAttachBean = parentBean.getBannerAttach();
rv.setTextViewText(R.id.tv_desc, bannerAttachBean.getMainContent());
// 填充Intent,填充在AppWdigetProvider中创建的PendingIntent
Intent fillInIntent = new Intent();
fillInIntent.putExtra("uri",gson.toJson(bannerAttachBean.getUri()));
rv.setOnClickFillInIntent(R.id.iv_image,fillInIntent);
if(imageMap.containsKey(parentBean.getBannerId())) {
rv.setImageViewBitmap(R.id.iv_image, imageMap.get(parentBean.getBannerId()));
} else {
final boolean[] isWait = {true};
Glide.with(MyApplication.myApp)
.asBitmap()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE) //禁用缓存
.load(bannerAttachBean.getBannerSourceURL())
.apply(RequestOptions.centerCropTransform().transform(new GlideRoundTransform(16)))
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
Log.i(TAG, "onResourceReady...resource: " + resource);
if (!resource.isRecycled()) {
rv.setImageViewBitmap(R.id.iv_image, resource);
Log.i(TAG, "onResourceReady...resource222: ");
isWait[0] = false;
imageMap.put(parentBean.getBannerId(),resource);
}
}
});
//2秒如果还没有回调,结束死循环;
Runnable runnable = () -> isWait[0] = false;
handler.postDelayed(runnable,2000);
Log.d(TAG,"getViewAt...while...isWait...start: " + position);
//必须要先接收到回调设置好图片,rv才能返回,无奈之举
while(isWait[0]){
Log.d(TAG,"getViewAt...while...isWait ");
}
Log.d(TAG,"getViewAt...while...isWait...end: " + position);
handler.removeCallbacks(runnable);
}
}
4、发送的点击pandingIntent消息在onReceive中接收处理;
/**
* 接收自定义的一些广播
*/
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
Log.d(TAG,"onReceive... ");
if(intent == null) {
return;
}
String intentAction = intent.getAction();
Log.d(TAG,"onReceive...intentAction: "+intentAction);
if("com.iflytek.autofly.skin".equals(intentAction)) {
changeSkin(context,intent);
} else if(Constant.ACTION_CLICK_FILPPER.equals(intentAction)) {
turnToApp(context,intent);
} else if(Constant.ACTION_CLICK_DEFAULTVIEW.equals(intentAction)) {
EventBus.getDefault().post(new EventBusBean.ReqWidgetDataEvent());
}
}
5、更新数据
更新widget
/**
* 更新widget
*/
@SuppressLint("ResourceType")
private void updateAppWidget() {
Log.d(TAG, "updateAppWidget... ");
//获取Widget的组件名
ComponentName componentName = new ComponentName(WidgetService.this, ImageTextWidget.class);
// 创建一个RemoteView
SkinRemoteViews remoteViews = new SkinRemoteViews(getPackageName(), R.layout.layout_widget);
// remoteViews.setImageViewResource(R.id.iv_background,R.mipmap.icon_widget_bg);
remoteViews.setImageViewResource(R.id.iv_default, R.mipmap.icon_widget_imagetext);
// 把这个Widget绑定到RemoteViewsService
Intent serviceIntent = new Intent(WidgetService.this, FilpperAdapterService.class);
serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)));
// 设置适配器
remoteViews.setRemoteAdapter(R.id.avfilpper, serviceIntent);
// 设置当显示的widget_list为空显示的View
remoteViews.setEmptyView(R.id.avfilpper, R.id.iv_default);
// 点击列表触发事件
Intent clickIntent = new Intent(WidgetService.this, ImageTextWidget.class);
// 设置Action,方便在onReceive中区别点击事件
clickIntent.setAction(Constant.ACTION_CLICK_FILPPER);
clickIntent.setData(Uri.parse(clickIntent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent pendingIntentTemplate = PendingIntent.getBroadcast(
MyApplication.myApp, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setPendingIntentTemplate(R.id.avfilpper,
pendingIntentTemplate);
//由AppWidgetManager处理Wiget。
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(MyApplication.myApp);
appWidgetManager.updateAppWidget(componentName, remoteViews);
}
更新数据
/**
* 更新widget数据
*/
private void updateWidget() {
ComponentName componentName = new ComponentName(WidgetService.this, ImageTextWidget.class);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(WidgetService.this.getApplicationContext());
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(componentName);
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.avfilpper);
}
6、课后讨论
widget滑动组件如何实现手动滑动翻页?