【移动设备交互应用】我的头条——仿安卓新闻APP制作

链接:https://pan.baidu.com/s/1eW_2TR2xt-LjOA6VD61gfA 提取码:badm

目录

  • 前言
  • 一、实验目的与内容
    • 目的
    • 实验要求
    • 注意
  • 二、实验过程和代码
    • 标题栏设计
      • 标题栏UI布局分析
      • 标题栏UI布局代码构建
      • 主界面UI布局构建
    • 标题栏RecyclerView代码编写
      • 标题栏RecyclerView布局代码
      • 标题栏RecyclerView Kotlin代码
    • 新闻栏设计
      • 新闻栏UI布局分析
      • 新闻界面UI布局构建
    • 新闻栏RecyclerView代码编写
      • 新闻栏RecyclerView布局代码
      • 新闻栏RecyclerView Kotlin代码
    • 动态效果设计
      • 新闻界面切换
      • 新闻阅览界面切换
  • 三、出现的关键问题及解决方案
  • 四、实验总结


前言

本文记录了编写《移动设备交互应用》实验报告2——“我的头条” 的全过程,主要参考书籍为《第一行代码Android 第三版》,开发环境为Android Studio 4.1,开发语言为Kotlin。


一、实验目的与内容

目的

掌握安卓中活动的编写、自定义用户界面的开发、能使用HTTP协议访问网络;并能通过自学能适当完善该APP界面,并使界面尽量美观。

实验要求

  1. 请尽量模拟如下APP界面的功能,参考:
    https://play.google.com/store/apps/details?id=mark.h.my_news_app&hl=en_US
    在这里插入图片描述

  2. 该实现的界面应至少包含3个菜单,分别展示个人3个方面的信息,菜单之间要表现出一定的差异性;每个菜单可以包含2-5个条目,每个条目能响应个人某方面的偏好信息;此外,如果是响应网页,需同时体现出a) 采用浏览器浏览 与 b) 下载到本地 两种技术方案。

  3. 尽量多的应用参考书《第一行代码 Android》第二版第2章(活动)、第3章(UI开发)与第9章(网络技术)的各个知识点。

注意

  1. 实验报告中需要有功能的描述、实验结果的截屏图像及详细说明
  2. 该实验报告的所需的部分内容需要自学(如第9章)
  3. 也欢迎采用其它章节的知识点完成本次实验报告,如果实现的功能言之合理,会考虑酌情加分。

二、实验过程和代码

标题栏设计

标题栏UI布局分析

在这里插入图片描述

  • 上半部分左侧Button控件控制侧菜单栏,右侧是TextView控件显示软件标题
  • 下半部分为一个横向的RecyclerView,每一个组件由ImageView与TextView组成

