Android 小部件 getViewAt 调用次数过多
Android Widget getViewAt called too many times
我正在开发的小部件有问题。这是一个由内容提供者支持的简单列表视图。问题是方法 getViewAt()
被位置 0 调用了两次。这是代码和日志。
小工具 Xml 提供商:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="120dp"
android:minHeight="160dp"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/calendar_appwidget"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen|keyguard">
小部件服务
...
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new CalendarRemoteViewsFactory(this.getApplicationContext(),intent);
}
...
工厂
@Override
public void onCreate() {
readDateFormat(mContext);
simpleDateFormat = new SimpleDateFormat(datePattern, Locale.US);
mCal = Calendar.getInstance();
}
@Override
public RemoteViews getViewAt(int i) {
RemoteViews rv = new RemoteViews(mContext.getPackageName(),R.layout.calendar_widget_item);
Log.d("Widgets","Get view at "+i);
UpcomingShow show = (UpcomingShow)items.get(i);
mCal.setTime(getShowDay(show.getAirs_at()));
int dayOfMonth = mCal.get(Calendar.DAY_OF_MONTH);
if (mLastDay != dayOfMonth){
mLastDay = dayOfMonth;
rv.setTextViewText(R.id.day,String.valueOf(dayOfMonth));
rv.setViewVisibility(R.id.day, View.VISIBLE);
}else
rv.setViewVisibility(R.id.day, View.INVISIBLE);
rv.setTextViewText(R.id.title, show.getShow().getTitle());
try {
Date showDate = loadDate(show.getAirs_at(), datePattern);
simpleDateFormat.applyPattern(timeFormat == Utils.TimeFormat.CLOCK_24H ? "k:mm" : "h:mm a");
simpleDateFormat.setTimeZone(mTimeZone);
rv.setTextViewText(R.id.date,simpleDateFormat.format(showDate) + " on " + show.getShow().getNetwork());
} catch (Exception e) {
e.printStackTrace();
}
//Episode
String episodeStr = show.getEpisode().getSeason() + "x" + show.getEpisode().getNumber() + " " + show.getEpisode().getTitle();
rv.setTextViewText(R.id.episode,Utils.getTextInBold(episodeStr, 0, episodeStr.indexOf(" "), Spanned.SPAN_INCLUSIVE_EXCLUSIVE));
return rv;
}
@Override
public int getCount() {
return items != null ? items.size() : 0;
}
@Override
public RemoteViews getLoadingView() {
return null;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public void onDataSetChanged() {
final long identityToken = Binder.clearCallingIdentity();
pos = 0;
Log.d("Widgets","OnDataSetChanged");
String selection = DataProviderContract.Upcoming.COLUMN_AIRS_AT + " >= " + mCal.getTimeInMillis();
try {
mCursor = mContext.getContentResolver().query(
DataProviderContract.Upcoming.CONTENT_URI,
null,
selection,
null,
DataProviderContract.Upcoming.COLUMN_AIRS_AT + " ASC "
);
if (items!=null && items.size()>0)
items.clear();
items = getShowsFromCursor(mCursor);
if (mCursor!=null && !mCursor.isClosed())
mCursor.close();
} catch (Exception e) {
e.printStackTrace();
}finally {
Binder.restoreCallingIdentity(identityToken);
}
}
小部件提供程序
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.length;
// Perform this loop procedure for each App Widget that belongs to this provider
for (int i=0; i<N; i++) {
// Set up the intent that starts the StackViewService, which will
// provide the views for this collection.
Intent intent = new Intent(context, CalendarWidgetService.class);
// Add the app widget ID to the intent extras.
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
// Instantiate the RemoteViews object for the app widget layout.
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.calendar_appwidget);
// Set up the RemoteViews object to use a RemoteViews adapter.
// This adapter connects
// to a RemoteViewsService through the specified intent.
// This is how you populate the data.
rv.setRemoteAdapter(appWidgetIds[i], R.id.list, intent);
//Calls update
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
Logcat
03-26 10:02:54.011 1781-1792/com.D/Widget﹕ Acaba o parse com: 5
03-26 10:02:54.019 1781-1791/com.D/Widgets﹕ Get view at 0
03-26 10:02:54.027 1781-1792/com.D/Widgets﹕ Get view at 0
03-26 10:02:54.031 1781-1792/com.D/Widgets﹕ Get view at 1
03-26 10:02:54.031 1781-1792/com.D/Widgets﹕ Get view at 2
03-26 10:02:54.035 1781-1792/com.D/Widgets﹕ Get view at 3
03-26 10:02:54.039 1781-1792/com.D/Widgets﹕ Get view at 4
PS:
小部件布局
<?xml version="1.0" encoding="utf-8"?>
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@color/background"
android:dividerHeight="0dp"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/list"/>
如您所见,该方法被 pos 0 调用两次,有时会跳转位置,例如 getViewAt(1) -> getViewAt(3) -> getViewAt(2)。我不知道为什么会这样。我做错了什么。
使用 5.0.2、4.4.4、4.1.1 进行测试
谢谢
嗯,也许你应该让你的 getItemId()
return 至少位置而不是全 0。但这可能不是你问题的原因。
事实上,我认为 Android 对调用 getViewAt()
的顺序或次数没有任何保证。这不是你的错。更糟糕的是,在我的生产应用程序中,有时我会遇到要求我的位置与我的尺寸相同的情况,所以我崩溃了。
所以我的建议是,您可以安全地忽略调用的顺序或次数,并且您应该防止错误的位置:检查对 getViewAt()
和 getItemId()
的调用是否具有无效位置并使它们return 为空或 -1。
为了提高效率,ListView 调用这些位置没有特定的顺序,同一位置可以调用两次,或者 more.This 是预期的行为。
我正在开发的小部件有问题。这是一个由内容提供者支持的简单列表视图。问题是方法 getViewAt()
被位置 0 调用了两次。这是代码和日志。
小工具 Xml 提供商:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="120dp"
android:minHeight="160dp"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/calendar_appwidget"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen|keyguard">
小部件服务
...
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new CalendarRemoteViewsFactory(this.getApplicationContext(),intent);
}
...
工厂
@Override
public void onCreate() {
readDateFormat(mContext);
simpleDateFormat = new SimpleDateFormat(datePattern, Locale.US);
mCal = Calendar.getInstance();
}
@Override
public RemoteViews getViewAt(int i) {
RemoteViews rv = new RemoteViews(mContext.getPackageName(),R.layout.calendar_widget_item);
Log.d("Widgets","Get view at "+i);
UpcomingShow show = (UpcomingShow)items.get(i);
mCal.setTime(getShowDay(show.getAirs_at()));
int dayOfMonth = mCal.get(Calendar.DAY_OF_MONTH);
if (mLastDay != dayOfMonth){
mLastDay = dayOfMonth;
rv.setTextViewText(R.id.day,String.valueOf(dayOfMonth));
rv.setViewVisibility(R.id.day, View.VISIBLE);
}else
rv.setViewVisibility(R.id.day, View.INVISIBLE);
rv.setTextViewText(R.id.title, show.getShow().getTitle());
try {
Date showDate = loadDate(show.getAirs_at(), datePattern);
simpleDateFormat.applyPattern(timeFormat == Utils.TimeFormat.CLOCK_24H ? "k:mm" : "h:mm a");
simpleDateFormat.setTimeZone(mTimeZone);
rv.setTextViewText(R.id.date,simpleDateFormat.format(showDate) + " on " + show.getShow().getNetwork());
} catch (Exception e) {
e.printStackTrace();
}
//Episode
String episodeStr = show.getEpisode().getSeason() + "x" + show.getEpisode().getNumber() + " " + show.getEpisode().getTitle();
rv.setTextViewText(R.id.episode,Utils.getTextInBold(episodeStr, 0, episodeStr.indexOf(" "), Spanned.SPAN_INCLUSIVE_EXCLUSIVE));
return rv;
}
@Override
public int getCount() {
return items != null ? items.size() : 0;
}
@Override
public RemoteViews getLoadingView() {
return null;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public void onDataSetChanged() {
final long identityToken = Binder.clearCallingIdentity();
pos = 0;
Log.d("Widgets","OnDataSetChanged");
String selection = DataProviderContract.Upcoming.COLUMN_AIRS_AT + " >= " + mCal.getTimeInMillis();
try {
mCursor = mContext.getContentResolver().query(
DataProviderContract.Upcoming.CONTENT_URI,
null,
selection,
null,
DataProviderContract.Upcoming.COLUMN_AIRS_AT + " ASC "
);
if (items!=null && items.size()>0)
items.clear();
items = getShowsFromCursor(mCursor);
if (mCursor!=null && !mCursor.isClosed())
mCursor.close();
} catch (Exception e) {
e.printStackTrace();
}finally {
Binder.restoreCallingIdentity(identityToken);
}
}
小部件提供程序
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.length;
// Perform this loop procedure for each App Widget that belongs to this provider
for (int i=0; i<N; i++) {
// Set up the intent that starts the StackViewService, which will
// provide the views for this collection.
Intent intent = new Intent(context, CalendarWidgetService.class);
// Add the app widget ID to the intent extras.
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
// Instantiate the RemoteViews object for the app widget layout.
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.calendar_appwidget);
// Set up the RemoteViews object to use a RemoteViews adapter.
// This adapter connects
// to a RemoteViewsService through the specified intent.
// This is how you populate the data.
rv.setRemoteAdapter(appWidgetIds[i], R.id.list, intent);
//Calls update
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
Logcat
03-26 10:02:54.011 1781-1792/com.D/Widget﹕ Acaba o parse com: 5
03-26 10:02:54.019 1781-1791/com.D/Widgets﹕ Get view at 0
03-26 10:02:54.027 1781-1792/com.D/Widgets﹕ Get view at 0
03-26 10:02:54.031 1781-1792/com.D/Widgets﹕ Get view at 1
03-26 10:02:54.031 1781-1792/com.D/Widgets﹕ Get view at 2
03-26 10:02:54.035 1781-1792/com.D/Widgets﹕ Get view at 3
03-26 10:02:54.039 1781-1792/com.D/Widgets﹕ Get view at 4
PS: 小部件布局
<?xml version="1.0" encoding="utf-8"?>
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@color/background"
android:dividerHeight="0dp"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/list"/>
如您所见,该方法被 pos 0 调用两次,有时会跳转位置,例如 getViewAt(1) -> getViewAt(3) -> getViewAt(2)。我不知道为什么会这样。我做错了什么。
使用 5.0.2、4.4.4、4.1.1 进行测试
谢谢
嗯,也许你应该让你的 getItemId()
return 至少位置而不是全 0。但这可能不是你问题的原因。
事实上,我认为 Android 对调用 getViewAt()
的顺序或次数没有任何保证。这不是你的错。更糟糕的是,在我的生产应用程序中,有时我会遇到要求我的位置与我的尺寸相同的情况,所以我崩溃了。
所以我的建议是,您可以安全地忽略调用的顺序或次数,并且您应该防止错误的位置:检查对 getViewAt()
和 getItemId()
的调用是否具有无效位置并使它们return 为空或 -1。
为了提高效率,ListView 调用这些位置没有特定的顺序,同一位置可以调用两次,或者 more.This 是预期的行为。