0
点赞
收藏
分享

微信扫一扫

实验2 用户界面设计

和谐幸福的人生 2022-10-04 阅读 118


使用Android的Fragment碎片控件、TextView文本标签控件、RecyclerVie列表控件、FrameLayout框架布局控件,采用双栏显示模式实现一个简易新闻阅读器应用程序,效果如图2所示。

实验2 用户界面设计_android


最终效果如shang :

第一阶段是能够单页展示新闻

这里使用了一个一个布局文件专门显示新闻内容,一个Activity用作显示单页的新闻内容。

具体是这样

首先设计一个fragment, 用于展示新闻内容。

基本布局格式如下:

图1 展示新闻内容fragment
然后加载该fragment新闻布局资源-NewsContentFragment

图2 加载新闻内容fragment资源
这里有两个方法, 第一个四onCreateVIew()以及refresh()
其中, onCreateView()是我们的最初目的函数-加载布局资源。并且使用全局变量得到加载好的布局资源view.这个view是为了refresh(0函数准备的, refresh(0函数的最终目的是将导入的资源显示出来, 这个过程中我们需要根据具体布局, 使用findViewById抽取我们需要的组件, 然后对这些抽取的组件赋值, 基本思路就是这样。
这只是展示的一个中间环节, 因为fragment不是Activity, fragment是需要Activity加载才可以, 同样, 我们就可以了解, refresh()是在Activity中调用,或者是在RecyclerView适配器中调用。

fragment布局(new_conetnt_frag)与加载做好之后, 就可以使用Activity将当前fragment展示出来, 也就是展示具体的新闻内容。 我们做的是一个新闻展示器, 按照屏幕的不同, 可以左右展示, 也可以单页单独展示。这里还没有涉及到RecyclerView的标题部分, 因此我们使用单页单独展示示范。

每个Activity都包含两个部分, 一个部分是xml布局文件, 一个部分是Java代码实现。
这里新建NewContentActivity, 以及new_content.xml
其中new_content.xml里面嵌套了new_content_frag布局文件, 由于每个fragment都有对应的Java代码加载部分, 因此只要在name中将这部分写入, 就可以做到将fragment加载进来的目的。

图3 如何嵌套fragment布局
这样, Activity的布局文件就写好了, 接下来就是初始化
NewContentActivity中有两个方法, 第一个方法是onCreate()界面初始化函数, 具体来说就是指定启动时要显示的布局, 同时加载内部布局文件以及配套的参数等资源。
在本环境中, 我们要将NewContentFragment布局(新闻内容布局)加载进来,加载进来之后还要传进参数(news_titile新闻标题, news_conetnt新闻内容), 调用refresh()方法, 为目的组件赋值。正常的获取参数的方式应该是通过intent,但是这里为了展示, 我们直接为两个参数赋值。
到了这里基本思路理清了, 代码也不多, 可以直接看结果了:

图4单页展示具体新闻内容效果
第二阶段设置新闻列表界面
这一阶段, 为了简单, 还是按照单页的设计模式。
由于新闻列表界面是一个滚动界面, 因此这里我们需要用到RecyclerView或者ListVIew其中的一个, 这里使用RecyclerView,ListVIew这里一模一样, 功能上仅仅不能横向滚动而已。
这里要注意使用RecyclerView的布局嵌套比其他碎片布局嵌套多一层, 即RecyclerView所在的碎片为整体布局, 具体展示布局为其子布局。 接下来会详细说明。

首先我们需要创建一个用于展示新闻列表的布局, 新建news_titile_frag.xml

图5 新闻列表的RecyclerView整体布局
, 代码里面注释也都十分清楚了。这里只是加载一个全局RecyclerView布局, 我们还需要设置一个RecyclerView的子布局, 进行具体的展示空间布置。
新建news_item.xml

图6 RecyclerVIew子项布局, 具体展示标题
值得注意的是layout_height=wrap_conetnt, 这个很重要, 不然一页才显示一个标题, 你可以把它理解为, 布局高度紧贴内容,与内容一致。

接下来就是Fragment的套路,资源加载代码。但是这里又有不同,可以很清晰的看到, 首先, 我们拥有子布局, 之前fragment都有资源加载代码, 子布局如何加载?另外之前的都是固定点击,这里滚动模式, RecyclerView又要如何加载?
新建NewsTitleFragment.java

package com.example.testnews;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**

  • 新闻列表资源加载
    */
    public class NewsTitleFragment extends Fragment {
    //是否是左右显示
    private boolean isTwoPane;
    private View view;
    /**
  • 加载资源文件到布局中
  • @param inflater
  • @param container
  • @param savedInstanceState
  • @return
    */
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    //首先还是加载资源布局,完成基本架构
    view = inflater.inflate(R.layout.new_titile_frag, container, false);
    //其次, 就是走RecyclerView的基本流程:
    //获取对应的recyclerView组件
    RecyclerView newsTitleRecyclerView = view.findViewById(R.id.news_title_recycler_view);
    //设置RecyclerView的布局管理器,
    //这里还是得说一下, getActivity说明就是这个页面的布局!!!
    LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
    newsTitleRecyclerView.setLayoutManager(layoutManager);
    //设置recyclerView的适配器
    //为了方便, 这里写成一个内部类,先不要管具体实现, 先走完流程
    //新建适配器, 传入我们要显示的内容,
    //这里可以看到与之前显示内容的不同, 那里的内容是在Activity中传入的,之后看看还能不能改
    NewsAdapter newsAdapter = new NewsAdapter(getNews());
    newsTitleRecyclerView.setAdapter(newsAdapter);
    return view;
    //接下来我们就要具体写适配器了
    }

/**

  • RecyclerView的适配器, 这里内部类的原因是因为要使用父类的一些变量
  • RecyclerViewAdapter需要继承Adapter,
  • 而且这个ViewHolder必须要RecyclerVIew的内部类VIewHolder通过继承RecyclerView的ViewHolder得到的
  • viewHolder的作用就是获取RecyclerView子项中需要展示组件,这里直说大概吗继续说RecyclerView适配器
    */
    class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder>{
    //这是传入的参数列表, 也就是需要展示的资源
    private List newsList;
    /**
  • 通过构造函数对参数赋值
  • @param newsList
    */
    public NewsAdapter(List newsList) {
    this.newsList = newsList;
    }

/**

  • onCreateView用于创建ViewHolder实例,
  • 并且把加载的布局传入到传入到构造函数中去, 再把ViewHolder实例返归
  • 不知大家是否发现, 这个和onCreateView中返回的view道理很相似。
  • 也就是说adapter也可以看成是一个fragment, 那个oncCraeteViewHolder也就是onCreateView

  • @param parent
  • @param viewType
  • @return
    */
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    //加载子项资源布局
    //这里还是很有意思的, parent代表的RecyclerView, 这里代表把子项加载到RecyclerView所在的布局
    //对比fragment,你会发现, 对于Recycler子项的操作是在适配器中完成的。
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_item,parent,false);
    //新建ViewHolder,如题哈哈
    //存入view参数, 是为了解析子项里面我们需要的组件
    final ViewHolder viewHolder = new ViewHolder(view);
    //在子项中设置单击事件, 没错这就是上次老师问的那里,
    //很重要的一点, 单击事件不是在RecyclerView整体,而是对他的子项进行的
    //我们甚至可以肯定, 如果子项组件不是一个还会继续细分执行,哈哈
    //回归正题, 点击事件在这里唯一的作用就是展示新闻内容呀,哈哈
    view.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    //获取当前view位置,对应的新闻
    //为什么要用viewHodler,我找了一下其他的也没找到
    //不过也对, viewHodler就是确定目标view的一切信息
    News news = newsList.get(viewHolder.getAdapterPosition());
    //这里还是先设置单页显示,因为要按照进度来
    //发现没有, 这个R.id.news_content_fragment是单独新闻内容显示activity对应界面
    // // 嵌入的new_content_frag资源的重新编码代了
    // NewsContentFragment newsContentFragment =
    // (NewsContentFragment) getFragmentManager().findFragmentById(R.id.news_content_fragment);
    //发现没有, 这就是启动非主ActivityNewContentActivity的地方
    //启动之后就会将参数传入, 然后初始化, 就会单独显示内容哈哈
    NewsContentActivity.actionStart(getActivity(), news.getTitle(), news.getContent());
    }
    });
    return viewHolder;
    }

