自定义车牌软键盘

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34781711/article/details/81012444

前人栽树后人凉

2015年的一篇文章,公布了核心代码,源码下载需要5C币。
Android 一个简易的自定义软键盘

2018年的一篇文章,在15年文章的基础上,公布了绝大多数代码,源码托管在Github上。
Android 自定义车牌键盘

这文章虽然是自己写的,但核心思想和代码还是离不开前辈们的基础,在此致谢!!
总的说要自定义软键盘,要解决三件事:
1.软键盘元素和布局制作。
2.软键盘弹起、消失和具体操作。
3.系统键盘的抑制。

下面来详细说一下:


一、软键盘的制作需要Keyboard类型的XML文件内部用Row,来详细规定一共有几行,每行有什么元素,以及元素的位置和编号。
由于是车牌,所以涉及:1.省简称,2.大写字母A-Z,3.数字0-9。布局的设计是省简称一组,数字字母另一组,所以需要两组布局。
第一组省简称:province_abbreviation.xml

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="0.0px"
    android:keyHeight="8%p"
    android:keyWidth="10%p"
    android:verticalGap="0.0px">

    <Row android:verticalGap="1%p">
        <Key
            android:codes="20140"
            android:horizontalGap="1%p"
            android:keyEdgeFlags="left"
            android:keyLabel="京"
            android:keyWidth="8%p" />
        <Key
            android:codes="27941"
            android:horizontalGap="2%p"
            android:keyLabel="津"
            android:keyWidth="8%p" />
        <Key
            android:codes="20864"
            android:horizontalGap="2%p"
            android:keyLabel="冀"
            android:keyWidth="8%p" />
        <Key
            android:codes="40065"
            android:horizontalGap="2%p"
            android:keyLabel="鲁"
            android:keyWidth="8%p" />
        <Key
            android:codes="26187"
            android:horizontalGap="2%p"
            android:keyLabel="晋"
            android:keyWidth="8%p" />
        <Key
            android:codes="33945"
            android:horizontalGap="2%p"
            android:keyLabel="蒙"
            android:keyWidth="8%p" />
        <Key
            android:codes="36797"
            android:horizontalGap="2%p"
            android:keyLabel="辽"
            android:keyWidth="8%p" />
        <Key
            android:codes="21513"
            android:horizontalGap="2%p"
            android:keyLabel="吉"
            android:keyWidth="8%p" />
        <Key
            android:codes="40657"
            android:horizontalGap="2%p"
            android:keyLabel="黑"
            android:keyWidth="8%p" />
        <Key
            android:codes="27818"
            android:horizontalGap="2%p"
            android:keyEdgeFlags="right"
            android:keyLabel="沪"
            android:keyWidth="8%p" />
    </Row>

    <Row android:verticalGap="1%p">
        <Key
            android:codes="33487"
            android:horizontalGap="1%p"
            android:keyEdgeFlags="left"
            android:keyLabel="苏"
            android:keyWidth="8%p" />
        <Key
            android:codes="27993"
            android:horizontalGap="2%p"
            android:keyLabel="浙"
            android:keyWidth="8%p" />
        <Key
            android:codes="30358"
            android:horizontalGap="2%p"
            android:keyLabel="皖"
            android:keyWidth="8%p" />
        <Key
            android:codes="38397"
            android:horizontalGap="2%p"
            android:keyLabel="闽"
            android:keyWidth="8%p" />
        <Key
            android:codes="36195"
            android:horizontalGap="2%p"
            android:keyLabel="赣"
            android:keyWidth="8%p" />
        <Key
            android:codes="35947"
            android:horizontalGap="2%p"
            android:keyLabel="豫"
            android:keyWidth="8%p" />
        <Key
            android:codes="37122"
            android:horizontalGap="2%p"
            android:keyLabel="鄂"
            android:keyWidth="8%p" />
        <Key
            android:codes="28248"
            android:horizontalGap="2%p"
            android:keyLabel="湘"
            android:keyWidth="8%p" />
        <Key
            android:codes="31908"
            android:horizontalGap="2%p"
            android:keyLabel="粤"
            android:keyWidth="8%p" />
        <Key
            android:codes="26690"
            android:horizontalGap="2%p"
            android:keyEdgeFlags="right"
            android:keyLabel="桂"
            android:keyWidth="8%p" />
    </Row>
    <Row android:verticalGap="1%p">
        <Key
            android:codes="28189"
            android:horizontalGap="11%p"
            android:keyEdgeFlags="left"
            android:keyLabel="渝"
            android:keyWidth="8%p" />
        <Key
            android:codes="24029"
            android:horizontalGap="2%p"
            android:keyLabel="川"
            android:keyWidth="8%p" />
        <Key
            android:codes="36149"
            android:horizontalGap="2%p"
            android:keyLabel="贵"
            android:keyWidth="8%p" />
        <Key
            android:codes="20113"
            android:horizontalGap="2%p"
            android:keyLabel="云"
            android:keyWidth="8%p" />
        <Key
            android:codes="34255"
            android:horizontalGap="2%p"
            android:keyLabel="藏"
            android:keyWidth="8%p" />
        <Key
            android:codes="38485"
            android:horizontalGap="2%p"
            android:keyLabel="陕"
            android:keyWidth="8%p" />
        <Key
            android:codes="29976"
            android:horizontalGap="2%p"
            android:keyLabel="甘"
            android:keyWidth="8%p" />
        <Key
            android:codes="38738"
            android:horizontalGap="2%p"
            android:keyEdgeFlags="right"
            android:keyLabel="青"
            android:keyWidth="8%p" />
    </Row>
    <Row>
        <Key
            android:codes="-1"
            android:horizontalGap="4%p"
            android:isModifier="true"
            android:isSticky="true"
            android:keyEdgeFlags="left"
            android:keyLabel="ABC"
            android:keyWidth="15%p" />
        <Key
            android:codes="29756"
            android:horizontalGap="8%p"
            android:keyLabel="琼"
            android:keyWidth="8%p" />
        <Key
            android:codes="26032"
            android:horizontalGap="2%p"
            android:keyLabel="新"
            android:keyWidth="8%p" />
        <Key
            android:codes="23425"
            android:horizontalGap="2%p"
            android:keyLabel="宁"
            android:keyWidth="8%p" />
        <Key
            android:codes="28207"
            android:horizontalGap="2%p"
            android:keyLabel="港"
            android:keyWidth="8%p" />
        <Key
            android:codes="28595"
            android:horizontalGap="2%p"
            android:keyLabel="澳"
            android:keyWidth="8%p" />
        <Key
            android:codes="21488"
            android:horizontalGap="2%p"
            android:keyLabel="台"
            android:keyWidth="8%p" />
        <Key
            android:codes="-3"
            android:horizontalGap="8%p"
            android:isRepeatable="true"
            android:keyEdgeFlags="right"
            android:keyLabel="删除"
            android:keyWidth="13%p" />
    </Row>

</Keyboard>

省简称分了四行,这里要注意下最后一行“ABC”。它是切换的关键,android:codes=”-1”中的“-1”是键位的编号,这个在做触发操作时会用到,其他的属性有时间可以研究。

第二组字母数字:number_or_letters.xml

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="0.0px"
    android:keyHeight="8%"
    android:keyWidth="10%p"
    android:verticalGap="0.0px">
    <Row android:verticalGap="1%p">
        <Key
            android:codes="49"
            android:horizontalGap="1%p"
            android:keyEdgeFlags="left"
            android:keyLabel="1"
            android:keyWidth="8%p" />
        <Key
            android:codes="50"
            android:horizontalGap="2%p"
            android:keyLabel="2"
            android:keyWidth="8%p" />
        <Key
            android:codes="51"
            android:horizontalGap="2%p"
            android:keyLabel="3"
            android:keyWidth="8%p" />
        <Key
            android:codes="52"
            android:horizontalGap="2%p"
            android:keyLabel="4"
            android:keyWidth="8%p" />
        <Key
            android:codes="53"
            android:horizontalGap="2%p"
            android:keyLabel="5"
            android:keyWidth="8%p" />
        <Key
            android:codes="54"
            android:horizontalGap="2%p"
            android:keyLabel="6"
            android:keyWidth="8%p" />
        <Key
            android:codes="55"
            android:horizontalGap="2%p"
            android:keyLabel="7"
            android:keyWidth="8%p" />
        <Key
            android:codes="56"
            android:horizontalGap="2%p"
            android:keyLabel="8"
            android:keyWidth="8%p" />
        <Key
            android:codes="57"
            android:horizontalGap="2%p"
            android:keyLabel="9"
            android:keyWidth="8%p" />
        <Key
            android:codes="48"
            android:horizontalGap="2%p"
            android:keyEdgeFlags="right"
            android:keyLabel="0"
            android:keyWidth="8%p" />
    </Row>

    <Row android:verticalGap="1%p">
        <Key
            android:codes="81"
            android:horizontalGap="1%p"
            android:keyEdgeFlags="left"
            android:keyLabel="Q"
            android:keyWidth="8%p" />
        <Key
            android:codes="87"
            android:horizontalGap="2%p"
            android:keyLabel="W"
            android:keyWidth="8%p" />
        <Key
            android:codes="69"
            android:horizontalGap="2%p"
            android:keyLabel="E"
            android:keyWidth="8%p" />
        <Key
            android:codes="82"
            android:horizontalGap="2%p"
            android:keyLabel="R"
            android:keyWidth="8%p" />
        <Key
            android:codes="84"
            android:horizontalGap="2%p"
            android:keyLabel="T"
            android:keyWidth="8%p" />
        <Key
            android:codes="89"
            android:horizontalGap="2%p"
            android:keyLabel="Y"
            android:keyWidth="8%p" />
        <Key
            android:codes="85"
            android:horizontalGap="2%p"
            android:keyLabel="U"
            android:keyWidth="8%p" />
        <Key
            android:codes="73"
            android:horizontalGap="2%p"
            android:keyLabel="I"
            android:keyWidth="8%p" />
        <Key
            android:codes="79"
            android:horizontalGap="2%p"
            android:keyLabel="O"
            android:keyWidth="8%p" />
        <Key
            android:codes="80"
            android:horizontalGap="2%p"
            android:keyEdgeFlags="right"
            android:keyLabel="P"
            android:keyWidth="8%p" />
    </Row>

    <Row android:verticalGap="1%p">
        <Key
            android:codes="65"
            android:horizontalGap="6%p"
            android:keyEdgeFlags="left"
            android:keyLabel="A"
            android:keyWidth="8%p" />
        <Key
            android:codes="83"
            android:horizontalGap="2%p"
            android:keyLabel="S"
            android:keyWidth="8%p" />
        <Key
            android:codes="68"
            android:horizontalGap="2%p"
            android:keyLabel="D"
            android:keyWidth="8%p" />
        <Key
            android:codes="70"
            android:horizontalGap="2%p"
            android:keyLabel="F"
            android:keyWidth="8%p" />
        <Key
            android:codes="71"
            android:horizontalGap="2%p"
            android:keyLabel="G"
            android:keyWidth="8%p" />
        <Key
            android:codes="72"
            android:horizontalGap="2%p"
            android:keyLabel="H"
            android:keyWidth="8%p" />
        <Key
            android:codes="74"
            android:horizontalGap="2%p"
            android:keyLabel="J"
            android:keyWidth="8%p" />
        <Key
            android:codes="75"
            android:horizontalGap="2%p"
            android:keyLabel="K"
            android:keyWidth="8%p" />
        <Key
            android:codes="76"
            android:horizontalGap="2%p"
            android:keyEdgeFlags="right"
            android:keyLabel="L"
            android:keyWidth="8%p" />
    </Row>

    <Row>
        <Key
            android:codes="-1"
            android:horizontalGap="1%p"
            android:isModifier="true"
            android:isSticky="true"
            android:keyEdgeFlags="left"
            android:keyLabel="省份"
            android:keyWidth="13%p" />
        <Key
            android:codes="90"
            android:horizontalGap="3%p"
            android:keyLabel="Z"
            android:keyWidth="8%p" />
        <Key
            android:codes="88"
            android:horizontalGap="2%p"
            android:keyLabel="X"
            android:keyWidth="8%p" />
        <Key
            android:codes="67"
            android:horizontalGap="2%p"
            android:keyLabel="C"
            android:keyWidth="8%p" />
        <Key
            android:codes="86"
            android:horizontalGap="2%p"
            android:keyLabel="V"
            android:keyWidth="8%p" />
        <Key
            android:codes="66"
            android:horizontalGap="2%p"
            android:keyLabel="B"
            android:keyWidth="8%p" />
        <Key
            android:codes="78"
            android:horizontalGap="2%p"
            android:keyLabel="N"
            android:keyWidth="8%p" />
        <Key
            android:codes="77"
            android:horizontalGap="2%p"
            android:keyLabel="M"
            android:keyWidth="8%p" />
        <Key
            android:codes="-3"
            android:horizontalGap="3%p"
            android:isRepeatable="true"
            android:keyEdgeFlags="right"
            android:keyLabel="删除"
            android:keyWidth="13%p" />
    </Row>
