繁体   English   中英

自动旋转设备时更改 RecyclerView 布局(从线性到网格和反向)

[英]Change RecyclerView Layout (from Linear to Grid and reverse) when rotate device automatically

我有 4 个不同的“卡片列表” 、“卡片杂志”“标题”“网格” ,每个视图持有者都相关,请在此处查看我的其他问题


现在我试图在设备旋转时自动更改布局,因此当方向为纵向时,布局为 LinearLayout“卡片布局”,当方向更改为横向时,布局将为 GridLayout,我还有一个changeAndSaveLayout方法让用户选择选项菜单中的每个布局之间

然后我使用DataStoreFlow将布局保存在ViewModel中,

问题

当我旋转设备时,RecyclerView 和列表消失了,我看到空屏幕,当我回到纵向时,列表又回来了,它又回到了默认布局“cardLayout”

我在更改布局后尝试了多种方法,例如notifyDataSetChanged并处理onConfigurationChanged方法中的更改,但所有这些方法都失败了

DataStore class 代码saveRecyclerViewLayoutreadRecyclerViewLayout

private val Context.dataStore by preferencesDataStore("user_preferences")
private const val TAG = "DataStoreRepository"

@ActivityRetainedScoped
class DataStoreRepository @Inject constructor(@ApplicationContext private val context: Context) {

 suspend fun saveRecyclerViewLayout(
        recyclerViewLayout: String,
    ) {
        datastore.edit { preferences ->
            preferences[PreferencesKeys.RECYCLER_VIEW_LAYOUT_KEY] = recyclerViewLayout
        }
    }

val readRecyclerViewLayout:
            Flow<String> = datastore.data.catch { ex ->
        if (ex is IOException) {
            ex.message?.let { Log.e(TAG, it) }
            emit(emptyPreferences())
        } else {
            throw ex
        }
    }.map { preferences ->
        val recyclerViewLayout: String =
            preferences[PreferencesKeys.RECYCLER_VIEW_LAYOUT_KEY] ?: "cardLayout"
        recyclerViewLayout
    }

}

我在ViewModel中使用它,如下所示

@HiltViewModel
class PostViewModel @Inject constructor(
    private val mainRepository: MainRepository,
    private val dataStoreRepository: DataStoreRepository,
    application: Application
) :
    AndroidViewModel(application) {
    val recyclerViewLayout = dataStoreRepository.readRecyclerViewLayout.asLiveData()

 fun saveRecyclerViewLayout(layout: String) {
        viewModelScope.launch {
            dataStoreRepository.saveRecyclerViewLayout(layout)
        }
    }

}

邮政适配器 Class

