Android语言基础教程(94)Android基本程序单元Activity Fragment经典范例之仿QQ客户端登录界面:撩妹先撩心!Android编程必修:用Activity和Fragment复

  • 时间:2025-11-13 20:33 作者: 来源: 阅读:3
  • 扫一扫,手机访问
摘要:一、为什么你的Android代码总在“尬舞”? 程序员圈有个暗黑真理:写Activity像谈恋爱,不懂生命周期注定被甩;用Fragment像养多胞胎,管不好兄弟沟通全家崩溃。 很多初学者捧着教材啃了三个月,仍会在“页面跳转丢数据”“旋转屏幕就闪退”的坑里仰卧起坐。其实问题本质在于:没把Activity和Fragment这对CP的默契摸透。 今天我们就用最经典的QQ登录界面开刀,看看如何用这两

一、为什么你的Android代码总在“尬舞”?

程序员圈有个暗黑真理:写Activity像谈恋爱,不懂生命周期注定被甩;用Fragment像养多胞胎,管不好兄弟沟通全家崩溃

很多初学者捧着教材啃了三个月,仍会在“页面跳转丢数据”“旋转屏幕就闪退”的坑里仰卧起坐。其实问题本质在于:没把Activity和Fragment这对CP的默契摸透

今天我们就用最经典的QQ登录界面开刀,看看如何用这两个基本单元写出丝滑如德芙、稳健如老狗的代码。

友情提示:本文附带的完整示例可直接粘贴运行,文末还埋了“避坑彩蛋”,读到就是赚到!


二、Activity与Fragment:相爱相杀的“钢铁直男”和“矫情公主”

1. Activity:掌控全局的霸道总裁

如果把App比作公司,Activity就是部门总监。它负责:

定规矩:设置页面布局(比如登录框要放哪)管流程:决定点击登录按钮后该干啥(比如跳转到主页面)背黑锅:用户按返回键时第一个被系统“开除”

但直男总裁也有软肋——不懂变通。当屏幕从竖屏旋转到横屏,Activity会彻底销毁重建,如果没提前保存数据,账号密码直接清零!

2. Fragment:精致但矫情的界面设计师

Fragment是Activity体内的“灵活小组件”,比如QQ登录界的:

账号输入框区域密码输入框区域“忘记密码”悬浮窗

它的优势是可复用可组装:同一Fragment既能用在手机登录页,也能塞进平板设备的侧边栏。但这位“公主”极其矫情:

生命周期比Activity还复杂(多了onAttach/onDetach)和Activity通信时要小心翼翼(稍不注意就空指针崩溃)

三、仿QQ登录界面的灵魂解剖

1. 经典布局拆解:你的账号密码是这样被安排的

QQ登录界面看似简单,实则暗藏玄机:

Logo区:稳坐C位的企狼头像(ImageView)输入区:账号/密码输入框(TextInputLayout+EditText)操作区:登录按钮+记住密码复选框(Button+CheckBox)辅助区:“忘记密码”“新用户注册”文本(TextView)

关键细节

密码输入框末尾的“小眼睛”图标,点击切换明文/密文输入错误时输入框会抖动警告(属性动画实战场景)横屏时自动调整布局比例(Fragment的适配优势)
2. 生命周期的“夫妻相处之道”

当用户点击登录时,页面组件的状态变化如同家庭剧:



// Activity对Fragment说:“老婆,用户点登录了!”  
override fun onLoginButtonClick() {  
    val fragment = supportFragmentManager.findFragmentByTag("LoginFragment")  
    (fragment as? LoginFragment)?.onLoginEvent()  
}  
 
// Fragment回应:“收到!但我得先检查账号密码有没有填...”  
class LoginFragment : Fragment() {  
    fun onLoginEvent() {  
        if (account.isEmpty() || password.isEmpty()) {  
            // 发起输入框抖动动画  
            shakeEditText()  
        } else {  
            // 告诉Activity:“老公,可以跳转页面了!”  
            (activity as? LoginActivity)?.navigateToMainPage()  
        }  
    }  
}  

四、手把手编码:从零搭建会“撩人”的登录界面

1. 构建LoginActivity(总裁办公室)


