Androidテキスト、ソフトキーボード使用ガイド

序文

記事を書いてから1ヶ月以上経ちますが、もちろん行方不明ではなく、まだ生きています!作業環境が新しくなったので、プロジェクトを引き継ぐのに少し時間がかかりました…言い訳はあまりしませんが、今後もコンテンツを更新していきます(毎回更新できるといいですね)週)!この記事は元々、入力ボックスが全画面モードのソフトキーボードで覆われていて、背景が移動せず、入力ボックスが上に移動する必要があるという問題を記録していました。プロジェクトがリリースされた後、しばらくの間、Androidテキストの使用の概要を作成できるかどうか疑問に思いました!私が以前に書いたいくつかのメモと組み合わせて、この記事はうまく作成されました!実際、転職後は質の高い記事を作りたいと思っているので、この記事への期待は非常に高く、Android開発の過程で発生するテキストの問題をすべての開発者が解決できることを願っています。もちろん、この記事には私が遭遇した問題のいくつかが記録されています(あまり包括的ではありません)。Android開発でさまざまなテキスト関連の問題が発生したときにメッセージを残していただければ幸いです。対応する解決策について話し合い、研究しましょう。

目次

  • TextViewの基本的な使用法
  • TextViewの基本的なプロパティ
  • グラフィックとテキストの混合配置の3つの実現方法
      1. drawableTop、DrawableBottom、DrawableLeft、drawableRight
      1. ImageSpanまたはDynamicDrawableSpanを介して実装
      1. HtmlコンテンツをTextViewに設定する
  • EditTextの基本的な使用法
      1. EditTextの基本的なプロパティ
        1. imeOptionプロパティ
        1. inputTypeプロパティ
        1. ソフトキーボードの右下隅にあるキーを監視します
        1. imeOptionを設定するための解決策は有効になりません
      1. EditTextの下線の色を変更します
      1. 入力ボックスを制御して、最大20文字(漢字10文字、英語20文字)を入力します。
      2. TextWatcherを使用して、フィルタリング用の入力文字列コンテンツを監視します
      3. フィルタリングするInputFileterを実装することにより、中国語は2文字としてカウントされ、英語は1文字としてカウントされます
      1. ソフトキーボード入力に式があるかどうかを確認します
  • ソフトキーボードソリューション
    • アクティビティのSoftInputMethodパラメータの説明
    • ソフトキーボードのツールクラスを非表示、表示、表示するかどうかを判断する
    • ソフトキーボードのポップアップ監視と高さの取得
  • ソフトキーボードに関するFAQ
    • 非全画面モードでは、ソフトキーボードが入力ボックスを覆い、背景は移動せず、ソフトキーボードは上に移動します
    • 全屏模式下软键盘覆盖输入框的问题,做背景不动,软键盘上移效果
        1. 第一种思路:获取软键盘高度后修改父布局的高度
        • 思路介绍图
        • 代码实现
        1. 第二种思路:通过添加占位图的方式将输入框上移
        • 思路介绍图
  • 代码
  • 项目地址
  • 总结

TextView的基本使用

TextView的基本属性

常用的属性:

<TextView
        android:text="@string/long_text"
        android:textSize="10sp"
        android:textColor="@color/black"
        android:shadowRadius="8"
        android:shadowColor="@color/black70"
        android:shadowDy="4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
复制代码

下面这些基本包含TextView的所有属性,里面很多属性大多数情况下我们都使用不到,可以稍微了解下,可以直接跳过属性列表!

