Android语言基础教程(50)Android基本组件之文本框与编辑框:[特殊字符]别让APP变哑巴!文本框与编辑框——Android界的社牛与社恐大揭秘

👻 第一章:认识Android世界的“社牛”和“社恐”

在Android应用的社交圈里,如果说ImageView是朋友圈晒图达人,Button是派对气氛组,那么TextView就是那个侃侃而谈的“社牛”——永远在输出信息,从不停歇。而EditText则是内心戏十足的“社恐”——表面安静如鸡,实则随时准备接收用户的悄悄话。

还记得你第一次打开某个APP时,那个热情洋溢的“欢迎来到XXX!”吗?那是TextView在打招呼。而当你要输入账号密码时,那个小心翼翼等你打字的空白框,就是EditText在默默递话筒。

说来有趣,这对CP其实是血缘至亲——EditText全盘继承了TextView的能力,却发展出截然不同的性格。就像哥哥继承家产当霸道总裁(专注展示),弟弟偏要搞艺术创作(专注输入)。今天,就让我们扒开这对兄弟的底裤,看看它们到底多有意思!

🎨 第二章:TextView——不只是“文字打印器”

2.1 基础人设:佛系展示官

TextView堪称组件界的劳模,从显示“Hello World”到展示万字小说,从单行标题到多行详情,无处不在。但你以为它只是个无情的文字机器?大错特错!

它的核心技能:

📢 单向输出:只显示,不接收输入(用户想修改?没门!)🎭 表情包大师:支持Emoji、颜文字、( ͡° ͜ʖ ͡°) 等各种骚操作🎨 美颜十级:字体、颜色、大小随心变,CSS看了都直呼内行

2.2 十八般武艺属性详解

别看TextView长得老实,打扮起来也是花枝招展。来感受下它的“美妆包”:



<TextView
    android:id="@+id/tv_title"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"
    android:text="我是标题我怕谁!"
    android:textColor="#FF6B6B"
    android:textSize="24sp"
    android:textStyle="bold"
    android:background="@drawable/bg_rounded"
    android:padding="16dp"
    android:gravity="center"
    android:shadowColor="#66000000"
    android:shadowDx="2"
    android:shadowDy="2"
    android:shadowRadius="4"
    android:lineSpacingExtra="8dp"/>

重点属性吐槽:


android:gravity
:控制文字在框内的位置,相当于给文字找座位(左中右随便坐)
android:textStyle
:文字要不要加粗斜体,或者又粗又斜(戏真多)
android:shadow
系列:给文字加阴影,瞬间从2D变3D,立体感拉满

2.3 编程花式玩法

光会在XML里静态装逼怎么行?真正的强者都在代码里动态换装:



val tvStatus = findViewById<TextView>(R.id.tv_status)
 
// 秒变红色警告文字
tvStatus.apply {
    text = "账号密码错误!"
    setTextColor(Color.RED)
    setBackgroundColor(Color.parseColor("#FFF0F0"))
    
    // 加点动画效果,让提示更吸睛
    animate().scaleX(1.1f).scaleY(1.1f).setDuration(300).start()
}
 
// 玩点高级的——部分文字变色
val spannable = SpannableString("第二杯半价!立即购买")
spannable.setSpan(
    ForegroundColorSpan(Color.RED), 
    3, 5, 
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
tvStatus.text = spannable

⌨️ 第三章:EditText——用户的“情绪收纳箱”

3.1 双重人格:安静时像天使,输入时像战场

如果说TextView是输出型人格,那EditText就是典型的输入型人格。它表面上是个乖巧的空白框,实际上内心OS:“用户爸爸快给我输入!我要数据!我要内容!”

它的核心定位:

🎤 话题引导者:通过hint属性温柔提示“请输入账号”🛡️ 秩序维护者:限制输入类型、长度、格式,防止用户乱来🔍 信息过滤师:实时验证输入内容,及时给出反馈

3.2 防手残属性大全

用户的手就像帕金森,什么奇葩输入都干得出来。EditText的职责就是提前设防:



<EditText
    android:id="@+id/et_email"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="请输入邮箱地址"
    android:inputType="textEmailAddress"
    android:maxLines="1"
    android:maxLength="50"
    android:drawableStart="@drawable/ic_email"
    android:drawablePadding="8dp"
    android:backgroundTint="#2196F3"
    android:autofillHints="email"/>

防呆设计解析:


android:inputType
:这是大杀器!能自动调出对应键盘:


textPassword
:密码键盘,显示***
number
:数字键盘,告别误触字母
phone
:拨号键盘,专治各种不服
textEmailAddress
:带@的邮箱专用键盘


android:maxLength
:防话痨,输入超过长度自动截断
android:drawableStart
:左边加个小图标,瞬间提升B格

3.3 监听与验证——给输入上紧箍咒

光有静态防御还不够,动态监控才是王道:



val etPassword = findViewById<EditText>(R.id.et_password)
 
// 实时监听文字变化
etPassword.addTextChangedListener(object : TextWatcher {
    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        // 文字变化前:朕知道了
    }
    
    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        // 文字变化中:实时验证
        val password = s.toString()
        if (password.length < 6) {
            etPassword.error = "密码至少6位!"
        } else {
            etPassword.error = null // 清除错误提示
        }
    }
    
    override fun afterTextChanged(s: Editable?) {
        // 文字变化后:可以搞事了
    }
})
 
// 焦点变化监听:失去焦点时进行最终验证
etPassword.setOnFocusChangeListener { _, hasFocus ->
    if (!hasFocus) {
        val password = etPassword.text.toString()
        if (!isValidPassword(password)) {
            Toast.makeText(this, "密码格式不正确!", Toast.LENGTH_SHORT).show()
        }
    }
}

🎯 第四章:实战!打造个人资料编辑页

理论说再多都是纸上谈兵,来撸个真实场景——几乎每个APP都有的“个人资料编辑页”。

4.1 布局文件:颜值即正义


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="20dp">
 
    <!-- 标题 -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="编辑个人资料"
        android:textSize="24sp"
        android:textStyle="bold"
        android:gravity="center"
        android:layout_marginBottom="30dp"/>
 
    <!-- 昵称输入 -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="昵称"
        android:textStyle="bold"
        android:layout_marginBottom="8dp"/>
    
    <EditText
        android:id="@+id/et_nickname"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="给自己取个酷炫的昵称"
        android:maxLines="1"
        android:maxLength="20"
        android:backgroundTint="#FF6B6B"
        android:layout_marginBottom="20dp"/>
 
    <!-- 个性签名 -->
    <TextView
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"
        android:text="个性签名"
        android:textStyle="bold"
        android:layout_marginBottom="8dp"/>
    
    <EditText
        android:id="@+id/et_bio"
        android:layout_width="match_parent"
        android:layout_height="120dp"
        android:gravity="top"
        android:hint="用一句话介绍自己吧~"
        android:maxLength="100"
        android:inputType="textMultiLine"
        android:backgroundTint="#4ECDC4"
        android:layout_marginBottom="20dp"/>
 
    <!-- 显示已输入字数 -->
    <TextView
        android:id="@+id/tv_counter"  
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="end"
        android:text="0/100"
        android:textColor="#666"
        android:layout_marginBottom="30dp"/>
 
    <!-- 保存按钮 -->
    <Button
        android:id="@+id/btn_save"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="保存资料"
        android:background="#FF6B6B"
        android:textColor="#FFF"
        android:textSize="16sp"/>
 
</LinearLayout>
4.2 逻辑代码:让界面活起来


class ProfileEditActivity : AppCompatActivity() {
 
    private lateinit var etNickname: EditText
    private lateinit var etBio: EditText
    private lateinit var tvCounter: TextView
    private lateinit var btnSave: Button
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_profile_edit)
        
        // 绑定视图
        initViews()
        
        // 设置监听器
        setupListeners()
    }
    
    private fun initViews() {
        etNickname = findViewById(R.id.et_nickname)
        etBio = findViewById(R.id.et_bio)
        tvCounter = findViewById(R.id.tv_counter)
        btnSave = findViewById(R.id.btn_save)
        
        // 设置初始数据
        etNickname.setText("Android小王子")
        etBio.setText("热爱编码的移动开发者")
        updateCounter(etBio.text.length)
    }
    
    private fun setupListeners() {
        // 个性签名字数实时统计
        etBio.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {}
            
            override fun onTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {
                updateCounter(s?.length ?: 0)
            }
            
            override fun afterTextChanged(s: Editable?) {}
        })
        
        // 保存按钮点击事件
        btnSave.setOnClickListener {
            saveProfile()
        }
        
        // 昵称输入完成验证
        etNickname.setOnFocusChangeListener { _, hasFocus ->
            if (!hasFocus) {
                validateNickname()
            }
        }
    }
    
    private fun updateCounter(length: Int) {
        tvCounter.text = "$length/100"
        // 超过80字给个警告色
        if (length > 80) {
            tvCounter.setTextColor(Color.RED)
        } else {
            tvCounter.setTextColor(Color.parseColor("#666"))
        }
    }
    
    private fun validateNickname(): Boolean {
        val nickname = etNickname.text.toString().trim()
        return when {
            nickname.isEmpty() -> {
                etNickname.error = "昵称不能为空"
                false
            }
            nickname.length < 2 -> {
                etNickname.error = "昵称至少2个字符"  
                false
            }
            nickname.length > 20 -> {
                etNickname.error = "昵称最多20个字符"
                false
            }
            else -> {
                etNickname.error = null
                true
            }
        }
    }
    
    private fun saveProfile() {
        val nickname = etNickname.text.toString().trim()
        val bio = etBio.text.toString().trim()
        
        if (!validateNickname()) {
            Toast.makeText(this, "请检查昵称格式", Toast.LENGTH_SHORT).show()
            return
        }
        
        // 模拟保存操作
        Toast.makeText(this, "资料保存成功!", Toast.LENGTH_SHORT).show()
        
        // 这里实际开发中应该调用API保存数据
        Log.d("Profile", "昵称: $nickname, 签名: $bio")
    }
}