标题栏UI布局代码构建

  • 创建drawable–xxhdpi文件夹,用于存放图片资源,利用自动匹配机制去选择对应的布局和图片资源(本项目矢量图来自阿里巴巴矢量图标库https://www.iconfont.cn/)
  • 在layout文件夹中,创建title.xml,编写布局代码
  • 标题栏为线性布局,背景颜色设置为浅蓝色,完成整体设计

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#17d9ff"android:orientation="vertical"><--上下部分代码-->LinearLayout>
  • 上半部分
 <LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:paddingBottom="10dp"><Buttonandroid:layout_width="30dp"android:layout_height="30dp"android:id="@+id/sideMenuButton"android:layout_marginStart="15dp"android:layout_marginTop="8dp"android:layout_marginBottom="8dp"android:layout_marginEnd="30dp"android:background="@drawable/sidemenu"/><TextViewandroid:id="@+id/appTitle"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_gravity="left"android:text="@string/my_top_news"android:textSize="30sp"android:textColor="#ffffff"tools:ignore="RtlHardcoded"android:typeface="serif"/>LinearLayout><TextViewandroid:id="@+id/appTitle"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_gravity="left"android:text="@string/my_top_news"android:textSize="30sp"android:textColor="#ffffff"tools:ignore="RtlHardcoded"android:typeface="serif"/>LinearLayout>
  • 下半部分
  • 使用RecyclerView,由于属于新增控件,Google将RecyclerView定义在AndroidX中,需要在项目的build.gradle中添加RecyclerView依赖,保证在所有Android系统版本上都可以使用RecyclerView控件。
  • 打开app/build.gradle文件,在dependencies闭包中添加以下内容
dependencies {implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"...implementation 'androidx.recyclerview:recyclerview:1.1.0'...androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
  • 打开title.xml,编写布局代码
<androidx.recyclerview.widget.RecyclerViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/recyclerViewForTitle"/>
  • 最终效果图
    在这里插入图片描述

主界面UI布局构建

  • 因为标题栏在所有的界面都需要展示,所以利用整体引入部分,简化代码
  • 主界面可以自行设计内容,此处为利用相对布局,居中展示个人信息
  • 打开activity_main.xml文件,编写布局代码

<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=".MainActivity"><include layout="@layout/title"/><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:id="@+id/name"android:textSize="30sp"android:text="姓名:沈晨玙"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_above="@id/name"android:layout_centerInParent="true"android:textSize="30sp"android:text="班级:2019计科国际班"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/name"android:layout_centerInParent="true"android:textSize="30sp"android:text="学号:2019092121"/>RelativeLayout>LinearLayout>
  • 最终效果图
    在这里插入图片描述

标题栏RecyclerView代码编写

  • RecyclerView基本组成:
  1. 标题类:TitleMenu.kt
  2. 适配器:TitleMenuAdapter.kt
  3. 子项布局:titlemenu_item.xml

标题栏RecyclerView布局代码

  • 创建子项布局:titlemenu_item.xml
  • 线性布局,由ImageView与TextView组成。因为是RecyclerView要完成水平排列,item要改成垂直排列,所以LinearLayout改成垂直方向排列,并使用layout_marginTop让文字与图片保持一定距离。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="90dp"android:orientation="vertical"android:layout_height="wrap_content"><ImageViewandroid:layout_width="40dp"android:layout_height="40dp"android:id="@+id/titleMenuImage"android:layout_gravity="center_horizontal"android:layout_marginTop="10dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/titleMenuName"android:layout_gravity="center_horizontal"android:layout_marginTop="10dp"/>LinearLayout>
  • 最终效果图
    在这里插入图片描述

标题栏RecyclerView Kotlin代码

  1. 标题类:TitleMenu.kt
  • 创建TitleMenu.kt
  • 编写TitleMenu类的具体内容,包含name与imageID,代表栏目名与图片
package com.example.experiment2
class TitleMenu(val name: String, val imageID: Int)
  1. 适配器:TitleMenuAdapter.kt
  • 创建TitleMenuAdapter.kt,传入参数为标题栏项目列表titleMenuList: List
class TitleMenuAdapter(val titleMenuList: List<TitleMenu>) :
RecyclerView.Adapter<TitleMenuAdapter.ViewHolder>() {
......
}
  • ViewHolder(view: View) : RecyclerView.ViewHolder(view)
    定义一个内部类ViewHolder,他要继承自RecyclerView.ViewHolder。然后ViewHolder的主构造函数中要传入一个View参数,这个参数通常就是RecyclerView子项的最外层布局,那么就可以通过findViewById()方法来获取布局中ImageView和TextView的实例了。
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {val titleMenuImage: ImageView = view.findViewById(R.id.titleMenuImage)val titleMenuName: TextView = view.findViewById(R.id.titleMenuName)}
  • onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder
    创建ViewHolder实例,将布局加载进来,然后创建一个ViewHolder实例,并把加载出来的布局传入构造函数,最后将ViewHolder的实例返回。
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val view =LayoutInflater.from(parent.context).inflate(R.layout.titlemenu_item, parent, false)//点击事件val viewHolder = ViewHolder(view)return viewHolder}
  • onBindViewHolder(holder: ViewHolder, position: Int)
    对RecyclerView子项的数据进行赋值,会在每个子项将滚到屏幕内的时候执行,通过position参数得到当前项的实例,然后将数据设置到ViewHolder的ImageView和TextView当中即可。
override fun onBindViewHolder(holder: ViewHolder, position: Int) {val titleMenu = titleMenuList[position]holder.titleMenuImage.setImageResource(titleMenu.imageID)holder.titleMenuName.text = titleMenu.name}
  • getItemCount()
    告诉RecyclerView一共有多少子项,返回数据源的长度。
 override fun getItemCount() = titleMenuList.size
  1. 主活动:MainActivity.kt
    因为有自定义的标题栏,所以隐藏程序默认的标题栏。在onCreate方法中加入如下函数。
supportActionBar?.hide()

使用initTitleMenu()方法,用于初始化所有标题数据。接着在onCreate()方法中创建了一个LinearLayoutManager用于指定RecylerView的布局方式,调用LinearLayoutManager的setOrientation()方法设置布局的排列方向,默认是纵向排列,传入LinearLayoutManager.HORIZONTAL,表示让布局横行排列,这样就可以横向滚动了。然后创建TitleMenuAdapter的实例,并将标题数据传入TitleMenuAdapter的构造函数中,最后调用RecyclerView的setAdapter方法来完成适配器设置,这样RecyclerView和数据之间的关联就建立完成了。

class MainActivity : AppCompatActivity() {private val titleMenu = ArrayList<TitleMenu>()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)
supportActionBar?.hide()initTitleMenu()val layoutManager = LinearLayoutManager(this)val recyclerView: RecyclerView = findViewById(R.id.recyclerViewForTitle)layoutManager.orientation=LinearLayoutManager.HORIZONTALrecyclerView.layoutManager = layoutManagerval adapter = TitleMenuAdapter(titleMenu)recyclerView.adapter = adapter}private fun initTitleMenu() {titleMenu.add(TitleMenu("Business", R.drawable.business))titleMenu.add(TitleMenu("Entertainment", R.drawable.entertainment))titleMenu.add(TitleMenu("Health", R.drawable.health))titleMenu.add(TitleMenu("Science", R.drawable.science))titleMenu.add(TitleMenu("Sport", R.drawable.sport))titleMenu.add(TitleMenu("Game", R.drawable.game))}
}
  • 最终效果图:
    在这里插入图片描述