属性 作用
android:text 设置显示文本.
android:textAppearance 设置文字外观
android:textColor 设置文本颜色
android:textColorHighlight 被选中文字的底色,默认为蓝色
android:textColorHint 设置提示信息文字的颜色,默认为灰色。与hint一起使用。
android:textColorLink 文字链接的颜色.
android:textScaleX 设置文字之间间隔,默认为1.0f。
android:textSize 设置文字大小,推荐度量单位”sp”,如”15sp”
android:textStyle 设置字形[bold(粗体) 0, italic(斜体) 1, bolditalic(又粗又斜) 2] 可以设置一个或多个,用“|”隔开
android:typeface 设置文本字体
android:height 设置文本区域的高度,支持度量单位:px(像素)/dp/sp/in/mm(毫米)
android:maxHeight 设置文本区域的最大高度
android:minHeight 设置文本区域的最小高度
android:width 设置文本区域的宽度,支持度量单位:px(像素)/dp/sp/in/mm(毫米),与layout_width 的区别看这里。
android:shadowColor 指定文本阴影的颜色,需要与shadowRadius一起使用。
android:shadowDx 设置阴影横向坐标开始位置。
android:shadowDy 设置阴影纵向坐标开始位置。
android:shadowRadius 设置阴影的半径。一般设置为5.0的效果比较好。
android:singleLine 设置单行显示。如果和layout_width一起使用,当文本不能全部显示时,后面用“…”来表示。如android:text=”test_ singleLine “
android:singleLine=”true” android:layout_width=”20dp” 将只显示“t…”。如果不设置singleLine或者设置为false,文本将自动换行
android:cursorVisible 设定光标为显示/隐藏,默认显示。
android:digits 设置允许输入哪些字符。如“1234567890.+-*/% ()”
android:drawableTop\Bottom\Left\Right 在text的上、下、左、右方输出一个drawable
android:drawablePadding 设置text与drawable(图片)的间隔
android:editable 设置是否可编辑。
android:editorExtras 设置文本的额外的输入数据。
android:ellipsize 设置当文字过长时,该控件该如何显示。有如下值设置:”start”—-省略号显示在开头;”end” ——省略号显示在结尾;”middle”—-省略号显示在中间;”marquee” ——以跑马灯的方式显示(动画横向移动)
android:freezesText 设置保存文本的内容以及光标的位置。
android:gravity 设置文本位置,设置成“center”,文本将居中显示。
android:hintText 为空时显示的文字提示信息,可通过textColorHint设置提示信息的颜色。此属性在 EditView中使用,但是这里也可以用。
android:imeOptions 附加功能,设置右下角IME动作与编辑框相关的动作,如actionDone右下角将显示一个“完成”,而不设置默认是一个回车符号。这个在EditView中再详细说明,此处无用。
android:imeActionId 设置IME动作ID
android:imeActionLabel 设置IME动作标签
android:includeFontPadding 设置文本是否包含顶部和底部额外空白,默认为true。
android:inputMethod 为文本指定输入法,需要完全限定名(完整的包名)。例如:com.google.android.inputmethod.pinyin
android:inputType 设置文本的类型,用于帮助输入法显示合适的键盘类型。在EditView中再详细说明,这里无效果。
android:linksClickable 设置链接是否点击连接,即使设置了autoLink。
android:marqueeRepeatLimit 在ellipsize指定marquee的情况下,设置重复滚动的次数,当设置marquee_forever时表示无限次。
android:ems 设置TextView的宽度为N个字符的宽度。这里测试为一个汉字字符宽度
android:maxEms 设置TextView的宽度为最长为N个字符的宽度。与ems同时使用时覆盖ems选项。
android:minEms 设置TextView的宽度为最短为N个字符的宽度。与ems同时使用时覆盖ems选项。
android:maxLength 限制显示的文本长度,超出部分不显示。
android:lines 设置文本的行数,设置两行就显示两行,即使第二行没有数据。
android:maxLines 设置文本的最大显示行数,与width或者layout_width结合使用,超出部分自动换行,超出行数将不显示。
android:minLines 设置文本的最小行数,与lines类似。
android:lineSpacingExtra 设置行间距。
android:lineSpacingMultiplier 设置行间距的倍数。如”1.2”
android:numeric 如果被设置,该TextView有一个数字输入法。此处无用,设置后唯一效果是TextView有点击效果,此属性在EdtiView将详细说明。
android:password 以小点”*”显示文本
android:phoneNumber 设置为电话号码的输入方式。
android:privateImeOptions 设置输入法选项,此处无用,在EditText将进一步讨论。
android:scrollHorizontally 设置文本超出TextView的宽度的情况下,是否出现横拉条。
android:selectAllOnFocus 如果文本是可选择的,让他获取焦点而不是将光标移动为文本的开始位置或者末尾位置。 需要再EditText中设置。
android:maxWidth 设置文本区域的最大宽度
android:minWidth 设置文本区域的最小宽度

图文混排的三种实现方式

1. drawableTop,DrawableBottom,DrawableLeft,drawableRight

<TextView
            android:id="@+id/one_pictxt_tv"
            android:drawableLeft="@drawable/emoji_00"
            android:drawableRight="@drawable/emoji_01"
            android:drawableBottom="@drawable/emoji_02"
            android:drawableTop="@drawable/emoji_03"
            android:text="第一种方式:\n通过drawableLeft来实现\n上下左右中间文字"
            style="@style/picTxt_tv_style"
             />
复制代码

2. 通过ImageSpan或者DynamicDrawableSpan实现

