You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio中保存条款同意状态为偏好设置后功能失效求助

问题:添加SharedPreferences条款后,条款页面功能全部失效

我正在为APP实现“条款与条件”页面,首次打开APP时弹出,用户同意后进入主应用,拒绝则关闭APP。页面用ScrollView包含TextView和两个按钮,滚动到底部才启用按钮。但添加SharedPreferences相关代码后,所有功能都失效了,之前按钮的启用/禁用逻辑和点击事件都正常。

原代码

package com.example.termsandconditionsview

import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
import android.view.ViewTreeObserver
import android.widget.Button
import android.widget.ScrollView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity(), View.OnTouchListener, ViewTreeObserver.OnScrollChangedListener {

    private var svMain: ScrollView? = null
    private lateinit var buttonAccept: Button
    private lateinit var buttonDecline: Button

    private lateinit var sharedPref: SharedPreferences

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.terms_and_conditions_view)

        svMain = findViewById(R.id.scrollView2)
        svMain?.viewTreeObserver?.addOnScrollChangedListener(this)
        buttonAccept = findViewById(R.id.button_accept)
        buttonDecline = findViewById(R.id.button_decline)

        sharedPref = getSharedPreferences("myAppPrefs", Context.MODE_PRIVATE)

        val termsAccepted = sharedPref.getBoolean("terms_accepted", false)
        if (termsAccepted) {
            setContentView(R.layout.activity_main)
        } else {
            setContentView(R.layout.terms_and_conditions_view)
        }
    }

    override fun onScrollChanged() {
        svMain?.let { scrollView ->
            val view = scrollView.getChildAt(scrollView.childCount - 1) as View
            val bottomDetector = view.bottom - (scrollView.height + scrollView.scrollY)

            if (bottomDetector <= 0) {
                buttonAccept.isEnabled = true
                buttonDecline.isEnabled = true
            } else {
                buttonAccept.isEnabled = false
                buttonDecline.isEnabled = false
            }
            buttonAccept.setOnClickListener {
                // Update the termsAccepted value to true in SharedPreferences
                val editor = sharedPref.edit()
                editor.putBoolean("terms_accepted", true)
                editor.apply()

                val textAcceptTac = getString(R.string.txt_accept_tac)
                val duration = Toast.LENGTH_LONG
                val toastAccept = Toast.makeText(this, textAcceptTac, duration)
                toastAccept.show()

                setContentView(R.layout.activity_main)
                // Proceed with the app as the user has accepted the terms&conditions
            }
            buttonDecline.setOnClickListener{
                val textDeclineTac = getString(R.string.txt_decline_tac)
                val duration = Toast.LENGTH_LONG
                val toastDecline = Toast.makeText(this, textDeclineTac, duration)
                toastDecline.show()
                // Close app when user clicks the "Decline" button.
                finish()
            }
        }
    }

    override fun onTouch(p0: View?, p1: MotionEvent?): Boolean {
        TODO("Not yet implemented")
    }
}

问题根源

你在onCreate里先调用了一次setContentView(R.layout.terms_and_conditions_view)并初始化控件,但之后又根据termsAccepted的值再次调用setContentView,这会导致之前初始化的svMain、buttonAccept等控件引用完全失效——新布局加载后,原来的控件对象和当前界面的控件已经没有关联了。另外,你在onScrollChanged里重复设置按钮点击事件,每次滚动都会绑定一次,也会导致逻辑混乱。

修复方案

1. 调整布局加载逻辑,避免重复加载

先判断偏好设置,再加载对应布局,之后再初始化控件:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    sharedPref = getSharedPreferences("myAppPrefs", Context.MODE_PRIVATE)
    val termsAccepted = sharedPref.getBoolean("terms_accepted", false)

    // 先判断状态再加载布局,避免重复加载导致控件引用失效
    if (termsAccepted) {
        setContentView(R.layout.activity_main)
    } else {
        setContentView(R.layout.terms_and_conditions_view)
        // 只有加载条款页面时才初始化相关控件和监听器
        svMain = findViewById(R.id.scrollView2)
        svMain?.viewTreeObserver?.addOnScrollChangedListener(this)
        buttonAccept = findViewById(R.id.button_accept)
        buttonDecline = findViewById(R.id.button_decline)
        
        // 单独设置按钮点击事件
        setupButtonListeners()
    }
}

2. 提取按钮点击事件到单独方法

避免在滚动回调里重复绑定事件:

private fun setupButtonListeners() {
    buttonAccept.setOnClickListener {
        val editor = sharedPref.edit()
        editor.putBoolean("terms_accepted", true)
        editor.apply()

        Toast.makeText(this, getString(R.string.txt_accept_tac), Toast.LENGTH_LONG).show()
        setContentView(R.layout.activity_main)
    }

    buttonDecline.setOnClickListener {
        Toast.makeText(this, getString(R.string.txt_decline_tac), Toast.LENGTH_LONG).show()
        finish()
    }
}

3. 简化滚动回调逻辑

只保留按钮启用/禁用的判断,移除事件绑定:

override fun onScrollChanged() {
    svMain?.let { scrollView ->
        val view = scrollView.getChildAt(scrollView.childCount - 1) as View
        val bottomDetector = view.bottom - (scrollView.height + scrollView.scrollY)

        // 简化判断逻辑
        val isAtBottom = bottomDetector <= 0
        buttonAccept.isEnabled = isAtBottom
        buttonDecline.isEnabled = isAtBottom
    }
}

4. 移除无用接口实现

你没有实现OnTouchListener的onTouch方法,还抛出了TODO异常,直接删掉这个接口和对应的方法:

// 修改类声明,去掉View.OnTouchListener
class MainActivity : AppCompatActivity(), ViewTreeObserver.OnScrollChangedListener {
    // ... 其他代码不变
}

额外建议

  • 尽量避免在同一个Activity中多次调用setContentView,可以考虑拆分出TermsActivity专门承载条款页面,逻辑会更清晰易维护。
  • 存储偏好设置时,apply()是异步提交,commit()是同步提交,这里使用apply()完全没问题。

内容的提问来源于stack exchange,提问作者Belial

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 04:35:54