如何保存和恢复 AndroidWidgetHostViews
How can I save and restore AndroidWidgetHostViews
我正在尝试构建自定义启动器应用程序。当 activity 重新启动时,我一直在恢复小部件。
选择一个小部件后,widgetID 将存储到共享首选项,在 onCreate 中,我正在尝试恢复之前设置的小部件。我正在关注 this 教程。
AppWidgetManager mAppWidgetManager;
AppWidgetHost mAppWidgetHost;
ViewGroup hostView;
int numWidgets = 0;
/**
* Called on the creation of the activity.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dashboard);
hostView = (ViewGroup) findViewById(R.id.LAYOUT_DASHBOARD);
hostView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if(numWidgets < 4) {
selectWidget();
}else {
removeAllWidgets();
numWidgets = 0;
}
return false;
}
});
mAppWidgetManager = AppWidgetManager.getInstance(this);
mAppWidgetHost = new AppWidgetHost(this, R.id.APPWIDGET_HOST_ID);
restoreWidgets();
}
/**
* Launches the menu to select the widget. The selected widget will be on
* the result of the activity.
*/
void selectWidget() {
int appWidgetId = this.mAppWidgetHost.allocateAppWidgetId();
Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
addEmptyData(pickIntent);
startActivityForResult(pickIntent, R.id.REQUEST_PICK_APPWIDGET);
}
/**
* This avoids a bug in the com.android.settings.AppWidgetPickActivity,
* which is used to select widgets. This just adds empty extras to the
* intent, avoiding the bug.
*
* See more: http://code.google.com/p/android/issues/detail?id=4272
*/
void addEmptyData(Intent pickIntent) {
ArrayList<AppWidgetProviderInfo> customInfo = new ArrayList<AppWidgetProviderInfo>();
pickIntent.putParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo);
ArrayList<Bundle> customExtras = new ArrayList<Bundle>();
pickIntent.putParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras);
}
/**
* If the user has selected an widget, the result will be in the 'data' when
* this function is called.
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == R.id.REQUEST_PICK_APPWIDGET) {
configureWidget(data);
} else if (requestCode == R.id.REQUEST_CREATE_APPWIDGET) {
createWidget(data);
}
} else if (resultCode == RESULT_CANCELED && data != null) {
int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
if (appWidgetId != -1) {
mAppWidgetHost.deleteAppWidgetId(appWidgetId);
}
}
}
/**
* Checks if the widget needs any configuration. If it needs, launches the
* configuration activity.
*/
private void configureWidget(Intent data) {
Bundle extras = data.getExtras();
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
if (appWidgetInfo.configure != null) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
intent.setComponent(appWidgetInfo.configure);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
startActivityForResult(intent, R.id.REQUEST_CREATE_APPWIDGET);
} else {
createWidget(data);
}
}
/**
* Creates the widget and adds to our view layout.
*/
public void createWidget(Intent data) {
if(numWidgets<4) {
Bundle extras = data.getExtras();
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
AppWidgetHostView widHostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
widHostView.setAppWidget(appWidgetId, appWidgetInfo);
SharedPreferences sharedPref = this.getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt("ID"+numWidgets, appWidgetId);
editor.commit();
attachWidget(widHostView);
}
}
/**
* Attaches a new widget at the right position on hostView
* @param widHostView widget to attach
*/
public void attachWidget(AppWidgetHostView widHostView){
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(hostView.getWidth() / 2, hostView.getHeight() / 2);
if (numWidgets < 2) {
lp.leftMargin = numWidgets * (hostView.getWidth() / 2);
lp.topMargin = 0;
} else {
lp.leftMargin = (numWidgets - 2) * (hostView.getWidth() / 2);
lp.topMargin = hostView.getHeight() / 2;
}
this.hostView.addView(widHostView, lp);
numWidgets++;
}
/**
* Restores all widgets from shared preferences
*/
public void restoreWidgets()
{
SharedPreferences sharedPref = this.getPreferences(Context.MODE_PRIVATE);
restoreWidget(sharedPref.getInt("ID0", -1));
restoreWidget(sharedPref.getInt("ID1", -1));
restoreWidget(sharedPref.getInt("ID2", -1));
restoreWidget(sharedPref.getInt("ID3", -1));
}
/**
* Restores a single widget
*/
public void restoreWidget(int _widgetId){
if(_widgetId < 0){
return;
}
AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(_widgetId);
AppWidgetHostView hostView = mAppWidgetHost.createView(this, _widgetId, appWidgetInfo);
hostView.setAppWidget(_widgetId, appWidgetInfo);
attachWidget(hostView);
}
/**
* Registers the AppWidgetHost to listen for updates to any widgets this app
* has.
*/
@Override
protected void onStart() {
super.onStart();
mAppWidgetHost.startListening();
}
/**
* Stop listen for updates for our widgets (saving battery).
*/
@Override
protected void onStop() {
super.onStop();
mAppWidgetHost.stopListening();
}
/**
* Removes the widget displayed by this AppWidgetHostView.
*/
public void removeWidget(AppWidgetHostView hostView) {
mAppWidgetHost.deleteAppWidgetId(hostView.getAppWidgetId());
this.hostView.removeView(hostView);
}
/**
* Removes all widgets displayed on screen.
*/
public void removeAllWidgets(){
boolean stop = false;
do{
int childCount = hostView.getChildCount();
if (childCount >= 1) {
View view = hostView.getChildAt(childCount - 1);
if (view instanceof AppWidgetHostView) {
removeWidget((AppWidgetHostView) view);
}
}else{
stop = true;
}
}while (!stop);
}
}
我为此做了一些调试并意识到,您需要为添加的每个小部件存储 appWidgetIds。当我存储 appWidgetIds 并尝试从中获取 AppWidgetProviderInfo 时,宽度和高度始终为 0,但 mAppWidgetHost.createView 将 return 成为有效视图。因此,要修复此存储最后的宽度和高度、id 以及您希望 WidgetHolder 的任何其他数据。然后,您可以根据需要将这些值存储到 SharedPreferences 中。
这是一个有效的 WidgetHolder class 我写了...
import android.util.Base64;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class WidgetHolder implements Serializable {
// any other data you want
public int id;
public int width, height;
public WidgetHolder() {}
public WidgetHolder(int id, int width, int height) {
this.id = id;
this.width = width;
this.height = height;
}
public static WidgetHolder deserialize( String data ) {
try {
byte [] bites = Base64.decode( data.getBytes(), 0);
ByteArrayInputStream bis = new ByteArrayInputStream( bites );
ObjectInputStream ois = new ObjectInputStream( bis );
WidgetHolder holder = (WidgetHolder) ois.readObject();
ois.close();
return holder;
} catch (IOException e ) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return new WidgetHolder();
}
public String serialize() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( baos );
oos.writeObject(this);
oos.flush();
return new String(Base64.encode(baos.toByteArray(), 0));
} catch( IOException e ) {
return "";
}
}
}
然后,每当您将新的 AppWidget 添加到 AppWidgetHost 时,您都会将 WidgetHolder 数据存储到 SharedPreferences 或缓存文件中。
private void handleAddNewAppWidget( int appWidgetId, AppWidgetProviderInfo info ) {
// use application context, for issues with support v7 libs causing AppCompatImage exceptions
AppWidgetHostView hostView = mAppWidgetHost
.createView(context.getApplicationContext(), appWidgetId, info);
hostView.setAppWidget(appWidgetId, info);
// calculate width and height for hostView from info.minHeight and info.minWidth
RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( width, height );
hostView.setLayoutParams(rlp);
// add hostView to widgetContainer (RelativeLayout)
widgetContainer.addView(hostView);
// create widgetHolder
WidgetHolder holder = new WidgetHolder( appWidgetId, width, height );
String serialized = holder.serialize();
// storage is a wrapper for SharedPreferences, the key is the appWidgetId, and the value is the serialized content
storage.store(appWidgetId + "", serialized);
}
private void handleRestoreWidgets(ArrayList<String> widgets) {
// Retrieve all saved widgets from Storage
for( int i = 0; i < widgets.size(); i++ ) {
String widgetString = widgets.get(i);
WidgetHolder holder = WidgetHolder.deserialize(widgetString);
AppWidgetProviderInfo info = AppWidgetManager
.getInstance(getContext().getApplicationContext() )
.getAppWidgetInfo(holder.id);
AppWidgetHostView hostView = mAppWidgetHost
.createView(context.getApplicationContext(), holder.id, info);
hostView.setAppWidget(holder.id, info);
RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( holder.width,
holder.height );
hostView.setLayoutParams(rlp);
// restore app widget from holder
widgetContainer.addView(hostView);
}
}
为简洁起见,未包括存储 class,因为此 post 已经很长了。但是,从 SharedPreferences 中获取所有键值对很容易。
一个小例子...
Map<String, String> allKeyValues = getContext().getSharedPreferences("WidgetHolders", Context.MODE_PRIVATE).getAll();
// then to get all stored String do
ArrayList<String> serializedWidgetHolders = new ArrayList<String>( allKeyValues.values() );
我正在尝试构建自定义启动器应用程序。当 activity 重新启动时,我一直在恢复小部件。
选择一个小部件后,widgetID 将存储到共享首选项,在 onCreate 中,我正在尝试恢复之前设置的小部件。我正在关注 this 教程。
AppWidgetManager mAppWidgetManager;
AppWidgetHost mAppWidgetHost;
ViewGroup hostView;
int numWidgets = 0;
/**
* Called on the creation of the activity.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dashboard);
hostView = (ViewGroup) findViewById(R.id.LAYOUT_DASHBOARD);
hostView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if(numWidgets < 4) {
selectWidget();
}else {
removeAllWidgets();
numWidgets = 0;
}
return false;
}
});
mAppWidgetManager = AppWidgetManager.getInstance(this);
mAppWidgetHost = new AppWidgetHost(this, R.id.APPWIDGET_HOST_ID);
restoreWidgets();
}
/**
* Launches the menu to select the widget. The selected widget will be on
* the result of the activity.
*/
void selectWidget() {
int appWidgetId = this.mAppWidgetHost.allocateAppWidgetId();
Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
addEmptyData(pickIntent);
startActivityForResult(pickIntent, R.id.REQUEST_PICK_APPWIDGET);
}
/**
* This avoids a bug in the com.android.settings.AppWidgetPickActivity,
* which is used to select widgets. This just adds empty extras to the
* intent, avoiding the bug.
*
* See more: http://code.google.com/p/android/issues/detail?id=4272
*/
void addEmptyData(Intent pickIntent) {
ArrayList<AppWidgetProviderInfo> customInfo = new ArrayList<AppWidgetProviderInfo>();
pickIntent.putParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo);
ArrayList<Bundle> customExtras = new ArrayList<Bundle>();
pickIntent.putParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras);
}
/**
* If the user has selected an widget, the result will be in the 'data' when
* this function is called.
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == R.id.REQUEST_PICK_APPWIDGET) {
configureWidget(data);
} else if (requestCode == R.id.REQUEST_CREATE_APPWIDGET) {
createWidget(data);
}
} else if (resultCode == RESULT_CANCELED && data != null) {
int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
if (appWidgetId != -1) {
mAppWidgetHost.deleteAppWidgetId(appWidgetId);
}
}
}
/**
* Checks if the widget needs any configuration. If it needs, launches the
* configuration activity.
*/
private void configureWidget(Intent data) {
Bundle extras = data.getExtras();
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
if (appWidgetInfo.configure != null) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
intent.setComponent(appWidgetInfo.configure);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
startActivityForResult(intent, R.id.REQUEST_CREATE_APPWIDGET);
} else {
createWidget(data);
}
}
/**
* Creates the widget and adds to our view layout.
*/
public void createWidget(Intent data) {
if(numWidgets<4) {
Bundle extras = data.getExtras();
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
AppWidgetHostView widHostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
widHostView.setAppWidget(appWidgetId, appWidgetInfo);
SharedPreferences sharedPref = this.getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt("ID"+numWidgets, appWidgetId);
editor.commit();
attachWidget(widHostView);
}
}
/**
* Attaches a new widget at the right position on hostView
* @param widHostView widget to attach
*/
public void attachWidget(AppWidgetHostView widHostView){
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(hostView.getWidth() / 2, hostView.getHeight() / 2);
if (numWidgets < 2) {
lp.leftMargin = numWidgets * (hostView.getWidth() / 2);
lp.topMargin = 0;
} else {
lp.leftMargin = (numWidgets - 2) * (hostView.getWidth() / 2);
lp.topMargin = hostView.getHeight() / 2;
}
this.hostView.addView(widHostView, lp);
numWidgets++;
}
/**
* Restores all widgets from shared preferences
*/
public void restoreWidgets()
{
SharedPreferences sharedPref = this.getPreferences(Context.MODE_PRIVATE);
restoreWidget(sharedPref.getInt("ID0", -1));
restoreWidget(sharedPref.getInt("ID1", -1));
restoreWidget(sharedPref.getInt("ID2", -1));
restoreWidget(sharedPref.getInt("ID3", -1));
}
/**
* Restores a single widget
*/
public void restoreWidget(int _widgetId){
if(_widgetId < 0){
return;
}
AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(_widgetId);
AppWidgetHostView hostView = mAppWidgetHost.createView(this, _widgetId, appWidgetInfo);
hostView.setAppWidget(_widgetId, appWidgetInfo);
attachWidget(hostView);
}
/**
* Registers the AppWidgetHost to listen for updates to any widgets this app
* has.
*/
@Override
protected void onStart() {
super.onStart();
mAppWidgetHost.startListening();
}
/**
* Stop listen for updates for our widgets (saving battery).
*/
@Override
protected void onStop() {
super.onStop();
mAppWidgetHost.stopListening();
}
/**
* Removes the widget displayed by this AppWidgetHostView.
*/
public void removeWidget(AppWidgetHostView hostView) {
mAppWidgetHost.deleteAppWidgetId(hostView.getAppWidgetId());
this.hostView.removeView(hostView);
}
/**
* Removes all widgets displayed on screen.
*/
public void removeAllWidgets(){
boolean stop = false;
do{
int childCount = hostView.getChildCount();
if (childCount >= 1) {
View view = hostView.getChildAt(childCount - 1);
if (view instanceof AppWidgetHostView) {
removeWidget((AppWidgetHostView) view);
}
}else{
stop = true;
}
}while (!stop);
}
}
我为此做了一些调试并意识到,您需要为添加的每个小部件存储 appWidgetIds。当我存储 appWidgetIds 并尝试从中获取 AppWidgetProviderInfo 时,宽度和高度始终为 0,但 mAppWidgetHost.createView 将 return 成为有效视图。因此,要修复此存储最后的宽度和高度、id 以及您希望 WidgetHolder 的任何其他数据。然后,您可以根据需要将这些值存储到 SharedPreferences 中。
这是一个有效的 WidgetHolder class 我写了...
import android.util.Base64;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class WidgetHolder implements Serializable {
// any other data you want
public int id;
public int width, height;
public WidgetHolder() {}
public WidgetHolder(int id, int width, int height) {
this.id = id;
this.width = width;
this.height = height;
}
public static WidgetHolder deserialize( String data ) {
try {
byte [] bites = Base64.decode( data.getBytes(), 0);
ByteArrayInputStream bis = new ByteArrayInputStream( bites );
ObjectInputStream ois = new ObjectInputStream( bis );
WidgetHolder holder = (WidgetHolder) ois.readObject();
ois.close();
return holder;
} catch (IOException e ) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return new WidgetHolder();
}
public String serialize() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( baos );
oos.writeObject(this);
oos.flush();
return new String(Base64.encode(baos.toByteArray(), 0));
} catch( IOException e ) {
return "";
}
}
}
然后,每当您将新的 AppWidget 添加到 AppWidgetHost 时,您都会将 WidgetHolder 数据存储到 SharedPreferences 或缓存文件中。
private void handleAddNewAppWidget( int appWidgetId, AppWidgetProviderInfo info ) {
// use application context, for issues with support v7 libs causing AppCompatImage exceptions
AppWidgetHostView hostView = mAppWidgetHost
.createView(context.getApplicationContext(), appWidgetId, info);
hostView.setAppWidget(appWidgetId, info);
// calculate width and height for hostView from info.minHeight and info.minWidth
RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( width, height );
hostView.setLayoutParams(rlp);
// add hostView to widgetContainer (RelativeLayout)
widgetContainer.addView(hostView);
// create widgetHolder
WidgetHolder holder = new WidgetHolder( appWidgetId, width, height );
String serialized = holder.serialize();
// storage is a wrapper for SharedPreferences, the key is the appWidgetId, and the value is the serialized content
storage.store(appWidgetId + "", serialized);
}
private void handleRestoreWidgets(ArrayList<String> widgets) {
// Retrieve all saved widgets from Storage
for( int i = 0; i < widgets.size(); i++ ) {
String widgetString = widgets.get(i);
WidgetHolder holder = WidgetHolder.deserialize(widgetString);
AppWidgetProviderInfo info = AppWidgetManager
.getInstance(getContext().getApplicationContext() )
.getAppWidgetInfo(holder.id);
AppWidgetHostView hostView = mAppWidgetHost
.createView(context.getApplicationContext(), holder.id, info);
hostView.setAppWidget(holder.id, info);
RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( holder.width,
holder.height );
hostView.setLayoutParams(rlp);
// restore app widget from holder
widgetContainer.addView(hostView);
}
}
为简洁起见,未包括存储 class,因为此 post 已经很长了。但是,从 SharedPreferences 中获取所有键值对很容易。
一个小例子...
Map<String, String> allKeyValues = getContext().getSharedPreferences("WidgetHolders", Context.MODE_PRIVATE).getAll();
// then to get all stored String do
ArrayList<String> serializedWidgetHolders = new ArrayList<String>( allKeyValues.values() );