Accessing Layout Items from inside Widget AppWidgetProvider
I am starting to go insane trying to figure this out. It seems like it should be very easy, I'm starting to wonder if it's possible.
What I am trying to do is create a home screen widget, that only contains an ImageButton. When it is pressed, the idea is to change some setting (like the wi-fi toggle) and then change the Buttons image.
I have the ImageButton declared like this in my main.xml
<ImageButton android:id="@+id/buttonOne"
android:src="@drawable/button_normal_ringer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
my AppWidgetProvider class, named ButtonWidget
*** note that the RemoteViews class is a locally stored variable. this allowed me to get access to the RViews layout elements... or so I thought.
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
remoteViews = new RemoteViews(context.getPackageName(),
R.layout.main);
Intent active = new Intent(context, ButtonWidget.class);
active.setAction(VIBRATE_UPDATE);
active.putExtra("msg","TESTING");
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context,
0, active, 0);
remoteViews.setOnClickPendingIntent(R.id.buttonOne,
actionPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
@Override
public void onReceive(Context context, Intent intent) {
// v1.5 fix that doesn't call onDelete Action
final String action = intent.getAction();
Log.d("onReceive",action);
if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
final int appWidgetId = intent.getExtras().getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
this.onDeleted(context, new int[] { appWidgetId });
}
} else {
// check, if our Action was called
if (intent.getAction().equals(VIBRATE_UPDATE)) {
String msg = "null";
try {
msg = intent.getStringExtra("msg");
} catch (NullPointerException e) {
Log.e("Error", "msg = null");
}
Log.d("onReceive",msg);
if(remoteViews != null){
Log.d("onReceive",""+remoteViews.getLayoutId());
remoteViews.setImageViewResource(R.id.buttonOne, R.drawable.butto开发者_如何学运维n_pressed_ringer);
Log.d("onReceive", "tried to switch");
}
else{
Log.d("F!", "--naughty language used here!!!--");
}
}
super.onReceive(context, intent);
}
}
so, I've been testing this and the onReceive method works great, I'm able to send notifications and all sorts of stuff (removed from code for ease of reading)
the one thing I can't do is change any properties of the view elements.
To try and fix this, I made RemoteViews a local and static private variable. Using log's I was able to see that When multiple instances of the app are on screen, they all refer to the one instance of RemoteViews. perfect for what I'm trying to do
The trouble is in trying to change the image of the ImageButton.
I can do this from within the onUpdate method using this.
remoteViews.setImageViewResource(R.id.buttonOne, R.drawable.button_pressed_ringer);
that doesn't do me any good though once the widget is created. For some reason, even though its inside the same class, being inside the onReceive method makes that line not work.
That line used to throw a Null pointer as a matter of fact, until I changed the variable to static. now it passes the null test, refers to the same layoutId as it did at the start, reads the line, but it does nothing.
Its like the code isn't even there, just keeps chugging along.
SO......
Is there any way to modify layout elements from within a widget after the widget has been created!? I want to do this based on the environment, not with a configuration activity launch.
I've been looking at various questions and this seems to be an issue that really hasn't been solved, such as link text and link text
oh and for anyone who finds this and wants a good starting tutorial for widgets, this is easy to follow (though a bit old, it gets you comfortable with widgets) .pdf link text
hopefully someone can help here. I kinda have the feeling that this is illegal and there is a different way to go about this. I would LOVE to be told another approach!!!!
Thanks
You can redraw the screen using another layout with only the ImageButton modified. This sounds like a hackish solution but it works for me.
// Your appWidgetProvider class should track which layout it's currently on
private static int layoutId;
// onReceive should be something like this
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
int newLayoutId = bundle.getInt("layout_id");
// Change the current layout id to the layout id contained in the bundle
layoutId = newLayoutId;
initViews(context);
} else {
initViews(context);
}
}
// Initialize the view according to the chosen layout
private void initViews(Context context) {
RemoteViews views = null;
// Set the initial layout
if (layoutId == 0) {
Intent layoutIntent = new Intent("android.appwidget.action.APPWIDGET_UPDATE");
Bundle layoutBundle = new Bundle();
// I put in the layoutId of the next layout. Note that the layoutId is not
// from R. I just made up some easy to remember number for my layoutId
layoutBundle.putInt("layout_id", 1);
PendingIntent lPendingIntent = PendingIntent.getBroadcast(context, 0,
layoutIntent, PendingIntent.FLAG_ONE_SHOT);
// Set the layout to the first layout
views = new RemoteViews(context.getPackageName(), R.layout.layout_zero);
// I used buttons to trigger a layout change
views.setOnClickPendingIntent(R.id.btnNext, lPendingIntent);
} // Else if there's some trigger to change the layout...
else if (layoutId == 1) {
Intent layoutIntent = new Intent("android.appwidget.action.APPWIDGET_UPDATE");
Bundle layoutBundle = new Bundle();
// Since I only have two layouts, I put here the id of the previous layout
layoutBundle.putInt("layout_id", 0);
PendingIntent lPendingIntent = PendingIntent.getBroadcast(context, 0,
layoutIntent, PendingIntent.FLAG_ONE_SHOT);
// Set the layout to the second layout
// This layout should be almost the same as the first layout except for the
// part that you want to change
views = new RemoteViews(context.getPackageName(), R.layout.layout_one);
views.setOnClickPendingIntent(R.id.btnPrev, lPendingIntent);
}
}
As you can see, I used two different layouts which the user is able to switch via button clicks. You can use different triggers to change the layout depending on what you need. A similar solution can be found link text. Pardon me for putting the code in onReceive instead of onUpdate :)
@Avendael Your code works perfectly. Only thing you need to do is add update widget. Some thing like this
public static void buildUpdate(Context context) {
RemoteViews updateViews;
//Have your own condition here
if (1==1){
layoutId = R.layout.main;
} else {
layoutId = R.layout.main1;
}
updateViews = new RemoteViews(context.getPackageName(),
layoutId);
//actually do things here
//then finally, return our remoteView
AppWidgetManager.getInstance(context).updateAppWidget(
new ComponentName(context, ButtonWidget.class), updateViews);
}
Call this method buildUpdate(context) after assigning new remote view to update widget layout. For eg we can place this after views.setOnClickPendingIntent(); in the above code. This is really a great approach, was really helpful for me cheers...
精彩评论