拾起碎片,拼凑简单的活动。
简易版的新闻应用
1.第一步我们要先准备好一个新闻的实体类,新建类 News。
2.新建一个 news_item.xml 布局,用于作为新闻列表中子项的布局。
3.接下来需要创建新闻列表的适配器NewsAdapter。
4.新建布局文件 news_content_frag.xml用于新闻内容部分。
5.然后再新建一个 NewsContentFragment 类,继承自 Fragment
6.在 onCreateView()方法里加载了我们刚刚创建的 news_content_frag 布局
7.提供了一个 refresh()方法,这个方法就是用于将新闻的标题和内容显示在界面上。
8.接着要创建一个在活动中使用的新闻内容布局,新建 news_content.xml
9.然后新建 NewsContentActivity,作为显示新闻内容的活动
10.接下来还需要再创建一个用于显示新闻列表的布局,新建 news_title_frag.xml,里面只有一个 RecyclerView(ListView)。
11.创建一个碎片来加载news_title_frag.xml这个布局。新建一个 NewsTitleFragment 类,继承自 Fragment
12.修改 activity_main.xml 中的代码(只显示一各显示title的Fragment),新建 layout-sw600dp文件夹,在这个文件夹下再新建一个 activity_main.xml 文件。(显示标题列表和内容Fragment)
News,实体类
Kotlin
class News(
var title: String ,
var content: String
)
news_item.xml,Recycleview列表项的子项布局,用于写每一个列表项的布局,记得每次用适配器在gradle写一下依赖配置,不记得可以看上一个实验。
Kotlin
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/news_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:textSize="18sp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="15dp"
android:paddingBottom="15dp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#90CAF9" />
</LinearLayout>
news_content_frag.xml,双页模式的内容布局,即平板页面的一边是新闻列表,另一边展示内容。
Kotlin
<?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"
android:background="#90CAF9">
<LinearLayout
android:id="@+id/visibility_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="invisible">
<TextView
android:id="@+id/news_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:textSize="20sp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000"/>
<TextView
android:id="@+id/news_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="15dp"
android:textSize="18sp"/>
<!-- android:textSize="50sp"-->
<!-- android:gravity="center"-->
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentLeft="true"
android:background="#90CAF9"/>
</RelativeLayout>
NewsContentFragment,对应双页模式的类,用了fragment的onCreateView方法创建视图与refresh方法进行刷新。当然你也可以用视图绑定viewbiding就不用inflater.inflate了。
Kotlin
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
class NewsContentFragment : Fragment() {
// 重写 onCreateView 方法,用于创建 Fragment 的视图
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 返回解析的视图
return inflater.inflate(R.layout.news_content_frag, container, false)
}
fun refresh(newsTitle: String, newsContent: String) {
val currentView = view ?: return // 如果 view 是 null,直接返回
val visibilityLayout = currentView.findViewById<View>(R.id.visibility_layout)
visibilityLayout.visibility = View.VISIBLE // 将新闻布局设置成可见
val newsTitleText = currentView.findViewById<TextView>(R.id.news_title)
val newsContentText = currentView.findViewById<TextView>(R.id.news_content)
newsTitleText.text = newsTitle // 刷新新闻的标题
newsContentText.text = newsContent // 刷新新闻的内容
}
}
news_content.xml,单页模式的内容布局,手机端屏幕尺寸相对平板较小,在界面设计上直接套用显然是不够美观的,因此可以编写一个单页模式,当点击列表项时跳转到具体的内容。这里直接用了fragment标签,体现了复用。注意一下,android:name这里可能会报错,要改成你的包名找到所需要的类。
Kotlin
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/news_content_fragment"
android:name="com.example.sy6.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/news_title_frag"/>
</LinearLayout>
NewsContentActivity,对应单页模式的活动,然后传递需要的属性,编写相应的方法。
Kotlin
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
class NewsContentActivity : AppCompatActivity() {
companion object {
//启动 NewsContentActivity
// context: 当前上下文,通常是 Activity 或 Application。
fun actionStart(context: Context, newsTitle: String, newsContent: String) {
//Intent intent = new Intent(context, NewsContentActivity.class);
val intent = Intent(context, NewsContentActivity::class.java).apply {
putExtra("news_title", newsTitle)
putExtra("news_content", newsContent)
}
context.startActivity(intent)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.news_content)
val newsTitle = intent.getStringExtra("news_title") ?: ""
val newsContent = intent.getStringExtra("news_content") ?: ""
val newsContentFragment = supportFragmentManager
.findFragmentById(R.id.news_content_fragment) as? NewsContentFragment
newsContentFragment?.refresh(newsTitle, newsContent) // 刷新 NewsContentFragment 界面
}
}
news_title_frag.xml,列表的整体布局,包含一个recycleview。
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFF4E1">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/news_title_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
NewsTitleFragment,对应news_title_frag,onCreateView是碎片的方法。同时编写内部类NewsAdapter进行适配器的绑定,然后就是viewholder的获取与layoutmanager的绑定,这些是recycleview的基本步骤。而getNews与getRandomLengthContent则是列表内容的获取。
Kotlin
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import java.util.*
/**
* 展示新闻列表
*/
class NewsTitleFragment : Fragment() {
private var isTwopane: Boolean = false // 判断是否显示双页
private lateinit var newsTitleRecyclerView: RecyclerView // RecyclerView声明
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.news_title_frag, container, false)
newsTitleRecyclerView = view.findViewById(R.id.news_title_recycler_view) // 初始化RecyclerView
val layoutManager = LinearLayoutManager(activity) // 设置布局管理器
newsTitleRecyclerView.layoutManager = layoutManager
val adapter = NewsAdapter(getNews()) // 设置适配器
newsTitleRecyclerView.adapter = adapter
return view
}
private fun getNews(): List<News> { // 获取新闻列表
val newsList = ArrayList<News>()
for (i in 1..50) {
val news = News("This is news title $i",getRandomLengthContent("This is news content $i."));
//val news = News("新闻标题","新闻详情");
newsList.add(news)
}
return newsList
}
private fun getRandomLengthContent(content: String): String { // 生成随机长度内容
val random = Random()
val n= random.nextInt(20) + 1
val builder = StringBuilder()
repeat(n){
builder.append(content)
}
return builder.toString()
}
override fun onActivityCreated( savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
isTwopane = activity?.findViewById<View>(R.id.news_content_layout) != null
}
inner class NewsAdapter(private val newsList: List<News>) : RecyclerView.Adapter<NewsAdapter.ViewHolder>() {
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val newsTitleText: TextView = view.findViewById(R.id.news_title) // 初始化新闻标题TextView
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.news_item, parent, false) // 加载子项布局
val holder = ViewHolder(view) // 将加载的布局传入到ViewHolder类构造函数中
view.setOnClickListener { // 点击事件
val news = newsList[holder.adapterPosition] // 获取当前项的News实例
if (isTwopane) { // 如果是双页模式,则刷新NewsContentFragment的内容
val newsContentFragment = fragmentManager?.findFragmentById(R.id.news_content_fragment) as? NewsContentFragment
newsContentFragment?.refresh(news.title, news.content)
} else { // 如果是单页模式,直接启动NewsContentActivity
NewsContentActivity.actionStart(parent.context, news.title, news.content)
}
}
return holder
}
override fun onBindViewHolder( holder: ViewHolder, position: Int) { // 对RecyclerView子项的数据进行赋值
val news = newsList[position] // 通过position得到当前项的News实例
holder.newsTitleText.text = news.title // 在将数据设置到ViewHolder的newsTitleText
}
override fun getItemCount(): Int { // 返回数据源长度
return newsList.size
}
}
}
activity_main.xml,单个手机页面,引入新闻标题碎片即可。
XML
<?xml version="1.0" encoding="utf-8"?>
<!--在单页模式下,只显示一个新闻标题碎片-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<fragment
android:id="@+id/news_title_fragment"
android:name="com.example.sy6.NewsTitleFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
新建 layout-sw600dp文件夹,在这个文件夹下再新建一个 activity_main.xml 文件,当屏幕尺寸宽度比600大时,用于展示平板页面,引入新闻标题碎片与内容碎片。
Kotlin
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/news_title_fragment"
android:name="com.example.sy6.NewsTitleFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<FrameLayout
android:id="@+id/news_content_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3">
<fragment
android:id="@+id/news_content_fragment"
android:name="com.example.sy6.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
</LinearLayout>
MainActivity不变,setContentView指向的是主活动,andriod studio会根据屏幕大小选以上两个页面的一个进行展现。
Kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
一个简单的新闻应用就写完了,手机端与屏幕端都适用。在这里也可以看出activity与fragment的区别,activity调用的是 onCreate方法,而fragment调用的是onCreateView,然后activity是可以包含多个fragment的,可以类似于前端页面包含多个div进行理解。
注:直接拿的cv小白可交这份。
会改的可交这份,当然,也可以再进行页面设计滴,不会很难。
实验心得
也许,吾真的很烦人哎。