当前位置:   article > 正文

【13】应用开发——简易版新闻应用实战教程_安卓新闻应用开发

安卓新闻应用开发

提示:此文章仅作为本人记录日常学习使用,若有存在错误或者不严谨得地方欢迎指正。

一、简易版新闻应用

1.1 创建实体类

首先我们在app/build.gradle中导入导入RecyclerView依赖:

implementation 'androidx.recyclerview:recyclerview:1.0.0'
  • 1

接下来创建新闻的实体类News。这个新闻实体类有两个字段,它们分别是titile标题和content内容:

在主构造函数声明成val或者var的参数会自动成为该类的字段

               新闻标题             新闻内容
class News(val title: String, val content: String) {
}
  • 1
  • 2
  • 3

1.2 创建新闻内容布局(双页)

然后我们需要创建新闻内容的布局,该布局分为上下两个部分。上面用来显示新闻的标题,下面用来显示新闻的正文内容。新闻内容这个布局默认应该是不可见的,因为当用户还没在屏幕左边选中新闻列表中的任何一条新闻时,是不应该在屏幕右边显示该新闻内容的。
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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

![在这里插入图片描述](https://img-blog.csdnimg.cn/e31e1ce43a174a87889c32d199b80af7.png

1.3 创建新闻内容Fragment(双页)

上面我们创建了新闻内容的布局文件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
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

1.3 创建新闻内容Activity(单页)

之前我们创建了新闻内容的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)
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

我们在companion object{ }结构体中创建了一个actionStart()方法,并要求传入title和content参数。该方法用于启动NewsContentActivity并将新闻标题和新闻内容传递给它。这样写的好处是当我们想跳转到当前Activity时必须要传入我们所需要的参数(titile和content)才可以。

1.4 创建新闻内容布局(单页)

在单页模式下点击新闻标题会跳转到新闻内容界面,而双页模式下用户点击左侧的新闻标题会在屏幕右边的空白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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

android:visibility="invisible"

1.5 创建新闻列表布局

新闻列表是用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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

既然有了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" />

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述

1.6 创建新闻列表Fragment

为了展示新闻列表,我们需要创建一个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
    }
    
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

上面这段代码表示在单页模式下只会显示新闻标题列表的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>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

在双页模式下我们引入两个Fragment,分别用来显示新闻列表和新闻内容。我们将新闻内容的Fragment放在了一个FrameLayout容器布局下,而这个容器的id正是newsContentLayout。因此能够找到这个id就是双页模式,否则就是单页模式。

1.7 创建RecyclerView适配器

我们已经完成大部分工作了,接下来就是通过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
        }

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

在onCreateViewHolder()中我们先获取点击项的News实例,然后通过isTwoPane来判断当前是单页模式还是双页模式。如果是单页模式就启动一个新的Activity(NewsContentActivity),如果是双页模式就刷新右边的Fragment让其显示标题和内容

1.8 往RecyclerView中填充数据

最后一步就是往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>() {
        · · ·
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

双页模式运行效果图:
在这里插入图片描述
点击新闻标题后:
在这里插入图片描述
单页模式效果图:
在这里插入图片描述
点击新闻标题后:
在这里插入图片描述
这样我们的程序就可以在手机和平板上运行得到两种不同的效果,这也就意味着我们的程序具有了一定的兼容性。
以下是完整的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
        }

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号