</Keyboard>

同上,这个切换在省份,code也是-1。
然后是放置软键盘,由于是自定义的,所以需要放置在相应的布局中(include应该也可以,我没试过)。要在底端显示,布局最好是RelativeLayout。示例代码如下

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/et_keyboard"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_margin="20dp"
        android:background="#ACE"
        android:hint="车牌号"
        android:padding="10dp" />

    <android.inputmethodservice.KeyboardView
        android:id="@+id/keyboard_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="#DCDCDC"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:keyBackground="@drawable/selector_key"
        android:keyTextColor="#000"
        android:keyTextSize="16sp"
        android:paddingBottom="8dp"
        android:paddingTop="8dp"
        android:shadowColor="#FFFFFF"
        android:shadowRadius="0.0"
        android:visibility="gone" />

</RelativeLayout>

相信我,自定义软键盘如果难看,会非常难看!!我们需要加上样式,
即:android:keyBackground=”@drawable/selector_key”
selector_key.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/shape_key_normal" android:state_pressed="true" />
    <item android:drawable="@drawable/shape_key_pressed" />
</selector>

shape_key_normal.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="5dp"/>
    <solid android:color="@android:color/darker_gray" />
</shape>

shape_key_pressed.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="5dp"/>
    <solid android:color="#ffffff" />
