Android 警告:Window 已经聚焦,忽略聚焦增益

Android Warning: Window already focused, ignoring focus gain

伙计们,我需要你们的眼睛。我用我的代码奋斗了很长时间,但我仍然像一个旧架子一样卡住了。

你看,有很多主题与 android 警告相关:

W/InputMethodManagerService: Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub...

我读了很多,没有发现任何有用的东西,我的眼睛很累。

我正在开发一个微调器组件,第一项名为 添加新... 会触发一个对话框以将另一个项目添加到列表中。因为我目前有两个这样的微调器(在一个 activity 中),所以我为微调器创建了一个包装器 class,它负责处理逻辑,并且我创建了 class,它扩展了 DialogFragment 因为两个微调器都使用 <EditText> 触发类似的对话框,只是标题不同。

但是在视图中,只有第一个微调器起作用。第二个旋转器,当choosig选项为添加new ... 时,什么也不做,只会产生上面写的警告。

微调包装器

KegAddActivity extends AppCompatActivity 的内部 class,看起来像这样:

public class ExtendableSpinner implements AdapterView.OnItemSelectedListener, View.OnTouchListener {
    private static final int ADD_NEW_ID = -1;

    private final Spinner _mSpinner;
    private final String _mTableName;
    private final int _mAddNewTitle;
    private final MatrixCursor _mAddOptionsCursor;

    private Uri _mAdapterUri;
    private SimpleCursorAdapter _mAdapter;
    private boolean _mWasTouched;

    public ExtendableSpinner(Spinner spinner, String tableName, int addNewTitle) {
        super();

        _mSpinner = spinner;
        _mTableName = tableName;
        _mAdapterUri = BeerBookUriHandler.getUri(tableName);
        _mAddNewTitle = addNewTitle;

        _mAddOptionsCursor = new MatrixCursor(new String[]{Table.COL_ID, BeerTable.COL_NAME});
        _mAddOptionsCursor.addRow(new String[]{"" + ADD_NEW_ID, getString(addNewTitle) + '\u2026'});

        _mSpinner.setOnTouchListener(this);
        _mSpinner.setOnItemSelectedListener(this);

    }

    public void setAdapterUri(Uri uri) {
        _mAdapterUri = uri;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        _mWasTouched = true;
        return false;
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (_mWasTouched) {
            // event fired by user
            if (id == ADD_NEW_ID) {
                Bundle args = new Bundle();
                args.putInt(AddNewDialogFragment.TITLE, _mAddNewTitle);

                _mAddNewDialogFragment = new AddNewDialogFragment();
                _mAddNewDialogFragment.setArguments(args);
                _mAddNewDialogFragment.show(getSupportFragmentManager(), _mTableName + "AddNewDialog");
            }
        } else {
            // event fired at activity start
            int selection = 0;
            if (_mAdapter.getCount() > 1) {
                selection = 1;
                Uri uri = BeerBookUriHandler.getUri(KegTable.NAME + "/last");
                Cursor cursor = getContentResolver().query(uri, new String[]{
                        _mTableName + "." + Table.COL_ID
                }, null, null, null);
                if (cursor != null && cursor.moveToFirst()) {
                    long lastUsedId = cursor.getLong(0);
                    selection = getPositionForId(lastUsedId);
                }
                cursor.close();
            }
            _mSpinner.setSelection(selection);
        }
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {

    }

    public void populate() {
        Cursor cursor = getContentResolver().query(_mAdapterUri,
                BEER_PROJECTION, null, null, BeerTable.COL_NAME);
        Cursor extendedCursor = new MergeCursor(new Cursor[]{ _mAddOptionsCursor, cursor});

        _mAdapter = new SimpleCursorAdapter(getApplicationContext(),
                SPINNER_LAYOUT, extendedCursor, new String[] { BeerTable.COL_NAME },
                new int[] {android.R.id.text1}, 0);
        _mAdapter.setDropDownViewResource(SPINNER_ITEM_LAYOUT);
        _mSpinner.setAdapter(_mAdapter);
    }

    public int getPositionForId(long itemId) {
        int pos = 0;
        for(int i = 1, l = _mAdapter.getCount(); i < l; i++)
            if (itemId == _mAdapter.getItemId(i)) {
                pos = i;
                break;
            }

        return pos;
    }

    public long getSelectedId() {
        return _mSpinner.getSelectedItemId();
    }

    public void setSelectionById(long id) {
        _mSpinner.setSelection(getPositionForId(id));
    }
}

KegAddActivity

onCreate() 方法中我像这样实例化微调器

protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ...

