安卓第一行代码第三版5.5,一个简易版的新闻应用踩坑(Kotlin)

Fragment实践(一个简易版的新闻应用)

  • 前提准备(view binding)
    参考链接
  • 创建FragmentBestPractice工程(build.gradle中添加)
android{
...
  buildFeatures {
        viewBinding true
    }
}

dependencies {
...
implementation 'androidx.recyclerview:recyclerview:1.3.0'
}

  • 新闻内容的布局(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/contentLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:visibility="invisible">

        <TextView
            android:id="@+id/newsTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="10dp"
            android:textSize="20sp" />

        <TextView
            android:id="@+id/newsContent"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:padding="15dp"
            android:textSize="18sp" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@color/black" />
    </LinearLayout>

    <View
        android:layout_width="1dp"
        android:layout_height="match_parent"
        android:layout_alignParentStart="true"
        android:background="@color/black" />
</RelativeLayout>

<!--新闻布局主要分为两部分,其中右边部分的标题和正文通过用view标签实现的细线分隔开,
整体的左边部分的细线是为了在双页模式时将左侧的新闻列表和新闻内容分隔开-->

<!--此外,要将新闻内容的布局通过 android:visibility="invisible" 设置为不可见,因为在双页模式下,
如果没有选中新闻列表中的任何一条新闻,就不应该显示新闻内容的布局-->
  • 新闻实体类(News)
//title表示新闻标题,content表示新闻内容
class News(val title:String,val content: String) {
}

  • 新闻内容的Fragment(NewsContentFragment)
class NewsContentFragment : Fragment() {
    private var binding :NewsContentFragBinding ?=null
    private val binding2 get()=binding!!
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        binding= NewsContentFragBinding.inflate(inflater,container,false)
        return binding2.root
    }
//通过refresh方法,将新闻内容显示到布局上
    fun refresh(title:String,content:String){
        binding2.contentLayout.visibility=View.VISIBLE //需要将刚才设置成隐藏的显示出来
        binding2.newsTitle.text=title //刷新新闻的标题
        binding2.newsContent.text=content //刷新新闻内容
    }
}
  • activity的新闻内容布局(activity_news_content)
<?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">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/newsContentFrag"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.example.fragmentbestpractice.NewsContentFragment"
        tools:layout="@layout/news_content_frag" />


</LinearLayout>
  • activity的新闻内容类(NewsContentFragment)
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)
        }
    }

    //编写actionStart方法的好处就是可以清晰知道需要传递的数据,
    //以及简化了启动方法直接通过NewsContentActivity.actionStart()来启动即可
    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) {
           val fragment =
                supportFragmentManager.findFragmentById(R.id.newsContentFrag) as NewsContentFragment
            fragment.refresh(title,content)
        }
    }
}
  • 新闻列表布局(news_title_frag.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">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/newsTitleRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    
</LinearLayout>
  • 新闻列表布局的子布局(news_item.xml)
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/newsTitle"
    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"
    />

<!--android:padding表示给控件的周围加上补白
    android:maxLines设置为 1 表示这个TextView只能单行
    android:ellipsize用于设定当文本内容超过文本宽度时文本的缩略方式 end 表示在尾部进行省略
-->
  • 新闻列表的Fragment类(NewsTitleFragment)
@Suppress("DEPRECATION")
class NewsTitleFragment : Fragment() {
    private var isTwoPane = false
    private var binding: NewsTitleFragBinding? = null
    private val binding2 get() = binding!!
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        binding = NewsTitleFragBinding.inflate(inflater, container, false)
        return binding2.root
    }

    @Deprecated("Deprecated in Java")
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        isTwoPane = activity?.findViewById<View>(R.id.newsContentLayout) != null
        val layoutManager=LinearLayoutManager(activity)
        binding2.newsTitleRecyclerView.layoutManager=layoutManager
        val adapter=NewsAdapter(getNews())
        binding2.newsTitleRecyclerView.adapter=adapter
    }


    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 builder=StringBuilder()
        repeat(n){
            builder.append(str)
        }
        return builder.toString()
    }

    inner class NewsAdapter(val newsList: List<News>) :
        RecyclerView.Adapter<NewsAdapter.ViewHolder>() {

        inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
            val newsTitle: TextView = view.findViewById(R.id.newsTitle)
        }

        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)
            holder.itemView.setOnClickListener {
                val news = newsList[holder.adapterPosition]
                if (isTwoPane) {
                    val fragment =
                        fragmentManager?.findFragmentById(R.id.newsContentFrag) as NewsContentFragment
                    fragment.refresh(news.title, news.content)
                } else {
                    println("---执行前---")
                    NewsContentActivity.actionStart(parent.context, news.title, news.content)
                    println("---执行后---")
                }
            }
            return holder
        }

        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            val news = newsList[position]
            holder.newsTitle.text = news.title
        }

        override fun getItemCount() = newsList.size
    }


}
  • 手机模式下的主布局(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:id="@+id/newsTitleLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/newsTitleFrag"
        android:name="com.example.fragmentbestpractice.NewsTitleFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:layout="@layout/news_title_frag" />
</FrameLayout>
  • 平板模式下的主布局(新建layout-sw600dp下的activity_main.xml)
<?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:id="@+id/newsTitleLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/newsTitleFrag"
        android:name="com.example.fragmentbestpractice.NewsTitleFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        tools:layout="@layout/news_title_frag" />

    <FrameLayout
        android:id="@+id/newsContentLayout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3">

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/newsContentFrag"
            android:name="com.example.fragmentbestpractice.NewsContentFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:layout="@layout/news_content_frag" />

    </FrameLayout>

</LinearLayout>

首先会报错Configurations for activity_main.xml must agree on the root element

参考大佬链接

上面的问题解决后在手机模式下会报空指针异常,平板模式下没有问题

在这里插入图片描述

原因并未找到,也希望各位大佬知道原因后评论区告知小弟

解决方法(仅实现功能)

  • 最后我采用的方法是将activity_news_content.xml这个布局直接移除,可以看到里面的代码采用的就是news_content_frag.xml的代码
  • 并修改了NewsContentActivity中的代码如下
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)
        //在此直接显示news_content_frag布局
        val binding=NewsContentFragBinding.inflate(layoutInflater)
         setContentView(binding.root)
        val title = intent.getStringExtra("news_title")
        val content = intent.getStringExtra("news_content")
        if (title != null && content != null) {
            //在此直接修改了布局中的内容,不再通过调用fragment中的refresh方法
            binding.contentLayout.visibility=View.VISIBLE
            binding.newsTitle.text=title
            binding.newsContent.text=content
        }
    }
}

