Im trying to scroll a ListView
to a particular position in an AppWidget
.
However it does not do anything, i also tried the setPosit
I think i have the solution, i use it in my own code. For information, i have some pages & want to show them on demand :
getListView().setSelection(((moreServices-1)*20)+1);
For you, it will be :
yourListView.setSelection(3)
;
Hope it helps...
EDIT
I think you should see that... that's older, but maybe that's never changed
How to make a scrollable app widget?
Two things:
views.setScrollPosition(R.id.lvWidget, 3);
is the correct call. Internally, setScrollPosition(int, int)
calls RemoteViews#setInt(viewId, "smoothScrollToPosition", position);
.
viewId : your ListView's id
"smoothScrollToPosition" : method name to invoke on the ListView
position : position to scroll to
So, you are calling the correct method.
Following is a comment for the method AppWidgetManager#partiallyUpdateAppWidget(int, RemoteViews)
- taken from the source code of AppWidgetManager.java
. I believe it answers your question: ... Use with {RemoteViews#showNext(int)}, {RemoteViews#showPrevious(int)},{RemoteViews#setScrollPosition(int, int)} and similar commands...
/**
* Perform an incremental update or command on the widget specified by appWidgetId.
*
* This update differs from {@link #updateAppWidget(int, RemoteViews)} in that the RemoteViews
* object which is passed is understood to be an incomplete representation of the widget, and
* hence is not cached by the AppWidgetService. Note that because these updates are not cached,
* any state that they modify that is not restored by restoreInstanceState will not persist in
* the case that the widgets are restored using the cached version in AppWidgetService.
*
* Use with {RemoteViews#showNext(int)}, {RemoteViews#showPrevious(int)},
* {RemoteViews#setScrollPosition(int, int)} and similar commands.
*
* <p>
* It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
* and outside of the handler.
* This method will only work when called from the uid that owns the AppWidget provider.
*
* <p>
* This method will be ignored if a widget has not received a full update via
* {@link #updateAppWidget(int[], RemoteViews)}.
*
* @param appWidgetId The AppWidget instance for which to set the RemoteViews.
* @param views The RemoteViews object containing the incremental update / command.
*/
public void partiallyUpdateAppWidget(int appWidgetId, RemoteViews views) {
partiallyUpdateAppWidget(new int[] { appWidgetId }, views);
}
As the comment indicates, call partiallyUpdateAppWidget(appWidgetId, views)
after views.setScrollPosition(R.id.lvWidget, 3)
.
The comment also warns you: This method will be ignored if a widget has not received a full update via {#updateAppWidget(int[], RemoteViews)}
. This might mean that the following calls:
views.setRemoteAdapter(R.id.lvWidget, svcIntent);
views.setScrollPosition(R.id.lvWidget, 3);
should not be made in one update. I suggest that you break these calls into two separate updates:
First:
views.setRemoteAdapter(R.id.lvWidget, svcIntent);
mAppWidgetManager.updateAppWidget(appWidgetIds, views);
Second:
views.setScrollPosition(R.id.lvWidget, 3);
mAppWidgetManager.partiallyUpdateAppWidget(appWidgetId, views);
Note that the first one is a full update, while the second one is only partial.
Whit the information I have, I think that the widget doesn't invalidate itself so it doesn't so the changes on it. So I would suggest to refresh your widget. There are two ways to do that, the first one is:
appWidgetManager.updateAppWidget(widgetId, remoteViews);
and there is a second way only in case we don't have direct access to AppWidgetManager
/**
* Refresh the views on the widget
* NOTE: Use only in case we dont have direct acces to AppWidgetManager
*/
private void invalidateWidget() {
ComponentName widget = new ComponentName(this, YourWidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(widget, mRv);
}
Like that the changes should be reflected in the widget. If this doesn't work I am gonna need more details to figure out the problem and edit this answer. Hope it helps.
Issue: ListView
does not have any children until it is displayed. Hence calling setScrollPosition
right after setting adpater has no effect. Following is the code in AbsListView which does this check:
final int childCount = getChildCount();
if (childCount == 0) {
// Can't scroll without children.
return;
}
Solution: Ideally I would have used ViewTreeObserver.OnGlobalLayoutListener
for setting the ListView
scroll position, but it is not possible in case of remote views. Set the scroll position and invoke partiallyUpdateAppWidget
in a runnable with some delay. I've modified the Android weather widget code and shared in git hub.
public class MyWidgetProvider extends AppWidgetProvider {
private static HandlerThread sWorkerThread;
private static Handler sWorkerQueue;
public MyWidgetProvider() {
// Start the worker thread
sWorkerThread = new HandlerThread("MyWidgetProvider-worker");
sWorkerThread.start();
sWorkerQueue = new Handler(sWorkerThread.getLooper());
}
public void onUpdate(Context context, final AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int i = 0; i < appWidgetIds.length; ++i) {
...
final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
views.setRemoteAdapter(R.id.lvWidget, svcIntent);
sWorkerQueue.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
views.setScrollPosition(R.id.list, 3);
appWidgetManager.partiallyUpdateAppWidget(appWidgetIds[i], views);
}
}, 1000);
appWidgetManager.updateAppWidget(appWidgetIds[i], views);
...
}
}
}
Here is the screen record. It scrolls to 5th position.