•准备工作

  新建一个项目,命名为 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 文件。

最新文章

  1. Zbrush 快捷键
  2. SQL Server2008附加数据库之后显示为只读
  3. Unity3D 游戏开发构架篇 ——角色类的设计与持久化
  4. 使用next-key locks 用于搜索和索引扫描,可以防止幻读
  5. 【Howie玩docker】-命令行只显示-bash-4.1#
  6. day4 liaoxuefeng---高级特性
  7. C#反射与特性使用简介
  8. [转帖]Docker容器CPU、memory资源限制
  9. Centos6.8 安装nginx
  10. Gsoap编译
  11. Java性能分析神器-JProfiler详解(一)(转)
  12. 通过DBMS_REDEFINITION包对表在线重定义
  13. Python 实现双向链表(图解)
  14. java stream collector
  15. CSS 学习路线(二)选择器
  16. HNOI2018 退役记
  17. SQL数据库有阻塞就自动发邮件警报
  18. Python subplot 绘画
  19. spark第七篇:Spark SQL, DataFrame and Dataset Guide
  20. HDOJ.2064 汉诺塔III

热门文章

  1. js 深入原理讲解系列-currying function
  2. tree ignore &amp; bash &amp; cmd
  3. Object to Array
  4. c++ string与wstring转换
  5. BGV币与YFI币、YFII币存在着怎样的相关性?
  6. python的with用法(转载)
  7. 【HTB靶场系列】靶机Carrier的渗透测试
  8. 无所不能的Embedding7 - 探索通用文本表达[FastSent/InferSent/GenSen/USE]
  9. 【翻译】Python PEP8编码规范(中文版)
  10. 【JAVA并发第四篇】线程安全