/**

  • 也就是一般显示, 而不是事件
  • onBindViewHolder用于对子项数据进行赋值, 会在每个子项被滚动到屏幕内时执行。
  • position是得到当前子项的实例的坐标
  • @param holder
  • @param position
    */
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
    News news =newsList.get(position);
    holder.newsTextText.setText(news.getTitle());
    }

/**

  • 这个是获得子项数目
  • @return
    */
    @Override
    public int getItemCount() {
    return newsList.size();
    }

class ViewHolder extends RecyclerView.ViewHolder{
TextView newsTextText;
public ViewHolder(@NonNull View itemView) {
super(itemView);
newsTextText = itemView.findViewById(R.id.news_title);
}
}
}

/**

  • 辅助类, 不需要记住
  • @return
    /
    private List getNews(){
    List newsList = new ArrayList<>();
    for (int i = 1; i <= 50; i++){
    News news = new News();
    news.setTitle(“This is news title " + i);
    news.setContent(getRandomLengthContent(“This is news content " + i +”.”));
    newsList.add(news);
    }
    return newsList;
    }
    /
    *
  • 辅助类, 不需要记住
  • @return
    */
    private String getRandomLengthContent(String content){
    Random random = new Random();
    int length = random.nextInt(20) + 1;
    StringBuilder builder = new StringBuilder();
    for (int i = 1; i < length; i++){
    builder.append(content);
    }
    return builder.toString();
    }

}
代码7 NewsFragment代码
这里代码虽然很多, 但是搞懂结构之后, 只是一个简单的填充问题, 接下来有请李登印同学一层层抽丝剥茧:
第一层次:首先,按照Fragment资源加载的套路 首先定义View,利用inflater加载资源布局, 返回view完成Fragment层面的基本架构
第二层次:其次,走完RecyclerVIew静态加载的流程。首先是获取RecyclerView组件,之后设置RecyclerVIew的布局管理器, 最后设置RecyclerView的适配器
第三层次:设置RecyclerView适配器(Adapter):这个也很简单,首先是要设置子类ViewHolder,其次继承Adapter,其次定义参数,并且定义传入该参数的构造函数(该参数就是传入数据),最后重写三个函数,onCreateVIewHolder, onBindViewHolder以及getItemCount()
第四层次:写完具体的ViewHolder, viewHolder是Adapter重写类的子类,作用是获取RecycerView子项中需要展示的组件。因此他也很简单,只有对应组件的参数以及传参够赞函数

具体写的时候, 可以先把整体的架子搭起来, 因为有许多定义要先具备,比如先定义ViewHolder, 在定义Adapter集成Adapter

第一层次具体来看:
//首先还是加载资源布局,完成基本架构
view = inflater.inflate(R.layout.new_titile_frag, container, false);
Return view
代码8 fragment资源加载代码
还是利用inflater.inflate(resourceId, parent, false);加载资源。 值得一提的是, resourceId,不是用的R.id,二十利用了R.layout,也就是fragment的资源名, 要记住。其他就是默认了

第二层次来看:
//其次, 就是走RecyclerView的基本流程:
//获取对应的recyclerView组件
RecyclerView newsTitleRecyclerView = view.findViewById(R.id.news_title_recycler_view);
//设置RecyclerView的布局管理器,
//这里还是得说一下, getActivity说明就是这个页面的布局!!!
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
newsTitleRecyclerView.setLayoutManager(layoutManager);
//设置recyclerView的适配器
//为了方便, 这里写成一个内部类,先不要管具体实现, 先走完流程
//新建适配器, 传入我们要显示的内容,
//这里可以看到与之前显示内容的不同, 那里的内容是在Activity中传入的,之后看看还能不能改
NewsAdapter newsAdapter = new NewsAdapter(getNews());
newsTitleRecyclerView.setAdapter(newsAdapter);
代码9 RecyclerView整体流程

RecyclerView组件的获取, 依赖第一层次的view,使用findVIewById,这里使用的就是R.id了
其次四布局管理器, 这里的布局管理器类型和RecyclerView所在整体布局是一致的。
其次是适配器, 适配器就是我们自定义的NewsAdapter,这里要传入显示的参数文件才可以。 参数文件并不是很重要, 因此我们就补贴上了。

第三层次:
第三层次代码太多还是不贴上来了,

首先还是先把ViewHolder简单定义好,因为自定义Adapter需要继承RecyclerVIew.Adapter这个ViewHolder就是我们自定义的继承了RecyclerView.ViewHolder的viewHolder。其次, 我们定义接收传入参数的对应参数, 并且构造相应的构造函数

代码10 定义NewsAdapter以及设置参数构造函数
其次这个时候已经有三个继承函数需要我们重写, 接下来先看onCreateVIewHolder函数

/**
* onCreateView用于创建ViewHolder实例,
* 并且把加载的布局传入到传入到构造函数中去, 再把ViewHolder实例返归
* 不知大家是否发现, 这个和onCreateView中返回的view道理很相似。
* 也就是说adapter也可以看成是一个fragment, 那个oncCraeteViewHolder也就是onCreateView
*
* @param parent
* @param viewType
* @return
*/
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//加载子项资源布局
//这里还是很有意思的, parent代表的RecyclerView, 这里代表把子项加载到RecyclerView所在的布局
//对比fragment,你会发现, 对于Recycler子项的操作是在适配器中完成的。
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_item,parent,false);
//新建ViewHolder,如题哈哈
//存入view参数, 是为了解析子项里面我们需要的组件
final ViewHolder viewHolder = new ViewHolder(view);
//在子项中设置单击事件, 没错这就是上次老师问的那里,
//很重要的一点, 单击事件不是在RecyclerView整体,而是对他的子项进行的
//我们甚至可以肯定, 如果子项组件不是一个还会继续细分执行,哈哈
//回归正题, 点击事件在这里唯一的作用就是展示新闻内容呀,哈哈
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//获取当前view位置,对应的新闻
//为什么要用viewHodler,我找了一下其他的也没找到
//不过也对, viewHodler就是确定目标view的一切信息
News news = newsList.get(viewHolder.getAdapterPosition());
//这里还是先设置单页显示,因为要按照进度来
//发现没有, 这个R.id.news_content_fragment是单独新闻内容显示activity对应界面
// // 嵌入的new_content_frag资源的重新编码代了
// NewsContentFragment newsContentFragment =
// (NewsContentFragment) getFragmentManager().findFragmentById(R.id.news_content_fragment);
//发现没有, 这就是启动非主ActivityNewContentActivity的地方
//启动之后就会将参数传入, 然后初始化, 就会单独显示内容哈哈
NewsContentActivity.actionStart(getActivity(), news.getTitle(), news.getContent());
}
});
return viewHolder;
}
代码11 onCreateVIewHolder函数
顾名思义, onCreateViewHolder函数就是为了构建一个ViewHolder来存储子布局展示组件,
那么他的性质和onCreateVIiew其实没什么区别了, 都是载入一个布局资源。唯一不同就是onCreteViewHolder载入的是二级资源。

首先是加载子布局资源文件,这里加载原理和加载fragemnt一致, 但是具体方式优点差异
LayoutFlater.from(parent.getContent()).inflate(R.layout.news_tem,parents, false ),这里是layoutFlater.from让我们自己确定要从那个布局里面加载子布局资源,parent就是我们的news_title_fragement,也就是recycler_view所在的整体布局,后面的inflate()和我们认识的就一样了, R.layout说明我们加载的确实是一个布局
接着新建VIewHolder, 这里根据VIewHolder保存组件的目的, 因此要传入组件所在的布局

接着应该是设置单击事件, 但是这一阶段我们是设置布局, 先不写。
最后返回viewHolder

接下来是onBindViewHolder函数, 顾名思义,就是为了给viewHolder保存的组件赋值, 也就是用于对子项数据进行赋值, 每个子项数据滚动到屏幕内时执行。
/**

  • 也就是一般显示, 而不是事件
  • onBindViewHolder用于对子项数据进行赋值, 会在每个子项被滚动到屏幕内时执行。
  • position是得到当前子项的实例的坐标
  • @param holder
  • @param position
    */
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
    News news =newsList.get(position);
    holder.newsTextText.setText(news.getTitle());
    }

代码12 onBindVIewHolder函数
Postion就是当前滚动到屏幕内子布局组件的坐标, 它对应的数据就应该是list.get(position),然后将这个对应的数据赋值给viewHolder保存的组件。就可以展示了哈哈

接下来是获取子项数目getItemCount():
/**

  • 这个是获得子项数目
  • @return
    /
    @Override
    public int getItemCount() {
    return newsList.size();
    }
    代码13getItemCount()
    这个也没啥好说的。就一句话代码
    第四层次:
    /
    *
  • 内部类
    */
    class ViewHolder extends RecyclerView.ViewHolder{
    TextView newsTextText;
    public ViewHolder(@NonNull View itemView) {
    super(itemView);
    newsTextText = itemView.findViewById(R.id.news_title);
    }
    }
    代码14 ViewHolder内部类
    很惊喜, 就是这么点, 只要将子布局的组件作为参数, 然后利用view得到这个部件即可。
    ViewHolder的作用在前三阶段都已说明

接下来看看效果:

图片15 新闻列表页效果, 可以滚动哟

第三阶段单页面跳转实现:
我们要接收这样一个现实, 即不完成事件触发, 无法做到双页面的展示, 这个很好理解,没有事件触发, 右边的fragment永远是空白的, 除非静态实现。
我们先思考一下,我们的点击事件是对子项布局的点击, 而不是对子项布局里面面组件的点击, 更不是对RecycelerView的点击, 因此需要在获取子项布局view的onCreateViewHolder里面写, 你说在onBindViewHolder行不行,我试过也可以。回到整体,单击事件的唯一作用就是展示新闻内容, 因此我们的事件涉及到两个方面, 第一个是子项布局界面解析, 第二个是跳转到内容页。

// 在子项中设置单击事件, 没错这就是上次老师问的那里,
// 很重要的一点, 单击事件不是在RecyclerView整体,而是对他的子项进行的
// 我们甚至可以肯定, 如果子项组件不是一个还会继续细分执行,哈哈
// 回归正题, 点击事件在这里唯一的作用就是展示新闻内容呀,哈哈
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//获取当前view位置,对应的新闻
//为什么要用viewHodler,我找了一下其他的也没找到
//不过也对, viewHodler就是确定目标view的一切信息
News news = newsList.get(viewHolder.getAdapterPosition());
//这里还是先设置单页显示,因为要按照进度来
//发现没有, 这就是启动非主ActivityNewContentActivity的地方
//启动之后就会将参数传入, 然后初始化, 就会单独显示内容哈哈
NewsContentActivity.actionStart(getActivity(), news.getTitle(), news.getContent());
}
});
代码16 设置跳转事件的代码

首先定位到当前点击子布局的位置,这里使用的是viewHolder.getAdapterPosition(),即在适配器中的位置, 对应着就可以拿到对应位置的参数, 即list.get(position)
之后就是启动新闻内容展示页面, 将参数传入就好了。
效果如下:
效果不太好表示,

图17 单页跳转效果哈哈
第四阶段单页左右内容展示实现
左右页实现咱们先弄的简单一点, 也就是默认手机屏幕足够宽, 支持左右显示。
因此这里我们直接调整activity_main.xml即可。

<?xml version="1.0" encoding="utf-8"?>


<fragment
android:layout_weight="1"
android:id="@+id/news_title_fragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:name="com.example.testnews.NewsTitleFragment"/>
<fragment
android:layout_weight="3"
android:id="@+id/news_content_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.testnews.NewsContentFragment"/>

图18 左右双fragment的activity_main.xml 使用线性水平布局 标题:内容=1:3

接下来, 我们要思考, 这样就不能再点击之后跳转了,而是在当前Activity展示, 这样我们就需要将NewsContentFragment像NewsTitleFragment一样。
//发现没有, 这个R.id.news_content_fragment是单独新闻内容显示activity对应界面
// // 嵌入的new_content_frag资源的重新编码代了
NewsContentFragment newsContentFragment =
(NewsContentFragment) getFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(news.getTitle(), news.getContent());
图19修改响应事件
查看运行结果:

图20 初步运行结果

第五阶段:复刻实验2
我们把自适应放到最后, 主要是实验二还是得看脸。
图21预期效果图
首先, 每个标题之间有间隔,上下间隔使用marig或者padding都可以, 其次, 左右fragment有间隔, 则两个fragment都整体缩小就可以
太麻烦了, 还得用PS调色, 直接看代码把

图22效果图对比

第六阶段:自适应屏幕大小
这个属于锦上添花花了, 待会再做把。


举报

相关推荐

0 条评论