当前位置: 首页 > 知识库问答 >
问题:

在使用FireBaseRecyclerPaginAdapter时,第二次单击RecyclerView中的项目时,碎片显示为空

潘志国
2023-03-14

在我的应用程序中,当我点击RecyclerView中的一个项目(问题)时,该项目使用Firebase RecyclerPagingAdapter从Firebase实时数据库中分页数据,它会显示在另一个片段中点击的项目的详细信息(使用导航组件)。第一次点击时效果很好,但是当我返回到前一个片段并在RecyclerView上第二次点击相同的项目时,项目的详细信息不会显示。

因为我使用安全args来将项目id传递给下一个片段,它用于查询Firebase实时数据库并检索要显示的详细信息,所以我决定将项目id记录到我的控制台onViewCreated()中,只是为了确保项目id在第二次单击时被传递,并且详细信息(添加问题的用户的姓名)正在从数据库中检索,但只是不显示。然后,我注意到一个奇怪的行为。

第一次单击时,条目id被记录到控制台,详细信息也被记录到控制台,片段显示详细信息。但是,在第二次单击时,条目id被记录到控制台(显示条目id正在按实际情况传递),但细节不会记录到控制台,也不会显示在片段中(因此片段显示为空)。奇怪的是,当我回到前面的片段时,我看到了两次显示的细节日志。

我注意到的另一件奇怪的事情是,除了最后一件物品,RecyclerView上的每件物品都有这种奇怪的行为。最后一个项目在第二次单击时显示其详细信息,但我单击的任何其他项目都不会显示。

我还注意到,当我返回时,日志显示了我之前单击过两次的每个项目的详细信息,即使我正在单击另一个项目

我将适配器从Firebase RecyclerPagingAdapter更改为Firebase RecyclerAdapter,一切正常。当我更改回使用Firebase RecyclerPagingAdapter时,同样的问题也存在。

这是我的代码中的错误还是FirebaseRecyclerPaginAdapter本身的错误。可能是什么问题?我能做些什么来解决它?

以下是FireBaseRecyclerPaginAdapter:

package com.colley.android.adapter

import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.colley.android.R
import com.colley.android.databinding.ItemIssueBinding
import com.colley.android.model.Issue
import com.colley.android.model.Profile
import com.firebase.ui.database.paging.DatabasePagingOptions
import com.firebase.ui.database.paging.FirebaseRecyclerPagingAdapter
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.ValueEventListener
import com.google.firebase.database.ktx.database
import com.google.firebase.database.ktx.getValue
import com.google.firebase.ktx.Firebase

class IssuesPagingAdapter(
    options: DatabasePagingOptions<Issue>,
    private val context: Context,
    private val currentUser: FirebaseUser?,
    private val clickListener: IssuePagingItemClickedListener
) : FirebaseRecyclerPagingAdapter<Issue, IssuePagingViewHolder>(options) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IssuePagingViewHolder {
        val viewBinding = ItemIssueBinding
            .inflate(LayoutInflater.from(parent.context), parent, false)
        return IssuePagingViewHolder(viewBinding)
    }

    override fun onBindViewHolder(viewHolder: IssuePagingViewHolder, position: Int, model: Issue) {
        viewHolder.bind(currentUser, model, context, clickListener)
    }

    interface IssuePagingItemClickedListener {
        fun onItemClick(issueId: String, view: View)
        fun onItemLongCLicked(issueId: String, view: View)
        fun onUserClicked(userId: String, view: View)
    }


}