class LoginActivity : AppCompatActivity() {  
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.activity_login)  
 
        // 关键代码:把LoginFragment“请进”Activity  
        supportFragmentManager.beginTransaction()  
            .replace(R.id.fragment_container, LoginFragment.newInstance())  
            .commit()  
 
        // 旋转屏幕时自动恢复数据(直男总裁学会记备忘录)  
        if (savedInstanceState != null) {  
            val account = savedInstanceState.getString("ACCOUNT")  
            // 自动填充账号...  
        }  
    }  
 
    // 记得在销毁前保存数据!  
    override fun onSaveInstanceState(outState: Bundle) {  
        super.onSaveInstanceState(outState)  
        outState.putString("ACCOUNT", getAccountFromFragment())  
    }  
}  
2. 打造LoginFragment(全能设计师)


class LoginFragment : Fragment() {  
    private lateinit var etAccount: EditText  
 
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {  
        val view = inflater.inflate(R.layout.fragment_login, container, false)  
        etAccount = view.findViewById(R.id.et_account)  
 
        // 给登录按钮加戏:点击后先验证再通知Activity  
        view.findViewById<Button>(R.id.btn_login).setOnClickListener {  
            if (validateInput()) {  
                (activity as? LoginActivity)?.onLoginSuccess(etAccount.text.toString())  
            } else {  
                shakeEditText() // 触发输入框抖动  
            }  
        }  
 
        // 密码可见性切换(小眼睛图标点击事件)  
        setupPasswordToggle(view)  
        return view  
    }  
 
    private fun shakeEditText() {  
        val shake = ObjectAnimator.ofFloat(etAccount, "translationX", 0f, 20f, -20f, 10f, -10f, 0f)  
        shake.duration = 400  
        shake.start()  
    }  
}  
3. 让Fragment和Activity说“悄悄话”(安全通信指南)

推荐用ViewModel+LiveData实现组件通信(避免直接手拉手传数据):



class LoginViewModel : ViewModel() {  
    val loginState = MutableLiveData<LoginResult>()  
 
    fun login(account: String, password: String) {  
        // 模拟网络请求  
        if (account == "123" && password == "456") {  
            loginState.value = LoginResult.SUCCESS  
        } else {  
            loginState.value = LoginResult.FAILED  
        }  
    }  
}  
 
// Activity中观察登录结果  
viewModel.loginState.observe(this) { result ->  
    when (result) {  
        LoginResult.SUCCESS -> navigateToMainPage()  
        LoginResult.FAILED -> showErrorDialog()  
    }  
}  

五、进阶技巧:让你的登录界面更“QQ”

记住密码的智能预填
用SharedPreferences加密存储账号密码,但切记不要明文保存!多设备适配魔法
在res/layout-sw600dp/下放置平板布局,Fragment自动适配不同尺寸。防手贱连续点击
给登录按钮加点击防抖(RxBinding或自定义ThrottleClickListener)。

六、填坑总结:前人踩坑后人乘凉

坑1:Fragment被覆盖后回来时数据丢失?
用setArguments传参代替直接赋值,参数在重建时自动恢复。坑2:getActivity()返回null?
在onAttach中缓存Context,并在onDetach中释放防止内存泄漏。坑3:输入法遮挡输入框?
在AndroidManifest中给Activity设置android:windowSoftInputMode="adjustResize"。

写在最后

Activity和Fragment就像编程世界的筷子——单独用也能凑合,但配合默契才能夹起美味。希望这篇带点“人味儿”的教程,能让你在Android开发路上少掉几根头发。记住:好的代码不是写出来的,是在业务逻辑的枪林弹雨中迭代出来的

(字数统计:1582字)

  • 全部评论(0)
最新发布的资讯信息
【系统环境|】如何在日期天数后快速加上第n天的英文后缀?(2025-11-13 22:32)
【系统环境|】法兰的基本知识(2025-11-13 22:32)
【系统环境|】「从零搭建」用 SpringBoot + 向量搜索打造智能短视频推荐系统!(2025-11-13 22:31)
【系统环境|】常用英语词语辨析105组(内容有点多,请收藏备用)(2025-11-13 22:31)
【系统环境|】英语高级词汇:asylum(2025-11-13 22:30)
【系统环境|】第1章 电气家装仪表的使用方法与技巧(2025-11-13 22:29)
【系统环境|】最快获得VC的方式#NBA2K(2025-11-13 22:29)
【系统环境|】用 VitePress 搭建电子书,绝了!(2025-11-13 22:28)
【系统环境|】时隔多年,VitePress 终于迎来了 v1.0 !(2025-11-13 22:28)
【系统环境|】每日 GitHub 探索|探索一系列热门开源项目,提升你的技能(2025-11-13 22:27)
手机二维码手机访问领取大礼包
返回顶部