🚀 第五章:进阶技巧——从能用→好用

5.1 自定义样式——告别原生丑

原生EditText长得太“安卓”,是时候给它整容了:



<!-- res/drawable/et_custom_bg.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#FFFFFF"/>
    <corners android:radius="8dp"/>
    <stroke android:width="1dp" android:color="#DDD"/>
</shape>
<!-- 使用自定义背景 -->
<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/et_custom_bg"
    android:padding="16dp"/>
5.2 输入过滤——精确控制内容

除了长度限制,还能玩得更细:



// 只允许输入字母和数字
val letterNumberFilter = InputFilter { source, start, end, dest, dstart, dend ->
    val regex = Regex("[a-zA-Z0-9]*")
    if (source.toString().matches(regex)) {
        source // 允许输入
    } else {
        "" // 拒绝输入
    }
}
 
etNickname.filters = arrayOf(letterNumberFilter)
 
// 或者用更简单的方式——正则表达式过滤
val noEmojiFilter = InputFilter { source, _, _, _, _, _ ->
    source.toString().replace(Regex("[uD83C-uDBFFuDC00-uDFFF]"), "")
}
etNickname.filters = arrayOf(noEmojiFilter)
5.3 自动完成——预测用户意图


// 模拟常用昵称推荐
val commonNicknames = arrayOf("Android大神", "Java小王子", "Kotlin爱好者", "代码诗人")
val adapter = ArrayAdapter(this, android.R.layout.simple_dropdown_item_1line, commonNicknames)
etNickname.setAdapter(adapter)
etNickname.threshold = 1 // 输入1个字符就开始提示

💡 第六章:避坑指南与性能优化

6.1 常见坑爹场景

内存泄漏:TextWatcher用完不移除?Activity哭了



// 正确做法:在适当时机移除监听器
override fun onDestroy() {
    etBio.removeTextChangedListener(textWatcher)
    super.onDestroy()
}

键盘遮挡:EditText在底部?输入时被键盘挡住?



<!-- 在Manifest中配置 -->
<activity
    android:name=".ProfileEditActivity"
    android:windowSoftInputMode="adjustResize"/>

性能杀手:在TextWatcher里做耗时操作?卡死你没商量



// 错误示范:在输入监听里请求网络
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
    // 千万别这么干!会频繁请求网络
    api.search(s.toString()) 
}
 
// 正确做法:使用延迟搜索
private fun setupSearch() {
    etSearch.addTextChangedListener(object : TextWatcher {
        private var timer: Timer? = null
        
        override fun onTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {
            timer?.cancel()
            timer = Timer().apply {
                schedule(object : TimerTask() {
                    override fun run() {
                        // 延迟500毫秒后搜索
                        runOnUiThread { doSearch(s.toString()) }
                    }
                }, 500)
            }
        }
    })
}

🎊 第七章:结语——让你的APP”会说话”

看到这里,你应该明白:TextView和EditText这对看似简单的组合,实际上蕴含着巨大的交互能量。

TextView就像APP的播音员,负责清晰、准确、美观地传递信息。它的每一个属性调整,都是在优化用户体验的细节。

EditText则是APP的倾听者,通过精心的输入控制和实时反馈,让用户在输入过程中感到被理解、被引导、被保护。

一个优秀的Android开发者,应该像导演调教演员一样,充分挖掘每个组件的潜力。当你的TextView能够用合适的语气说话,当你的EditText能够温柔地引导用户输入,你的APP就真正”活”过来了。

现在,打开Android Studio,用今天学到的技巧,打造一个让用户忍不住想打字的输入体验吧!记住:好的界面自己会说话,而伟大的界面会让用户愿意跟它对话。

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...