class IssuePagingViewHolder (private val itemBinding : ItemIssueBinding) : RecyclerView.ViewHolder(itemBinding.root) {
    @SuppressLint("SetTextI18n")
    fun bind(
        currentUser: FirebaseUser?,
        issue: Issue, context: Context,
        clickListener: IssuesPagingAdapter.IssuePagingItemClickedListener) = with(itemBinding) {

        //set issue title, body, timeStamp, contributions and endorsements count
        issueTitleTextView.text = issue.title
        issueBodyTextView.text = issue.body
        issueTimeStampTextView.text = issue.timeStamp
        contributionsTextView.text = issue.contributionsCount.toString()
        endorsementTextView.text = issue.endorsementsCount.toString()

        //check if userId is not null
        issue.userId?.let { userId ->
            //retrieve user profile
            Firebase.database.reference.child("profiles").child(userId)
                .addListenerForSingleValueEvent(
                object : ValueEventListener {
                    override fun onDataChange(snapshot: DataSnapshot) {
                        val profile = snapshot.getValue<Profile>()
                       if (profile != null) {
                           //set the name of user who raised this issue
                           userNameTextView.text = profile.name
                           //set the school of the user who raised this issue
                           userSchoolTextView.text = profile.school
                       }
                    }

                    override fun onCancelled(error: DatabaseError) {}
                }
            )

            //retrieve user photo
            Firebase.database.reference.child("photos").child(userId)
                .addListenerForSingleValueEvent(
                object : ValueEventListener {
                    override fun onDataChange(snapshot: DataSnapshot) {
                        val photo = snapshot.getValue<String>()
                        //set photo
                        if (photo != null) {
                            Glide.with(root.context).load(photo)
                                .diskCacheStrategy(DiskCacheStrategy.RESOURCE).into(userImageView)
                        } else {
                            Glide.with(root.context).load(R.drawable.ic_person).into(userImageView)
                        }
                    }

                    override fun onCancelled(error: DatabaseError) {}
                }
            )
        }
        

        root.setOnClickListener {
            if(issue.issueId != null) {
                clickListener.onItemClick(issue.issueId, it)
            }
        }

        root.setOnLongClickListener {
            if(issue.issueId != null) {
                clickListener.onItemLongCLicked(issue.issueId, it)
            }
            true
        }

        userNameTextView.setOnClickListener {
            if(issue.userId != null) {
                clickListener.onUserClicked(issue.userId, it)
            }
        }
    }
}

以下是显示项目详细信息的片段:

package com.colley.android.view.fragment

import android.os.Bundle
import android.util.Log
import android.view.*
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.Toast
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.colley.android.R
import com.colley.android.adapter.IssuesCommentsRecyclerAdapter
import com.colley.android.databinding.FragmentViewIssueBinding
import com.colley.android.model.Comment
import com.colley.android.model.Issue
import com.colley.android.model.Profile
import com.colley.android.view.dialog.IssueCommentBottomSheetDialogFragment
import com.firebase.ui.database.FirebaseRecyclerOptions
import com.firebase.ui.database.ObservableSnapshotArray
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.auth.ktx.auth
import com.google.firebase.database.*
import com.google.firebase.database.ktx.database
import com.google.firebase.database.ktx.getValue
import com.google.firebase.ktx.Firebase


class ViewIssueFragment :
    Fragment(),
    IssuesCommentsRecyclerAdapter.ItemClickedListener,
    IssuesCommentsRecyclerAdapter.DataChangedListener {

    private val args: ViewIssueFragmentArgs by navArgs()
    private var _binding: FragmentViewIssueBinding? = null
    private val binding get() = _binding
    private lateinit var dbRef: DatabaseReference
    private lateinit var auth: FirebaseAuth
    private lateinit var currentUser: FirebaseUser
    private lateinit var recyclerView: RecyclerView
    private lateinit var commentSheetDialog: IssueCommentBottomSheetDialogFragment
    private var issue: Issue? = null
    private var adapter: IssuesCommentsRecyclerAdapter? = null
    private var manager: LinearLayoutManager? = null
    private val uid: String
        get() = currentUser.uid


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentViewIssueBinding.inflate(inflater, container, false)
        recyclerView = binding?.issuesCommentsRecyclerView!!
        return binding?.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        //initialize Realtime Database
        dbRef = Firebase.database.reference

        //initialize authentication
        auth = Firebase.auth

        //initialize currentUser
        currentUser = auth.currentUser!!

        //log item id
        Log.d("Log itemId", args.issueId)

        //get a query reference to issue comments //order by time stamp
        val commentsRef = dbRef.child("issues").child(args.issueId)
            .child("comments").orderByChild("commentTimeStamp")

        //the FirebaseRecyclerAdapter class and options come from the FirebaseUI library
        //build an options to configure adapter. setQuery takes firebase query to listen to and a
        //model class to which snapShots should be parsed
        val options = FirebaseRecyclerOptions.Builder<Comment>()
            .setQuery(commentsRef, Comment::class.java)
            .build()

        //initialize issue comments adapter
        adapter = IssuesCommentsRecyclerAdapter(
            options,
            currentUser,
            this,
            this,
            requireContext())
        manager = LinearLayoutManager(requireContext())
        //reversing layout and stacking fron end so that the most recent comments appear at the top
        manager?.reverseLayout = true
        manager?.stackFromEnd = true
        recyclerView.layoutManager = manager
        recyclerView.adapter = adapter


        dbRef.child("issues").child(args.issueId).addValueEventListener(
            object : ValueEventListener {
                override fun onDataChange(snapshot: DataSnapshot) {
                    issue = snapshot.getValue<Issue>()
                    if(issue != null) {

                        //listener for contrbutions count used to set count text
                        dbRef.child("issues").child(args.issueId)
                            .child("contributionsCount").addListenerForSingleValueEvent(
                            object : ValueEventListener {
                                override fun onDataChange(snapshot: DataSnapshot) {
                                    val count = snapshot.getValue<Int>()
                                    if(count != null) {
                                        binding?.contributionsTextView?.text = count.toString()
                                    }
                                }

                                override fun onCancelled(error: DatabaseError) {}
                            }
                        )

                        //listener for endorsement counts used to set endorsement count text
                        dbRef.child("issues").child(args.issueId)
                            .child("endorsementsCount").addListenerForSingleValueEvent(
                            object : ValueEventListener {
                                override fun onDataChange(snapshot: DataSnapshot) {
                                    val count = snapshot.getValue<Int>()
                                    if(count != null) {
                                        binding?.endorsementTextView?.text = count.toString()
                                    }
                                }

                                override fun onCancelled(error: DatabaseError) {} }
                        )

                        //set issue title, body and time stamp, these don't need to change
                        binding?.issueTitleTextView?.text = issue?.title
                        binding?.issueBodyTextView?.text = issue?.body
                        binding?.issueTimeStampTextView?.text = issue?.timeStamp.toString()

                        //listener for user photo
                        dbRef.child("photos").child(issue?.userId.toString())
                            .addListenerForSingleValueEvent(
                            object : ValueEventListener {
                                override fun onDataChange(snapshot: DataSnapshot) {
                                    val photo = snapshot.getValue<String>()
                                    if(photo != null) {
                                        context?.let { context -> binding?.userImageView?.let {
                                                imageView ->
                                            Glide.with(context).load(photo).into(
                                                imageView
                                            )
                                        } }
                                    } else {
                                        context?.let { context -> binding?.userImageView?.let {
                                                imageView ->
                                            Glide.with(context).load(R.drawable.ic_profile).into(
                                                imageView
                                            )
                                        } }
                                    }
                                }

                                override fun onCancelled(error: DatabaseError) {}
                            }
                        )

                        //listener for profile to set name and school
                        dbRef.child("profiles").child(issue?.userId.toString())
                            .addListenerForSingleValueEvent(
                            object : ValueEventListener {
                                override fun onDataChange(snapshot: DataSnapshot) {
                                    val profile = snapshot.getValue<Profile>()
                                    if (profile != null) {
                                        
                                        //log name details to console
                                        profile.name?.let { Log.d("Log Details", it) }

                                        binding?.userNameTextView?.text = profile.name
                                        binding?.userSchoolTextView?.text = profile.school
                                    }
                                }

                                override fun onCancelled(error: DatabaseError) {}
                            }
                        )
                    }
                }

                override fun onCancelled(error: DatabaseError) {}
            }
        )

        binding?.commentLinearLayout?.setOnClickListener {
            commentSheetDialog = IssueCommentBottomSheetDialogFragment(
                requireContext(),
                requireView())
            commentSheetDialog.arguments = bundleOf("issueIdKey" to args.issueId)
            commentSheetDialog.show(parentFragmentManager, null)
        }

        binding?.endorseLinearLayout?.setOnClickListener {
            //update contributions count
            dbRef.child("issues").child(args.issueId).child("endorsementsCount")
                .runTransaction(
                object : Transaction.Handler {
                    override fun doTransaction(currentData: MutableData): Transaction.Result {
                        //retrieve the current value of endorsement count at this location
                        var endorsementsCount = currentData.getValue<Int>()
                        if (endorsementsCount != null) {
                            //increase the count by 1
                            endorsementsCount++
                            //reassign the value to reflect the new update
                            currentData.value = endorsementsCount
                        }
                        //set database issue value to the new update
                        return Transaction.success(currentData)
                    }

                    override fun onComplete(
                        error: DatabaseError?,
                        committed: Boolean,
                        currentData: DataSnapshot?
                    ) {
                        if (error == null && committed) {
                            Toast.makeText(requireContext(), "Endorsed", Toast.LENGTH_SHORT)
                                .show()
                        }
                    }

                }
            )
        }

        //view profile when clicked
        binding?.userImageView?.setOnClickListener {
            val action = issue?.userId?.let { it1 ->
                ViewIssueFragmentDirections.actionViewIssueFragmentToUserInfoFragment(it1)
            }
            if (action != null) {
                parentFragment?.findNavController()?.navigate(action)
            }
        }

        //view user profile when clicked
        binding?.userNameTextView?.setOnClickListener {
            val action = issue?.userId?.let { it1 ->
                ViewIssueFragmentDirections.actionViewIssueFragmentToUserInfoFragment(it1)
            }
            if (action != null) {
                parentFragment?.findNavController()?.navigate(action)
            }
        }
    }



    override fun onItemClick(comment: Comment, view: View) {
        //expand comment
    }

    override fun onItemLongCLicked(comment: Comment, view: View) {
        //create option to delete
        //create option to respond
    }

    //view user profile
    override fun onUserClicked(userId: String, view: View) {
        val action = ViewIssueFragmentDirections.actionViewIssueFragmentToUserInfoFragment(userId)
        parentFragment?.findNavController()?.navigate(action)
    }

    override fun onStart() {
        super.onStart()
        adapter?.startListening()
    }


    override fun onStop() {
        super.onStop()
        adapter?.stopListening()
    }


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

    override fun onDataAvailable(snapshotArray: ObservableSnapshotArray<Comment>) {
        //dismiss progress bar once snapshot is available
        binding?.issuesCommentProgressBar?.visibility = GONE

        //show that there are no comments if snapshot is empty else hide view
        //show recycler view if snapshot is not empty else hide
        if (snapshotArray.isEmpty()) {
            binding?.noCommentsLayout?.visibility = VISIBLE
        } else {
            binding?.noCommentsLayout?.visibility = GONE
            binding?.issuesCommentsRecyclerView?.visibility = VISIBLE
        }
    }


}

