•准备工作

  新建一个项目,命名为 FragmentBestProject,并选择 Empty Activity;

  并将项目的模式结构改为 Project 模式;

•进入主题

  首先,准备好一个新闻实体类,新建类 News;

News.java

public class News {
private String title;
private String content; public String getTitle() {
return title;
} public void setTitle(String title) {
this.title = title;
} public String getContent() {
return content;
} public void setContent(String content) {
this.content = content;
}
}

  title 表示新闻标题,content 表示新闻内容;

  接着新建布局文件 news_content_frag.xml,用于作为新闻内容的布局;

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/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="@color/black"/> <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"
/> </LinearLayout> <View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="@color/black"
/> </RelativeLayout>

  新闻的内容部分主要可以分为两个部分:

  • 头部部分显示新闻标题
  • 正文部分显示新闻内容
  • 中间使用一条细线分隔开(利用 View 来实现)
  • 左侧一条竖线区分标题列表(利用 View 来实现)

  下面提供了双页模式和单页模式的布局示意图,双页和单页模式下文会讲,莫着急;

  双页模式示意图:

  单页模式示意图:

  在新建一个 NewsContentFragment 类,继承自 Fragmet;

NewsContentFragment.java

public class NewsContentFragment extends Fragment {

    private View view;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.news_content_frag,container,false);
return view;
} public void refresh(String newsTitle,String newsContent){
View visibilityLayout = view.findViewById(R.id.visibility_layout);
visibilityLayout.setVisibility(View.VISIBLE); TextView newsTitleText = view.findViewById(R.id.news_title);
TextView newsContentText = view.findViewById(R.id.news_content); newsTitleText.setText(newsTitle);//刷新新闻的标题
newsContentText.setText(newsContent);//刷新新闻的内容
}
}

  首先在  onCreateView() 方法里加载了我们刚刚创建的 news_content_frag 布局;

  接下来又提供了一个 refresh() 方法;

  这个方法就是用于将新闻的标题和内容显示在界面上;

  这里通过  view.findViewById() 方法分别获取新闻的标题和内容控件,并通过  setText() 将传递的参数设置上;

  到目前为止,我们就把新闻内容的碎片和布局都创建好了;

  但上述布局都是在双页模式中使用的,如果想在单页模式中启动的话,我们还需要在创建一个活动;

  新建一个 Empty Activity,命名为 NewsContentActivity,并将布局名指定为 news_content;

  修改 news_content.xml 中的代码;

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"> <fragment
android:id="@+id/news_content_fragment"
android:name="com.example.fragmentbestproject.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/> </LinearLayout>

  此处代码充分发挥了代码的复用性,直接在布局中通过  android:name 属性引入 NewsContentFragment;

  这样也就相当于把 news_content_frag 布局的内容自动添加进来;

  接下来修改 NewsContentActivity.java 中的代码;

NewsContentActivity.java

public class NewsContentActivity extends AppCompatActivity {

    public static void actionStart(Context context, String newsTitle,String newsContent){
Intent intent = new Intent(context,NewsContentActivity.class);
intent.putExtra("news_title",newsTitle);
intent.putExtra("news_content",newsContent);
context.startActivity(intent);
} @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_content); //获取传入的新闻标题
String newsTitle = getIntent().getStringExtra("news_title");
//获取传入的新闻内容
String newsContent = getIntent().getStringExtra("news_content"); //获取news_content中的fragment
NewsContentFragment newsContentFrgment =
(NewsContentFragment) getSupportFragmentManager()
.findFragmentById(R.id.news_content_fragment); newsContentFrgment.refresh(newsTitle,newsContent);//刷新NewsContentFragment界面
}
}

  可以看到,在  onCreate() 方法中,我们通过 Intent 获取到了传入的新闻标题和内容;

  然后调用 FragmentManager 的  findFragmentById() 方法得到了 NewsContentFragment 实例;

  接着调用它的  refresh() 方法,并将新闻的标题和内容传入,就可以把这些数据显示出来了;

  这里我们还提供了一个  actionStart() 方法,有关该方法的使用,可以参考我的这篇博客;

  接下来创建一个用于显示新闻列表的布局,新建 news_title_frag.xml;

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/news_title_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/> </LinearLayout>

  这个布局的代码就非常简单,里面只有一个用于显示新闻列表的 RecyclerView

  新建 news_item.xml 作为 RecyclerView 子项的布局;

