一篇技術好文之Android文本,TextView,EditText,軟鍵盤全解《初級》

十月你好.png

前言

已經有一個多月沒有寫文章了,當然,我沒失蹤,我還活著!因為換了新的工作環(huán)境,所以在接手項目的時候花了點時間……不為自己做過多借口,今后持續(xù)更新好內容(還是希望自己能周更)!
這篇文章最初是記錄一個全屏模式下輸入框被軟鍵盤覆蓋的問題,要求背景不動,輸入框上移!碰巧之后項目發(fā)版,有一點時間,就想著能不能將Android文本使用做一個總結!結合之前自己做的一些筆記,這篇文章就順利產出了!
其實換工作之后,我更希望能產出高質量的文章,所以我對于這篇文章的期待其實蠻高的,希望這篇文章能幫助所有開發(fā)者解決在Android開發(fā)過程中遇到的文本問題。當然,現在文章記錄的是自己遇到的一些問題(不是很全面),希望各位在Android開發(fā)中遇到各種關于文本類的問題可以給我留言,我們一起來討論,研究對應的解決方案,完善這篇文章及項目!

目錄

項目效果
TextView的基本使用
TextView的基本屬性
圖文混排的三種實現方式
1. drawableTop,DrawableBottom,DrawableLeft,drawableRight
2. 通過ImageSpan或者DynamicDrawableSpan實現
3. 通過給TextView設置Html內容
EditText的基本使用

  1. EditText的基本屬性
    1. imeOption屬性
    2. inputType屬性
    3. 監(jiān)聽軟鍵盤右下角按鍵
    4. 設置imeOption不生效的解決辦法
  2. 修改EditText下劃線的顏色
  3. 控制輸入框最多輸入20個字符(10個漢字,20個英文字符)
    1. 通過TextWatcher來監(jiān)聽輸入字符串內容進行過濾
    2. 通過實現InputFileter來過濾,中文算兩個字符,英文算一個
  4. 判斷軟鍵盤輸入的是否有表情
    軟鍵盤全解
    Activity的SoftInputMethod參數講解
    軟鍵盤的隱藏,顯示,及判斷是否顯示工具類
    軟鍵盤彈出監(jiān)聽及高度獲取
    軟鍵盤常見問題
    非全屏模式下軟鍵盤覆蓋輸入框,做背景不動,軟鍵盤上移效果
    全屏模式下軟鍵盤覆蓋輸入框的問題,做背景不動,軟鍵盤上移效果
    1. 第一種思路:獲取軟鍵盤高度后修改父布局的高度
      思路介紹圖
      代碼實現
    2. 第二種思路:通過添加占位圖的方式將輸入框上移
      思路介紹圖
      代碼
      項目地址
      總結
      ![文章CSDN目錄點擊地址][https://blog.csdn.net/qq_32175491/article/details/82763067]

項目效果

文本:


圖文混排.gif
文字陰影.gif
顯示html.gif

軟鍵盤:


軟鍵盤基本屬性.gif
軟鍵盤第一種方式.gif
軟鍵盤第二種方式.gif

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 設置文本區(qū)域的高度,支持度量單位:px(像素)/dp/sp/in/mm(毫米)
android:maxHeight 設置文本區(qū)域的最大高度
android:minHeight 設置文本區(qū)域的最小高度
android:width 設置文本區(qū)域的寬度,支持度量單位:px(像素)/dp/sp/in/mm(毫米),與layout_width 的區(qū)別看這里。
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 設置文本區(qū)域的最大寬度
android:minWidth 設置文本區(qū)域的最小寬度

圖文混排的三種實現方式

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性能優(yōu)化之APK瘦身詳解(瘦身73%)</h3>" +"\t<P>公司項目在不斷的改版迭代中,代碼在不斷的累加,終于apk包不負重負了,已經到了八十多M了??赡芤獡Q種方式表達,到目前為止沒有正真的往外推過,一直在內部執(zhí)行7天討論需求,5天代碼實現的階段。你在寫上個版本的內容,好了,下個版本的更新內容已經定稿了?;谶@種快速開發(fā)的現狀,我們app優(yōu)化前已經有87.1M了,包大了,運營說這樣轉化不高,只能好好搞一下咯。優(yōu)化過后包大小為23.1M(優(yōu)化了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"http://@null 取消下劃線
                    android:hint="輸入話題"
                    android:maxLength="30"
                    android:textColorHint="@color/white"
                    android:textColor="@color/white"
                    android:singleLine="true"
                    android:imeOptions="actionSearch"http://軟鍵盤右下方修改為搜索
                    android:layout_width="0dp"
                    android:textCursorDrawable="@drawable/text_view_cursor" //修改光標的顏色
                    android:textSize="15sp"
                    android:cursorVisible="false"http://是否顯示光標
                    android:focusable="true"http://是否可以focu
                    android:layout_height="match_parent"
                    />

1. imeOption屬性

imeOptions:值:

  • actionDone:完成,對應常量EditorInfo.IME_ACTION_DONE
  • actionSend :發(fā)送,對應常量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. 監(jiān)聽軟鍵盤右下角按鍵

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原生計算方法沒有漢字和英文字符的區(qū)分,所以當產品有這個需求的時候,只能通過過濾計算去限制輸入!這里提供兩種方案:

1. 通過TextWatcher來監(jiān)聽輸入字符串內容進行過濾

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 未指定狀態(tài),系統默認采用的交互方式,默認不彈出軟鍵盤,但是對于輸入框界面有滾動布局時且EditText獲得焦點時,軟鍵盤彈出
stateUnchanged 狀態(tài)不改變 ,當前界面的軟鍵盤是否顯示,取決于上一個Activity軟鍵盤的狀態(tài)
stateHidden 軟鍵盤一定是隱藏
stateAlwaysHidden 軟鍵盤一定是隱藏,暫時沒發(fā)現和stateHidden有啥區(qū)別
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沒有任何區(qū)別!無論是否有滾動布局,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);
    }

    /**
     * 切換軟鍵盤的狀態(tài)
     * 如當前為收起變?yōu)閺棾?若當前為彈出變?yōu)槭掌?     */
    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;
    }
}

