【问题标题】:How to preserve AutoCompleteTextView's DropDown state when gets back from launched Activity从启动活动返回时如何保留 AutoCompleteTextView DropDown 状态
【发布时间】:2018-02-13 02:53:32
【问题描述】:

目前,当我

  • 通过点击AutoCompleteTextView 的下拉菜单启动新的Activity
  • 关闭已启动的Activity
  • AutoCompleteTextView 的下拉菜单已隐藏。

我想保留AutoCompleteTextView 的下拉状态,其中包括

  • 从启动Activity返回时不应隐藏下拉菜单
  • 应保留下拉菜单的滚动位置。

我不确定为什么当我从启动的Activity 回来时AutoCompleteTextView 的下拉菜单会被隐藏。因此,我尝试了 2 件事

  1. windowSoftInputMode 的已启动 ActivitystateAlwaysHidden 更改为 stateUnchanged
  2. onActivityResult中,当启动的Activity关闭时,显式执行mSearchSrcTextView.showDropDown();

但是,我仍然面临这个问题。 AutoCompleteTextView 的下拉列表之前的滚动位置不会被保留。它被重置回列表顶部。

这是更好地说明我面临的问题的屏幕截图。


(当前 AutoCompleteTextView 的下拉菜单滚动到末尾。我单击最后一项并启动新的Activity


(新Activity启动。现在,我点击BACK软键两次,关闭键盘,然后关闭Activity


(由于在onActivityResult 中显式调用了mSearchSrcTextView.showDropDown();,下拉列表再次显示。但是,它之前的滚动位置没有被保留。显示的是列表的开头而不是列表的结尾)

我想知道,在关闭之前启动的Activity 时,有什么方法可以保留 AutoCompleteTextView 的 DropDown 状态?

【问题讨论】:

  • 你需要一个布尔变量,如果下拉菜单显示为真,否则为假。然后将该变量与下拉列表一起保存。并且您可以轻松检查变量是否为真,这意味着下拉菜单可见,您只需使其可见。对于特定位置,我相信您需要使用该位置来获取下拉列表所在的项目位置,您可以轻松滚动或转到该位置?
  • 如何获取 AutoComplete 下拉菜单的滚动位置,并以编程方式更改其滚动位置?
  • 我没有尝试过,但就像我们保留列表视图的滚动位置一样,我相信我们也可以通过自动完成来做类似的事情。
  • 当您从列表中选择一个项目时,如何防止自动完成视图更改其文本?

标签: android


【解决方案1】:

对于AutoCompleteTextView,它有一个名为dismissDropDown() 的方法。我相信当从新推出的活动回来时,这个功能正在被触发。所以我们通过扩展 AutoCompleteTextView 并覆盖它的 dismissDropDown() 来解决这个问题。

我们添加一个布尔标志temporaryIgnoreDismissDropDown,表示是否暂时忽略dismissDropDown

public class MyAutoCompleteTextView extends AutoCompleteTextView {
    private boolean temporaryIgnoreDismissDropDown = false;

    .....

    @Override
    public void dismissDropDown() {
        if (this.temporaryIgnoreDismissDropDown) {
            this.temporaryIgnoreDismissDropDown = false;
            return;
        }

        super.dismissDropDown();
    }

    public void setTemporaryIgnoreDismissDropDown(boolean flag) {
        this.temporaryIgnoreDismissDropDown = flag;
    }
}

在启动新 Activity 之前,我们将 dismissDropDown 设置为 true。从启动的活动回来后,dismissDropDown 被调用。 override 方法检查temporaryIgnoreDismissDropDown 是否为真,只需将其设置为假并且什么都不做。所以跳过了真正的dismissDropDown

// myAutoCompleteTextView is instance of MyAutoCompleteTextView
myAutoCompleteTextView.setTemporaryIgnoreDismissDropDown(true);

// launch new Activity
startActivity(....);

希望对您有所帮助,祝您好运!

【讨论】:

  • 谢谢。潜入dismissDropDown 解决了我的问题。
【解决方案2】:

经过一个小时的编码、大量尝试和大量谷歌搜索,我已经制定了一个解决方案,可以满足您的需求。它使用反射来访问下拉菜单中的 ListView,并在您离开活动时访问下拉状态。

此代码有点长,所以我将引导您完成所有部分。首先,我需要一些变量:

boolean wasDropdownOpen;
int oldDropdownY;
Handler handler;

处理程序稍后将是必需的,因为我们必须在 onResume() 方法中做一些小技巧。在你的 onCreate() 方法中像往常一样初始化它:

handler = new Handler(getMainLooper());

现在,让我们进入棘手的部分。

您需要在开始任何活动之前调用以下方法。它不能在onPause() 中完成,因为在调用此方法时下拉菜单已经关闭。在我的测试代码中,我重写了 startActivity()startActivityForResult() 方法,并在那里调用了它,但您可以随意执行此操作。

private void processBeforeStart() {
    ListPopupWindow window = getWindow(textView);
    if(window == null) return;
    wasDropdownOpen = window.isShowing();

    ListView lv = getListView(window);
    if(lv == null) return;

    View view = lv.getChildAt(0);
    oldDropdownY = -view.getTop() + lv.getFirstVisiblePosition() * view.getHeight();
}

这将保存您的下拉列表视图的状态以备后用。现在,我们将加载它。这是我们需要的onResume() 方法:

@Override
protected void onResume() {
    super.onResume();
    if (wasDropdownOpen)
        textView.showDropDown();

    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            ListView lv = getListView(getWindow(textView));
            if (lv != null)
                scrollToY(lv, oldDropdownY);
        }
    }, 150);
}