下面是带有recyclerView的片段,显示了我是如何初始化适配器的:

package com.colley.android.view.fragment

import android.os.Bundle
import android.view.*
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.paging.LoadState
import androidx.paging.PagingConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.colley.android.R
import com.colley.android.adapter.IssuesPagingAdapter
import com.colley.android.databinding.FragmentIssuesBinding
import com.colley.android.model.Issue
import com.firebase.ui.database.paging.DatabasePagingOptions
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.auth.ktx.auth
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.ktx.database
import com.google.firebase.ktx.Firebase
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch


class IssuesFragment :
    Fragment(),
    IssuesPagingAdapter.IssuePagingItemClickedListener {

    private var _binding: FragmentIssuesBinding? = null
    private val binding get() = _binding!!
    private lateinit var dbRef: DatabaseReference
    private lateinit var auth: FirebaseAuth
    private lateinit var currentUser: FirebaseUser
    private var adapter: IssuesPagingAdapter? = null
    private var manager: LinearLayoutManager? = null
    private lateinit var recyclerView: RecyclerView
    private lateinit var swipeRefreshLayout: SwipeRefreshLayout
    private val uid: String
        get() = currentUser.uid

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //fragment can participate in populating the options menu
        setHasOptionsMenu(true)

        //initialize Realtime Database
        dbRef = Firebase.database.reference

        //initialize authentication
        auth = Firebase.auth

        //initialize currentUser
        currentUser = auth.currentUser!!
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        super.onCreateOptionsMenu(menu, inflater)
        menu.clear()
        inflater.inflate(R.menu.isssues_menu, menu)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.search_issues_menu_item -> {
                Toast.makeText(context, "Searching issues", Toast.LENGTH_LONG).show()
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentIssuesBinding.inflate(inflater, container, false)
        recyclerView = binding.issueRecyclerView
        swipeRefreshLayout = binding.swipeRefreshLayout
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        //get a query reference to issues 
        val issuesQuery = dbRef.child("issues")

        //configuration for how the FirebaseRecyclerPagingAdapter should load pages
        val config = PagingConfig(
            pageSize = 30,
            prefetchDistance = 15,
            enablePlaceholders = false
        )

        //Options to configure an FirebasePagingAdapter
        val options = DatabasePagingOptions.Builder<Issue>()
            .setLifecycleOwner(viewLifecycleOwner)
            .setQuery(issuesQuery, config, Issue::class.java)
            .setDiffCallback(object : DiffUtil.ItemCallback<DataSnapshot>() {
                override fun areItemsTheSame(
                    oldItem: DataSnapshot,
                    newItem: DataSnapshot
                ): Boolean {
                    return oldItem.getValue(Issue::class.java)?.issueId == newItem.getValue(Issue::class.java)?.issueId
                }

                override fun areContentsTheSame(
                    oldItem: DataSnapshot,
                    newItem: DataSnapshot
                ): Boolean {
                    return oldItem.getValue(Issue::class.java) == newItem.getValue(Issue::class.java)
                }

            })
            .build()

        //instantiate adapter
        adapter = IssuesPagingAdapter(
            options,
            requireContext(),
            currentUser,
            this)

        //Perform some action every time data changes or when there is an error.
        viewLifecycleOwner.lifecycleScope.launch {
            adapter?.loadStateFlow?.collectLatest { loadStates ->

                when (loadStates.refresh) {
                    is LoadState.Error -> {

                        // The initial load failed. Call the retry() method
                        // in order to retry the load operation.
                        Toast.makeText(context, "Error fetching issues! Retrying..", Toast.LENGTH_SHORT).show()
                        //display no posts available at the moment
                        binding.noIssuesLayout.visibility = VISIBLE
                        adapter?.retry()
                    }
                    is LoadState.Loading -> {
                        // The initial Load has begun
                        // ...
                        swipeRefreshLayout.isRefreshing = true
                    }
                    is LoadState.NotLoading -> {
                        // The previous load (either initial or additional) completed
                        swipeRefreshLayout.isRefreshing = false
                        //remove display no posts available at the moment
                        binding.noIssuesLayout.visibility = GONE

                    }
                }

                when (loadStates.append) {
                    is LoadState.Error -> {
                        // The additional load failed. Call the retry() method
                        // in order to retry the load operation.
                        adapter?.retry()
                    }
                    is LoadState.Loading -> {
                        // The adapter has started to load an additional page
                        // ...
                        swipeRefreshLayout.isRefreshing = true
                    }
                    is LoadState.NotLoading -> {
                        if (loadStates.append.endOfPaginationReached) {
                            // The adapter has finished loading all of the data set
                            swipeRefreshLayout.isRefreshing = false
                        }
                    }
                }
            }
        }

        //set recycler view layout manager
        manager = LinearLayoutManager(requireContext())
        recyclerView.layoutManager = manager

        //initialize adapter
        recyclerView.adapter = adapter

        swipeRefreshLayout.setOnRefreshListener {
            adapter?.refresh()
        }

    }


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

    //navigate to new fragment with issue id
    override fun onItemClick(issueId: String, view: View) {
        val action = HomeFragmentDirections.actionHomeFragmentToViewIssueFragment(issueId)
        parentFragment?.findNavController()?.navigate(action)

    }

    override fun onItemLongCLicked(issueId: String, view: View) {
    }

    override fun onUserClicked(userId: String, view: View) {
        val action = HomeFragmentDirections.actionHomeFragmentToUserInfoFragment(userId)
        parentFragment?.findNavController()?.navigate(action)
    }

}