news_item.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/news_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:textSize="18sp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="15dp"
android:paddingBottom="15dp"
/>

  子项布局的代码也非常简单,只有一个 TextView。

  代码进行到这,新闻列表和子项布局都已经创建好了;

  接下来我们就需要一个用于展示新闻列表的地方;

  新建一个 NewsTitleFragment 类作为展示新闻列表的碎片;

NewsTitleFragment.java

public class NewsTitleFragment extends Fragment {

    private boolean isTwoPane;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.news_title_frag,container,false);
return view;
} @Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//通过判断是否可以找到 news_content_layout 布局来判定是单页模式还是双页模式
if(getActivity().findViewById(R.id.news_content_layout) != null){
isTwoPane = true;//找到->双页模式
}
else
isTwoPane = false;//未找到->单页模式
}
}

  在 onCreateView() 方法中加载了 news_title_frag 布局;

  在  onActivityCreated() 方法中,通过在活动中能否找到 news_content_layout 布局来判定是什么模式;

  屏幕前的你是不是在往上扒拉代码,看看 news_content_layout 在哪个文件里?

  其实,他还未出现,在下面代码里呢;

  修改 activity_main.xml 中的代码;

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/news_title_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"> <fragment
android:id="@+id/news_title_fragment"
android:name="com.example.fragmentbestproject.NewsTitleFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/> </FrameLayout>

  该代码表示,在单页模式下,只会加载一个新闻标题的碎片;

  然后,新建 layout-sw600dp 文件夹,在这个文件夹下新建一个 activity_main.xml 文件;

sw600dp\activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<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.fragmentbestproject.NewsTitleFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<LinearLayout
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.fragmentbestproject.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
</LinearLayout>

  可以看出,在双页模式下我们同时引入了两个碎片,并将新闻内容的碎片放在了 LinearLayout 布局下;

  注意看这个 <LinearLayout> 布局的 id,你发现了什么?

  现在我们已经将绝大部分工作都完成了,但还剩下至关重要的一点;

  就是在 NewsTitleFragment 中通过 RecyclerView 将新闻展示出来;

  现在,我们在 NewsTitleFragment.java 中新建一个内部类 NewsAdapter 来作为 RecyclerView 的适配器;

NewsTitleFragment.java

public class NewsTitleFragment extends Fragment {

    private boolean isTwoPane;

    ..........
  
class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder>{
private List<News> mNewsList; class ViewHolder extends RecyclerView.ViewHolder{
TextView newsTitleText;
public ViewHolder(@NonNull View view) {
super(view);
newsTitleText = view.findViewById(R.id.news_title);
}
} public NewsAdapter(List<News> list){
mNewsList = list;
} @NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_item,parent,false);
final ViewHolder holder = new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
News news = mNewsList.get(holder.getAdapterPosition());
//通过 isTwoPane 的取值判断是单页模式还是双页模式
if(isTwoPane){
/**
* isTwoPane == true
* 双页模式,刷新 NewsContentFragment 中的内容
*/
NewsContentFragment newsContentFragment =
(NewsContentFragment) getFragmentManager()
.findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(news.getTitle(),news.getContent());
}
else{
/**
* isTwoPane == false
* 单页模式,直接启动 NewsContentActivity
*/
NewsContentActivity.actionStart(getActivity(),news.getTitle(),news.getContent());
}
}
});
return holder;
} @Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
News news = mNewsList.get(position);
holder.newsTitleText.setText(news.getTitle());
} @Override
public int getItemCount() {
return mNewsList.size();
}
}
  
}

  需要注意的是,之前都是将适配器写成一个独立的类,而这次写成了内部类;

  本次写成内部类的好处就是可以直接访问  isTwoPlane ;

  观察  onCreateViewHolder() 方法中注册的点击事件;

  首先获取到了被点击项的 News 实例,然后通过  isTwoPlane 判断模式;

  如果是单页模式,就启动一个新的活动显示新闻内容;

  如果是双页模式,就更新新闻内容碎片里的数据;

  到目前为止,此次项目接近尾声,仅剩下最后一步的收尾工作——向 RecyclerView 中填充数据;

  修改 NewsTitleFragment.java 中的代码;

