输入法是dialog,还是全屏dialog,且不会被dismiss,显示与隐藏系统调用方法为showWindow(true)和hide(),以下是输入法Window创建,SoftInputWindow继承Dialog,这个不必多说,
mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState, WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false); mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars()); mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM); mWindow.getWindow().getAttributes().setFitInsetsIgnoringVisibility(true);
至于全屏的依据,可以看SoftInputWindow initDockWindow方法,
看,写入了WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
这个Flag啥意思呢?就是将窗口放置在整个屏幕内,忽略父容器的限制,
private void initDockWindow() { WindowManager.LayoutParams lp = getWindow().getAttributes(); lp.type = mWindowType; lp.setTitle(mName); lp.gravity = mGravity; updateWidthHeight(lp); getWindow().setAttributes(lp); int windowSetFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; int windowModFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_DIM_BEHIND; if (!mTakesFocus) { windowSetFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; } else { windowSetFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; windowModFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; } getWindow().setFlags(windowSetFlags, windowModFlags); }
如果要输入法上移,必须调整dialog中的布局,
在InputMethodService方法中,initView如下,
mWindow.setContentView(mRootView),关键,
mWindow是SoftInputWindow,继承Dialog,因此这是设置视角;
如何,一些成员的初始化在此进行,如mInputframe和mCandidatesframe,这是啥呢?在setInputView中回调onCreateView方法,拿到三方输入法自定义的View,
所以显示在我们面前的视野就是mInputframe;
void initViews() { mInitialized = false; mViewsCreated = false; mShowInputRequested = false; mShowInputFlags = 0; mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService); mRootView = mInflater.inflate( com.android.internal.R.layout.input_method, null); mWindow.setContentView(mRootView); mRootView.getViewTreeObserver().removeonComputeInternalInsetsListener(mInsetsComputer); mRootView.getViewTreeObserver().addonComputeInternalInsetsListener(mInsetsComputer); if (Settings.Global.getInt(getContentResolver(), Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) { mWindow.getWindow().setWindowAnimations( com.android.internal.R.style.Animation_InputMethodFancy); } mFullscreenArea = mRootView.findViewById(com.android.internal.R.id.fullscreenArea); mExtractViewHidden = false; mExtractframe = mRootView.findViewById(android.R.id.extractArea); mExtractView = null; mExtractEditText = null; mExtractAccessories = null; mExtractAction = null; mFullscreenApplied = false; mCandidatesframe = mRootView.findViewById(android.R.id.candidatesArea); mInputframe = mRootView.findViewById(android.R.id.inputArea); mInputView = null; mIsInputViewShown = false; mExtractframe.setVisibility(View.GONE); mCandidatesVisibility = getCandidatesHiddenVisibility(); mCandidatesframe.setVisibility(mCandidatesVisibility); mInputframe.setVisibility(View.GONE); }
因为要上移输入法,尝试对window的attribute参数下手,失败告终,因为设置y参数对全屏输入法无效,由于service的onCreate方法new SoftInputWindow时,传入Gravity和Bottom,输入法就默认显示底部?调试时改为Top,依然显示在底部,比较费解。
FullscreenArea = mRootView.findViewById(com.android.internal.R.id.fullscreenArea);具体资源看不到,这个应该覆盖了整个屏幕
综上所述,输入法类似以下布局,暂不讨论屏幕旋转之内,这个在SoftInputWindow会更新Gravity,
处理上移问题,就要上移mInputframe,
怎么移?mInputframe本身是framelayout,而且位置在android资源文件中写死了,通过代码动态移动?
想过WindowManager的updateLayout,可是布局参数呢?
或者直接调用setY,可问题是这样的效果类似动画,原View并没有移动,也就导致点击事件不可用,
或者view的startMoveTask方法?感觉都不行,
一般三方自定义输入法时,需要继承InputMethodService类,这是个标准,
重写其中onCreateVIew方法,也就传入自定义输入法布局,系统通过如下方法回调,
public void updateInputViewShown() { boolean isShown = mShowInputRequested && onevaluateInputViewShown(); if (mIsInputViewShown != isShown && mDecorViewVisible) { mIsInputViewShown = isShown; mInputframe.setVisibility(isShown ? View.VISIBLE : View.GONE); if (mInputView == null) { initialize(); View v = onCreateInputView(); if (v != null) { setInputView(v); } } } }
之后,通过setInputView方法设置显示在用户面前的视野,可以看到,视野添加到mInputframe中,对于另外一个方法setCandidatesView,就是候选视图,暂不多说,候选嘛。
public void setInputView(View view) { mInputframe.removeAllViews(); mInputframe.addView(view, new frameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); mInputView = view; }
所以我们要挪动输入法,可以在这里做文章,即添加一点参数,让mInputView上移一点,
frameLayout.LayoutParams提供了bottomMargin参数,即让咱们的mInputView相对bottom移动一点距离,单位是像素不是dp,
-------------------------------------------------------------------------------------------------------------------------
接下来,分析下输入法调用栈,
首先,窗口获得焦距后,才会调用输入法,这是起点,位于ViewRootImpl.handleWindowFocusChanged方法,
跟进其中,
// Note: must be done after the focus change callbacks, // so all of the view state is set up correctly. mImeFocusController.onPostWindowFocus(mView.findFocus(), hasWindowFocus, mWindowAttributes);
跟进,
immDelegate.startInputAsynconWindowFocusGain(viewForWindowFocus, windowAttribute.softInputMode, windowAttribute.flags, forceFocus);
跟进,
mService.startInputOrWindowGainedFocus( startInputReason, mClient, focusedView.getWindowToken(), startInputFlags, softInputMode, windowFlags, null, null, 0 , mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
跟进,
result = startInputOrWindowGainedFocusInternalLocked(startInputReason, client, windowToken, startInputFlags, softInputMode, windowFlags, attribute, inputContext, missingMethods, unverifiedTargetSdkVersion, userId);
跟进,
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV);
发Message到IInputMethodWrapper,
case DO_SHOW_SOFT_INPUT: { final SomeArgs args = (SomeArgs)msg.obj; inputMethod.showSoftInputWithToken( msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1); args.recycle(); return; }
跟进,
public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver, IBinder showInputToken) { mSystemCallingShowSoftInput = true; mCurShowInputToken = showInputToken; showSoftInput(flags, resultReceiver); mCurShowInputToken = null; mSystemCallingShowSoftInput = false; }
跟进showSoftInput,
try { showWindow(true); } catch (BadTokenException e) {
跟进showWinodw,
先准备InputVIew,也就是在onCreateInputVIew中返回的View
startViews(prepareWindow(showInput));
跟进prepareWindow,
private boolean prepareWindow(boolean showInput) { boolean doShowInput = false; mDecorViewVisible = true; if (!mShowInputRequested && mInputStarted && showInput) { doShowInput = true; mShowInputRequested = true; } if (DEBUG) Log.v(TAG, "showWindow: updating UI"); initialize(); updateFullscreenMode(); updateInputViewShown(); if (!mViewsCreated) { mViewsCreated = true; initialize(); if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView"); View v = onCreateCandidatesView(); if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v); if (v != null) { setCandidatesView(v); } } return doShowInput; }
跟进updateInputViewShow,
public void updateInputViewShown() { boolean isShown = mShowInputRequested && onevaluateInputViewShown(); if (mIsInputViewShown != isShown && mDecorViewVisible) { mIsInputViewShown = isShown; mInputframe.setVisibility(isShown ? View.VISIBLE : View.GONE); if (mInputView == null) { initialize(); View v = onCreateInputView(); if (v != null) { setInputView(v); } } } }
ok,这里回调了onCreateInputVIew,接下来就是dialog的show过程,不在此赘述;
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)