新闻栏设计

在这里插入图片描述

新闻栏UI布局分析

  • 新闻栏整体为一个垂直的RecyclerView
  • 每一个组件由左侧的ImageView与右侧的线性布局组成,其中右侧又由三个TextView组成,其中分为线性与相对布局。最下方为一个View组件,颜色为纯灰色,用于分割新闻。

新闻界面UI布局构建

  • 因为标题栏在所有的界面都需要展示,所以利用整体引入部分,简化代码
  • 创建新活动,以business界面为例,New->Activity->Empty Activity,创建Business.kt与activity_business.xml文件,首先打开activity_business.xml,编写布局代码。(其他界面同原理)

<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:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".Business"><include layout="@layout/title"/><androidx.recyclerview.widget.RecyclerViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:id="@+id/recyclerViewForBusiness"/>LinearLayout>
  • 最终效果图:
    在这里插入图片描述

新闻栏RecyclerView代码编写

  • RecyclerView基本组成:
    新闻类:vertical_news.kt和flow_news.kt
    适配器:vertical_newsAdapter.kt和flow_newsAdapter.kt
    子项布局:vertical_news_item.xml和flow_news_item.xml

新闻栏RecyclerView布局代码

  • 在layout文件夹中,创建vertical_news_item.xml,编写垂直布局代码
  • 每一个组件由左侧的ImageView与右侧的线性布局组成,其中右侧又由三个TextView组成,其中分为线性与相对布局。相对布局中利用alignParentEnd,alignParentStart,alignParentBottom属性确定左下角右下角属性。最下方为一个View组件,颜色为纯灰色,用于分割新闻。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="102dp"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="100dp"><ImageViewandroid:id="@+id/verticalNewsImage"android:layout_width="130dp"android:layout_height="100dp"android:layout_marginLeft="10dp" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="100dp"android:orientation="vertical"><TextViewandroid:id="@+id/verticalNewsTitle"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="top"android:fontFamily="sans-serif-condensed"android:gravity="left"android:padding="5dp"android:textSize="16sp"tools:ignore="RtlHardcoded " /><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal"><TextViewandroid:id="@+id/verticalNewsURL"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_alignParentStart="true"android:padding="5dp"android:textSize="8sp"/><TextViewandroid:id="@+id/verticalNewsTime"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentEnd="true"android:layout_alignParentBottom="true"android:textSize="8sp"android:padding="5dp"/>RelativeLayout>LinearLayout>LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:background="#A3A0A0" />LinearLayout>
  • 最终效果图:
    在这里插入图片描述
  • 在layout文件夹中,创建flow_news_item.xml,编写瀑布布局代码。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="5dp"android:orientation="vertical"><ImageViewandroid:layout_width="130dp"android:layout_height="100dp"android:id="@+id/flowNewsImage"android:layout_gravity="center_horizontal"android:layout_marginTop="10dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="left"android:id="@+id/flowNewsTitle"android:layout_marginTop="10dp"/>LinearLayout>
  • 最终效果图:
  • 在这里插入图片描述