最后(找到错误)

  • 关于这个小demo,平板模式下显示新闻内容,是直接在主activity布局中通过刷新新闻内容的fragment来实现内容的变更,而在手机模式下是通过intent切换到一个新的activity后再根据其布局中的fragment来实现内容的变更,大致思路其实是一致的,实在是想不明白是哪里空指针了
  • 最终解决发现的问题还是出在了activity_news_content.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"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".NewsContentActivity"
    >

    <!--<androidx.fragment.app.FragmentContainerView
        android:id="@+id/newsContentFrag"
        android:name="com.example.fragmentbestpractice.NewsContentFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:layout="@layout/news_content_frag" />-->

    <fragment
        android:id="@+id/newsContentFrag"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.example.fragmentbestpractice.NewsContentFragment"/>
    
</LinearLayout>
<!--书上使用的标签是fragment,但是编译器中使用此标签会报错,根据编译器的提示最终改为了以上写法,
修改过来即可解决报错问题-->
  • 最后提醒两点,如果是通过class直接新建的activity别忘记在AndroidManifest.xml中注册!!!不要自作主张修改书上的代码!!!
  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
抱歉,作为AI语言模型,我无法提供完整的安卓应用程序。下面是一个简单的示例,可用于开始编写新闻应用程序: 1. 在Android Studio中创建一个新项目,并选择Kotlin作为主要语言。 2. 在项目中创建一个RecyclerView,用于显示新闻列表。 3. 创建一个新闻数据模型类,包括标题、描述、图片等信息。例如: ``` data class News(val title: String, val description: String, val image: Int) ``` 4. 创建一个新闻适配器类,用于将新闻数据绑定到RecyclerView中的项。例如: ``` class NewsAdapter(private val newsList: List<News>) : RecyclerView.Adapter<NewsAdapter.ViewHolder>() { class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val titleTextView: TextView = view.findViewById(R.id.title_text_view) val descriptionTextView: TextView = view.findViewById(R.id.description_text_view) val imageView: ImageView = view.findViewById(R.id.image_view) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.news_item, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val news = newsList[position] holder.titleTextView.text = news.title holder.descriptionTextView.text = news.description holder.imageView.setImageResource(news.image) } override fun getItemCount() = newsList.size } ``` 5. 在MainActivity中初始化新闻数据,并将其传递给适配器。例如: ``` class MainActivity : AppCompatActivity() { private lateinit var newsRecyclerView: RecyclerView private lateinit var newsAdapter: NewsAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) newsRecyclerView = findViewById(R.id.news_recycler_view) newsAdapter = NewsAdapter(getNewsList()) newsRecyclerView.adapter = newsAdapter newsRecyclerView.layoutManager = LinearLayoutManager(this) } private fun getNewsList(): List<News> { return listOf( News("新闻标题1", "新闻描述1", R.drawable.news_image1), News("新闻标题2", "新闻描述2", R.drawable.news_image2), News("新闻标题3", "新闻描述3", R.drawable.news_image3), // 添加更多新闻... ) } } ``` 6. 在布局文件中添加RecyclerView和新闻项布局。例如: ``` <androidx.recyclerview.widget.RecyclerView android:id="@+id/news_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"/> <LinearLayout android:id="@+id/news_item" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:id="@+id/image_view" android:layout_width="match_parent" android:layout_height="200dp" android:scaleType="centerCrop"/> <TextView android:id="@+id/title_text_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="18sp" android:textStyle="bold" android:padding="8dp"/> <TextView android:id="@+id/description_text_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="14sp" android:padding="8dp"/> </LinearLayout> ``` 这只是一个简单的示例,你可以根据自己的需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值