安卓远程视图列表视图滚动

4

我正在尝试在AppWidget中将ListView滚动到特定位置。

但它没有做任何事情,我还尝试了setPosition方法,但也不起作用。

同时没有错误或堆栈跟踪。

代码:

                    if (list.size() == 0) {
                        loadLayout(R.layout.rooster_widget_header);
                        views.addView(R.id.header_container,
                                views);
                    } else {
                        views.setRemoteAdapter(R.id.lvWidget, svcIntent);
                        views.setScrollPosition(R.id.lvWidget, 3);
                    }
4个回答

7

问题:在显示之前,ListView没有任何子项。因此,在设置适配器后立即调用setScrollPosition无效。以下是在AbsListView中执行此检查的代码:

final int childCount = getChildCount();
if (childCount == 0) {
    // Can't scroll without children.
    return;
}

解决方案: 理想情况下,我会使用 ViewTreeObserver.OnGlobalLayoutListener 来设置 ListView 的滚动位置,但是在远程视图中不可能。通过一个带有延迟的可运行程序来设置滚动位置并调用 partiallyUpdateAppWidget。我已经修改了 Android 天气小部件代码,并在 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);
            ...
        }
    }
}

这是屏幕录制。它滚动到第五个位置。

应用程序小部件中的ListView自动滚动


实际上这个可以工作,但它没有滚动到正确的位置,我想我可以修复它。谢谢,拿走我的声望! - user2157571
@BenjaminFaal 这不是跟我的回答一模一样吗? - Vikram
不完全准确,因为涉及到postDelayed函数。 - user2157571

0

我认为我有解决方案,我在自己的代码中使用它。 顺便提一下,我有一些页面并希望按需显示它们:

getListView().setSelection(((moreServices-1)*20)+1);

对于你来说,这将是:

yourListView.setSelection(3);

希望能有所帮助...

编辑

我认为你应该看看那个... 那是老的,但也许从未改变过

如何制作可滚动的应用程序小部件?


我希望是那样,但我正在使用RemoteViews,所以不是那么回事。 - user2157571
你尝试过使用 setRelativeScrollPosition(int viewId, int offset) 吗? - Sidd
smoothScrollToPosition (int position, int boundPosition) 呢? - Sidd
这并不是不可能的,因为 RemoteViews 类中的那些方法都不是无用的。我只是认为它们没有正确地更新或同步。 - user2157571
你在滚动之前尝试过“通知数据已更改”吗? - Sidd

0
根据我所掌握的信息,我认为小部件没有使自身无效,因此它不会显示其上的更改。因此,我建议您刷新您的小部件。有两种方法可以做到这一点,第一种方法是:
appWidgetManager.updateAppWidget(widgetId, remoteViews);

如果我们没有直接访问 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);
}

这样更改应该会反映在小部件中。如果这不起作用,我需要更多细节来找出问题并编辑此答案。希望能有所帮助。


我需要你分享更多的代码来帮助你。 - jpardogo

0

两件事:

views.setScrollPosition(R.id.lvWidget, 3);

是正确的调用。在内部,setScrollPosition(int, int) 调用了 RemoteViews#setInt(viewId,"smoothScrollToPosition",position);

viewId                   :    your ListView's id
"smoothScrollToPosition" :    method name to invoke on the ListView
position                 :    position to scroll to

所以,你正在调用正确的方法。

以下是对方法AppWidgetManager#partiallyUpdateAppWidget(int, RemoteViews)的注释 - 取自AppWidgetManager.java的源代码。我相信它可以回答你的问题:...与{RemoteViews#showNext(int)},{RemoteViews#showPrevious(int)},{RemoteViews#setScrollPosition(int, int)}和类似命令一起使用...

/**
 * 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);
}

正如注释所示,在views.setScrollPosition(R.id.lvWidget, 3)之后调用partiallyUpdateAppWidget(appWidgetId, views)

注释还警告您:如果小部件没有通过{#updateAppWidget(int[], RemoteViews)}接收到完整的更新,则将忽略此方法。这可能意味着以下调用:

views.setRemoteAdapter(R.id.lvWidget, svcIntent);
views.setScrollPosition(R.id.lvWidget, 3);

不应该在一个更新中完成。我建议您将这些调用分成两个单独的更新:

首先:

views.setRemoteAdapter(R.id.lvWidget, svcIntent);
mAppWidgetManager.updateAppWidget(appWidgetIds, views);

第二点:

views.setScrollPosition(R.id.lvWidget, 3);
mAppWidgetManager.partiallyUpdateAppWidget(appWidgetId, views);

请注意,第一个是完全更新,而第二个仅为部分更新。

2
@BenjaminFaal 不完全是这样,因为涉及到postDelayed函数。很抱歉回复晚了。但是,这正是我所说的“建议将这些调用分成两个单独的更新”的意思。我认为这部分很清楚。无论如何,祝你好运。 - Vikram

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接