【问题标题】:Unable start activity from fragment无法从片段开始活动
【发布时间】:2021-10-26 14:16:21
【问题描述】:

我正在使用 Cloud Firestore 和 Kotlin 创建一个应用。当我单击登录按钮时,它将跳转到具有 homeFragment、搜索片段和 settingsFragment 的仪表板活动。在 settingsFragment 中,有一个编辑按钮可以让用户编辑他们的个人资料。但是当我点击编辑按钮时,程序会崩溃。你能帮我解决这个问题吗?

错误信息

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.spaceus, PID: 14094
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.spaceus/com.example.spaceus.ui.activities.UserProfileActivity}: kotlin.UninitializedPropertyAccessException: lateinit property mUserDetails has not been initialized
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3270)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
    at android.os.Handler.dispatchMessage(Handler.java:107)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7356)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
 Caused by: kotlin.UninitializedPropertyAccessException: lateinit property mUserDetails has not been initialized
    at com.example.spaceus.ui.activities.UserProfileActivity.onCreate(UserProfileActivity.kt:41)
    at android.app.Activity.performCreate(Activity.java:7802)
    at android.app.Activity.performCreate(Activity.java:7791)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409) 
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) 
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016) 
    at android.os.Handler.dispatchMessage(Handler.java:107) 
    at android.os.Looper.loop(Looper.java:214) 
    at android.app.ActivityThread.main(ActivityThread.java:7356) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) 

SettingsFragment.kt

class SettingsFragment : Fragment() {

private var _binding: FragmentSettingsBinding? = null
private lateinit var mUserDetails: User
private val binding get() = _binding!!

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    //notificationsViewModel =ViewModelProvider(this).get(NotificationsViewModel::class.java)

    _binding = FragmentSettingsBinding.inflate(inflater, container, false)
    val root: View = binding.root

    val sharedPreferences = this.requireActivity().getSharedPreferences(Constants.MYSHOPPAL_PREFERENCES, Context.MODE_PRIVATE)
    //val sharedPreferences = getSharedPreferences(Constants.MYSHOPPAL_PREFERENCES,Context.MODE_PRIVATE)

    val username = sharedPreferences.getString(Constants.LOGGED_IN_USERNAME,"")!!
    val email = sharedPreferences.getString(Constants.EMAIL,"")!!
    //GlideLoader(this).loadUserPicture(mUserDetails.image.toUri(),iv_user_photo)
    root.tv_username.text = "$username"
    root.tv_email.text = "$email"



    root.btn_logout.setOnClickListener {
        FirebaseAuth.getInstance().signOut()
        val intent = Intent(requireContext(), LoginActivity::class.java)
        startActivity(intent)
        Toast.makeText(activity, "Logout Successfully.", Toast.LENGTH_SHORT).show()
    }

    root.iv_edit.setOnClickListener{
        val intent = Intent(requireContext(), UserProfileActivity::class.java)
        startActivity(intent)
    }

    return root
}

override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}

}

UserProfileActivity.kt

class UserProfileActivity : BaseActivity(), View.OnClickListener {

private lateinit var mUserDetails: User
private var mSelectedImageFileUri: Uri? =null
private var mUserProfileImageURL: String =""

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

    if(intent.hasExtra(Constants.EXTRA_USER_DETAILS)){
        //Get the user details from instant as a ParcelableExtra.
        mUserDetails = intent.getParcelableExtra(Constants.EXTRA_USER_DETAILS)!!
    }


    et_usernameProfile.isEnabled=false
    et_usernameProfile.setText(mUserDetails.username)

    et_emailProfile.isEnabled=false
    et_emailProfile.setText(mUserDetails.email)

    GlideLoader(this@UserProfileActivity).loadUserPicture(mUserDetails.image.toUri(),iv_user_photo)

    iv_user_photo.setOnClickListener(this@UserProfileActivity)
    btn_save.setOnClickListener(this@UserProfileActivity)
    btn_cancel.setOnClickListener(this@UserProfileActivity)
}


override fun onClick(v: View?) {
    if(v != null){
        when (v.id){
            R.id.iv_user_photo ->{
                if(ContextCompat.checkSelfPermission(this,android.Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
                    //showErrorSnackBar("You already have the storage permission.",false)
                    Constants.showImageChooser(this)
                }else{
                    ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE),Constants.READ_STORAGE_PERMISSION_CODE)
                }
            }

            R.id.btn_save ->{

                if(validateUserProfileDetails()){

                    showProgressDialog("Please Wait ...")

                    if(mSelectedImageFileUri!=null)
                        FirestoreClass().uploadImageToCloudStorage(this,mSelectedImageFileUri)
                    else{
                        updateUserProfileDetails()
                    }
                }
            }

            R.id.btn_cancel ->{
                val intent = Intent(this@UserProfileActivity, DashboardActivity::class.java)
                startActivity(intent)
            }
        }
    }
}