SpannableString dynamicDrawableSpan = new SpannableString("DynamicDrawableSpan");
        DynamicDrawableSpan drawableSpan =
                new DynamicDrawableSpan(DynamicDrawableSpan.ALIGN_BASELINE) {
                    @Override
                    public Drawable getDrawable() {
                        Drawable d = getResources().getDrawable(R.drawable.emoji_00);
                        d.setBounds(0, 0, 150, 150);
                        return d;
                    }
                };
        DynamicDrawableSpan drawableSpan2 = new DynamicDrawableSpan(
                DynamicDrawableSpan.ALIGN_BOTTOM) {
            @Override
            public Drawable getDrawable() {
                Drawable d = getResources().getDrawable(R.drawable.emoji_01);
                d.setBounds(0, 0, 150, 150);
                return d;
            }
        };
        dynamicDrawableSpan.setSpan(drawableSpan, 3, 4, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
        dynamicDrawableSpan.setSpan(drawableSpan2, 7, 8, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
        mDynamicDrawableSpanTv.setText(dynamicDrawableSpan);



        SpannableString imageSpan = new SpannableString("ImageSpan");
        Drawable d = getResources().getDrawable(R.drawable.emoji_02);
        d.setBounds(0, 0, 150, 150);
        imageSpan.setSpan(new ImageSpan(d), 3, 4, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
        mIamgeSpanTv.setText(imageSpan);

复制代码

3. 通过给TextView设置Html内容

如果要包含图片的话需要重写一个类实现ImageGetter接口并重写getDrawable方法,具体实现可以下载底部项目源码!

mThreePictxtTv.setText(Html.fromHtml("点击我,带你到HtmlTextActivity中去<font color= '#ff0000'>textView通过Html实现图文混排</font> 点击这一段"));
复制代码

带图片的html的设置:

String g = "<html>" +"<head>" + "</head>" +"<body style=\"text-align: justify\">" +
"\t<h3>Android性能优化之APK瘦身详解(瘦身73%)</h3>" +"\t<P>公司项目在不断的改版迭代中,代码在不断的累加,终于apk包不负重负了,已经到了八十多M了。可能要换种方式表达,到目前为止没有正真的往外推过,一直在内部执行7天讨论需求,5天代码实现的阶段。你在写上个版本的内容,好了,下个版本的更新内容已经定稿了。基于这种快速开发的现状,我们app优化前已经有87.1M了,包大了,运营说这样转化不高,只能好好搞一下咯。优化过后包大小为23.1M(优化了73%,不要说我标题党)。好了好了,我要阐述我的apk超级无敌魔鬼瘦身之心得了。</p>" +"" +"\t<img src=\"imgs/0.png\" style=\"width: 100%;\" />" + "" + "</body>" +"</html>";
   mHtmlTv.setText(Html.fromHtml(htmlContent, new MImageGetter(mHtmlTv,HtmlTextActivity.this),null));

public class MImageGetter implements ImageGetter {
		Context c;
	    public MImageGetter(TextView text, Context c) {
	        this.c = c;
	    }
	 public Drawable getDrawable(String source) {
		    Drawable drawable = null;
		    InputStream is = null;
			try {
				is = c.getResources().getAssets().open(source);
			} catch (IOException e1) {
				e1.printStackTrace();
			}
            try {
                TypedValue typedValue = new TypedValue();
                typedValue.density = TypedValue.DENSITY_DEFAULT;
                drawable = Drawable.createFromResourceStream(null, typedValue, is, "src");
                DisplayMetrics dm = c.getResources().getDisplayMetrics();
        		int dwidth = dm.widthPixels-10;//padding left + padding right 
        		float dheight = (float)drawable.getIntrinsicHeight()*(float)dwidth/(float)drawable.getIntrinsicWidth();
        		int dh = (int)(dheight+0.5);
        		int wid = dwidth;
                int hei = dh;
                drawable.setBounds(0, 0, wid, hei);
                return drawable;
            } catch (Exception e) {
            	System.out.println(e);
                return null;
            }   
	    }
}
复制代码

EditText的基本使用

1. EditText的基本属性

 <EditText
                    android:textIsSelectable="true"     //文本是否可选,复制粘贴剪辑,在TextVew中使用,在EditText中使用此属性将收不到软键盘输入内容
                    android:id="@+id/pop_select_label_et"
                    android:layout_weight="1"
                    android:paddingLeft="25dp"
                    android:background="@drawable/find_num_tv_bg"//@null 取消下划线
                    android:hint="输入话题"
                    android:maxLength="30"
                    android:textColorHint="@color/white"
                    android:textColor="@color/white"
                    android:singleLine="true"
                    android:imeOptions="actionSearch"//软键盘右下方修改为搜索
                    android:layout_width="0dp"
                    android:textCursorDrawable="@drawable/text_view_cursor" //修改光标的颜色
                    android:textSize="15sp"
                    android:cursorVisible="false"//是否显示光标
                    android:focusable="true"//是否可以focu
                    android:layout_height="match_parent"
                    />
复制代码

1. imeOption属性

imeOptions:值:

  • actionDone:完成,对应常量EditorInfo.IME_ACTION_DONE
  • actionSend :发送,对应常量EditorInfo.IME_ACTION_SEND
  • actionSearch 搜索,对应常量EditorInfo.IME_ACTION_SEARCH
  • actionGo 去往,对应常量EditorInfo.IME_ACTION_GO
  • actionNone 没有动作,对应常量EditorInfo.IME_ACTION_NONE
  • actionUnspecified 未指定,默认,对应常量EditorInfo.IME_ACTION_UNSPECIFIED.
  • actionNext 下一个,对应常量EditorInfo.IME_ACTION_NEXT

2. inputType属性

android:inputType="phone" //电话号码
    android:inputType="none" 
    //文本类型,多为大写、小写和数字符号。 
    android:inputType="text"  
    android:inputType="textCapCharacters" //字母大写 
    android:inputType="textCapWords" //首字母大写 
    android:inputType="textCapSentences" //仅第一个字母大写 
    android:inputType="textAutoCorrect" //自动完成 
    android:inputType="textAutoComplete" //自动完成 
    android:inputType="textMultiLine" //多行输入 
    android:inputType="textImeMultiLine" //输入法多行(如果支持) 
    android:inputType="textNoSuggestions" //不提示 
    android:inputType="textUri" //网址 
    android:inputType="textEmailAddress" //电子邮件地址 
    android:inputType="textEmailSubject" //邮件主题 
    android:inputType="textShortMessage" //短讯 
    android:inputType="textLongMessage" //长信息 
    android:inputType="textPersonName" //人名 
    android:inputType="textPostalAddress" //地址
    android:inputType="textPassword" //密码 
    android:inputType="textVisiblePassword" //可见密码
    android:inputType="textWebEditText" //作为网页表单的文本 
    android:inputType="textFilter" //文本筛选过滤 
    android:inputType="textPhonetic" //拼音输入 

    //数值类型 
    android:inputType="number" //数字 
    android:inputType="numberSigned" //带符号数字格式 
    android:inputType="numberDecimal" //带小数点的浮点格式 
    android:inputType="datetime" //时间日期 
    android:inputType="date" //日期键盘 
    android:inputType="time" //时间键盘
复制代码

2. 监听软键盘右下角按键

editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                switch (actionId){
                    case EditorInfo.IME_ACTION_SEARCH:
                        break;
                    case EditorInfo.IME_ACTION_DONE:
                        break;
                    case EditorInfo.IME_ACTION_SEND:
                        break;
                    case EditorInfo.IME_ACTION_GO:
                        break;
                    case EditorInfo.IME_ACTION_NONE:
                        break;
                    case EditorInfo.IME_ACTION_NEXT:
                        break;
                    case EditorInfo.IME_ACTION_UNSPECIFIED:
                        break;
                }
                return false;
            }
        });
