在过去的几天里一直努力使它工作,并且会非常感谢您的帮助。
我想要的很简单:我有一个元素队列,我想向用户显示该队列中的下一个元素。
现在,让我们假装只有一种类型的内容:文本
因此,队列只能处于两种状态
由于这个想法是以后支持更多的内容类型,所以我希望我的“ QueueFragment”具有ViewPager,通过它我可以将一个视图交换为另一个视图。
队列片段就是所有
所以只有三个用户故事:
我的问题很简单:
如何获取片段以进行更新?
这是我当前的尝试:
由于我们仅支持文本,因此我可以采用单一的内容视图布局:
<?xml version="1.0" encoding="utf-8"?>
<android.widget.LinearLayout 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:orientation="vertical">
<TextView
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:textAlignment="center"
android:gravity="center"
tools:text="PLACEHOLDER" />
</android.widget.LinearLayout>
我们为此创建了两个实例...
一个带有硬编码字符串的队列,当队列为空时显示
class NoContentView : AbstractContentView() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val fragmentView = inflater.inflate(R.layout.fragment_process_component_text_view, container, false)
fragmentView.content.text = Html.fromHtml(getString(R.string.process_queueIsEmpty))
return fragmentView
}
override fun displayThought(thought: IThoughtWithContent) {
throw IllegalStateException("this view cannot display any thoughts")
}
}
和一个内容可设置的
class TextContentView:AbstractContentView(){
@Volatile private var displayedData:String? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_process_component_text_view, container, false)
displayedData?.let { view.content.text = it }
return view
}
/**The idea here behind setting [displayedData] is as follows:
if the view has already been created, [content] will not be null
and we can set its text directly
otherwise, the content will be set in [onCreateView]
*/
override fun displayThought(thought: IThoughtWithContent) {
displayedData = String(thought.data, Charsets.UTF_8)
content?.text = displayedData
}
}
其中
import android.support.v4.app.Fragment
abstract class AbstractContentView:Fragment(){
abstract fun displayThought(thought:IThoughtWithContent)
}
和
interface IThoughtWithContent: IThought {
val type : IContentType
val data : ByteArray
val timestampCreated:Date
}
现在,我被告知FragmentStatePagerAdapter
必须始终返回一个新实例,而不是缓存片段以供重用。
所以当前的想法是使适配器知道它应该显示的数据并在返回碎片时更新这些碎片:
class ProcessFragmentStatePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
companion object {
private const val NOF_VIEWS_AVAILABLE = 2
const val EMPTY = 0
const val TEXT = 1
}
override fun getCount(): Int { return NOF_VIEWS_AVAILABLE }
override fun getItemPosition(`object`: Any): Int {
return PagerAdapter.POSITION_NONE
}
@Volatile var nextThought: IThoughtWithContent? = null
override fun getItem(position: Int): Fragment {
return when(position){
EMPTY -> {
nextThought = null
NoContentView()
}
TEXT -> {
val fragment = TextContentView()
nextThought?.let { fragment.displayThought(it) }
fragment
}
else -> throw IllegalArgumentException("unexpected fragment position: $position")
}
}
}
进入我们的队列片段...
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/view_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/btn_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:src="@drawable/ic_checkmark" />
</android.support.design.widget.CoordinatorLayout>
再次,非常简单地布局。
片段本身也很简单:
我们设置了deleteButton.setOnClickListener {dequeue()}
。 dequeue
从队列中删除当前元素,并显示下一个元素(或者至少应该这样做):
private fun dequeue(thoughts: IThoughtSet? = null){
val queue = thoughts ?: currentThoughts()
queue.pop()
displayNextThought(queue)
}
我们也在{p}中displayNextThought
override fun onResume() {
super.onResume()
if(initialized)displayNextThought()
}
和
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
if(userVisibleHint && initialized) displayNextThought()
super.setUserVisibleHint(isVisibleToUser)
}
displayNextThought
呼叫
private fun displayNoThought(){
viewContainer.currentItem = VIEW_EMPTY
}
或
private fun displayTextThought(thought:Thought.ThoughtWithContent){
val adapter = viewContainer.adapter as ProcessFragmentStatePagerAdapter
adapter.nextThought = thought
viewContainer.currentItem = VIEW_TEXT
}
取决于队列是否为空。
(本文底部的完整片段代码。)
那么这个设置怎么了?让我们来看看我们的三个用户故事
案例1:用户启动应用程序
将还原队列并显示第一项,无论队列是否为空,它都能正确执行此操作。很好。
案例2:用户位于不同的片段中,并在片段之间来回切换
如果队列中有项目,那么在某一时刻,队列片段将只显示一个空的文本视图。
如果队列为空,它将始终正确显示NO CONTENT视图。
案例3:用户打开了队列片段,并按下了删除按钮
片段出队,但视图未更新(仍然显示当用户使用新项目切换到时队列队列显示为空时显示的元素,此时片段正确显示了NO CONTENT视图。 / p>
是的,不是很好。
我将尽一切帮助。
完整的片段代码:
class ProcessFragment : Fragment() {
companion object {
private const val VIEW_EMPTY = ProcessFragmentStatePagerAdapter.EMPTY
private const val VIEW_TEXT = ProcessFragmentStatePagerAdapter.TEXT
}
private lateinit var fragmentView:View
private lateinit var viewContainer: ViewPager
private lateinit var deleteButton: FloatingActionButton
private var initialized = false
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
super.onCreateView(inflater, container, savedInstanceState)
fragmentView = inflater.inflate(R.layout.fragment_process, container, false)
viewContainer = fragmentView.view_container
deleteButton = fragmentView.btn_delete
setupViewPager(viewContainer)
deleteButton.setOnClickListener {dequeue()}
initialized = true
return fragmentView
}
private fun setupViewPager(pager:ViewPager){
pager.adapter = ProcessFragmentStatePagerAdapter(childFragmentManager)
}
override fun onResume() {
super.onResume()
if(initialized)displayNextThought()
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
if(userVisibleHint && initialized) displayNextThought()
super.setUserVisibleHint(isVisibleToUser)
}
private fun currentThoughts():IThoughtSet{
val um = DataConfig.getUserManager()
val au = um.getActiveUser()
return um.getThoughts(au)
}
private fun dequeue(thoughts: IThoughtSet? = null){
val queue = thoughts ?: currentThoughts()
queue.pop()
displayNextThought(queue)
}
private fun displayNextThought(thoughts: IThoughtSet? = null){
val queue = thoughts ?: currentThoughts()
val peek = queue.peek()
if(peek is Reply.OK && peek.result is IThoughtWithContent){
when(peek.result.type){
ContentType.Text -> displayTextThought(peek.result)
else -> throw IllegalArgumentException("don't know how to handle content type ${peek.result.type}")
}
}
else displayNoThought()
}
private fun displayNoThought(){
viewContainer.currentItem = VIEW_EMPTY
}
private fun displayTextThought(thought:IThoughtWithContent){
val adapter = viewContainer.adapter as ProcessFragmentStatePagerAdapter
adapter.nextThought = thought
viewContainer.currentItem = VIEW_TEXT
}
}