[Android开发] 重学Android-EditText的进阶操作

news/2024/7/16 8:47:38 标签: android, android studio, ide

EditText的进阶使用

EditText 是我们常用的输入框控件,平常我们只是使用它输入文本,这里记录一些它不太常见的操作和一些解决方案。

一、焦点的自动获取

如果一个页面内定义了EditText,那么有可能我们进入此页面的时候会自动弹起软键盘,(分机型,有的会弹,有的不弹)。如果我们需要弹软键盘,我们制定给 EditText 设置

    android:focusable="true"
    android:focusableInTouchMode="true"

但是如果我们不想这个页面进去就弹出软键盘,我们可以给根布局或者 EditText 的父布局设置 focusable 。

二、光标和背景的控制

默认的 EditText 是带下划线和粗光标的,我们可以对它们进行简单的修改

android:background="@null" //去掉了下划线

android:textCursorDrawable="@null"  //去掉光标的颜色

自定义光标的颜色和宽度:

<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <size android:width="2dp" />

    <solid android:color="#BDC7D8" />

</shape>

使用自定义光标

android:textCursorDrawable="@drawable/edittext_cursor"

三、限制小数点位数

我们可以通过监听 EditText 的文本变化的方式来改变文本值,我们还能通过 DigitsKeyListener 的方式监听文本的改变。

3.1 TextWatcher的方式

我们可以通过监听 EditText 的文本变化,比如我们只想要小数点后面2位数,我们就监听文本变化,点后面的2位数,如果多了就把他删除掉。

public class MoneyTextWatcher implements TextWatcher {
    private EditText editText;
    private int digits = 2;

    public MoneyTextWatcher(EditText et) {
        editText = et;
    }
    public MoneyTextWatcher setDigits(int d) {
        digits = d;
        return this;
    }


    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        //删除“.”后面超过2位后的数据
        if (s.toString().contains(".")) {
            if (s.length() - 1 - s.toString().indexOf(".") > digits) {
                s = s.toString().subSequence(0,
                        s.toString().indexOf(".") + digits+1);
                editText.setText(s);
                editText.setSelection(s.length()); //光标移到最后
            }
        }
        //如果"."在起始位置,则起始位置自动补0
        if (s.toString().trim().substring(0).equals(".")) {
            s = "0" + s;
            editText.setText(s);
            editText.setSelection(2);
        }

        //如果起始位置为0,且第二位跟的不是".",则无法后续输入
        if (s.toString().startsWith("0")
                && s.toString().trim().length() > 1) {
            if (!s.toString().substring(1, 2).equals(".")) {
                editText.setText(s.subSequence(0, 1));
                editText.setSelection(1);
                return;
            }
        }
    }

    @Override
    public void afterTextChanged(Editable s) {

    }
}

使用:

//默认两位小数
mEditText.addTextChangedListener(new MoneyTextWatcher(mEditText1));