点击之前

第一次点击后

第二次点击后

共有1个答案

伍溪叠
2023-03-14

使用addListenerForSingleValueEvent而不是addValueEventListener从显示单击的项目详细信息的片段中的数据库中查询项目详细信息(问题)。否则,请删除onStop()中的addValueEventListener,以便在导航回上一个片段时,侦听器不再连接到数据库。

 类似资料:
  • 我正在使用React-useState钩子更新状态。单击表单提交按钮时,状态直到第二次单击才更新。我相信我需要使用useffect,但不确定如何使用onClick实现。

  • 当我触摸列表视图中的项目时,应用程序崩溃 这是logcat 2020-03-15 20:26:50.123 19174-19174/com.zeroXmohamed。TN19 E/Minikin:无法获取cmap表大小!2020-03-15 20:26:50.158 19174-19202/com.zeroXmohamed。TN19 E/MemoryLeakMonitor orManager:Me

  • 这是我现在面临的一个奇怪的问题。从我的应用程序中,我从JSON文件中从链接中获取数据,然后用Volley解析它。现在我想在一个看起来不起作用的RecyclerView上显示数据,我很高兴我的代码很好,但是有些东西似乎不起作用,我找不到它。 这是活动代码 HomeActivity.java: 这是我从适配器中扩展的布局,并从JSON文件中设置数据 和适配器类相关的代码,我猜: @Override p

  • 任何人都知道当用户第二次单击按钮时如何显示间隙广告。我的意思是,当用户单击一次按钮时,广告不应出现,但每当用户再次单击同一按钮时,广告必须显示。。 这里显示了我在不同活动中调用的中间代码。

  • 我正在使用ComboBox在JavaFX中编写一个程序,并为布局加载一个FXML。 当我第一次在一个只有几个项目(例如只有两个)的组合框中单击时,滚动条显示在右侧。再次打开后,滚动条不再显示。 我尝试了一些解决方案。一种有效的方法是直接在FXML中应用CSS,将单元格大小设置为固定值。但是代码中的解决方案(例如,在控制器中的初始化函数中)更适合我的情况。 谢谢你的帮助。

  • 如何使用Espresso单击RecyclerView项目中的特定视图?我知道我可以使用以下方法单击位置0处的项目: 但我需要点击项目内部的特定视图,而不是项目本身。 提前道谢。 --编辑-- 更准确地说:我有一个RecolyerView(),其中的项目是CardView()。在每个CardView中,我有四个按钮(以及其他东西),我想单击一个特定的按钮()。 我想使用Espresso2.0的新功能