class PostAdapter(
    private val titleAndGridLayout: TitleAndGridLayout,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {


    var viewType = 0


    private val differCallback = object : DiffUtil.ItemCallback<Item>() {
        override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
            return (oldItem.id == newItem.id)
        }

        override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
            return (oldItem == newItem)
        }
    }

    val differ = AsyncListDiffer(this, differCallback)


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {


        return when (this.viewType) {
            CARD -> {
                fromCardViewHolder(parent)
            }
            CARD_MAGAZINE -> {
                fromCardMagazineViewHolder(parent)
            }
            TITLE -> {
                fromTitleViewHolder(parent)
            }
            else -> {
                fromGridViewHolder(parent)
            }
        }

    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val item: Item = differ.currentList[position]






        when (this.viewType) {
            CARD -> if (holder is CardViewHolder) {

                holder.bind(item)


            }
            CARD_MAGAZINE -> if (holder is CardMagazineViewHolder) {
                holder.bind(item)


            }
            TITLE -> if (holder is TitleViewHolder) {
                holder.bind(item)

                if (position == itemCount - 1)
                    titleAndGridLayout.tellFragmentToGetItems()


            }
            GRID -> if (holder is GridViewHolder) {
                holder.bind(item)

                if (position == itemCount - 1)
                    titleAndGridLayout.tellFragmentToGetItems()


            }
        }
    }


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

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }


    class CardViewHolder(private val cardLayoutBinding: CardLayoutBinding) :
        RecyclerView.ViewHolder(cardLayoutBinding.root) {

        fun bind(item: Item) {


            val document = Jsoup.parse(item.content)
            val elements = document.select("img")


            var date: Date? = Date()
            val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault())
            cardLayoutBinding.postTitle.text = item.title
            try {

                Glide.with(cardLayoutBinding.root).load(elements[0].attr("src"))
                    .transition(DrawableTransitionOptions.withCrossFade(600))
                    .placeholder(R.drawable.loading_animation)
                    .error(R.drawable.no_image)
                    .into(cardLayoutBinding.postImage)
            } catch (e: IndexOutOfBoundsException) {
                cardLayoutBinding.postImage.setImageResource(R.drawable.no_image)

            }
            cardLayoutBinding.postDescription.text = document.text()
            try {
                date = format.parse(item.published)
            } catch (e: ParseException) {
                e.printStackTrace()
            }
            val prettyTime = PrettyTime()
            cardLayoutBinding.postDate.text = prettyTime.format(date)
        }


    }

    class CardMagazineViewHolder(private val cardMagazineBinding: CardMagazineBinding) :
        RecyclerView.ViewHolder(cardMagazineBinding.root) {

        fun bind(item: Item) {
            val document = Jsoup.parse(item.content)
            val elements = document.select("img")
            var date: Date? = Date()
            val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault())



            cardMagazineBinding.postTitle.text = item.title
            try {

                Glide.with(itemView.context).load(elements[0].attr("src"))
                    .transition(DrawableTransitionOptions.withCrossFade(600))
                    .placeholder(R.drawable.loading_animation)
                    .error(R.drawable.no_image)
                    .into(cardMagazineBinding.postImage)
            } catch (e: IndexOutOfBoundsException) {
                cardMagazineBinding.postImage.setImageResource(R.drawable.no_image)

            }
            try {
                date = format.parse(item.published)
            } catch (e: ParseException) {
                e.printStackTrace()
            }
            val prettyTime = PrettyTime()
            cardMagazineBinding.postDate.text = prettyTime.format(date)
        }

    }

    class TitleViewHolder(private val binding: TitleLayoutBinding) :
        RecyclerView.ViewHolder(binding.root) {

        fun bind(item: Item) {
            val document = Jsoup.parse(item.content)
            val elements = document.select("img")


            binding.postTitle.text = item.title
            try {

                Glide.with(itemView.context).load(elements[0].attr("src"))
                    .transition(DrawableTransitionOptions.withCrossFade(600))
                    .placeholder(R.drawable.loading_animation)
                    .error(R.drawable.no_image)
                    .into(binding.postImage)
            } catch (e: IndexOutOfBoundsException) {
                binding.postImage.setImageResource(R.drawable.no_image)

            }
        }


    }

    class GridViewHolder constructor(private val binding: GridLayoutBinding) :
        RecyclerView.ViewHolder(binding.root) {
        fun bind(item: Item) {
            val document = Jsoup.parse(item.content)
            val elements = document.select("img")


            binding.postTitle.text = item.title
            try {

                Glide.with(itemView.context).load(elements[0].attr("src"))
                    .transition(DrawableTransitionOptions.withCrossFade(600))
                    .placeholder(R.drawable.loading_animation)
                    .error(R.drawable.no_image)
                    .into(binding.postImage)
            } catch (e: IndexOutOfBoundsException) {
                binding.postImage.setImageResource(R.drawable.no_image)

            }
        }

    }


    companion object {
        private const val CARD = 0
        private const val CARD_MAGAZINE = 1
        private const val TITLE = 2
        private const val GRID = 3
        private const val TAG = "POST_ADAPTER"


        fun fromCardViewHolder(parent: ViewGroup): CardViewHolder {
            val cardLayoutBinding: CardLayoutBinding =
                CardLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
            return CardViewHolder(cardLayoutBinding)
        }

        fun fromCardMagazineViewHolder(parent: ViewGroup): CardMagazineViewHolder {
            val cardMagazineBinding: CardMagazineBinding =
                CardMagazineBinding.inflate(
                    LayoutInflater.from(parent.context),
                    parent,
                    false
                )
            return CardMagazineViewHolder(cardMagazineBinding)
        }

        fun fromTitleViewHolder(parent: ViewGroup): TitleViewHolder {
            val titleLayoutBinding: TitleLayoutBinding =
                TitleLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
            return TitleViewHolder(titleLayoutBinding)
        }

        fun fromGridViewHolder(
            parent: ViewGroup
        ): GridViewHolder {
            val gridLayoutBinding: GridLayoutBinding =
                GridLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
            return GridViewHolder(gridLayoutBinding)
        }

    }

    init {


        setHasStableIds(true)


    }


}

最后是HomeFragment

@AndroidEntryPoint
class HomeFragment : Fragment(), TitleAndGridLayout, MenuProvider {

    private var _binding: FragmentHomeBinding? = null
    private val binding get() = _binding!!