新闻栏RecyclerView Kotlin代码

  • 大致与标题栏RecyclerView代码功能一致,所以基本以贴代码为主。
  • 新闻类:vertical_news.kt和flow_news.kt
    创建vertical_news.kt和flow_news.kt
package com.example.experiment2class vertical_news(val imageID: Int,val newsTitle: String,val newsURL: String,val newsTime: String,val URL: String,val content: String
)
package com.example.experiment2class flow_news(val imageID: Int,val newsTitle: String,val newsURL: String,val newsTime: String,val URL: String,val content: String
)
  • 适配器:vertical_newsAdapter.kt和flow_newsAdapter.kt
package com.example.experiment2import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerViewclass vertical_newsAdapter(val vertical_newsList: List<vertical_news>) :RecyclerView.Adapter<vertical_newsAdapter.ViewHolder>() {inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {val newsImage: ImageView = view.findViewById(R.id.verticalNewsImage)val newsTitle: TextView = view.findViewById(R.id.verticalNewsTitle)val newsURL: TextView = view.findViewById(R.id.verticalNewsURL)val newsTime: TextView = view.findViewById(R.id.verticalNewsTime)}inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {val newsImage: ImageView = view.findViewById(R.id.verticalNewsImage)val newsTitle: TextView = view.findViewById(R.id.verticalNewsTitle)val newsURL: TextView = view.findViewById(R.id.verticalNewsURL)val newsTime: TextView = view.findViewById(R.id.verticalNewsTime)}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val view = LayoutInflater.from(parent.context).inflate(R.layout.vertical_news_item, parent, false)return viewHolder}override fun onBindViewHolder(holder: ViewHolder, position: Int) {val verticle_news = vertical_newsList[position]holder.newsImage.setImageResource(verticle_news.imageID)holder.newsTitle.text = verticle_news.newsTitleholder.newsTime.text = verticle_news.newsTimeholder.newsURL.text = verticle_news.newsURL}override fun getItemCount() = vertical_newsList.size}

创建flow_newsAdapter.kt

package com.example.experiment2import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerViewclass flow_newsAdapter(val flow_newsList: List<flow_news>) :RecyclerView.Adapter<flow_newsAdapter.ViewHolder>() {inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {val newsImage: ImageView = view.findViewById(R.id.flowNewsImage)val newsTitle: TextView = view.findViewById(R.id.flowNewsTitle)}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val view = LayoutInflater.from(parent.context).inflate(R.layout.flow_news_item, parent, false)val viewHolder = ViewHolder(view)       return viewHolder}override fun onBindViewHolder(holder: ViewHolder, position: Int) {val flow_news = flow_newsList[position]holder.newsImage.setImageResource(flow_news.imageID)holder.newsTitle.text = flow_news.newsTitle}override fun getItemCount() = flow_newsList.size}
  • 新闻界面活动(Business作为垂直布局例子,Game作为瀑布布局例子)
  • 编写Business.kt
  • 因为有自定义的标题栏,所以隐藏程序默认的标题栏,并引入标题栏RecyclerView Kotlin代码。在onCreate()方法中创建了一个LinearLayoutManager用于指定新闻栏RecylerView的布局方式,默认是纵向排列。然后创建TitleMenuAdapter的实例,并将标题数据传入TitleMenuAdapter的构造函数中,最后调用RecyclerView的setAdapter方法来完成适配器设置,这样RecyclerView和数据之间的关联就建立完成了。
