kotlin - 2个Fragment实现左右显示,左边列表,右边详情,平板横、竖屏切换
(要使用平板测试)平板横屏:左右fragment实现分屏效果,平板竖屏:只显示左边的fragment,点击才显示右边fragment
屏幕旋转,会销毁重新创建,执行onCreate方法。
在AndroidManifest.xml配置android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboardHidden|navigation"属性,只调用onConfigurationChanged方法
fragment的
add方法,
如果新打开一个activity,该fragment只回调onStop()方法,没有调用onDestroy()方法。
如果popBackStack或者finish,会回调onStop()和onDestroy()方法
replace方法:旧的fragment执行onDestroy()方法,然后新建一个fragment,执行onViewCreated()方法。

package com.example.androidkotlindemo2.pad.leftright
import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.androidkotlindemo2.R
import com.example.androidkotlindemo2.utils.LogUtils
/**
* Author : wn
* Email : maoning20080809@163.com
* Date : 2025/8/30 21:41
* Description : (要使用平板测试)平板横屏:左右fragment实现分屏效果,平板竖屏:只显示左边的fragment,点击才显示右边fragment
* 屏幕旋转,会销毁重新创建,执行onCreate方法。
* 在AndroidManifest.xml配置android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboardHidden|navigation"属性,只调用onConfigurationChanged方法
* fragment的
* add方法,
* 如果新打开一个activity,该fragment只回调onStop()方法,没有调用onDestroy()方法。
* 如果popBackStack或者finish,会回调onStop()和onDestroy()方法
* replace:旧的fragment执行onDestroy()方法,然后新建一个fragment,执行onViewCreated()方法。
*/
class LRFragmentActivity : AppCompatActivity() {
//平板横屏
private var isLandscape: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.lr_fragment_main)
LogUtils.d("LeftRightMainActivity onCreate");
// 初始化显示列表Fragment
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_list_container, LRListFragment())
.commit()
}
checkOrientation()
}
//检查横、竖屏
private fun checkOrientation() {
val orientation = resources.configuration.orientation
isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE
}
fun setOnItemClick(position: Int, lrItem: LRItem) {
if(position < 3){
if(isLandscape){
//平板横屏,左右显示fragment
showItemDetailReplace(lrItem)
} else {
//平板竖屏,使用新的Activity显示
startRightActivity(lrItem)
}
} else if(position < 6){
//只适配平板横屏效果
showItemDetailAdd(lrItem)
} else {
showItemNewActivity(lrItem)
}
}
//竖屏打开新的Activity
fun startRightActivity(lrItem: LRItem){
val intent = Intent(this, LRRightActivity::class.java)
intent.putExtra("lr_item", lrItem)
startActivity(intent)
}
//使用replace方法
fun showItemDetailReplace(lrItem: LRItem) {
val detailFragment = LRDetailReplaceFragment.newInstance(lrItem)
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_detail_container, detailFragment)
.commit()
}
//使用add方法, 只回调onStop方法,不回调onDestroy方法
fun showItemDetailAdd(LRItem: LRItem) {
val detailNewFragment = LRDetailAddFragment.newInstance(LRItem)
supportFragmentManager.beginTransaction()
.add(R.id.fragment_detail_container, detailNewFragment)
.addToBackStack("detail_new_fragment") // 添加这行才能popBackStack回退
.commit()
}
//屏幕旋转,移除fragment
fun removeDetailFragment(){
val detailFragment = supportFragmentManager.findFragmentById(R.id.fragment_detail_container)
if(detailFragment != null){
supportFragmentManager.beginTransaction()
.remove(detailFragment)
.commit()
}
}
//使用add方法, 只回调onStop方法,不回调onDestroy方法
fun showItemNewActivity(LRItem: LRItem) {
startActivity(Intent(this, LRNewActivity::class.java))
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
LogUtils.i("LeftRightMainActivity onConfigurationChanged");
checkOrientation()
removeDetailFragment()
}
override fun onStop() {
super.onStop()
LogUtils.d("LeftRightMainActivity onDestroy")
}
override fun onDestroy() {
super.onDestroy()
LogUtils.d("LeftRightMainActivity onDestroy")
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
LogUtils.i("LeftRightMainActivity onNewIntent")
}
}
package com.example.androidkotlindemo2.pad.leftright
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.androidkotlindemo2.R
import com.example.androidkotlindemo2.utils.LogUtils
/**
* Author : wn
* Email : maoning20080809@163.com
* Date : 2025/8/30 21:41
* Description :
*/
class LRListFragment : Fragment(){
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: LRAdapter
private var LRItemList = mutableListOf<LRItem>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.lr_fragment_item_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRecyclerView(view)
loadItems()
}
private fun setupRecyclerView(view: View) {
recyclerView = view.findViewById(R.id.recycler_view)
adapter = LRAdapter(LRItemList, object : LROnItemClickInter{
override fun setOnItemClick(position: Int, lrItem: LRItem) {
// 通知Activity显示详情
(activity as? LRFragmentActivity)?.setOnItemClick(position,lrItem)
}
override fun setOnLongClickListener(position: Int, lrItem: LRItem) {
LogUtils.i("长按:${position}")
}
})
recyclerView.layoutManager = LinearLayoutManager(requireContext())
recyclerView.adapter = adapter
recyclerView.addItemDecoration(
DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
)
}
private fun loadItems() {
for(i in 1 .. 20){
LRItemList.add(LRItem(i, "项目${i}", "这是第${i}个项目的详细描述"))
}
adapter.notifyDataSetChanged()
}
}
package com.example.androidkotlindemo2.pad.leftright
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import com.example.androidkotlindemo2.MyApp
import com.example.androidkotlindemo2.R
import com.example.androidkotlindemo2.utils.LogUtils
/**
* Author : wn
* Email : maoning20080809@163.com
* Date : 2025/8/30 21:45
* Description : 新的详情,使用fragment的add方法调用,只会调用onStop()
*/
class LRDetailAddFragment : Fragment() {
companion object {
private const val ARG_ITEM = "item"
fun newInstance(LRItem: LRItem): LRDetailAddFragment {
val fragment = LRDetailAddFragment()
val args = Bundle().apply {
putParcelable(ARG_ITEM, LRItem)
}
fragment.arguments = args
return fragment
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.lr_fragment_item_detail, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
LogUtils.w("LRDetailAddFragment onViewCreated ----------------------------")
LogUtils.w("LRDetailAddFragment onViewCreated() ${this}")
arguments?.getParcelable<LRItem>(ARG_ITEM)?.let { item ->
displayItemDetails(view, item)
}
view.findViewById<Button>(R.id.lr_item_details_back).visibility = View.VISIBLE
//返回
view.findViewById<Button>(R.id.lr_item_details_back).setOnClickListener {
LogUtils.i("LRDetailNewFragment返回")
requireActivity().supportFragmentManager.popBackStack()
}
view.findViewById<Button>(R.id.lr_item_details_finish).visibility = View.VISIBLE
//关闭 -
view.findViewById<Button>(R.id.lr_item_details_finish).setOnClickListener {
LogUtils.i("LRDetailNewFragment关闭")
requireActivity().finish()
}
}
override fun onStart() {
super.onStart()
LogUtils.w("LRDetailAddFragment onStart() ${this}")
}
override fun onResume() {
super.onResume()
LogUtils.w("LRDetailAddFragment onResume() ${this}")
}
override fun onPause() {
super.onPause()
LogUtils.w("LRDetailAddFragment onPause() ${this}")
}
override fun onStop() {
super.onStop()
LogUtils.w("LRDetailAddFragment onStop() ${this}")
}
override fun onDestroy() {
super.onDestroy()
LogUtils.w("LRDetailAddFragment onDestroy() ${this}")
}
private fun displayItemDetails(view: View, LRItem: LRItem) {
view.findViewById<TextView>(R.id.title_text_view).text = LRItem.title
view.findViewById<TextView>(R.id.description_text_view).text = "新页面:" + LRItem.description
view.findViewById<TextView>(R.id.description_text_view).setTextColor(ContextCompat.getColor(MyApp.myApp,R.color.blue))
view.findViewById<TextView>(R.id.description_text_view).setTextSize(30f)
}
}
package com.example.androidkotlindemo2.pad.leftright
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.fragment.app.Fragment
import com.example.androidkotlindemo2.R
import com.example.androidkotlindemo2.utils.LogUtils
/**
* Author : wn
* Email : maoning20080809@163.com
* Date : 2025/8/30 21:45
* Description : 使用replace方法,会调用onStop()和onDestroy()方法,
*/
class LRDetailReplaceFragment : Fragment() {
companion object {
private const val ARG_ITEM = "item"
fun newInstance(LRItem: LRItem): LRDetailReplaceFragment {
val fragment = LRDetailReplaceFragment()
val args = Bundle().apply {
putParcelable(ARG_ITEM, LRItem)
}
fragment.arguments = args
return fragment
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.lr_fragment_item_detail, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
LogUtils.i("LRDetailReplaceFragment onViewCreated ----------------------------")
LogUtils.d("LRDetailReplaceFragment onViewCreated() ${this}")
arguments?.getParcelable<LRItem>(ARG_ITEM)?.let { item ->
displayItemDetails(view, item)
}
//返回
view.findViewById<Button>(R.id.lr_item_details_back).visibility = View.GONE
view.findViewById<Button>(R.id.lr_item_details_finish).visibility = View.GONE
}
override fun onStart() {
super.onStart()
LogUtils.d("LRDetailReplaceFragment onStart() ${this}")
}
override fun onResume() {
super.onResume()
LogUtils.d("LRDetailReplaceFragment onResume() ${this}")
}
override fun onPause() {
super.onPause()
LogUtils.d("LRDetailReplaceFragment onPause() ${this}")
}
override fun onStop() {
super.onStop()
LogUtils.d("LRDetailReplaceFragment onStop() ${this}")
}
override fun onDestroy() {
super.onDestroy()
LogUtils.d("LRDetailReplaceFragment onDestroy() ${this}")
}
private fun displayItemDetails(view: View, LRItem: LRItem) {
view.findViewById<TextView>(R.id.title_text_view).text = LRItem.title
view.findViewById<TextView>(R.id.description_text_view).text = LRItem.description
}
}
package com.example.androidkotlindemo2.pad.leftright
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.example.androidkotlindemo2.MyApp
import com.example.androidkotlindemo2.R
/**
* Author : wn
* Email : maoning20080809@163.com
* Date : 2025/8/30 21:42
* Description :
*/
class LRAdapter(
private val itemList: List<LRItem>,
private val listener: LROnItemClickInter
) : RecyclerView.Adapter<LRAdapter.ItemViewHolder>() {
var selectedPosition = -1
inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val titleTextView: TextView = itemView.findViewById(R.id.title_text_view)
val container: LinearLayout = itemView.findViewById(R.id.item_container)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.lr_fragment_item, parent, false)
return ItemViewHolder(view)
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val lrItem = itemList[position]
holder.titleTextView.text = lrItem.title
holder.container.setOnClickListener {
listener.setOnItemClick(position, lrItem)
selectedPosition = position
notifyDataSetChanged()
}
holder.container.setOnLongClickListener {
listener.setOnLongClickListener(position, lrItem)
true
}
if(selectedPosition == position){
holder.container.setBackgroundColor(ContextCompat.getColor(MyApp.myApp, R.color.lr_item_selected_bg))
} else {
holder.container.setBackgroundColor(ContextCompat.getColor(MyApp.myApp, R.color.lr_item_default_bg))
}
}
override fun getItemCount(): Int = itemList.size
}
package com.example.androidkotlindemo2.pad.leftright
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import com.example.androidkotlindemo2.R
import com.example.androidkotlindemo2.utils.LogUtils
/**
* Author : wn
* Email : maoning20080809@163.com
* Date : 2025/9/6 13:04
* Description : 打开新的页面
*/
class LRNewActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.lr_activity_new)
LogUtils.e("LRNewActivity onViewCreated ----------------------------")
findViewById<Button>(R.id.lr_activity_new_close).setOnClickListener {
finish()
}
}
override fun onCreateDescription(): CharSequence? {
return super.onCreateDescription()
}
override fun onDestroy() {
super.onDestroy()
LogUtils.e("LRNewActivity onDestroy ----------------------------")
}
}
package com.example.androidkotlindemo2.pad.leftright
/**
* Author : wn
* Email : maoning20080809@163.com
* Date : 2025/9/6 9:46
* Description :
*/
interface LROnItemClickInter {
//点击
fun setOnItemClick(position: Int, lrItem: LRItem)
//长按
fun setOnLongClickListener(position: Int, lrItem: LRItem)
}
package com.example.androidkotlindemo2.pad.leftright
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import com.example.androidkotlindemo2.R
import com.example.androidkotlindemo2.utils.LogUtils
/**
* Author : wn
* Email : maoning20080809@163.com
* Date : 2025/9/6 13:04
* Description : 在Activity中显示右边的fragment
*/
class LRRightActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.lr_activity_right)
val lrItem = intent.getParcelableExtra<LRItem>("lr_item") as LRItem
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_right_container, LRDetailReplaceFragment.newInstance(lrItem))
.commit()
}
}
}
package com.example.androidkotlindemo2.pad.leftright
import android.os.Parcel
import android.os.Parcelable
/**
* Author : wn
* Email : maoning20080809@163.com
* Date : 2025/8/30 21:41
* Description :
*/
data class LRItem(
val id: Int,
val title: String,
val description: String,
val imageUrl: String? = null
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readString() ?: "",
parcel.readString() ?: "",
parcel.readString()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
parcel.writeString(title)
parcel.writeString(description)
parcel.writeString(imageUrl)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<LRItem> {
override fun createFromParcel(parcel: Parcel): LRItem {
return LRItem(parcel)
}
override fun newArray(size: Int): Array<LRItem?> {
return arrayOfNulls(size)
}
}
}
lr_fragment_main.xml布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:weightSum="2">
<FrameLayout
android:id="@+id/fragment_list_container"
android:layout_width="300dp"
android:layout_height="match_parent"
/>
<TextView
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@color/blue"/>
<FrameLayout
android:id="@+id/fragment_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
lr_fragment_item_list.xml布局
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp" />
lr_fragment_item_detail.xml布局
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/gray_e5e5e5"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/lr_item_details_finish"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:textSize="30sp"
android:textColor="@color/green"
android:text="关闭finish"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/lr_item_details_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:textSize="30sp"
android:textColor="@color/green"
android:text="返回"/>
<TextView
android:id="@+id/title_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textStyle="bold"
android:layout_marginBottom="16dp" />
<TextView
android:id="@+id/description_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp" />
<TextView
android:id="@+id/description_tip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/red"
android:text="1-3 使用 fragment 的 replace \n 4-6使用 fragment 的add() \n 其他使用打开activity"
android:textSize="26sp" />
</LinearLayout>
</ScrollView>
lr_fragment_item.xml布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/item_container"
android:background="@drawable/lr_click_item_bg"
android:clickable="true"
android:focusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/title_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
lr_activity_new.xml布局
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/gray_e5e5e5"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/lr_activity_new_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="关闭"/>
<TextView
android:id="@+id/title_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textStyle="bold"
android:text="新的Activity"
android:layout_marginBottom="16dp" />
<TextView
android:id="@+id/description_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp" />
</LinearLayout>
</ScrollView>
lr_activity_right.xml布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/gray_e5e5e5"
android:orientation="vertical">
<FrameLayout
android:id="@+id/fragment_right_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
drawable:
lr_click_item_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/lr_item_selected_bg" android:state_selected="true"/>
<item android:drawable="@color/lr_item_selected_bg" android:state_pressed="true"/>
<item android:drawable="@color/lr_item_default_bg"/>
</selector>
<color name="lr_item_default_bg">#FFFFFF</color>
<color name="lr_item_selected_bg">#FF00FF</color>
<activity android:name=".pad.leftright.LRFragmentActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboardHidden|navigation"/>
<activity android:name=".pad.leftright.LRNewActivity"/>
<activity android:name=".pad.leftright.LRRightActivity"/>