赞
踩
提示:此文章仅作为本人记录日常学习使用,若有存在错误或者不严谨得地方欢迎指正。
首先我们在app/build.gradle中导入导入RecyclerView依赖:
implementation 'androidx.recyclerview:recyclerview:1.0.0'
接下来创建新闻的实体类News。这个新闻实体类有两个字段,它们分别是titile标题和content内容:
在主构造函数中声明成val或者var的参数会自动成为该类的字段
新闻标题 新闻内容
class News(val title: String, val content: String) {
}
然后我们需要创建新闻内容的布局,该布局分为上下两个部分。上面用来显示新闻的标题,下面用来显示新闻的正文内容。新闻内容这个布局默认应该是不可见的,因为当用户还没在屏幕左边选中新闻列表中的任何一条新闻时,是不应该在屏幕右边显示该新闻内容的。
news_content_frag.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/myContentLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:visibility="invisible"> <TextView android:id="@+id/myNewsTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:padding="100dp" android:text="新闻标题" android:textSize="20sp" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#000" /> <TextView android:id="@+id/myNewsContent" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:padding="15dp" android:text="新闻正文内容" android:textSize="18sp" /> </LinearLayout> <View android:layout_width="1dp" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:background="#000" /> </RelativeLayout>
上面我们创建了新闻内容的布局文件news_content_frag,接下来需要创建新闻内容的NewsContentFragment ,并让其继承自Fragment。
NewsContentFragment.kt:
class NewsContentFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
//加载新闻内容布局 news_content_frag
return inflater.inflate(R.layout.news_content_frag, container, false)
}
//显示新闻内容
fun refresh(title: String, content: String) {
//取消inVisible 让新闻内容布局显示出来
myContentLayout.visibility = View.VISIBLE
//将新闻标题和内容填充到布局中
myNewsTitle.text = title
myNewsContent.text = content
}
}
之前我们创建了新闻内容的Fragment布局和Fragment类,这些都是为双页模式作准备的。接下来我们还需要为单页模式创建新闻内容的布局和类,单页模式就直接通过Activity实现就可以了:
NewsContentActivity.kt:
class NewsContentActivity : AppCompatActivity() { companion object { fun actionStart(context: Context, title: String, content: String) { val intent = Intent(context, NewsContentActivity::class.java).apply { putExtra("news_title", title) putExtra("news_content", content) } context.startActivity(intent) } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_news_content) val title = intent.getStringExtra("news_title") val content = intent.getStringExtra("news_content") if (title != null && content != null) { //获得NewsContentFragment实例 val fragment = myNewsContentFrag as NewsContentFragment //刷新NewsContentFragment界面 fragment.refresh(title, content) } } }
我们在companion object{ }结构体中创建了一个actionStart()方法,并要求传入title和content参数。该方法用于启动NewsContentActivity并将新闻标题和新闻内容传递给它。这样写的好处是当我们想跳转到当前Activity时必须要传入我们所需要的参数(titile和content)才可以。
在单页模式下点击新闻标题会跳转到新闻内容界面,而双页模式下用户点击左侧的新闻标题会在屏幕右边的空白Fragment中显示新闻内容。这两者用于显示新闻内容的界面其实是一样的,我们直接在这里复用前面的NewsContentFragment就好。我们引用NewsContentFragment就相当于把news_content_frag中的布局内容自动加进来了。
activity_news_content.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">
<!--单页模式——新闻内容页面——引用NewsContentFragment-->
<fragment
android:id="@+id/myNewsContentFrag"
android:name="com.example.fragmentbestpractice.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
新闻列表是用RecyclerView显示新闻标题来实现的,在这里我们添加了id为myNewsTitleRecyclerView的RecyclerView。
news_title_frag.xml:
//RecyclerView列表
<?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="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/myNewsTitleRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
既然有了RecyclerView的布局,那我们还需要创建RecyclerView列表项的布局。
news_item.xml:
//RecyclerView列表子项
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/myNewsTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:paddingLeft="10dp"
android:paddingTop="15dp"
android:paddingRight="10dp"
android:paddingBottom="15dp"
android:text="RecyclerView列表项"
android:textSize="18sp" />
为了展示新闻列表,我们需要创建一个Fragment。
NewsTitleFragment.kt
class NewsTitleFragment : Fragment() { //双页模式标识符 private var isTwoPane = false //新闻标题RecyclerView列表 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.news_title_frag, container, false) } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) //通过判断当前Activity中是否存在一个View的id来判断是否为双页模式 isTwoPane = activity?.findViewById<View>(R.id.myNewsContentLayout) != null } }
在onActivityCreated()方法中我们通过在Activity中能否找到一个id为newsContentLayout的View来判断当前是双页还是单页模式。因此我们需要保证只有在双页模式中才会出现id为newsContentLayout的View。为了实现只有在双页模式中才会出现id为newsContentLayout的View,我们需要借助限定符进行操作。首先修改layout/activity_main.xml中的代码,作为单页模式下的主界面:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context=".MainActivity">
<fragment
android:id="@+id/myNewsTitleFrag"
android:name="com.example.fragmentbestpractice.NewsTitleFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
上面这段代码表示在单页模式下只会显示新闻标题列表的Fragment。然后我们新建layout-600dp文件夹,在该文件夹内新建一个activity_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"> <!--屏幕左侧的NewsTitleFragment用来显示新闻标题--> <fragment android:id="@+id/myNewsTitleFrag" android:name="com.example.fragmentbestpractice.NewsTitleFragment" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> <FrameLayout 标识双页模式的id android:id="@+id/myNewsContentLayout" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3"> <!--屏幕右侧的NewsContentFragment用来显示新闻内容--> <fragment android:id="@+id/myNewsContentFrag" android:name="com.example.fragmentbestpractice.NewsContentFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> </LinearLayout>
在双页模式下我们引入两个Fragment,分别用来显示新闻列表和新闻内容。我们将新闻内容的Fragment放在了一个FrameLayout容器布局下,而这个容器的id正是newsContentLayout。因此能够找到这个id就是双页模式,否则就是单页模式。
我们已经完成大部分工作了,接下来就是通过RecyclerView将新闻列表显示出来。我们在NewsTitleFragment中新建一个内部类NewsAdapter作为RecyclerView的适配器:
class NewsTitleFragment : Fragment() { · · · //RecyclerView的适配器 inner class NewsAdapter(val newsList: List<News>) : RecyclerView.Adapter<NewsAdapter.MyViewHolder>() { //RecyclerView中的ViewHolder用于缓存列表子项控件 inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { val newsTitle: TextView = view.findViewById(R.id.myNewsTitle) } //返回ViewHolder override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { //列表项View val myView = LayoutInflater.from(parent.context).inflate(R.layout.news_item,parent,false) //创建列表项的ViewHolder val myHolder = MyViewHolder(myView) myHolder.itemView.setOnClickListener { //获得点击项的实例 val news = newsList[myHolder.adapterPosition] if(isTwoPane){ /* 如果是双页模式 获取myNewsContentFrag控件实例(myNewsContentFrag用来显示双页模式下的新闻内容) 让myNewsContentFrag控件显示新闻标题和新闻内容*/ val fragment = myNewsContentFrag as NewsContentFragment fragment.refresh(news.title,news.content) }else{ /* 如果是单页模式 跳转到NewsContentActivity并将新闻标题和新闻内容作为参数传入 */ NewsContentActivity.actionStart(parent.context,news.title,news.content) } } return myHolder } //给RecyclerView列表子项赋值 override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val news = newsList[position] holder.newsTitle.text = news.title } //RecyclerView列表长度 override fun getItemCount(): Int { return newsList.size } } }
在onCreateViewHolder()中我们先获取点击项的News实例,然后通过isTwoPane来判断当前是单页模式还是双页模式。如果是单页模式就启动一个新的Activity(NewsContentActivity),如果是双页模式就刷新右边的Fragment让其显示标题和内容。
最后一步就是往RecyclerView中填充数据。我们在NewsTitleFragment中填入以下代码:
lass NewsTitleFragment : Fragment() { · · · override fun onActivityCreated(savedInstanceState: Bundle?) { · · · val myLayoutManager = LinearLayoutManager(activity) myNewsTitleRecyclerView.layoutManager = myLayoutManager val myAdapter = NewsAdapter(getNews()) myNewsTitleRecyclerView.adapter = myAdapter } private fun getNews(): List<News> { val newsList = ArrayList<News>() for (i in 1..50) { val news = News("This is news title $i", getRandomLengthString("This is news content $i. ")) newsList.add(news) } return newsList } private fun getRandomLengthString(str: String): String { val n = (1..20).random() val myBuilder = StringBuilder() repeat(n) { myBuilder.append(str) } return myBuilder.toString() } //RecyclerView的适配器 inner class NewsAdapter(val newsList: List<News>) : RecyclerView.Adapter<NewsAdapter.MyViewHolder>() { · · · } }
双页模式运行效果图:
点击新闻标题后:
单页模式效果图:
点击新闻标题后:
这样我们的程序就可以在手机和平板上运行得到两种不同的效果,这也就意味着我们的程序具有了一定的兼容性。
以下是完整的NewsTitleFragment.kt代码:
class NewsTitleFragment : Fragment() { //双页模式标识符 private var isTwoPane = false //新闻标题RecyclerView override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.news_title_frag, container, false) } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) //通过判断当前Activity中是否存在一个View的id来判断是否为双页模式 isTwoPane = activity?.findViewById<View>(R.id.myNewsContentLayout) != null val myLayoutManager = LinearLayoutManager(activity) myNewsTitleRecyclerView.layoutManager = myLayoutManager val myAdapter = NewsAdapter(getNews()) myNewsTitleRecyclerView.adapter = myAdapter } private fun getNews(): List<News> { val newsList = ArrayList<News>() for (i in 1..50) { val news = News("This is news title $i", getRandomLengthString("This is news content $i. ")) newsList.add(news) } return newsList } private fun getRandomLengthString(str: String): String { val n = (1..20).random() val myBuilder = StringBuilder() repeat(n) { myBuilder.append(str) } return myBuilder.toString() } //RecyclerView的适配器 inner class NewsAdapter(val newsList: List<News>) : RecyclerView.Adapter<NewsAdapter.MyViewHolder>() { //RecyclerView中的ViewHolder用于缓存列表子项控件 inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { val newsTitle: TextView = view.findViewById(R.id.myNewsTitle) } //返回ViewHolder override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { //列表项View val myView = LayoutInflater.from(parent.context).inflate(R.layout.news_item,parent,false) //创建列表项的ViewHolder val myHolder = MyViewHolder(myView) myHolder.itemView.setOnClickListener { val news = newsList[myHolder.adapterPosition] if(isTwoPane){ /* 如果是双页模式 获取myNewsContentFrag控件实例(myNewsContentFrag用来显示双页模式下的新闻内容) 让myNewsContentFrag控件显示新闻标题和新闻内容*/ val fragment = myNewsContentFrag as NewsContentFragment fragment.refresh(news.title,news.content) }else{ /* 如果是单页模式 跳转到NewsContentActivity并将新闻标题和新闻内容作为参数传入 */ NewsContentActivity.actionStart(parent.context,news.title,news.content) } } return myHolder } //给RecyclerView列表子项赋值 override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val news = newsList[position] holder.newsTitle.text = news.title } //RecyclerView列表长度 override fun getItemCount(): Int { return newsList.size } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。