从启动的活动

时间:2018-01-02 12:52:31

标签: android

目前,当我

  • 点击Activity的下拉列表
  • 启动新的AutoCompleteTextView
  • 关闭已启动的Activity
  • AutoCompleteTextView的下拉隐藏。

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

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

当我从发起的AutoCompleteTextView回来时,我不确定隐藏Activity下拉列表的原因。因此,我尝试了两件事

  1. 将已发起的windowSoftInputMode的{​​{1}}从Activity更改为stateAlwaysHidden
  2. stateUnchanged中,当关闭已启动的onActivityResult时,请明确执行Activity
  3. 但是,我仍然面临着这个问题。不保留mSearchSrcTextView.showDropDown();下拉列表的上一个滚动位置。它会重置回列表顶部。

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

    enter image description here

    (当前AutoCompleteTextView的下拉列表滚动到结束。我点击最后一项并启动新的AutoCompleteTextView

    enter image description here

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

    enter image description here

    (由于ActivitymSearchSrcTextView.showDropDown();的显式调用,再次显示下拉列表。但是,之前的滚动位置未被保留。列表的开头显示而不是结束列表)

    我想知道,在关闭之前发布的onActivityResult时,有没有办法保留AutoCompleteTextView的DropDown状态?

3 个答案:

答案 0 :(得分:2)

对于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;
    }
}

在启动新活动之前,我们将dismissDropDown设置为true。从已启动的活动返回后,将调用dismissDropDown。覆盖方法检查temporaryIgnoreDismissDropDown是否为真,只需将其设置为false&没做什么。所以跳过真正的dismissDropDown

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

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

希望这有帮助,祝你好运!

答案 1 :(得分:1)

经过一个小时的编码,大量的尝试和大量的Google搜索,我已经整理出了一个可以满足你想要的解决方案。它使用反射来访问下拉菜单中的ListView,并在您离开活动时访问下拉状态。

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

boolean wasDropdownOpen;
int oldDropdownY;
Handler handler;

稍后需要处理程序,因为我们必须在onResume()方法中做一些小技巧。在onCreate()方法中照常初始化:

handler = new Handler(getMainLooper());

现在,让我们来看看棘手的部分。

在开始任何活动之前,您需要调用以下方法。它不能在onPause()中完成,因为在调用此方法时,Dropdown菜单已经关闭。在我的测试代码中,我已经覆盖了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();
}

这将保存您的下拉ListView的状态以供日后使用。现在,我们将加载它。这是我们需要的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来访问ListPopupWindow AutoCompleteTextView内的ListView。此外,DropDownListViewListView的一个子类,实际上在此对象中使用,对于oudside也是不可见的,所以我们必须再次使用Reflection。

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

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)上对此进行了测试,它的工作原理就像您所描述的那样。

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

答案 2 :(得分:0)

听起来你已经知道如何按需显示下拉菜单(通过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处的项目完全可见,因此如果它在视线中间滚动,则列表位置将无法完美恢复。不幸的是,我不相信有任何方法可以保存/恢复此部分视图偏移。