复制代码

3. 设置imeOption不生效的解决办法

设置imeOption无效:需要将singleLine设置为true或者 将inputType设置为text

2. 修改EditText下划线的颜色

//1. 通过修改colorAccent属性来修改下划线颜色,此方法会全局修改
 <item name="colorAccent">@color/colorWhite80</item>
   
//2. 通过修改EditText的style来修改下划线颜色
<style name="MyEditText2" parent="Theme.AppCompat.Light">
        <item name="colorControlNormal">@color/colorWhite80</item> //控件默认的颜色
        <item name="colorControlActivated">@color/colorWhite50</item> // 控件被激活的颜色
</style>
复制代码

3. 控制输入框最多输入20个字符(10个汉字,20个英文字符)

Android原生计算方法没有汉字和英文字符的区分,所以当产品有这个需求的时候,只能通过过滤计算去限制输入!这里提供两种方案:

1. 通过TextWatcher来监听输入字符串内容进行过滤

editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }
            @Override
            public void afterTextChanged(Editable s) {
                int charSequenceCount = countChineseChar(s);
                if (s.length() + charSequenceCount > StaticFinalValues.MAX_CHAR_NUM_SELECT) {
                    CharSequence text = s.subSequence(0, s.length() - 1);
                    editText.setText(text);
                editText.setSelection(text.length());//光标跳最后
                    if(System.currentTimeMillis() - mLastTime > 500) {
                        Toast.makeText(mContext, "输入不能多于" + String.valueOf( StaticFinalValues.MAX_CHAR_NUM_SELECT) +"字符", Toast.LENGTH_SHORT).show();
                        mLastTime = System.currentTimeMillis();
                    }
                    return;
                }
            }
        });
       
        /**
     * 计算中文字符
     *
     * @param sequence
     * @return
     */
    public static int countChineseChar(CharSequence sequence) {
        if (TextUtils.isEmpty(sequence)) {
            return 0;
        }
        int charNum = 0;
        for (int i = 0; i < sequence.length(); i++) {
            char word = sequence.charAt(i);
            if (UiUtils.isChineseChar(word)) {//中文
                charNum++;
            }
        }
        return charNum;
    }
    /**
     * 判断是否是中文
     * @param c
     * @return
     */
    public static boolean isChineseChar(char c) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
        if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION) {
            return true;
        }
        return false;
    }