package com.example.experiment2import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerViewclass Business : AppCompatActivity() {private val verticalNews = ArrayList<vertical_news>()private val titleMenu = ArrayList<TitleMenu>()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_business)supportActionBar?.hide()//菜单栏配置initTitleMenu()val layoutManager1 = LinearLayoutManager(this)val recyclerView1: RecyclerView = findViewById(R.id.recyclerViewForTitle)layoutManager1.orientation = LinearLayoutManager.HORIZONTALrecyclerView1.layoutManager = layoutManager1val adapter1 = TitleMenuAdapter(titleMenu)recyclerView1.adapter = adapter1//新闻栏配置initVerticalNewsMenu()val layoutManager = LinearLayoutManager(this)val recyclerView: RecyclerView = findViewById(R.id.recyclerViewForBusiness)recyclerView.layoutManager = layoutManagerval adapter = vertical_newsAdapter(verticalNews)recyclerView.adapter = adapter}private fun initVerticalNewsMenu() {repeat(3){verticalNews.add(vertical_news(R.drawable.businessnew1,"富士康的两难境地:美国工厂难产 中国市场竞争压力剧增","finance.sina.com.cn","2020年10月24日 07:13","https://finance.sina.com.cn/chanjing/gsnews/2020-10-24/doc-iiznezxr7762539.shtml","  曾经被美国总统特朗普誉....."))verticalNews.add(...)verticalNews.add(...)}}private fun initTitleMenu() {titleMenu.add(TitleMenu("Business", R.drawable.business))titleMenu.add(TitleMenu("Entertainment", R.drawable.entertainment))titleMenu.add(TitleMenu("Health", R.drawable.health))titleMenu.add(TitleMenu("Science", R.drawable.science))titleMenu.add(TitleMenu("Sport", R.drawable.sport))titleMenu.add(TitleMenu("Game", R.drawable.game))}
}
  • 编写Game.kt
  • 大部分部分与上方代码相同。不同点在于瀑布布局的编写。在onCreate()方法中,创建了一个StaggeredGridLayoutManager的实例。StaggeredGridLayoutManager的构造函数接受两个参数:第一个参数用于指定布局的列数,传入3表示会把布局分为3列;第二个参数用于指定布局的排列方式,传入StaggeredGridLayoutManager.VERTICAL表示会让布局纵向排列。最后把创建好的实例设置到RecyclerView当中就可以了。
package com.example.experiment2import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManagerclass Game : AppCompatActivity() {private val flowNews = ArrayList<flow_news>()private val titleMenu = ArrayList<TitleMenu>()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_game)supportActionBar?.hide()//菜单栏配置initTitleMenu()val layoutManager1 = LinearLayoutManager(this)val recyclerView1: RecyclerView = findViewById(R.id.recyclerViewForTitle)layoutManager1.orientation = LinearLayoutManager.HORIZONTALrecyclerView1.layoutManager = layoutManager1val adapter1 = TitleMenuAdapter(titleMenu)recyclerView1.adapter = adapter1//新闻栏配置initFlowNewsMenu()var layoutManager=StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL)val recyclerView: RecyclerView = findViewById(R.id.recyclerViewForGame)recyclerView.layoutManager = layoutManagerval adapter = flow_newsAdapter(flowNews)recyclerView.adapter = adapter}private fun initFlowNewsMenu() {repeat(5) {flowNews.add(flow_news(R.drawable.gamenews1,"《黑神话:悟空》游戏早期概念图 猴子和白龙亮相\n《黑神话:悟空》游戏早期概念图 猴子和白龙亮相\n《黑神话:悟空》游戏早期概念图 猴子和白龙亮相","www.3dmgame.com","2020-10-22 11:35:53","https://www.3dmgame.com/news/202010/3800200.html","来不排除会有DLC和非数值向的内购。"))flowNews.add(flow_news(R.drawable.gamenews2,"《战争机器5》XSX输入延迟大幅降低 最高达57%\n《战争机器5》XSX输入延迟大幅降低 最高达57%","baijiahao.baidu.com","2020-10-22 08:29:47","https://baijiahao.baidu.com/s?id=1681212969147287593&wfr=spider&for=pc",""))flowNews.add(flow_news(R.drawable.gamenews3,"《赛博朋克2077》角色使用各种语言都能实现口型同步","www.3dmgame.com","2020-10-21 00:02:28","https://www.3dmgame.com/news/202010/3800061.html",""))}}private fun initTitleMenu() {titleMenu.add(TitleMenu("Business", R.drawable.business))titleMenu.add(TitleMenu("Entertainment", R.drawable.entertainment))titleMenu.add(TitleMenu("Health", R.drawable.health))titleMenu.add(TitleMenu("Science", R.drawable.science))titleMenu.add(TitleMenu("Sport", R.drawable.sport))titleMenu.add(TitleMenu("Game", R.drawable.game))}
}
  • 最终效果图:
    在这里插入图片描述
    在这里插入图片描述