首先,让我解释一下这个方法。如果下拉菜单打开,我们会保存状态,所以如果菜单打开,我们会重新打开。简单的。下一部分是滚动。我们需要在 Handler 中执行此操作,因为调用 onResume() 时 UI 尚未完全加载,因此仍然无法访问 ListView。

您看到的scrollToY() 方法是this post 代码的修改版本,因为Android 的ListView 没有内置方法来精确地设置滚动位置。

该方法的实现如下:

private void scrollToY(ListView lv, int position) {
    int itemHeight = lv.getChildAt(0).getHeight();
    int item = (int) Math.floor(position / itemHeight);
    int scroll = (item * itemHeight) - position;
    lv.setSelectionFromTop(item, scroll);// Important
}

现在,您可能已经看到了我在上面使用的getWindow()getListView() 方法。这些是我们必须使用的反射方法,因为 Android 没有公开公共 API 来访问 ListPopupWindowAutoCompleteTextView 内的 ListView。此外,DropDownListViewListView 的子类,实际在该对象中使用,对于外部也不可见,因此我们必须再次使用反射。

这是我的两个辅助方法的实现:

private ListView getListView(ListPopupWindow window) {
    for (Field field : window.getClass().getDeclaredFields()) {
        if (field.getType().getName().equals("android.widget.DropDownListView")) {
            field.setAccessible(true);
            try {
                return (ListView) field.get(window);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    return null;
}

private ListPopupWindow getWindow(AutoCompleteTextView tv) {
    Class realClass = tv.getClass().getName().contains("support") ? tv.getClass().getSuperclass() : tv.getClass();
    for (Field field : realClass.getDeclaredFields()) {
        if (field.getType().getName().equals(ListPopupWindow.class.getName())) {
            field.setAccessible(true);
            try {
                return (ListPopupWindow) field.get(tv);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    return null;
}

我已经在 Android O(API 级别 26)上对此进行了测试,它的工作原理与您描述的一样。

我希望我在这个答案中付出的努力让我有机会获得赏金;-)

【讨论】:

  • 谢谢。但是第一个答案通过防止下拉列表关闭来帮助我解决问题的根源。
  • @CheokYanCheng 好的,然后 1 小时一无所获
【解决方案3】:

听起来您已经想出了如何按需显示下拉菜单(通过showDropDown()),所以我只讨论如何恢复下拉菜单的滚动位置。

您可以像这样访问下拉菜单的第一个可见位置:

autocomplete.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        int firstVisiblePosition = parent.getFirstVisiblePosition();
        // save this value somehow
    }
});

保存此int 的值,但您想保存(在内存中,通过onSaveInstanceState(),将其传递给已启动的活动,以便它可以通过onActivityResult() 将其传回,等等)。然后,无论您在何处重新显示下拉菜单,都请执行以下操作:

autocomplete.showDropDown();
autocomplete.setListSelection(firstVisiblePosition);

这种技术的缺点是它使firstVisiblePosition处的项目完全可见,所以如果它被中途滚动到视野之外,列表位置将无法完美恢复。不幸的是,我不相信有任何方法可以保存/恢复这个部分视图偏移量。

【讨论】:

  • 我的回答甚至恢复了部分视图偏移
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-08
  • 1970-01-01
相关资源
最近更新 更多