    private var itemArrayList = arrayListOf<Item>()

    private var searchItemList = arrayListOf<Item>()
    private val postViewModel: PostViewModel by viewModels()

    private var linearLayoutManager: LinearLayoutManager? = null

    private val titleLayoutManager: GridLayoutManager by lazy {
        GridLayoutManager(requireContext(), 2)
    }
    private val gridLayoutManager: GridLayoutManager by lazy {
        GridLayoutManager(requireContext(), 3)
    }


    private var menuHost: MenuHost? = null


    private lateinit var networkListener: NetworkListener

    private lateinit var adapter:PostAdapter

    private var isScrolling = false
    var currentItems = 0
    var totalItems: Int = 0
    var scrollOutItems: Int = 0
    private var postsAPiFlag = false

    private val recyclerStateKey = "recycler_state"
    private val mBundleRecyclerViewState by lazy { Bundle() }

    private var keyword: String? = null

    private var orientation: Int? = null


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        postViewModel.finalURL.value = "$BASE_URL?key=$API_KEY"
        networkListener = NetworkListener()

    }


    // This property is only valid between onCreateView and
    // onDestroyView.

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {

        adapter = PostAdapter(this)
        orientation = resources.configuration.orientation

        _binding = FragmentHomeBinding.inflate(inflater, container, false)

        menuHost = requireActivity()

        menuHost?.addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.CREATED)

        postViewModel.recyclerViewLayout.observe(viewLifecycleOwner) { layout ->

            linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)

            Log.w(TAG, "getSavedLayout called")

            Log.w(TAG, "getSavedLayout: orientation ${orientation.toString()}", )

            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                when (layout) {
                    "cardLayout" -> {

//
                        adapter.viewType = 0
                        binding.apply {
                            homeRecyclerView.layoutManager = linearLayoutManager
                            homeRecyclerView.adapter = adapter
                        }

                    }
                    "cardMagazineLayout" -> {
//                    binding.loadMoreBtn.visibility = View.VISIBLE
                        binding.homeRecyclerView.layoutManager = linearLayoutManager
                        adapter.viewType = 1
                        binding.homeRecyclerView.adapter = adapter

                    }
                }
            } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                when (layout) {
                    "titleLayout" -> {
//                    binding.loadMoreBtn.visibility = View.GONE
                        binding.homeRecyclerView.layoutManager = titleLayoutManager
                        adapter.viewType = 2
                        binding.homeRecyclerView.adapter = adapter

                    }
                    "gridLayout" -> {
                        binding.homeRecyclerView.layoutManager = gridLayoutManager
                        adapter.viewType = 3
                        binding.homeRecyclerView.adapter = adapter

                    }

                }
            }

 override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
        return if (menuItem.itemId == R.id.change_layout) {
            changeAndSaveLayout()
            true
        } else false
    }




   private fun changeAndSaveLayout() {
//        Log.w(TAG, "changeAndSaveLayout: called")
        val builder = AlertDialog.Builder(requireContext())
        builder.setTitle(getString(R.string.choose_layout))
        val recyclerViewPortraitLayout =
            resources.getStringArray(R.array.RecyclerViewPortraitLayout)
        val recyclerViewLandscapeLayout =
            resources.getStringArray(R.array.RecyclerViewLandscapeLayout)
        //        SharedPreferences.Editor editor = sharedPreferences.edit();

        Log.d(TAG, "changeAndSaveLayout: ${orientation.toString()}")

        if (orientation == 1) {
            builder.setItems(
                recyclerViewPortraitLayout
            ) { _: DialogInterface?, index: Int ->
                try {
                    when (index) {
                        0 -> {
                            adapter.viewType = 0
                            binding.homeRecyclerView.layoutManager = linearLayoutManager
                            binding.homeRecyclerView.adapter = adapter
                            postViewModel.saveRecyclerViewLayout("cardLayout")
                        }
                        1 -> {
                            adapter.viewType = 1
                            binding.homeRecyclerView.layoutManager = linearLayoutManager
                            binding.homeRecyclerView.adapter = adapter
                            postViewModel.saveRecyclerViewLayout("cardMagazineLayout")

                        }

                    }
                } catch (e: Exception) {
                    Log.e(TAG, "changeAndSaveLayout: " + e.message)
                    Log.e(TAG, "changeAndSaveLayout: " + e.cause)
                }
            }
        } else if (orientation == 2) {
            builder.setItems(
                recyclerViewLandscapeLayout
            ) { _: DialogInterface?, index: Int ->
                try {
                    when (index) {
                        2 -> {
                            adapter.viewType = 2
                            binding.homeRecyclerView.layoutManager = titleLayoutManager
                            binding.homeRecyclerView.adapter = adapter
                            postViewModel.saveRecyclerViewLayout("titleLayout")
                        }
                        3 -> {
                            adapter.viewType = 3
                            binding.homeRecyclerView.layoutManager = gridLayoutManager
                            binding.homeRecyclerView.adapter = adapter
                            postViewModel.saveRecyclerViewLayout("gridLayout")
                        }

                    }
                } catch (e: Exception) {
                    Log.e(TAG, "changeAndSaveLayout: " + e.message)
                    Log.e(TAG, "changeAndSaveLayout: " + e.cause)
                }
            }
        }
        val alertDialog = builder.create()
        alertDialog.show()
    }
        }