</shape>

布局到这里就完了,具体情况需要微调布局。下面是软键盘的处理逻辑。


二、软键盘的处理逻辑
由于需要对软键盘进行一系列的操作,所以最好定义一个工具类来处理这些事,这也是比较关键的一环。
所以定义KeyboardUtil类,由于习惯的原因和18年那篇文章有些不同键盘可以切换,允许出现不符合格式的情况,示例代码如下:

package com.xxx.xxx.utils;

import android.app.Activity;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.text.Editable;
import android.text.InputType;
import android.view.View;
import android.view.WindowManager;
import android.widget.EditText;
import com.haorizi.dipuchuangxin.R;
import com.orhanobut.logger.Logger;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Class Name:
 * Created by xxx on 2018/7/11.
 * 备注:自定义软键盘的工具类
 *
 * @version 2018071101
 */
public class KeyboardUtil {
    private Activity mActivity;
    private KeyboardView mKeyboardView;
    private EditText mEdit;
    /**
     * 省简称键盘
     */
    private Keyboard provinceKeyboard;
    /**
     * 数字、字母键盘
     */
    private Keyboard numberKeyboard;
    private boolean isProvince = true;

    public KeyboardUtil(Activity activity, EditText edit) {
        mActivity = activity;
        mEdit = edit;
        // 绑定布局
        numberKeyboard = new Keyboard(activity, R.xml.number_or_letters);
        provinceKeyboard = new Keyboard(activity, R.xml.province_abbreviation);
        // 还是别用ButterKnife了
        mKeyboardView = (KeyboardView) activity.findViewById(R.id.keyboard_view);
        // 设置省简称键盘
        mKeyboardView.setKeyboard(provinceKeyboard);
        // 设置焦点
        mKeyboardView.setEnabled(true);
        mKeyboardView.setPreviewEnabled(false);
        // 键盘监听事件
        mKeyboardView.setOnKeyboardActionListener(new KeyboardView.OnKeyboardActionListener() {
            @Override
            public void onPress(int primaryCode) {

            }

            @Override
            public void onRelease(int primaryCode) {

            }

            @Override
            public void onKey(int primaryCode, int[] keyCodes) {
                Editable editable = mEdit.getText();
                int start = mEdit.getSelectionStart();
                // 判定是否是中文的正则表达式 [\\u4e00-\\u9fa5]判断一个中文 [\\u4e00-\\u9fa5]+多个中文
                String reg = "[\\u4e00-\\u9fa5]";
                Logger.e("=  ==" + primaryCode);
                if (primaryCode == -1) {// 省份简称与数字键盘切换
//                    if (mEdit.getText().toString().matches(reg)) {
                    Logger.e("切换。。。");
                    if (isProvince) {
                        // 切数字
                        changeKeyboard(true);
                    } else {
                        // 切省份
                        changeKeyboard(false);
                    }
//                    }
                } else if (primaryCode == -3) {
                    if (editable != null && editable.length() > 0) {
                        //没有输入内容时软键盘重置为省份简称软键盘
                        if (editable.length() == 1) {
                            changeKeyboard(false);
                        }
                        if (start > 0) {
                            editable.delete(start - 1, start);
                        }
                    }
                } else {
                    editable.insert(start, Character.toString((char) primaryCode));
                    // 判断第一个字符是否是中文,是,则自动切换到数字软键盘
                    if (mEdit.getText().toString().matches(reg)) {
                        changeKeyboard(true);
                    }
                }
            }

            @Override
            public void onText(CharSequence text) {

            }

            @Override
            public void swipeLeft() {

            }

            @Override
            public void swipeRight() {

            }

            @Override
            public void swipeDown() {

            }

            @Override
            public void swipeUp() {

            }
        });
    }