public class NewsTitleFragment extends Fragment {

    private boolean isTwoPane;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.news_title_frag,container,false);
RecyclerView rv = view.findViewById(R.id.news_title_recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
rv.setLayoutManager(layoutManager);
NewsAdapter adapter = new NewsAdapter(getNews());
rv.setAdapter(adapter);
return view;
} @Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//通过判断是否可以找到 news_content_layout 布局来判定是单页模式还是双页模式
if(getActivity().findViewById(R.id.news_content_layout) != null){
isTwoPane = true;//找到->双页模式
}
else
isTwoPane = false;//未找到->单页模式
} class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder>{
private List<News> mNewsList; class ViewHolder extends RecyclerView.ViewHolder{
TextView newsTitleText;
public ViewHolder(@NonNull View view) {
super(view);
newsTitleText = view.findViewById(R.id.news_title);
}
} public NewsAdapter(List<News> list){
mNewsList = list;
} @NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_item,parent,false);
final ViewHolder holder = new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
News news = mNewsList.get(holder.getAdapterPosition());
//通过 isTwoPane 的取值判断是单页模式还是双页模式
if(isTwoPane){
/**
* isTwoPane == true
* 双页模式,刷新 NewsContentFragment 中的内容
*/
NewsContentFragment newsContentFragment =
(NewsContentFragment) getFragmentManager()
.findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(news.getTitle(),news.getContent());
}
else{
/**
* isTwoPane == false
* 单页模式,直接启动 NewsContentActivity
*/
NewsContentActivity.actionStart(getActivity(),news.getTitle(),news.getContent());
}
}
});
return holder;
} @Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
News news = mNewsList.get(position);
holder.newsTitleText.setText(news.getTitle());
} @Override
public int getItemCount() {
return mNewsList.size();
}
} private List<News> getNews(){
List<News> list = new ArrayList<>();
for(int i = 1;i <= 10;i++){
News news = new News();
news.setTitle("title "+i);
news.setContent("this is news content "+i+".");
list.add(news);
}
return list;
}
}

  这样,我们所有的编写工作就已经完成了,快来试一下吧!

运行结果

  Tablet

  Phone

•小结

  最后,让我们站在上帝视角总结一下这次的设计;

  首先,新建一个包含 Empty Activity 的项目,命名为 FragmentBestProject;

  为了能实现在手机上单页显示,在平板上双页显示,又新建了一个 layout-sw600dp 文件夹;

  并在这个文件夹里新建了一个 activity_main.xml;

  接下来该为界面设计标题和内容布局了;

  考虑到一点,不论是单页模式,还是双页模式,都会有新闻标题的身影;

  所以,我们新建 news_title_frag.xml,并配套新建了 NewsTitleFragment 用来显示该布局;

  新建完显示 title 的文件,接下来该新建显示 content 的文件了;

  新建 news_content_frag.xml,并配套新建了 NewsContentFragment 用来显示该布局;

  双页模式下点击 title 直接在右边显示相应的 content;

  而单页模式需要为 title 设置相应的点击跳转事件;

  点击 title 开启对应的新的活动;

  新建一个 Empty Activity,设置类名为 NewsContentActivity,布局文件名为 news_content;

  这个新建的活动代表点击 title 跳转的新活动;

  到这,有趣的图解式就结束了,接下来就是为页面添加数据了;

  枯燥无味的东西不想再写第二遍了,看上面的 Adapter 就可以了;

  画图不易,码字不易,求多支持,最后附上该图的 .psd 文件。