动态效果设计

  • 通过点击标题栏的图标,实现不同新闻界面之间的切换。
  • 通过点击新闻栏,实现不同方式的新闻阅览。

新闻界面切换

  • 在标题栏适配器TitleMenuAdapter.kt中进行修改。在onCreateViewHolder()方法中注册点击事件,为最外层布局注册点击事件,itemView表示的就是最外层布局。在两个点击事件中先获取用户点击的position,然后通过position拿到相应的实例,在使用intent完成activity之间的跳转。
  • 因为每个界面都设置了标题栏,所以多次点击会导致activity多次进入堆栈。解决方法是设置新闻界面的launchMode为singleTask。然后再点击前判断父活动是否为同一界面或是主菜单,如果是,则关闭上一个活动。
 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
...
val viewHolder = ViewHolder(view)viewHolder.titleMenuImage.setOnClickListener() {val position: Int = viewHolder.layoutPositionwhen(position){0->{val intent = Intent(parent.context, Business::class.java)parent.context.startActivity(intent)if (parent.context !is MainActivity && parent.context !is Business){(parent.context as Activity).finish()}}1->{val intent = Intent(parent.context, Entertainment::class.java)parent.context.startActivity(intent)if (parent.context !is MainActivity && parent.context !is Entertainment){(parent.context as Activity).finish()}}2->{val intent = Intent(parent.context, Health::class.java)parent.context.startActivity(intent)if (parent.context !is MainActivity && parent.context !is Health){(parent.context as Activity).finish()}}3->{val intent = Intent(parent.context, Science::class.java)parent.context.startActivity(intent)if (parent.context !is MainActivity && parent.context !is Science){(parent.context as Activity).finish()}}4->{val intent = Intent(parent.context, Sport::class.java)parent.context.startActivity(intent)if (parent.context !is MainActivity && parent.context !is Sport){(parent.context as Activity).finish()}}5->{val intent = Intent(parent.context, Game::class.java)parent.context.startActivity(intent)if (parent.context !is MainActivity && parent.context !is Game){(parent.context as Activity).finish()}}}}}

新闻阅览界面切换

  • 采取三种新闻阅览方式,第一种是通过本地下载的标题和文字来进行展示,第二种方式是通过手机自带的浏览器进行展示,第三种是通过WebView控件,在APP内部进行展示。
  • 默认每个新闻界面第一条新闻通过本地下载进行展示,第二条通过机自带的浏览器进行展示,第三条通过WebView控件,在APP内部进行展示。剩余新闻均通过第三种方式进行展示。
  • 首先在标题栏适配器vertical_newsAdapter.kt和flow_newsAdapter.kt中进行修改(两处代码基本一致,只需要注意修改具体对应变量,以vertical_newsAdapter.kt为例),过程与先前介绍方式大致相同,此处先将具体实现代码空着,后面讲提到具体实现方法。
 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val view = LayoutInflater.from(parent.context).inflate(R.layout.vertical_news_item, parent, false)val viewHolder = ViewHolder(view)viewHolder.itemView.setOnClickListener() {val position: Int = viewHolder.layoutPositionval verticalNews = vertical_newsList[position]when (position) {//本地文本查看0 -> {...}//调用系统浏览器1 -> {...}//使用WebView2 -> {...}else->{...}}}return viewHolder}
  • 接下载介绍三种打开方式的具体实现
  1. 本地下载方式
  • 创建新活动,New->Activity->Empty Activity,创建LocalNews.kt与activity_local_news.xml文件,首先打开activity_local_news.xml,编写布局代码,实现本地显示界面设计。
  • 使用ScrollView控件,方式文章因为篇幅过长而导致不能完全显示。

<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:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".LocalNews"><include layout="@layout/title"/><ScrollViewandroid:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:textSize="30sp"android:fontFamily="sans-serif-black"android:id="@+id/LocalTitle"/><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="12sp"android:id="@+id/LocalContent"/>LinearLayout>ScrollView>LinearLayout>
  • 在vertical_newsAdapter.kt适配器中添加具体实现代码,使用intent完成activity之间的跳转,并通过putExtra()方法传递数据。
.......
when (position) {//本地文本查看0 -> {val intent = Intent(parent.context, LocalNews::class.java)intent.putExtra("Title", verticalNews.newsTitle)intent.putExtra("Content", verticalNews.content)parent.context.startActivity(intent)}
......
}
......
  • 修改LocalNews.kt代码,获取intent中传输的数据,并修改界面TextView的内容。
package com.example.experiment2import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_local_news.*class LocalNews : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_local_news)supportActionBar?.hide()val Title: String? =intent.getStringExtra("Title")val Content: String?=intent.getStringExtra("Content")LocalTitle.text=TitleLocalContent.text=Content}
}
  • 最终效果图:
    在这里插入图片描述
  1. 自带浏览器方式
  • 在vertical_newsAdapter.kt适配器中添加具体实现代码,通过intent跳转到手机自带浏览器进行阅览。
1 -> {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(verticalNews.URL)
parent.context.startActivity(intent)
}
  • 最终效果图:
    在这里插入图片描述
  1. WebView控件方式
  • 创建新活动,New->Activity->Empty Activity,创建WebView.kt与activity_web_view.xml文件,首先打开activity_web_view.xml,编写布局代码。
  • 插入WebView控件,在应用程序里嵌入一个浏览器

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><include layout="@layout/title"/><WebViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/webView"/>LinearLayout>
  • 在vertical_newsAdapter.kt适配器中添加具体实现代码,使用intent完成activity之间的跳转,并通过putExtra()方法传递URL数据。
2 -> {val intent = Intent(parent.context, WebView::class.java)intent.putExtra("URL", flowNews.URL)parent.context.startActivity(intent)
}
  • 修改WebView.kt。通过WebView的getSettings()方法可以设置一些浏览器的属性,这里并没有设置过多的属性,只是调用了色图JavaScript Enable()方法,让WebView支持JavaScript脚本。接下来,调用setWebViewClient()方法,并传入一个WebViewClient的实例,作用是当需要从一个网页跳转到另一个网页时,我们希望目标网页任然在当前WebView中显示,而不是打开系统浏览器。最后一步调用loadURL()方法,并将网址传入,展示相应网页的内容。
package com.example.experiment2import android.annotation.SuppressLint
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.webkit.WebViewClient
import kotlinx.android.synthetic.main.activity_web_view.*class WebView : AppCompatActivity() {@SuppressLint("SetJavaScriptEnabled")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_web_view)supportActionBar?.hide()val URL: String? =intent.getStringExtra("URL")setContentView(R.layout.activity_web_view)webView.settings.javaScriptEnabled=truewebView.webViewClient= WebViewClient()if (URL != null) {webView.loadUrl(URL)}}
}
  • 需要注意,由于程序使用到了网络功能,而访问网络是需要声明权限的,因此还得修改AndroidManifest.xml文件,加入权限声明。
  • 最终效果图:
    在这里插入图片描述

三、出现的关键问题及解决方案

  1. 问题:你的主机中的软件种植了一个已建立的连接。
    解决方法:网络连接错误,重启电脑或网络。

  2. 问题:AS同步慢
    解决方法:在这里插入图片描述

  3. 问题:gradle文件更新下载,同不满
    解决方法:查看一下在gradle的版本,修改gradle-wrapper.property中的版本

  4. 问题:RecyclerView只出现一个图标
    解决方法:item.xm中LinearLayout包含在了一个大的Layout中,应修改为只包含一个线性布局。

  5. 问题:kotlin无法使用语法糖,省去findviewbyid等
    解决方法:缺少kotlin依赖配置。gradle.build中添加语句:apply plugin ”kotlin-android-extensions”.

  6. 问题:因为每个界面都设置了标题栏,所以多次点击会导致activity多次进入堆栈。
    解决方法:设置新闻界面的launchMode为singleTask。然后再点击前判断父活动是否为 同一界面或是主菜单,如果是,则关闭上一个活动。

  7. 问题:WebView无法打开网页。
    在这里插入图片描述

    解决方法:从Android 9.0(API级别28)开始,默认情况下禁用明文支持。因此http 的url均无法在webview中加载。在manifest 中application节点添加: android:usesCleartextTraffic=“true”

  8. 问题:文章篇幅过长,不能完全显示。
    解决方法:使用ScrollView控件。

四、实验总结

本次实验结合了《第一行代码 Android》第二版第2章(活动)、第3章(UI开发)与第9章(网络技术)的各个知识点。大部分代码都是在书本上demo的基础上进行修改,完成了实验的基本需求。遇到的问题在先前已经进行了说明,并提出了解决方案。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部