    /**
     * 指定切换软键盘 isNumber false表示要切换为省份简称软键盘 true表示要切换为数字软键盘
     */
    private void changeKeyboard(boolean isNumber) {
        if (isNumber) {
            // 切数字
            mKeyboardView.setKeyboard(numberKeyboard);
            isProvince = false;
        } else {
            // 切省份
            mKeyboardView.setKeyboard(provinceKeyboard);
            isProvince = true;
        }
    }

    /**
     * 软键盘展示状态
     */
    public boolean isShow() {
        return mKeyboardView.getVisibility() == View.VISIBLE;
    }

    /**
     * 软键盘展示
     */
    public void showKeyboard() {
        int visibility = mKeyboardView.getVisibility();
        if (visibility == View.GONE || visibility == View.INVISIBLE) {
            mKeyboardView.setVisibility(View.VISIBLE);
        }
    }

    /**
     * 软键盘隐藏
     */
    public void hideKeyboard() {
        int visibility = mKeyboardView.getVisibility();
        if (visibility == View.VISIBLE) {
            mKeyboardView.setVisibility(View.INVISIBLE);
        }
    }

    /**
     * 禁掉系统软键盘
     */
    public void hideSoftInputMethod() {
        mActivity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
        int currentVersion = android.os.Build.VERSION.SDK_INT;
        String methodName = null;
        if (currentVersion >= 16) {
            // 4.2
            methodName = "setShowSoftInputOnFocus";
        } else if (currentVersion >= 14) {
            // 4.0
            methodName = "setSoftInputShownOnFocus";
        }
        if (methodName == null) {
            mEdit.setInputType(InputType.TYPE_NULL);
        } else {
            Class<EditText> cls = EditText.class;
            Method setShowSoftInputOnFocus;
            try {
                setShowSoftInputOnFocus = cls.getMethod(methodName, boolean.class);
                setShowSoftInputOnFocus.setAccessible(true);
                setShowSoftInputOnFocus.invoke(mEdit, false);
            } catch (NoSuchMethodException e) {
                mEdit.setInputType(InputType.TYPE_NULL);
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
}

这部分的代码大同小异。然后是Activity中的引用(只在需要车牌的地方触发,其他的Edit Text是不影响的)。


三、Activity示例代码:

package com.xxx.xxx;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import com.haorizi.dipuchuangxin.ui.BaseAty;
import com.haorizi.dipuchuangxin.utils.KeyboardUtil;
import com.haorizi.dipuchuangxin.utils.PtrHelper;
import com.orhanobut.logger.Logger;
import butterknife.Bind;
import butterknife.OnClick;
import in.srain.cube.views.ptr.PtrDefaultHandler;
import in.srain.cube.views.ptr.PtrFrameLayout;

/**
 * Class Name:
 * Created by xxx .
 * 简介:
 * Data: 2018/5/14.
 *
 * @version 2018061401 xxx
 */

public class TextAty extends BaseAty {

    @Bind(R.id.keyboard_view)
    KeyboardView mKeyboardView;
    @Bind(R.id.et_keyboard)
    EditText et;

    private KeyboardUtil keyboardUtil;

    @Override
    public int getLayoutId() {
        return R.layout.soft_key_board;
    }

    @Override
    public void initData() {
       // 触发自定义键盘
        et.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (keyboardUtil == null){
                    keyboardUtil = new KeyboardUtil(TextAty.this, et);
                    keyboardUtil.hideSoftInputMethod();
                    keyboardUtil.showKeyboard();
                } else {
                    keyboardUtil.showKeyboard();
                }
                return false;
            }
        });

        et.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) {

            }
        });
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK){
            //if (keyboardUtil.isShow()){
            //    keyboardUtil.hideKeyboard();
            //} else {
            //    finish();
            // }

            // version 2018071801
            if(keyboardUtil != null && keyboardUtil.isShow()){
                keyboardUtil.hideKeyboard();
            } else {
                finish();
            }
        }
        return false;
    }

    @Override
    public void requestData() {
        Logger.e("stop refresh...");
        showToast("stop refresh...");
    }
}

至此完结,眼下键盘还有不能自己消失,需要点击手机上的返回键,后期优化吧,这倒不难。


version 2018171801 刚发现一个错误,在返回键监听的方法中,要先判断keyboardUtil是否为null,如果没弹起直接点返回会报空指针。

猜你喜欢

转载自blog.csdn.net/qq_34781711/article/details/81012444