    final Spinner beerSpinner = (Spinner) findViewById(R.id.keg_add_beer);
    _mBeerSpinner = new ExtendableSpinner(beerSpinner, BeerTable.NAME, R.string.beer_add);
    // initially disable, wait for brewery selection
    beerSpinner.setEnabled(false);

    final Spinner brewerySpinner = (Spinner) findViewById(R.id.keg_add_brewery);
    _mBrewerySpinner = new ExtendableSpinner(brewerySpinner, BreweryTable.NAME, R.string.brewery_add) {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            super.onItemSelected(parent, view, position, id);
            if (id > 0) {
                // brewery selected, populate beerSpinner
                Uri uri = BeerBookUriHandler.getUri(BreweryTable.NAME + "/" +
                        getSelectedId() + "/" + BeerTable.NAME);
                _mBeerSpinner.setAdapterUri(uri);
                _mBeerSpinner.populate();
                beerSpinner.setEnabled(true);
            }
        }
    };
    _mBrewerySpinner.populate();

    ...
}

我目前的怀疑

因为第一个微调器工作而第二个微调器不工作并且它们非常相似,我认为微调器以某种方式并发聚焦,但我真的不知道。 我尝试将 DialogFragment 替换为专用于每个微调器的单独活动和微调器的 onItemSelected 事件,然后调用 startActivityForResult() 但这导致了同样的问题,第一个微调器运行良好,第二个微调器失败警告,所以问题可能出在 Spinners 的代码中。

终于有进步了!

幸运的是,我找到了它起作用的星座,然后找出它不起作用的原因。

如果 添加新... 的项目当前在微调器中 selected,然后 selecting 添加新... 项目再次产生 ignoring focus gain 警告。但是,如果您 select 微调器中的一些其他项目,然后 select 添加新的...,它会起作用。问题是,如果微调器中没有其他项目,则无法执行此类 reselection。如果您 select 一个已经 select 的选项,似乎 onItemSelected 事件没有传播。但这是一个主要问题,因为用户可以在 AddNewDialog 处点击 取消 以 return 返回主视图,然后 添加新的。 .. 将保持 selected 并且可能是唯一的选择,因此无法再次打开 AddNewDialog

如何让它工作的解决方案是添加另一个默认项,例如 从列表中选择... 项,如果用户取消对话框,重新select微调器为默认选项。我会测试它并保持这个 post 更新。

结论

如果您从 Spinner 中选择一个之前 selected 的项目,则 itemSelected 事件不会传播,并且 W/InputMethodManagerService: Window already focused, ignoring focus gain 警告会出现在您的日志中。

由于 Spinner 不支持 onItemClick 事件,我发现唯一可行的解​​决方法是定义一个默认选项 从列表中选择...如果用户取消 AddNewDialogonDialogNegativeClick 侦听器会将微调器重置为默认值 Choose from... 项,因此在单击 添加新项之后。 .. select离子发生变化,事件传播。

我们遇到了同样的问题,并确定解决此问题的唯一方法是创建一个调用 onItemSelected() 的自定义 Spinner,即使选择了相同的项目也是如此:

public class CustomSpinner extends Spinner {

        public CustomSpinner(Context contextArg){
            super(contextArg);
        }

        public CustomSpinner(Context contextArg, AttributeSet attributeSetArg){
            super(contextArg, attributeSetArg);
        }

        public CustomSpinner(Context contextArg, AttributeSet attributeSetArg, int styleArg){
            super(contextArg, attributeSetArg, styleArg);
        }

        @Override
        public void setSelection(int positionArg){
            boolean samePosition = positionArg == getSelectedItemPosition();
            super.setSelection(positionArg, false);
            // here we modifiy the Spinner's default behavior
            if(samePosition){
                // we dispatch the event, even if the position is the same
                OnItemSelectedListener onItemSelectedListener = getOnItemSelectedListener();
                if(onItemSelectedListener != null){
                    onItemSelectedListener.onItemSelected(this,
                            getSelectedView(),
                            positionArg,
                            getSelectedItemId());
                }
            }
        }

        @Override
        public void setSelection(int positionArg, boolean animateArg){
            boolean samePosition = positionArg == getSelectedItemPosition();
            super.setSelection(positionArg, animateArg);
            // here we modifiy the Spinner's default behavior
            if(samePosition){
                // we dispatch the event, even if the position is the same
                OnItemSelectedListener onItemSelectedListener = getOnItemSelectedListener();
                if(onItemSelectedListener != null){
                    onItemSelectedListener.onItemSelected(this,
                            getSelectedView(),
                            positionArg,
                            getSelectedItemId());
                }
            }
        }
}