軟鍵盤彈出監(jiān)聽及高度獲取

Android系統沒有對軟鍵盤做特別的開放監(jiān)聽接口,一般情況下我們可以通過布局的addOnGlobalLayoutListener接口來獲取軟鍵盤是否顯示的監(jiān)聽!
提別提醒:如果設置了屬性adjustNothing,布局沒有任何改變,addOnGlobalLayoutListener這個監(jiān)聽是不會有回調的!

特別說明:下面計算軟鍵盤高度通過兩種方式來獲取,為了兼容,這里采用兩種方式取最小值來獲取軟鍵盤高度,一種是通過反射系統方法getInputMethodWindowVisibleHeight()方法來獲取軟鍵盤高度,一種通過計算布局顯示高度來確認軟鍵盤高度!

 //拿到當前XML文件的根布局
        mChildContent = (FrameLayout) findViewById(android.R.id.content);
        //監(jiān)聽當前View的狀態(tài),進行通知回調,即"軟鍵盤彈出""
        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


軟鍵盤1.jpg
代碼實現
//思路參考于: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();
        //這行代碼能夠獲取到去除標題欄和被軟鍵盤擋住的部分,所剩下的矩形區(qū)域  
        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的高度


    軟鍵盤第二種.jpg
代碼
  1. 在輸入框onTouch事件的時候將占位視圖顯示出來,防止閃爍問題
  2. 在監(jiān)聽到軟鍵盤彈出之后,通過視圖的偏移高度和反射調用getInputMethodWindowVisibleHeight獲取軟鍵盤高度取最小值?。榱诉m配手機虛擬鍵盤高度計算,本人自測,任何一種方式都不能兼容到所有手機,最終通過取兩種計算結果下的最小值來解決這個問題?。?/li>
 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);

        //監(jiān)聽當前View的狀態(tài),進行通知回調,即"軟鍵盤彈出""
        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開發(fā)學習項目,這是個美好的愿景,項目中還有很多未涉及到的地方,有很多沒有講到的點,希望看到這個項目的朋友,如果你在開發(fā)中遇到什么問題,在這個項目中沒有找到對應的解決辦法,希望你能夠提出來,給我留言或者在項目github地址提issues,我有時間就會更新項目沒有涉及到的部分!項目會一直維護下去。當然,我希望是Aserbao'sAndroid 能為所有Android開發(fā)者提供到幫助!也期望更多Android開發(fā)者能參與進來,只要你熟悉Android某一塊,都可以將你的代碼pull上分支供大家學習!

總結

這篇文章是新環(huán)境下的第一篇文章,斷斷續(xù)續(xù)就這么過了一周了,當時是關于軟鍵盤的問題,全屏顯示情況下,軟鍵盤的顯示,背景不移動!為什么到現在才發(fā),主要有下面兩方面原因:

  1. 希望今后所有的關于哪一方面的問題都能在一篇文章里面找到!
  2. 我希望自己的每篇文章內容是有價值的,無論是對別人還是自己,能夠記錄下一個系列下自己踩過的所有坑!

這篇文章最后定義為《初級》,意為所有常見開發(fā)中會遇到的文本問題都會在這篇文章中同步更新,后面會有兩篇《中級》《高級》,中級會講自定義軟鍵盤的內容,高級會分析Andriod軟件軟鍵盤的整體實現架構!目前的思路是這樣的!

如果你在Android開發(fā)的過程中遇到文本系列的問題在文章中找不到對應的解決辦法,可以在文章底部或者我的公眾號aserbao給我留言!我會和你一起探討研究問題并持續(xù)更新文章!

百密難免一疏,文章純手打,若有出錯之處,還請各位幫忙指出!若文章內容對各位有幫助,幫忙留言點個贊,給作者一絲鼓勵,謝謝!


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容