显示问题的 GIF

很长一段时间以来我一直在寻找一个灵魂,我找到了它并将它添加到我的旧项目中,我使用的是共享偏好但在你的情况下我的意思是数据存储它会正常工作

  1. 你需要为每个 orintation 创建两个 arrays
 <?xml version="1.0" encoding="utf-8"?>
<resources>

    <array name="recyclerViewPortraitList">
        <item>Card List</item>
        <item>Card Magazine</item>
        <item>Title</item>
    </array>

    <array name="recyclerViewLandscapeList">
        <item>Grid with 3 Span</item>
        <item>Grid with 4 Span</item>
    </array>
</resources>
  1. 在您的数据存储中将如下所示
    private object PreferencesKeys {
        var RECYCLER_VIEW_PORTRAIT_LAYOUT_KEY = stringPreferencesKey("recyclerViewPortraitLayout")
        var RECYCLER_VIEW_LANDSCAPE_LAYOUT_KEY = stringPreferencesKey("recyclerViewLandscapeLayout")
}

suspend fun saveRecyclerViewPortraitLayout(
        recyclerViewLayout: String,
    ) {
        datastore.edit { preferences ->
            preferences[PreferencesKeys.RECYCLER_VIEW_PORTRAIT_LAYOUT_KEY] = recyclerViewLayout
        }
    }

    suspend fun saveRecyclerViewLandscapeLayout(recyclerViewLayout: String) {
        datastore.edit { preferences ->
            preferences[PreferencesKeys.RECYCLER_VIEW_LANDSCAPE_LAYOUT_KEY] = recyclerViewLayout
        }
    }

val readRecyclerViewPortraitLayout:
            Flow<String> = datastore.data.catch { ex ->
        if (ex is IOException) {
            ex.message?.let { Log.e(TAG, it) }
            emit(emptyPreferences())
        } else {
            throw ex
        }
    }.map { preferences ->
        val recyclerViewLayout: String =
            preferences[PreferencesKeys.RECYCLER_VIEW_PORTRAIT_LAYOUT_KEY] ?: "cardLayout"
        recyclerViewLayout
    }


    val readRecyclerViewLandscpaeLayout:
            Flow<String> = datastore.data.catch { ex ->
        if (ex is IOException) {
            ex.message?.let { Log.e(TAG, it) }
            emit(emptyPreferences())
        } else {
            throw ex
        }
    }.map { preferences ->
        val recyclerViewLayout: String =
            preferences[PreferencesKeys.RECYCLER_VIEW_LANDSCAPE_LAYOUT_KEY] ?: "gridWith3Span"
        recyclerViewLayout
    }

  1. 在视图模型中
    val readRecyclerViewPortraitLayout =
        dataStoreRepository.readRecyclerViewPortraitLayout.asLiveData()
    val readRecyclerViewLandscapeLayout =
        dataStoreRepository.readRecyclerViewLandscpaeLayout.asLiveData()

 fun saveRecyclerViewPortraitLayout(layout: String) {
        viewModelScope.launch {
            dataStoreRepository.saveRecyclerViewPortraitLayout(layout)
        }
    }

    fun saveRecyclerViewLandscapeLayout(layout: String) {
        viewModelScope.launch {
            dataStoreRepository.saveRecyclerViewLandscapeLayout(layout)
        }
    }
  1. 在适配器更改常量
 companion object {
        private const val CARD = 0
        private const val CARD_MAGAZINE = 1
        private const val TITLE = 2
        private const val GRID_WITH_3_SPAN = 3
        private const val GRID_WITH_4_SPAN = 4
}

5.最后在片段或活动中你可以像下面这样使用它