//手动设置其他位数,例如3
mEditText.addTextChangedListener(new MoneyTextWatcher(mEditText1).setDigits(3);

3.2 DigitsKeyListener的方式

public class ETMoneyValueFilter extends DigitsKeyListener {

    public ETMoneyValueFilter(int d) {
        super(false, true);
        digits = d;
    }

    private int digits = 2;  //默认显示二位数的小数点

    public ETMoneyValueFilter setDigits(int d) {
        digits = d;
        return this;
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        CharSequence out = super.filter(source, start, end, dest, dstart, dend);

        if (out != null) {
            source = out;
            start = 0;
            end = out.length();
        }

        int len = end - start;

        if (len == 0) {
            return source;
        }

        //以点开始的时候,自动在前面添加0
        if (source.toString().equals(".") && dstart == 0) {
            return "0.";
        }
        //如果起始位置为0,且第二位跟的不是".",则无法后续输入
        if (!source.toString().equals(".") && dest.toString().equals("0")) {
            return "";
        }

        int dlen = dest.length();

        for (int i = 0; i < dstart; i++) {
            if (dest.charAt(i) == '.') {
                return (dlen - (i + 1) + len > digits) ?
                        "" :
                        new SpannableStringBuilder(source, start, end);
            }
        }

        for (int i = start; i < end; ++i) {
            if (source.charAt(i) == '.') {
                if ((dlen - dend) + (end - (i + 1)) > digits)
                    return "";
                else
                    break;  
            }
        }

        return new SpannableStringBuilder(source, start, end);
    }
}

其实是和 TextWatcher 类似的方式,那么使用的时候我们这样使用:

//默认两位小数
mEditText.setFilters(new InputFilter[]{new MoneyValueFilter()});

//手动设置其他位数,例如3
mEditText.setFilters(new InputFilter[]{new MoneyValueFilter().setDigits(3)});

在Kotlin代码中是这样使用:

et_input.filters = arrayOf(ETMoneyValueFilter().setDigits(3))

这样就可以实现小数点后面二位数的控制,还顺便加入了.的判断,自动加0的操作。

四、EditText的Search操作

当此 EditText 软键盘弹起的时候,右下角的确定变为搜索,我们需要给 EditText 设置一个属性:

 android:imeOptions="actionSearch"

然后给软键盘设置一个监听

       //点击软键盘搜索按钮
        etSearch.setOnKeyListener(new View.OnKeyListener() {

            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {

                if (keyCode == KeyEvent.KEYCODE_ENTER) {
                    // 先隐藏键盘
                    ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE))
                            .hideSoftInputFromWindow(TransactionHistorySearchActivity.this.getCurrentFocus()
                                    .getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);

                    if (isSearch){
                        isSearch = false;
                        if (!TextUtils.isEmpty(etSearch.getText().toString()))
                            searchHistory(etSearch.getText().toString());
                    }

                }
                return false;
            }
        });

这里使用一个flag来判断,是因为部分机型会回调2次。所以为了统一效果,我们使用拦截判断只调用一次。

当然Search的逻辑如果你使用 Kotlin + DataBinding 来实现,那么就更简单了。

        //执行搜索
        fun doSearch() {
            KeyboardUtils.hideSoftInput(mActivity)
            scrollTopRefresh()
        }

        //搜索的删除
        fun searchDel() {
            mViewModel.mKeywordLiveData.value = ""
            doSearch()
        }
           <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="32dp"
                android:layout_marginLeft="@dimen/d_15dp"
                android:background="@drawable/shape_search_gray_bg_corners20"
                android:gravity="center_vertical"
                android:orientation="horizontal">

                <ImageView
                    android:layout_width="@dimen/d_16dp"
                    android:layout_height="@dimen/d_16dp"
                    android:layout_marginLeft="@dimen/d_12dp"
                    android:src="@drawable/search_icon"
                    binding:clicks="@{click.doSearch}" />

                <EditText
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="@dimen/d_12dp"
                    android:layout_weight="1"
                    android:background="@color/transparent"
                    android:hint="大家都在搜"
                    android:imeOptions="actionSearch"
                    android:singleLine="true"
                    android:text="@={viewModel.mKeywordLiveData}"
                    android:textColor="@color/black"
                    android:textColorHint="@color/gray_99"
                    android:textSize="@dimen/d_14sp"
                    binding:onKeyEnter="@{click.doSearch}"
                    binding:typefaceMedium="@{true}" />

                <ImageView
                    android:layout_width="@dimen/d_16dp"
                    android:layout_height="@dimen/d_16dp"
                    android:layout_marginRight="@dimen/d_10dp"
                    android:src="@drawable/search_delete"
                    android:visibility="gone"
                    binding:clicks="@{click.searchDel}"
                    binding:isVisibleGone="@{!TextUtils.isEmpty(viewModel.MKeywordLiveData)}" />

            </LinearLayout>

主要的 Binding Adapter 方法为 onKeyEnter ,它实现了软键盘的搜索。

下面是自定义BindingAdapter的方法:

var _viewClickFlag = false
var _clickRunnable = Runnable { _viewClickFlag = false }

/**
 * Edit的确认按键事件
 */