private fun updateUserProfileDetails(){
    val userHashMap =HashMap<String,Any>()

    val mobileNumber = et_phoneProfile.text.toString().trim{it <= ' '}

    val gender = if(rb_male.isChecked){
        Constants.MALE
    }else{
        Constants.FEMALE
    }

    if(mUserProfileImageURL.isNotEmpty()){
        userHashMap[Constants.IMAGE] = mUserProfileImageURL
    }

    if(mobileNumber.isNotEmpty()){
        userHashMap[Constants.MOBILE]=mobileNumber
    }

    userHashMap[Constants.GENDER]= gender

    userHashMap[Constants.COMPLETE_PROFILE] = 1

    FirestoreClass().updateUserProfileData(this,userHashMap)
}

fun userProfileUpdateSuccess(){
    hideProgressDialog()
    Toast.makeText(this@UserProfileActivity,resources.getString(R.string.msg_profile_update_success),Toast.LENGTH_SHORT).show()
    startActivity(Intent(this@UserProfileActivity, DashboardActivity::class.java))
    finish()
}

override fun onRequestPermissionsResult(requestCode:Int,permissions:Array<out String>,grantResults:IntArray){
    super.onRequestPermissionsResult(requestCode,permissions,grantResults)

    if(requestCode == Constants.READ_STORAGE_PERMISSION_CODE){

        if(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED){
            //showErrorSnackBar("The storage permission is granted.",false)
            Constants.showImageChooser(this)
        }else{
            Toast.makeText(this,resources.getString(R.string.read_storage_permission_denied),Toast.LENGTH_LONG).show()
        }
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    if(resultCode == Activity.RESULT_OK){
        if(requestCode == Constants.PICK_IMAGE_REQUEST_CODE){
            if(data !=null){
                try{
                    //the uri of selected image from photo storage
                    mSelectedImageFileUri= data.data!!
                    GlideLoader(this).loadUserPicture(mSelectedImageFileUri!!,iv_user_photo)
                }catch (e:IOException){
                    e.printStackTrace()
                    Toast.makeText(this@UserProfileActivity,resources.getString(R.string.image_selection_failed),Toast.LENGTH_SHORT).show()
                }
            }
        }
    }else if (resultCode == Activity.RESULT_CANCELED){
        Log.e("Request Cancelled","Image selection cancelled")
    }
}

private fun validateUserProfileDetails(): Boolean{
    return when{
        TextUtils.isEmpty(et_phoneProfile.text.toString().trim { it <= ' ' }) -> {
            showErrorSnackBar(resources.getString(R.string.err_msg_enter_phoneNum),true)
            false
        }
        else ->{
            true
        }
    }
}

fun imageUploadSuccess(imageURL:String){
    //hideProgressDialog()

    mUserProfileImageURL = imageURL
    updateUserProfileDetails()
}

}

FirestoreClass.kt

class FirestoreClass {

private val mFireStore = FirebaseFirestore.getInstance()

fun registerUser(activity: RegisterActivity, userInfo:User){
    mFireStore.collection(Constants.USERS)
        //Document ID for users fields.Here the document it is the User ID.
        .document(userInfo.id)
        .set(userInfo, SetOptions.merge())
        .addOnSuccessListener {
            activity.userRegistrationSuccess()
        }
        .addOnFailureListener { e ->
            activity.hideProgressDialog()
            Log.e(activity.javaClass.simpleName,"Error while registering the user.",e)
        }

}

fun getCurrentUserID(): String{
    //An Instance of currentUser using FirebaseAuth
    val currentUser = FirebaseAuth.getInstance().currentUser

    //A variable to assign the currentUserId if it is not null or else it will be blank
    var currentUserID=""
    if(currentUser != null){
        currentUserID = currentUser.uid
    }
    return currentUserID
}

fun getUserDetails(activity: Activity){
    //Here we pass the collection name from which we wants the data.
    mFireStore.collection(Constants.USERS)
        // The document id to get the Fields of user.
        .document(getCurrentUserID())
        .get()
        .addOnSuccessListener { document ->
            Log.i(activity.javaClass.simpleName,document.toString())

            val user = document.toObject(User::class.java)!!

            val sharedPreferences = activity.getSharedPreferences(Constants.MYSHOPPAL_PREFERENCES, Context.MODE_PRIVATE)
            val editor : SharedPreferences.Editor = sharedPreferences.edit()
            editor.putString(Constants.LOGGED_IN_USERNAME,"${user.username}")
            editor.putString(Constants.EMAIL,"${user.email}")
            editor.apply()

            when(activity){
                is LoginActivity ->{
                    activity.userLoggedInSuccess(user)
                }
            }
            //END
        }
        .addOnFailureListener { e ->
            when (activity){
                is LoginActivity ->{
                    activity.hideProgressDialog()
                }
            }

            Log.e(activity.javaClass.simpleName,"Error while getting user details",e)
        }
}

fun updateUserProfileData(activity: Activity,userHashMap: HashMap<String,Any>){
    mFireStore.collection(Constants.USERS).document(getCurrentUserID())
        .update(userHashMap)
        .addOnSuccessListener {
            when(activity){
                is UserProfileActivity -> {
                    activity.userProfileUpdateSuccess()
                }
            }
        }
        .addOnFailureListener { e ->
            when (activity){
                is UserProfileActivity -> {
                    //Hide the progress dialog if there is any error.And print the error in log.
                    activity.hideProgressDialog()
                }
            }
            Log.e(activity.javaClass.simpleName,"Error while updating the user details.",e)
        }
}

fun uploadImageToCloudStorage(activity:Activity,imageFileURI: Uri?){
    val sRef: StorageReference = FirebaseStorage.getInstance().reference.child(
        Constants.USER_PROFILE_IMAGE + System.currentTimeMillis() + "." +Constants.getFileExtension(activity,imageFileURI)
    )

    sRef.putFile(imageFileURI!!).addOnSuccessListener { taskSnapshot ->
        Log.e("Firebase Image URL",taskSnapshot.metadata!!.reference!!.downloadUrl.toString())
        taskSnapshot.metadata!!.reference!!.downloadUrl
            .addOnSuccessListener { uri ->
                Log.e("Downloadable Image URL",uri.toString())
                when(activity){
                    is UserProfileActivity ->{
                        activity.imageUploadSuccess(uri.toString())
                    }
                }
            }
    }
        .addOnFailureListener{exception ->
            when (activity){
                is UserProfileActivity -> {
                    activity.hideProgressDialog()
                }
            }

            Log.e(activity.javaClass.simpleName,exception.message,exception)
        }

}

}

常量.kt

object Constants {
const val USERS : String = "users"
const val MYSHOPPAL_PREFERENCES:String = "MyShopPalPrefs"
const val LOGGED_IN_USERNAME: String = "logged_in_username"
const val EXTRA_USER_DETAILS:String = "extra_user_details"
const val READ_STORAGE_PERMISSION_CODE = 2
const val PICK_IMAGE_REQUEST_CODE = 1

const val MALE:String = "Male"
const val FEMALE:String = "Female"
const val MOBILE:String = "mobile"
const val GENDER:String = "gender"
const val IMAGE:String = "image"
const val EMAIL:String ="email"
const val COMPLETE_PROFILE:String = "profileCompleted"
const val USER_PROFILE_IMAGE: String = "User_Profile_Image"

fun showImageChooser(activity : Activity){
    val galleryIntent = Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
    activity.startActivityForResult(galleryIntent, PICK_IMAGE_REQUEST_CODE)
}

fun getFileExtension(activity: Activity,uri: Uri?):String? {
    return MimeTypeMap.getSingleton()
        .getExtensionFromMimeType(activity.contentResolver.getType(uri!!))
}

}

数据库结构

【问题讨论】:

  • 如果您遇到问题,最好在发布问题时创建MCVE。您为此问题发布了近 400(四百)行行代码。人们需要解析和尝试在线调试的内容很多。请编辑您的问题并隔离问题,这样可以增加获得帮助的机会。
  • intent.getParcelableExtra(Constants.EXTRA_USER_DETAILS) 是什么意思?
  • 即时获取用户详细信息
  • 如果记录值,是你想要的吗?
  • 我认为我的问题是 SettingsFragment 在单击编辑按钮时无法转到 UserProfileActivity。如果LoginActivity->UserProfileActivity,就可以工作了。

标签: firebase android-studio kotlin android-fragments google-cloud-firestore


【解决方案1】:

问题是你的Activity 需要一个额外的Intent 来初始化lateinit 属性,但是当你从SettingsFragment 启动Activity 时,你似乎不会在@ 987654326@

IntentActivity 中需要额外的

if(intent.hasExtra(Constants.EXTRA_USER_DETAILS)){
   // Get the user details from instant as a ParcelableExtra.
   mUserDetails = intent.getParcelableExtra(Constants.EXTRA_USER_DETAILS)!!
}

Intent extra 未从 SettingsFragment 传递

root.iv_edit.setOnClickListener{
    val intent = Intent(requireContext(), UserProfileActivity::class.java)
    // Need to pass userDetails when starting UserProfileActivity
    // intent.putExtra(Constants.EXTRA_USER_DETAILS, userDetails)
    startActivity(intent)
}

【讨论】:

  • 您好,先生,我可以为 SettingsFragment 做什么?
  • 你需要从SettinsFragment额外的intent中paas用户详细信息
猜你喜欢
  • 2020-08-12
  • 2013-03-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多