private fun setUpRecyclerViewLayout() {
        if (requireActivity().resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {

            postViewModel.readRecyclerViewPortraitLayout.observe(viewLifecycleOwner) { layout ->

                recyclerViewLayout = layout
                
                when (layout) {
                    "cardLayout" -> {
                        adapter.viewType = 0
                        binding.apply {
                            homeRecyclerView.layoutManager = linearLayoutManager
                            homeRecyclerView.adapter = adapter
                            homeRecyclerView.setHasFixedSize(true)
                        }

                    }
                    "cardMagazineLayout" -> {
                        binding.homeRecyclerView.layoutManager = linearLayoutManager
                        adapter.viewType = 1
                        binding.homeRecyclerView.adapter = adapter
                    }
                    "titleLayout" -> {
                        binding.homeRecyclerView.layoutManager = titleLayoutManager
                        adapter.viewType = 2
                        binding.homeRecyclerView.adapter = adapter
                    }
                }
            }
        } else {
            postViewModel.readRecyclerViewLandscapeLayout.observe(viewLifecycleOwner) { layout ->

                recyclerViewLayout = layout

                when (layout) {

                    "gridWith3Span" -> {
      
                        binding.homeRecyclerView.layoutManager = gridWith3SpanLayoutManager
                        adapter.viewType = 3
                        binding.homeRecyclerView.adapter = adapter
                    }
                    "gridWith4Span" -> {
            
                        binding.homeRecyclerView.layoutManager = gridWith4SpanLayoutManager
                        adapter.viewType = 4
                        binding.homeRecyclerView.adapter = adapter

                    }
                }
            }
        }

    }


    private fun changeAndSaveLayout() {

        val builder = AlertDialog.Builder(requireContext())
        builder.setTitle(getString(R.string.choose_layout))

        //        SharedPreferences.Editor editor = sharedPreferences.edit();

        if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
            builder.setItems(
                resources.getStringArray(R.array.recyclerViewPortraitList)
            ) { _: DialogInterface?, index: Int ->
                try {
                    when (index) {
                        0 -> {
                            adapter.viewType = 0
                            binding.homeRecyclerView.layoutManager = linearLayoutManager
                            binding.homeRecyclerView.adapter = adapter
                            postViewModel.saveRecyclerViewPortraitLayout("cardLayout")
                        }
                        1 -> {
                            adapter.viewType = 1
                            binding.homeRecyclerView.layoutManager = linearLayoutManager
                            binding.homeRecyclerView.adapter = adapter
                            postViewModel.saveRecyclerViewPortraitLayout("cardMagazineLayout")

                        }
                        2 -> {
                            adapter.viewType = 2
                            binding.homeRecyclerView.layoutManager = titleLayoutManager
                            binding.homeRecyclerView.adapter = adapter
                            postViewModel.saveRecyclerViewPortraitLayout("titleLayout")
                        }
                        else -> {
                            throw Exception("Unknown layout")
                        }
                    }
                } catch (e: Exception) {
                    Log.e(TAG, "changeAndSaveLayout: " + e.message)
                    Log.e(TAG, "changeAndSaveLayout: " + e.cause)
                }
            }
        } else {
            builder.setItems(
                resources.getStringArray(R.array.recyclerViewLandscapeList)
            ) { _: DialogInterface?, index: Int ->
                try {
                    when (index) {
                        0 -> {
                            adapter.viewType = 3
                            binding.homeRecyclerView.layoutManager = gridWith3SpanLayoutManager
                            binding.homeRecyclerView.adapter = adapter
                            postViewModel.saveRecyclerViewLandscapeLayout("gridWith3Span")
                        }
                        1 -> {
                            adapter.viewType = 4
                            binding.homeRecyclerView.layoutManager = gridWith4SpanLayoutManager
                            binding.homeRecyclerView.adapter = adapter
                            postViewModel.saveRecyclerViewLandscapeLayout("gridWith4Span")

                        }
                        else -> {
                            throw Exception("Unknown layout")
                        }
                    }
                } catch (e: Exception) {
                    Log.e(TAG, "changeAndSaveLayout: " + e.message)
                    Log.e(TAG, "changeAndSaveLayout: " + e.cause)
                }
            }
        }
        val alertDialog = builder.create()
        alertDialog.show()
    }

如果您在清单中设置配置更改,如android:configChanges="orientation|screenSize"您将需要从onConfigurationChanged调用setUpRecyclerViewLayout()

 override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        setUpRecyclerViewLayout()
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM