在Fragment的学习中,我们在了解Fragment设计的原理时,举了一个类似新闻的例子。在平板运行上时,展示如下图的左侧部分;在手机上运行时,展示如下图的右侧部分。
接下来,我们就要运用之前学习的知识,来编写一个简易的新闻客户端,并且要求它可以同时兼容平板和手机。
首先我们需要一个新闻的实体类,新建News类,代码如下:
public class News {
private String title;
private String content;
public void setTitle(String title) {
this.title = title;
}
public void setContent(String content) {
this.content = content;
}
public String getTitle(){
return title;
}
public String getContent() {
return content;
}
}
News类还是很简单的,title表示新闻的标题,content表示新闻的具体内容。接着,我们新建一个新闻内容的布局文件news_content_frag.xml:
新闻内容的布局主要分为两个部分,一个是新闻的Title,另外一个是新闻的内容。我们利用一个高度为1dp、背景色为黑色的view来作为他们之间的分割线。
然后创建NewsContentFragment类,继承自Fragment,代码如下:
public class NewsContentFragment extends Fragment {
private View view;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
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 textTitle = view.findViewById(R.id.news_content_title);
TextView textContent = view.findViewById(R.id.news_content);
textTitle.setText(newsTitle);
textContent.setText(newsContent);
}
}
首先在onCreateView()中加载了我们刚才创建的news_content_frag布局。接下来提供了一个refresh()方法,这个方法用于将新闻的标题和内容显示在界面上。可以看到,通过findViewById()方法得到新闻标题和内容的控件,然后将方法传递进来的参数设置进去。
这样我们就创建好了新闻内容的布局和Fragment。但是考虑到我们还得用单页模式来适配手机屏幕,所以需要新建一个对应的Activity--NewsContentActivity。首先修改一下布局文件:
我们在这里通过android:name直接引入com.johnhao.listviewdemo.Fragment.NewsContentFragment,这样也就相当于把news_content_frag布局的内容自动加了进来。接着修改Activity代码:
public class NewsContentActivity extends BaseActivity {
public static void actionStart(Context context, String newsTitile, String newsContent) {
Intent intent = new Intent(context, NewsContentActivity.class);
intent.putExtra("news Title", newsTitile);
intent.putExtra("news Content", newsContent);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news_content);
String newsTitle = getIntent().getStringExtra("news Title");
String newsContent = getIntent().getStringExtra("news Content");
setTitle(newsTitle);
NewsContentFragment newsContentFragment = (NewsContentFragment)getSupportFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(newsTitle, newsContent);
}
}
首先我们提供了一个actionStart()方法用于启动NewsContentActivity。然后在onCreate()方法中通过Intent获取到传入的新闻和标题,接着调用FragmentManager的findFragmentById()方法得到NewsContentFragment的实例,最后调用NewsContentFragment中的refresh()方法,将新闻的标题和内容传进去,就可以把这些数据显示出来了。
搞定了新闻内容,接下来还需要一个新闻标题的列表的布局,新建news_title_frag.xml布局文件:
内容很简单,只有一个RecyclerView用于展示新闻列表。既然用到RecyclerView,那么我们就需要创建子项布局,新建news_item.xml:
子项布局也很简单,因为我们只需要存放新闻的标题,因此只有一个TextView。android:singleLine为true表示只能单行显示,android:ellipsize为end表示当文本超出控件长度,在尾部进行缩略。android:padding表示在控件周围加上补白。
紧接着我们创建一个NewsTitleFragment类用于展示新闻列表:
public class NewsTitleFragment extends Fragment {
private Boolean isTwoPane;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.news_title_frag, container, false);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getActivity().findViewById(R.id.news_content_layout) != null) {
isTwoPane = true;
} else {
isTwoPane = false;
}
}
}
在onCreateView()方法中加载了news_title_frag布局,然后主要在onActivityCreated()中,我们增加了一个双页展示的判断。判断的依据为是否能找到一个id为news_content_layout的布局,找的到就位双页展示,找不到就为单页展示。
这里用Android的限定符就可以实现,修改主Activity的布局文件:
在单页模式下,只会加载NewsTitleFragment。然后在res目录下新建目录layout-sw600dp,再在这个目录下新建一个一样名字的布局文件并添加代码:
可以看到在这里,同时引入了NewsTitleFragment和NewsContentFragment,并且将NewsContentFragment放在了一个id为news_content_layout的FrameLayout中。因此,能够找到这个id的时候,就是要展示双页的时候。
当然我们的工作仍然没有结束,别忘了还得在NewsTitleFragment通过RecycleView将新闻内容展示出来。这时就需要创建一个适配器,我们通过内部类的形式来实现。
public class NewsTitleFragment extends Fragment {
private Boolean isTwoPane;
...
class NewsAdapter extends RecyclerView.Adapter{
private List mNewsList;
public NewsAdapter(List newsList) {
mNewsList = newsList;
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView newsTitle;
public ViewHolder(View itemView) {
super(itemView);
newsTitle = itemView.findViewById(R.id.news_title);
}
}
@Override
public NewsAdapter.ViewHolder onCreateViewHolder(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());
if (!isTwoPane) {
// 如果是单页模式,则启动NewsContentActivity,传递当前的标题和内容
NewsContentActivity.actionStart(getActivity(), news.getTitle(), news.getContent());
} else {
// 如果是双页模式,刷新NewsContentFragment的内容
NewsContentFragment newsContentFragment = (NewsContentFragment) getFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(news.getTitle(), news.getContent());
}
}
});
return holder;
}
@Override
public void onBindViewHolder(NewsAdapter.ViewHolder holder, int position) {
News news = mNewsList.get(position);
holder.newsTitle.setText(news.getTitle());
}
@Override
public int getItemCount() {
return mNewsList.size();
}
}
RecycleView的用法我们应该很熟悉的,这里写成内部类主要是方便访问全局变量isTwoPane。主要看onCreateViewHolder()方法中注册的点击事件,通过isTwoPane判断模式,如果为单页模式,则启动NewsContentActivity来展示新闻内容;如果为双页模式,则先获取到NewsContentFragment的实例,然后调用NewsContentFragment的refresh()方法,传进新闻标题和内容,将内容展示出来。
适配器完成后,最后我们就可以往RecycleView填充数据了:
public class NewsTitleFragment extends Fragment {
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.news_title_frag, container, false);
RecyclerView newsTitleRecycleView = view.findViewById(R.id.news_title_recycle_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
newsTitleRecycleView.setLayoutManager(layoutManager);
NewsAdapter adapter = new NewsAdapter(getNews());
newsTitleRecycleView.setAdapter(adapter);
return view;
}
private List getNews() {
List newsList = new ArrayList<>();
for (int i = 1; i < 51; i++) {
News news = new News();
news.setTitle("This is news title " + i);
news.setContent(getRandomLengthContent("This is news title " + i + "."));
newsList.add(news);
}
return newsList;
}
private String getRandomLengthContent(String content) {
Random random = new Random();
int length = random.nextInt(20) + 1;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append(content);
}
return builder.toString();
}
在onCreateView()方法中添加了RecycleView的标准使用方法,在Fragment中使用RecycleView和在Activity中几乎是一模一样的。我们通过getNews()方法初始化了一些模拟新闻数据,同样使用了getRandomLengthContent()方法来生成一些随机长度的新闻内容。
到这儿,我们的编码工作就算结束了,运行程序试一下,手机版本的:
平板在模拟器上运行的: