首页 > 解决方案 > 如何从活动中更改片段的视图/按钮的可见性?

问题描述

正如标题所示,我正在尝试从活动中更改片段的视图/按钮的可见性。

片段代码:

package nus.is3261.kotlinapp


import android.content.Context
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button

// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

/**
 * A simple [Fragment] subclass.
 *
 */
class SettingFragment : Fragment() {
    private var listener:SettingFragment.OnFragmentInteractionListener? = null

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_setting, container, false)
        val signIn = view.findViewById<View>(R.id.btn_sign_in)
        signIn.setOnClickListener {
            onButtonPressed("signIn")
        }
        val signOut = view.findViewById<Button>(R.id.btn_sign_out)
        signOut.setOnClickListener {
            onButtonPressed("signOut")
        }
        return view
    }

    fun changeVisibility(isSignedIn : Boolean){
        if (isSignedIn) {
            val signIn = view?.findViewById<View>(R.id.btn_sign_in)
            signIn?.visibility = View.GONE
            val signOut = view?.findViewById<View>(R.id.btn_sign_out)
            signOut?.visibility = View.VISIBLE
        } else {
            val signIn = view?.findViewById<View>(R.id.btn_sign_in)
            signIn?.visibility = View.VISIBLE
            val signOut = view?.findViewById<View>(R.id.btn_sign_out)
            signOut?.visibility = View.GONE

        }
    }

    fun onButtonPressed(str: String) {
        listener?.onFragmentInteraction(str)
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        if (context is SettingFragment.OnFragmentInteractionListener) {
            listener = context
        } else {
            throw RuntimeException(context.toString() + " must implement OnFragmentInteractionListener")
        }
    }

    override fun onDetach() {
        super.onDetach()
        listener = null
    }

    interface OnFragmentInteractionListener {
        fun onFragmentInteraction(str: String)
    }

}

如您所见,我有 changeVisibility 函数来更改已设置按钮的可见性。现在,我如何从主要活动中调用这些函数?我从主要活动中尝试了这个,但遗憾的是它不起作用:

    private fun updateUI(user: FirebaseUser?) {
        if (user != null) {
//            tvStatus.text = "Google User email: " + user.email!!
//            tvDetail.text = "Firebase User ID: " + user.uid
            val fragment = SettingFragment()
            fragment.changeVisibility(true)
//            btn_sign_in.visibility = View.GONE
//            layout_sign_out_and_disconnect.visibility = View.VISIBLE
        } else {
//            tvStatus.text = "Signed Out"
//            tvDetail.text = null
            val fragment = SettingFragment()
            fragment.changeVisibility(false)
//            btn_sign_in.visibility = View.VISIBLE
//            layout_sign_out_and_disconnect.visibility = View.GONE
        }
    }

这是我的xml文件:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/dracula"
    tools:context=".SettingFragment">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.google.android.gms.common.SignInButton
            android:id="@+id/btn_sign_in"
            android:layout_width="match_parent"
            android:layout_weight="1"
            android:layout_height="0dp"
            android:visibility="visible"
            tools:visibility="gone" />

        <Button
            android:id="@+id/btn_sign_out"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:visibility="gone"
            tools:visibility="visible"
            android:backgroundTint="@color/draculalight"
            android:textColor="@color/green"
            android:text="@string/signout" />

    </LinearLayout>

</FrameLayout>

标签: androidandroid-fragmentskotlin

解决方案


好的,所以你有几个问题,但最好是我为你一步一步地提供一个完整的步骤。所以让我们从头开始。

所以首先是问题

你引用了错误的内存。首先,您将一个片段放入您的 xml 中,然后创建一个不同的实例,这就像倒一杯咖啡,然后从一个新的空杯子中喝水,然后想知道为什么咖啡没有在里面。

现在寻求解决方案。

首先,您的 MainActivity(或片段的父活动)必须包含您尝试包含的片段的元素。你有几个选择来做到这一点。让我们从最简单的方法开始,假设它是一个不会被换出的静态片段。

选项 1(固定片段)

<ParentActivityLayoutOfYourChoice>

    <fragment
        android:name="com.yourpath.FooFragment"
        android:id="@+id/fooFragment"
        android:layout_width="match_parent" 
        android:layout_height="match_parent" />

</ParentActivityLayoutOfYourChoice>

然后在 Activity 中,您只需创建一个成员变量并像这样访问它:

//lateinit only if you guarantee it will be there in the oncreate
private lateinit var fooFragment: FooFragment
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    fooFragment = findViewById(R.id.fooFragment)
}

fun btnSignIn_onClick(){
    //onSuccess
    fooFragment.isSignedIn(true)
}

选项 2(动态片段)

<ParentActivityLayoutOfYourChoice>

   <FrameLayout
            android:id="@+id/fragPlaceholder"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

</ParentActivityLayoutOfYourChoice>

然后当然可以在你的 onCreate 或者合适的地方(比如抽屉切换片段)创建 Fragment 并交换到占位符中。

例子:

//MainActivity(或父Activity)内部

 //lazy will new it up the first time you use it.
 private val mFooFragment by lazy {
    FooFragment()
 }

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    swapFragment(mFooFragment) //will auto new the fragment with lazy
}

//Let's start simple before I show you thorough
fun swapFragment(fragment: Fragment){
     val fragmentManager = supportFragmentManager
     val fragmentTransaction = fragmentManager.beginTransaction()
     fragmentTransaction.replace(R.id.fragPlaceholder, fragment)
     fragmentTransaction.commit() //puts the fragment into the placeholder
}

fun btnSignIn_onClick(){
    //onSuccess
    mFooFragment.isSignedIn(true)
}

*现在,在我们走得更远之前,我觉得重要的是我告诉你,如果你动态交换片段,那么它还有更多。你应该处理捆绑,你应该知道你是否在隐藏、显示、替换等。处理事务有很多方法。更改片段时,您必须决定是隐藏还是删除。当你把它们放回去时,它会影响 onResume 和 onCreate 的生命周期,所以要明智地管理它。

我已经构建了一个简单的 swapFragment 方法,我在 BaseActivity 中的几乎所有项目中都使用了该方法。我现在将分享,只是为了彻底。

在基础活动中存储所选片段并动态控制片段交换的示例

    private var mSelectedFragment: BaseFragment? = null

    protected fun swapFragment(fragment: BaseFragment, @Nullable bundle: Bundle?, hideCurrentFrag: Boolean = false) {
    if (fragment.isVisible) {
        A35Log.e(mClassTag, "swapFragment called on already visible fragment")
        return
    }

    A35Log.v(mClassTag, "swapFragment( ${fragment.javaClass.simpleName} )")
    val currentFragBundle = fragment.arguments
    if (currentFragBundle == null && bundle != null) {
        fragment.arguments = bundle
        A35Log.v(mClassTag, "current bundle is null, so setting new bundle passed in")
    } else if (bundle != null) {
        fragment.arguments?.putAll(bundle)
        A35Log.v(mClassTag, "current fragment bundle was not null, so add new bundle to it")
    }

    //make sure no pending transactions are still floating and not complete
    val fragmentManager = supportFragmentManager
    fragmentManager.executePendingTransactions()
    val fragmentTransaction = fragmentManager.beginTransaction()

    //Make sure the requested fragment isn't already on the screen before adding it
    if (fragment.isAdded) {
        A35Log.v(mClassTag, "Fragment is already added")
        if (fragment.isHidden) {
            A35Log.v(mClassTag, "Fragment is hidden, so show it")
            fragmentTransaction.show(fragment)
            if(hideCurrentFrag) {
                A35Log.v(mClassTag, "hideCurrentFlag = true, hiding current fragment $mSelectedFragment")
                fragmentTransaction.hide(mSelectedFragment!!)
            }else{
                A35Log.v(mClassTag, "hideCurrentFlag = false, removing current fragment $mSelectedFragment")
                fragmentTransaction.remove(mSelectedFragment!!)
            }
        }else{
            A35Log.v(mClassTag, "Fragment is already visible")
        }
    }else if(mSelectedFragment == null){
        A35Log.v(mClassTag,"mSelectedFragment = null, so replacing active fragment with new one ${fragment.javaClass.simpleName}")
        fragmentTransaction.replace(R.id.fragPlaceholder, fragment)
    }else{
        A35Log.v(mClassTag, "Fragment is not added, so adding to the screen ${fragment.javaClass.simpleName}")
        fragmentTransaction.add(R.id.fragPlaceholder, fragment)
        if(hideCurrentFrag) {
            A35Log.v(mClassTag, "hideCurrentFlag = true, hiding current fragment $mSelectedFragment")
            fragmentTransaction.hide(mSelectedFragment!!)
        }else{
            A35Log.v(mClassTag, "hideCurrentFlag = false, removing current fragment $mSelectedFragment")
            fragmentTransaction.remove(mSelectedFragment!!)
        }
    }

    A35Log.v(mClassTag, "committing swap fragment transaction")
    fragmentTransaction.commit()
    A35Log.v(mClassTag, "mSelectedFragment = ${fragment.javaClass.simpleName}")
    mSelectedFragment = fragment
}

所有示例都在 Kotlin 中提供,因为这是 Android 的发展方向,如果您还没有学习的话,您应该使用 Kotlin 而不是 Java。如果您正在使用 Java,那么您可以将其粘贴到 Java 文件中,我相信它会为您提供将其翻译成 Java 的功能。

快乐编码!


推荐阅读