Как реализовать общий элемент перехода от элемента RcyclerView к фрагменту с помощью компонента навигации Android? - PullRequest
0 голосов
/ 04 декабря 2018

У меня довольно простой случай.Я хочу реализовать общий переход элемента между элементами в RececlerView и fragment.Я использую компонент навигации Android в моем приложении.

Существует статья о совместном переходе на developer.android и теме на stackoverflow , но это решение работает только для представления, расположенного в fragment макете, который запускает переходи не работает для предметов из RecyclerView.Также есть библиотека на github , но я не хочу полагаться на сторонние библиотеки и делать это самостоятельно.

Есть ли какое-то решение для этого?Может быть, это должно работать, и это просто ошибка?Но я не нашел никакой информации об этом.

пример кода:

начало перехода

class TransitionStartFragment: Fragment() {

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.fragment_transition_start, container, false)
    }

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val testData = listOf("one", "two", "three")
    val adapter = TestAdapter(testData, View.OnClickListener { transitionWithTextViewInRecyclerViewItem(it) })
    val recyclerView = view.findViewById<RecyclerView>(R.id.test_list)
    recyclerView.adapter = adapter
    val button = view.findViewById<Button>(R.id.open_transition_end_fragment)
    button.setOnClickListener { transitionWithTextViewInFragment() }
    }

private fun transitionWithTextViewInFragment(){
    val destination = TransitionStartFragmentDirections.openTransitionEndFragment()
    val extras = FragmentNavigatorExtras(transition_start_text to "transitionTextEnd")
    findNavController().navigate(destination, extras)
    }

private fun transitionWithTextViewInRecyclerViewItem(view: View){
    val destination = TransitionStartFragmentDirections.openTransitionEndFragment()
    val extras = FragmentNavigatorExtras(view to "transitionTextEnd")
    findNavController().navigate(destination, extras)
   }

} ​​

макет

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
    android:id="@+id/transition_start_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="transition"
    android:transitionName="transitionTextStart"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<Button
    android:id="@+id/open_transition_end_fragment"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toBottomOf="@id/transition_start_text"
    android:text="open transition end fragment" />

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/test_list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@id/open_transition_end_fragment"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

адаптер для recyclerView

class TestAdapter(
    private val items: List<String>,
    private val onItemClickListener: View.OnClickListener
) : RecyclerView.Adapter<TestAdapter.ViewHodler>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHodler {
    return ViewHodler(LayoutInflater.from(parent.context).inflate(R.layout.item_test, parent, false))
    }

override fun getItemCount(): Int {
    return items.size
    }

override fun onBindViewHolder(holder: ViewHodler, position: Int) {
    val item = items[position]
    holder.transitionText.text = item
    holder.itemView.setOnClickListener { onItemClickListener.onClick(holder.transitionText) }

    }

class ViewHodler(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val transitionText = itemView.findViewById<TextView>(R.id.item_test_text)
    }
}

в onItemClick Я передаю элемент формы textView в recyclerView для перехода

end end

class TransitionEndFragment : Fragment() {

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    setUpTransition()
    return inflater.inflate(R.layout.fragment_transition_end, container, false)
    }

private fun setUpTransition(){
    sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move)

    }
}

layout

<androidx.constraintlayout.widget.ConstraintLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
    android:id="@+id/transition_end_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="transition"
    android:transitionName="transitionTextEnd"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

fun transitionWithTextViewInFragment () - имеет переход.

fun transitionWithTextViewInRecyclerViewItem (view: View) - без перехода.

Ответы [ 3 ]

0 голосов
/ 30 мая 2019

Чтобы решить проблему с обратным переходом, вам нужно добавить эти строки во фрагмент исходного кода (фрагмент с представлением рециркулятора), где вы инициализируете ваше представление рециркулятора

// your recyclerView
recyclerView.apply {
                ...
                adapter = myAdapter
                postponeEnterTransition()
                viewTreeObserver
                    .addOnPreDrawListener {
                        startPostponedEnterTransition()
                        true
                    }
}
0 голосов
/ 09 июля 2019

Столкнулся с той же проблемой, что и многие в SO с обратным переходом, но для меня основной причиной проблемы было то, что Navigation в настоящее время использует replace только для транзакций фрагмента, и это заставило мой переработчик в начальном фрагменте перезагружать каждуюкогда вы наносите ответный удар, что само по себе является проблемой.

Таким образом, при решении второй (корневой) проблемы обратный переход начал работать без отложенной анимации.Для тех из вас, кто хочет сохранить исходное состояние при ответном ударе, вот что я сделал:

просто добавив простую проверку в onCreateView, как это

private lateinit var binding: FragmentSearchResultsBinding

override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return if (::binding.isInitialized) {
            binding.root
        } else {
            binding = DataBindingUtil.inflate(inflater, R.layout.fragment_search_results, container, false)

            with(binding) {
                //doing some stuff here
                root
            }
        }

Так что тройной выигрыш здесь: переработчик не перерисовывается, никакие повторные загрузки с сервера, а также обратные переходы работают как положено.

0 голосов
/ 06 декабря 2018

Вот мой пример с RecyclerView, у которого есть общий переход фрагмента.В моем адаптере я устанавливаю разные имена переходов для каждого элемента в зависимости от положения (в моем примере это ImageView).

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val item = items[position]
    holder.itemView.txtView.text=item
    ViewCompat.setTransitionName(holder.itemView.imgViewIcon, "Test_$position")
    holder.setClickListener(object : ViewHolder.ClickListener {
        override fun onClick(v: View, position: Int) {
            when (v.id) {
                R.id.linearLayout -> listener.onClick(item, holder.itemView.imgViewIcon, position)
            }
        }
    })

}

И при нажатии на элемент мой интерфейс, реализованный во фрагменте исходного кода:

override fun onClick(text: String, img: ImageView, position: Int) {
    val action = MainFragmentDirections.actionMainFragmentToSecondFragment(text, position)
    val extras = FragmentNavigator.Extras.Builder()
            .addSharedElement(img, ViewCompat.getTransitionName(img)!!)
            .build()
    NavHostFragment.findNavController(this@MainFragment).navigate(action, extras)
}

И в моем пункте назначения:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    info("onCreate")
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move)
    }
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    info("onCreateView")
    return inflater.inflate(R.layout.fragment_second, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    info("onViewCreated")
    val name=SecondFragmentArgs.fromBundle(arguments).name
    val position=SecondFragmentArgs.fromBundle(arguments).position
    txtViewName.text=name
    ViewCompat.setTransitionName(imgViewSecond, "Test_$position")
}
...