@BindingAdapter("onKeyEnter")
fun EditText.onKeyEnter(action: () -> Unit) {
    setOnKeyListener { _, keyCode, _ ->
        if (keyCode == KeyEvent.KEYCODE_ENTER) {
            KeyboardUtils.closeSoftKeyboard(this)

            if (!_viewClickFlag) {
                _viewClickFlag = true
                action()
            }
            removeCallbacks(_clickRunnable)
            postDelayed(_clickRunnable, 1000)
        }
        return@setOnKeyListener false
    }
}

和上面Java的实现方式类似,同样的做了防抖的操作。为了部分机型连续调用多次的问题。
效果:

五、焦点与软键盘的自由控制

上面说到的焦点,不自动弹出软键盘,如果我想自由的控制焦点与软键盘怎么办?

一个例子来说明,比如我们的需求,点击 EditText 的时候弹出弹框提示用户注意事项,当点击确定或者取消之后再继续输入。分解步骤如下:

  1. 我们点击EditText不能弹出软键盘
  2. 监听焦点获取之后弹出弹框
  3. 弹框完成之后我们需要手动的给EditText焦点
  4. 获取焦点之后需要设置光标与软键盘

代码逻辑如下:

      mBankAccountEt.setShowSoftInputOnFocus(false);
        mBankAccountEt.setOnFocusChangeListener((v, hasFocus) -> {
            if (hasFocus && !isShowedBankAccountNotice) {
                showBankAccountNoticePopup();
            }
        });


     private void showBankAccountNoticePopup() {
        BasePopupView mPopupView = new XPopup.Builder(mActivity)
                .moveUpToKeyboard(false)
                .hasShadowBg(true)
                .asCustom(new BankNameNoticePopup(mActivity, () -> {
                    isShowedBankAccountNotice = true;
                    mBankAccountEt.setShowSoftInputOnFocus(true);

                    //需要把焦点设置回EditText
                    mBankAccountEt.setFocusable(true);
                    mBankAccountEt.setFocusableInTouchMode(true);
                    mBankAccountEt.requestFocus();
                    mBankAccountEt.setSelection(mBankAccountEt.getText().toString().length());

                    KeyboardUtils.showKeyboard(mBankAccountEt);
                }));

        if (mPopupView != null && !mPopupView.isShow()) {
            mPopupView.show();
        }
    }

弹框就不给大家展示了,非常简单的弹窗,定义使用的弹窗库,逻辑都在完成的回调中。

KeyboardUtils工具类,控制EditText的软键盘展示与隐藏

	/*
	* 显示键盘
	* */
	public static void showKeyboard(View view) {
		InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
		if (imm != null) {
			view.requestFocus();
			imm.showSoftInput(view, 0);
		}
	}

	/*
	* 隐藏键盘
	* */
	public static void hideKeyboard(View view){
		InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
		if (imm != null) {
			imm.hideSoftInputFromWindow(view.getWindowToken(),0);
		}
	}

效果:

六、RV + EditText复用的问题

不知道大家有没有在RV中使用过 EditText ,Item中如果有 EditText 那么在滚出屏幕之后 再拉回来可能刚才输入的文本就消失了,或者换成不是刚才输入的文本了,是因为缓存复用,可能复用了别的Item上面的 EditText 控件。
有几种解决方法如下:

方法一: 强制的停用Recyclerview的复用

helper.setIsRecyclable(false);

但是RV就无法缓存与回收了,如果你的Item数量就是固定的并且不多,那么使用这个方法是最好的。
方法二: 通过监听焦点来添加或移除Edittext的TextChangedListener

@Override
protected void convert(BaseViewHolder helper, EdittextInRecyclerViewOfBean item) {
    EditText editText = helper.getView(R.id.et);
    editText.setText(item.getNum() + "");

    TextWatcher textWatcher = 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) {
            //这里处理数据
            if (TextUtils.isEmpty(s.toString())) {
                item.setNum(0);
            } else {
                item.setNum(Integer.parseInt(s.toString()));
            }
        }
    };
    
    editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if (hasFocus){
                editText.addTextChangedListener(textWatcher);
            }else {
                editText.removeTextChangedListener(textWatcher);
            }
        }
    });
}

方法三: 通过view的setTag()方法解决

@Override
protected void convert(BaseViewHolder helper, EdittextInRecyclerViewOfBean item) {
    TextWatcher textWatcher = 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) {
            if (TextUtils.isEmpty(s.toString())) {
                item.setNum(0);
            } else {
                item.setNum(Integer.parseInt(s.toString()));
            }
        }
    };

    EditText editText = helper.getView(R.id.et);
    //为了避免TextWatcher在调用settext()时被调用,提前将它移除
    if (editText.getTag() instanceof TextWatcher) {
        editText.removeTextChangedListener((TextWatcher) editText.getTag());
    }
    editText.setText(item.getNum() + "");
    //重新添加上TextWatcher监听
    editText.addTextChangedListener(textWatcher);
    //将TextWatcher绑定到EditText
    editText.setTag(textWatcher);
}

方法四: 为每个EditText的绑定位置

public class EditTextInRecyclerViewAdapter extends RecyclerView.Adapter {
    private List<EdittextInRecyclerViewOfBean> mList = new ArrayList<>();

    public void setData(List<EdittextInRecyclerViewOfBean> list) {
        this.mList = list;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_edittext, parent, false);
        return new ViewHolder(v, new ITextWatcher());
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        ViewHolder viewHolder = (ViewHolder) holder;
        viewHolder.mITextWatcher.bindPosition(position);
        viewHolder.mEditText.setText(mList.get(position).getNum()+"");
    }

    @Override
    public int getItemCount() {
        return mList.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        EditText mEditText;
        ITextWatcher mITextWatcher;

        private ViewHolder(View v, ITextWatcher watcher) {
            super(v);
            this.mEditText = v.findViewById(R.id.et);
            this.mITextWatcher = watcher;
            this.mEditText.addTextChangedListener(watcher);
        }
    }

    class ITextWatcher implements TextWatcher {
        private int position;

        private void bindPosition(int position) {
            this.position = position;
        }

        @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) {
            if (TextUtils.isEmpty(s.toString())) {
                mList.get(position).setNum(0);
            } else {
                mList.get(position).setNum(Integer.parseInt(s.toString()));
            }
        }
    }
}

方法五: 构造方法中添加TextChanged

class PicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

    var ivPic: ImageView = itemView.findViewById(R.id.ivPic)
    var etScore: EditText = itemView.findViewById(R.id.etScore)
    var tvTitle: TextView = itemView.findViewById(R.id.tvTitle)
    var myTextWatcher: MyTextWatcher = MyTextWatcher()

    init {
        etScore.addTextChangedListener(myTextWatcher)
    }

    fun updateView(picItem: PicItem) {
        myTextWatcher.picItem = picItem
        ivPic.setImageResource(picItem.picResId)
        tvTitle.text = picItem.title
        if (picItem.score == null) {
            etScore.hint = "请输入分数"
            etScore.setText("")
        } else {
            etScore.setText(picItem.score)
        }
    }
}

class MyTextWatcher: TextWatcher {

    lateinit var picItem:PicItem

    override fun afterTextChanged(s: Editable?) {
        picItem?.apply {
            score=s?.toString()
        }
    }

    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
    }

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
    }
}

方法六: 让产品改需求,不要使用EditText,或者我们干脆使用TextView,然后点击Item弹出输入框的弹框方式来实现。

后记

总结了很多 EditText 不常见的一些问题与解决方案,不知道大家有没有遇到别的问题,或者一些 EditText 的骚操作,都可以评论区留言补充哦!
关于 EditText 的分享就到这里了,后面如果有补充我会继续更新!

最后,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、架构师筑基必备技能

1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……

在这里插入图片描述

二、Android百大框架源码解析

1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程

在这里插入图片描述

三、Android性能优化实战解析

  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

在这里插入图片描述

四、高级kotlin强化实战

1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始

  • Kotlin 写 Gradle 脚本是一种什么体验?

  • Kotlin 编程的三重境界

  • Kotlin 高阶函数

  • Kotlin 泛型

  • Kotlin 扩展

  • Kotlin 委托

  • 协程“不为人知”的调试技巧

  • 图解协程:suspend

在这里插入图片描述

五、Android高级UI开源框架进阶解密

1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
在这里插入图片描述

六、NDK模块开发

1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习

在这里插入图片描述

七、Flutter技术进阶

1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)

在这里插入图片描述

八、微信小程序开发

1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……

在这里插入图片描述

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

作者:newki
链接:https://juejin.cn/post/7115621556700577828


http://www.niftyadmin.cn/n/1458580.html

相关文章

【Android】插件化系列之加载异常问题如何解决?

背景 目前我们国内的游戏 SDK 采用了插件化的技术&#xff0c;优点是 SDK 可以通过热更新来完成自更新&#xff0c;缺点是会遇到各种各样奇奇怪怪的问题&#xff0c;最近就我个人遇到的一些插件化问题来给大家做一次分享&#xff0c;主要分为两个部分&#xff1a; 排查和解决资…

Android 面试——如何写一个又好又快的日志库?

在面试中&#xff0c;这类题目称为场景题&#xff0c;即就一个实际业务场景给出解决方案&#xff0c;难度较高&#xff0c;若无与之相关的实战经验&#xff0c;非常考验临场应变及综合运用储备知识的能力。这篇就来分析下“写一个 Log 需要考虑些哪些方面&#xff1f;” 先抛一…

Kotlin和Java相比有什么优点?Koltin全家桶学习资料领取

最近在学Kotlin&#xff0c;虽然还没有像其他博主一样立马就爱上它.但是不得不说&#xff0c;Kotlin对比起Java还是有不少优势&#xff1a; 代码简洁。相比较于Java代码&#xff0c;Kotlin大量使用了高阶函数和lambda语法保证在较少的代码下实现同等的功能。非空判断。在Java中…

最新《Android面试合集+完整体系学习指南》,腾讯T10工程师整理,祝你备战金九银十

金三银四没赶上&#xff0c;即将到来的金九银十可不能再错过了。每逢招聘旺季&#xff0c;都是程序员跳槽涨薪的好时机&#xff0c;把握时机&#xff0c;充分准备&#xff0c;就能获得更多更好的机会&#xff0c;让你的人生更上一步台阶。 对于Android开发者而言&#xff0c;面…

2022 Android11.0最新 Framework解析,底层开发人员必备

如果有人问&#xff1a;“在当下环境中&#xff0c;什么样的安卓程序员&#xff0c;不容易被淘汰&#xff1f;” 我的答案是&#xff1a;“底层开发&#xff01;” 互联网行业&#xff0c;更新迭代非常快&#xff0c;目前Android底层的架构也在不断调整和优化中&#xff0c;这…

【Android面试】史上最全的Android技术面试360问,全文高能

春风如贵客&#xff0c;一到便繁华。各位看官点赞再看&#xff0c;养成好习惯(●∀&#xff40;●) 不用关注公众号&#xff0c;没有什么套路&#xff0c;就是直接上干货&#xff01; 金九银十想要跳槽涨薪的&#xff0c;一定要收藏这篇文章&#xff01;&#xff01; 之后还…

一文解读Android设计思想开源框架,学完没有拿不到的offer

缘起 随着互联网企业的不断发展&#xff0c;产品项目中的模块越来越多&#xff0c;用户体验要求也越来越高&#xff0c;想实现小步快跑、快速迭代的目的越来越难&#xff0c;还有65535&#xff0c;应用之间的互相调用等等问题&#xff0c;插件化技术应用而生。如果没有插件化技…

Android Glide源码解读(一) : 主流程分析

前言 本篇作为史上最细Glide源码解读系列第一篇 , 主要对主流程进行分析 以后还会有若干篇对Glide 中 运用的设计模式 / 线程池 /图片优化/ 解码/转码/缓存 等细节的文章发布 使用 假设调用以下代码进行图片加载 Glide.with(activity).load("https/http开头的图片链接…