Android学习之简易版的新闻应用的更多相关文章

  1. Android学习:简易图片浏览

    这个例子学习混合使用XML布局和代码来控制UI,习惯上把变化小.行为固定的组件放在XML布局文件中管理,而那些变化较多.行为控制复杂的组件则交给Java代码来管理. 先在布局文件中定义一个线性布局容器 ...

  2. 记一篇Python学习的简易版教程

    廖雪峰的教学博客https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143178 ...

  3. springboot学习入门简易版九---springboot2.0整合多数据源mybatis mysql8+(22)

    一个项目中配置多个数据源(链接不同库jdbc),无限大,具体多少根据内存大小 项目中多数据源如何划分:分包名(业务)或注解方式.分包名方式类似多个不同的jar,同业务需求放一个包中. 分包方式配置多数 ...

  4. springboot学习入门简易版八---springboot2.0多环境配置、整合mybatis mysql8+(19-20)

    2.11 SpringBoot多环境配置(19)  application.properties中配置 Spring.profiles.active=prd 配置环境: Application-dev ...

  5. springboot学习入门简易版七---springboot2.0使用@Async异步执行方法(17)

    1启动类开启异步调用注解 @SpringBootApplication @EnableAsync //开启异步调用 public class StartApplication { 不开启则异步调用无效 ...

  6. springboot学习入门简易版六---springboot2.0整合全局捕获异常及log4j日志(12-13)

    使用Aop实现 1创建异常请求 在原有项目基础上,jspController中创建一个可能发生异常的请求: /** * 全局捕获异常测试 * @param i * @return */ @Reques ...

  7. springboot学习入门简易版五---springboot2.0整合jsp(11)

    springboot对jsp支持不友好,内部tomcat对jsp不支持,需要使用外部tomcat,且必须打包为war包. 1 创建maven项目 注意:必须为war类型,否则找不到页面. 且不要把js ...

  8. springboot学习入门简易版四---springboot2.0静态资源访问及整合freemarker视图层

    2.4.4 SpringBoot静态资源访问(9) Springboot默认提供静态资源目录位置需放在classpath下,目录名需要符合如下规则 /static  /public  /resourc ...

  9. springboot学习入门简易版三---springboot2.0启动方式

    2.4使用@componentscan方式启动 2.4.1 @EnableAutoConfiguration 默认只扫描当前类 @EnableAutoConfiguration 默认只扫描当前类,如果 ...

随机推荐

  1. Angular 2 for 2017 web full stack development

    1 1 1 Angular 2 for 2017 web full stack development 1 1 https://angular2.xgqfrms.xyz/ https://ng2-he ...

  2. browser parse CSS style order

    browser parse CSS style order 浏览器解析 CSS style 的顺序 从右到左 https://juejin.im/entry/5a123c55f265da432240c ...

  3. TypeScript 3.7 RC & Nullish Coalescing

    TypeScript 3.7 RC & Nullish Coalescing null, undefined default value https://devblogs.microsoft. ...

  4. post upload file & application/x-www-form-urlencoded & multipart/form-data

    post upload file application/x-www-form-urlencoded & multipart/form-data https://stackoverflow.c ...

  5. ts 修改readonly参数

    readonly name = "xxx"; updateValueAndValidity(): void { // this.name = 'a'; (this as { nam ...

  6. C++算法代码——求数列[coci2014/2015 contest #1]

    题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1815 题目描述 Mirko在数学课上以一种有趣的方式操作数列,首先,他写下一个数列A ...

  7. 【python接口自动化】- 正则用例参数化

    ​ 我们在做接口自动化的时候,处理接口依赖的相关数据时,通常会使用正则表达式来进行提取相关的数据. ​ 正则表达式,又称正规表示式.正规表示法.正规表达式.规则表达式.常规表示法(Regular Ex ...

  8. 那些容易犯错的c++保留字

    本文首发 | 公众号:lunvey 目前正在学习vc++6.0开发,而这里面使用的是c++98标准. 保留字,也称关键字,是指在变量.函数.类中不得重新声明的名称. c++98中大致有48个保留字,这 ...

  9. Redis集群简介及部署

    1简介 在 Redis 3.0 之前,使用 哨兵(sentinel)机制来监控各个节点之间的状态.Redis Cluster 是 Redis 的 分布式解决方案,在 3.0 版本正式推出,有效地解决了 ...

  10. centos7.5+nginx+php急速配置

    centos7.5+nginx+php急速配置 centosnginxphp 更新系统以及添加源 yum update yum -y install epel-release 安装php以及配置 yu ...