复制代码

2. 通过实现InputFileter来过滤,中文算两个字符,英文算一个

public class MaxLengthEditText extends AppCompatEditText {
    public MaxLengthEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        initLength(attrs,context);
    }
    public MaxLengthEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initLength(attrs,context);
    }
    private void initLength(AttributeSet a, Context context) {
        //命名空间(别告诉我不熟悉)
        String namespace = "http://schemas.android.com/apk/res/android";
        //获取属性中设置的最大长度
        int maxLength = a.getAttributeIntValue(namespace, "maxLength", -1);
        //如果设置了最大长度,给出相应的处理
        if (maxLength > -1) {
            setFilters(new InputFilter[]{new MaxLengthEditText.MyLengthFilter(maxLength,context)});
        }
    }
    /**
     * 从源码中复制出来的
     * 来源:InputFilter.LengthFilter
     * 这里只是添加了一句话:
     *              Toast.makeText(context, "字数不能超过" + mMax, Toast.LENGTH_SHORT).show();
     *
     * This filter will constrain edits not to make the length of the text
     * greater than the specified length.
     */
    class MyLengthFilter implements InputFilter {

        private final int mMax;
        private Context context;

        public MyLengthFilter(int max, Context context) {
            mMax = max;
            this.context = context;
        }

        public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
                                   int dstart, int dend) {
            int keep = 0;
            for (int i = 0; i < dest.length(); i++) {
                char charAt = dest.charAt(i);
                //32-122包含了空格,大小写字母,数字和一些常用的符号,
                //如果在这个范围内则算一个字符,
                //如果不在这个范围比如是汉字的话就是两个字符
                if (charAt >= 32 && charAt <= 122) {
                    keep++;
                } else {
                    keep += 2;
                }
            }
            if(keep <= mMax){
                 return source.subSequence(start, source.length());
            }else{
                 Toast.makeText(mContext, "输入少一点,太多了", Toast.LENGTH_SHORT).show();
                return "";
            }
        }
        /**
         * @return the maximum length enforced by this input filter
         */
        public int getMax() {
            return mMax;
        }
    }
}
复制代码

4. 判断软键盘输入的是否有表情

若需求声明,表情只能算一个字符,这时候就需要在输入后进行判断:

            @Override
            public void afterTextChanged(Editable s) {
                String s1 = s.toString();
                char[] sC = new char[s1.length()];
                s1.getChars(0,s1.length(),sC,0);
                for (char c : sC) {
                    Log.e(TAG, "afterTextChanged: "+ isEmojiCharacter(c));
                }
            }
                
  private static boolean isEmojiCharacter(char codePoint) {
        return !((codePoint == 0x0) || (codePoint == 0x9) || (codePoint == 0xA) || (codePoint == 0xD) || ((codePoint >= 0x20) && codePoint <= 0xD7FF))|| ((codePoint >= 0xE000) && (codePoint <= 0xFFFD)) || ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF));
    }               
复制代码

软键盘全解

Activity的SoftInputMethod参数讲解

属性 作用
stateUnspecified 未指定状态,系统默认采用的交互方式,默认不弹出软键盘,但是对于输入框界面有滚动布局时且EditText获得焦点时,软键盘弹出
stateUnchanged 状态不改变 ,当前界面的软键盘是否显示,取决于上一个Activity软键盘的状态
stateHidden 软键盘一定是隐藏
stateAlwaysHidden 软键盘一定是隐藏,暂时没发现和stateHidden有啥区别
stateVisible 设置为这个属性,可以将软键盘召唤出来,即使在界面上没有输入框的情况下也可以强制召唤出来
stateAlwaysVisible 软键盘默认显示,当给AActivity设置stateVisible属性时,从当前AActivity跳转到BActivity,软键盘隐藏,再从BActivity返回AActivity,软键盘不显示!当设置stateAlwaysVisible属性时,跳转后的返回软键盘依旧显示!
adjustUnspecified 系统默认属性,默认adjustPan的效果!如果在设置这个属性之前设置过adjustResize,则会是adjustResize的效果!如果上一次设置为adjustPan,再设置为adjustUnspecified,则会是adjustPan的效果!
adjustResize 设置这个属性,当前Activity总会给软键盘预留显示空间,输入框被弹出软键盘覆盖掉,有两种情况:1. 有滚动布局,其他布局不移动且大小不改变,输入框移动到软键盘上面 2. 无滚动布局,通过修改其他布局的大小达到输入框移动到软键盘的效果
adjustPan 设置这个属性,Activity不会预留软键盘显示空间,而是通过布局移动来保证输入框不被软键盘覆盖!只要输入框被软键盘覆盖,就会通过移动整个布局来达到显示输入框的效果!
注意 当Activity设置全屏后,adjustResize和adjustPan没有任何区别!无论是否有滚动布局,Activity都会往上移动

软键盘的隐藏,显示,及判断是否显示工具类

public class AppKeyBoardMgr {
    /**
     * 打开软键盘
     * @param mEditText  输入框
     * @param mContext   上下文
     */
    public static void openKeybord(EditText mEditText, Context mContext)
    {
        InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.showSoftInput(mEditText, InputMethodManager.RESULT_SHOWN);
        imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
    }

    /**
     * 显示输入法
     * @param mAct activity
     */
    public static void showInputMethod(final Activity mAct) {
        View v = mAct.getCurrentFocus();
        if (null == v) {
            return;
        }
        ((InputMethodManager) mAct.getSystemService(Activity.INPUT_METHOD_SERVICE)).showSoftInput(v, 0);
    }

    /**
     * 强制显示输入法键盘
     */
    public static void showKeybord(EditText edittext) {
        InputMethodManager inputMethodManager = (InputMethodManager)
                edittext.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        inputMethodManager.showSoftInput(edittext, InputMethodManager.SHOW_FORCED);
    }
    
    /**
     * 关闭软键盘
     * @param mEditText 输入框
     * @param mContext  上下文
     */
    public static void closeKeybord(EditText mEditText, Context mContext)
    {
        InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
    }
    /**
     * 强制隐藏输入法键盘
     */
    public static void hideKeybord(EditText edittext) {
        InputMethodManager inputMethodManager = (InputMethodManager)
                edittext.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        if (inputMethodManager.isActive()) {
            inputMethodManager.hideSoftInputFromWindow(edittext.getWindowToken(), 0);
        }
    }
    
    /**
     * 隐藏输入法
     * @param mAct activity
     */
    public static void hideInputMethod(Activity mAct) {
        try {// hide keybord anyway
            View v = mAct.getWindow().getCurrentFocus();
            if (v != null) {
                InputMethodManager imm = (InputMethodManager) mAct.getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
            }
        } catch (Exception e) {
        }
    }
    /**
     * 通过定时器强制隐藏虚拟键盘
     */
    public static void TimerHideKeyboard(final View v) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
                if (imm.isActive()) {
                    imm.hideSoftInputFromWindow(v.getApplicationWindowToken(),0);
                }
            }
        }, 10);
    }

    /**
     * 切换软键盘的状态
     * 如当前为收起变为弹出,若当前为弹出变为收起
     */
    public static void toggleKeybord(EditText edittext) {
        InputMethodManager inputMethodManager = (InputMethodManager)
            edittext.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        inputMethodManager.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
    }
    /**
     * 输入法是否显示
     */
    public static boolean isKeybord(EditText edittext) {
        boolean bool = false;
        InputMethodManager inputMethodManager = (InputMethodManager)
            edittext.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        if (inputMethodManager.isActive()) {
            bool = true;
        }
        return bool;
    }
}
复制代码

软键盘弹出监听及高度获取

Android系统没有对软键盘做特别的开放监听接口,一般情况下我们可以通过布局的addOnGlobalLayoutListener接口来获取软键盘是否显示的监听!

提别提醒:如果设置了属性adjustNothing,布局没有任何改变,addOnGlobalLayoutListener这个监听是不会有回调的!

特别说明:下面计算软键盘高度通过两种方式来获取,为了兼容,这里采用两种方式取最小值来获取软键盘高度,一种是通过反射系统方法getInputMethodWindowVisibleHeight()方法来获取软键盘高度,一种通过计算布局显示高度来确认软键盘高度!

 //拿到当前XML文件的根布局
        mChildContent = (FrameLayout) findViewById(android.R.id.content);
        //监听当前View的状态,进行通知回调,即"软键盘弹出""
        View childew = mChildContent.getChildAt(0);
        childew.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            public void onGlobalLayout() {
            	//反射获取
                InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
                int injectSoftHeight = 0;
                try {
                    Method method = inputMethodManager.getClass().getDeclaredMethod("getInputMethodWindowVisibleHeight", null);
                    method.setAccessible(true);
                    injectSoftHeight = (Integer) method.invoke(inputMethodManager, null);
                } catch (Exception e) {
                    e.printStackTrace();
                }
				//布局显示高度差来计算
                View decorView = getWindow().getDecorView();
                Rect r = new Rect();
                //r will be populated with the coordinates of your view that area still visible.
                decorView.getWindowVisibleDisplayFrame(r);
                int rootHeight = decorView.getRootView().getHeight();
                int rH = r.bottom - r.top;
                int measureDVHeight = rootHeight - rH;

                if (injectSoftHeight > 200) {
                    mMeasureSoftKBHeight = injectSoftHeight < measureDVHeight ? injectSoftHeight : measureDVHeight;
                } else if (injectSoftHeight <= 200) {
                    mMeasureSoftKBHeight = measureDVHeight;
                }
              
                if (mLastHeight != mMeasureSoftKBHeight) {
                    if (mMeasureSoftKBHeight > 200) {//200这个值视情况而定,目前设置这个值没有出现兼容问题
                        //软键盘显示
                    } else {
                        //软键盘隐藏
                    }
                    mLastHeight = mMeasureSoftKBHeight;
                }
            }
        });
复制代码

软键盘常见问题

非全屏模式下软键盘覆盖输入框,做背景不动,软键盘上移效果

这种情况,直接通过设置带滚动布局,设置adjustResize属性就可以实现效果

全屏模式下软键盘覆盖输入框的问题,做背景不动,软键盘上移效果

1. 第一种思路:获取软键盘高度后修改父布局的高度

思路介绍图

思路参考于:AndroidBug5497Workaround

3965704-4d186f78459b0171.webp

代码实现
//思路参考于:AndroidBug5497Workaround
public class AndroidSoftBoardAdjustHeightUtil {

    public static void assistActivity(Activity activity) {
        new AndroidSoftBoardAdjustHeightUtil(activity);  
    }  
  
    private View mChildOfContent;
    private int                      usableHeightPrevious;  
    private FrameLayout.LayoutParams frameLayoutParams;
  
    private AndroidSoftBoardAdjustHeightUtil(Activity activity) {
        FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
        mChildOfContent = content.getChildAt(0);  
        mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            public void onGlobalLayout() {  
                possiblyResizeChildOfContent();  
            }  
        });  
        frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();  
    }  
    
    private void possiblyResizeChildOfContent() {  
        int usableHeightNow = computeUsableHeight();  
        if (usableHeightNow != usableHeightPrevious) {  
            int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();  
            int heightDifference = usableHeightSansKeyboard - usableHeightNow;  
            //排除其他View引起的变化,专注软键盘变化  
            if (heightDifference > (usableHeightSansKeyboard / 4)) {  
                // keyboard probably just became visible  
                frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;  //减掉软键盘的高度
            } else {  
                // keyboard probably just became hidden  
                frameLayoutParams.height = usableHeightSansKeyboard;  
            }  
            mChildOfContent.requestLayout();  
            usableHeightPrevious = usableHeightNow;  
        }  
    }  
    
    private int computeUsableHeight() {  
        Rect r = new Rect();
        //这行代码能够获取到去除标题栏和被软键盘挡住的部分,所剩下的矩形区域  
        mChildOfContent.getWindowVisibleDisplayFrame(r);  
        //r.top : 标题栏的高度  
        //屏幕高度-r.bottom : 软键盘的高度  
        //可用高度(全屏模式) : rect.bottom  
        //可用高度(非全屏模式) : rect.bottom - rect.top  
        return (r.bottom - r.top);// 全屏模式下: return r.bottom  
    }  
  
}  
复制代码

2. 第二种思路:通过添加占位图的方式将输入框上移

思路介绍图

由于第一种方式会有兼容问题,而且软键盘弹出的时候部分手机会出现闪烁现象!

  1. 通过设置下图中的PlaceholderView的Visible和Gone来控制EditText的高度

3965704-59c456549c53a5f5.webp

代码
  1. 在输入框onTouch事件的时候将占位视图显示出来,防止闪烁问题
  2. 在监听到软键盘弹出之后,通过视图的偏移高度和反射调用getInputMethodWindowVisibleHeight获取软键盘高度取最小值!(为了适配手机虚拟键盘高度计算,本人自测,任何一种方式都不能兼容到所有手机,最终通过取两种计算结果下的最小值来解决这个问题!)
 private void initCheckKeyBoardIsShow(final EditText editText) {
        editText.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                editText.setCursorVisible(true);
                mPlaceholderTv.setVisibility(View.VISIBLE);
                return false;
            }
        });
        //拿到当前XML文件的根布局
        mChildContent = (FrameLayout) findViewById(android.R.id.content);

        //监听当前View的状态,进行通知回调,即"软键盘弹出""
        View childew = mChildContent.getChildAt(0);
        childew.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
                int injectSoftHeight = 0;
                try {
                    Method method = inputMethodManager.getClass().getDeclaredMethod("getInputMethodWindowVisibleHeight", null);
                    method.setAccessible(true);
                    injectSoftHeight = (Integer) method.invoke(inputMethodManager, null);
                } catch (Exception e) {
                    e.printStackTrace();
                }

                View decorView = getWindow().getDecorView();
                Rect r = new Rect();
                decorView.getWindowVisibleDisplayFrame(r);
                int rootHeight = decorView.getRootView().getHeight();
                int rH = r.bottom - r.top;
                int measureDVHeight = rootHeight - rH;
                
                if (injectSoftHeight > 200) {
                    mMeasureSoftKBHeight = injectSoftHeight < measureDVHeight ? injectSoftHeight : measureDVHeight;
                } else if (injectSoftHeight <= 200) {
                    mMeasureSoftKBHeight = measureDVHeight;
                }
                if (mLastHeight != mMeasureSoftKBHeight) {
                    if (mMeasureSoftKBHeight > 200) {
                        LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mPlaceholderTv.getLayoutParams();
                        int result = 0;
                        result = mMeasureSoftKBHeight;
                        layoutParams.height = result;
                        mPlaceholderTv.setLayoutParams(layoutParams);
                        mPlaceholderTv.postInvalidate();
                        setRecordBtnMargain(mMeasureSoftKBHeight);
                    } else {
                        setRecordBtnMargain(0);
                        mNewStoryEt.setCursorVisible(false);
                        mPlaceholderTv.setVisibility(View.GONE);
                    }
                    mLastHeight = mMeasureSoftKBHeight;
                }
            }
        });
    }

复制代码

项目地址

AserbaosAndroid aserbao的个人Android总结项目,希望这个项目能成为最全面的Android开发学习项目,这是个美好的愿景,项目中还有很多未涉及到的地方,有很多没有讲到的点,希望看到这个项目的朋友,如果你在开发中遇到什么问题,在这个项目中没有找到对应的解决办法,希望你能够提出来,给我留言或者在项目github地址提issues,我有时间就会更新项目没有涉及到的部分!项目会一直维护下去。当然,我希望是Aserbao'sAndroid 能为所有Android开发者提供到帮助!也期望更多Android开发者能参与进来,只要你熟悉Android某一块,都可以将你的代码pull上分支供大家学习!

总结

这篇文章是新环境下的第一篇文章,断断续续就这么过了一周了,当时是关于软键盘的问题,全屏显示情况下,软键盘的显示,背景不移动!为什么到现在才发,主要有下面两方面原因:

  1. 希望今后所有的关于哪一方面的问题都能在一篇文章里面找到!
  2. 我希望自己的每篇文章内容是有价值的,无论是对别人还是自己,能够记录下一个系列下自己踩过的所有坑!

如果你在Android开发的过程中遇到文本系列的问题在文章中找不到对应的解决办法,可以在文章底部或者我的公众号aserbao给我留言!我会和你一起探讨研究问题并持续更新文章!

百密难免一疏,文章纯手打,若有出错之处,还请各位帮忙指出!若文章内容对各位有帮助,帮忙留言点个赞,给作者一丝鼓励,谢谢!

18年的一篇老文章,希望对各位有用。

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿

おすすめ

転載: juejin.im/post/7120119215264104485
おすすめ