以下代码均来自郭霖的《第一行代码》第2版,想了解详细内容的可以去看原书。
涉及知识点:静态创建Fragment,APP在手机屏幕与平板屏幕上的适配操作,RecyclerView的使用。
手机效果图:
平板效果图:
文件一览:
关于手机屏幕与平板屏幕的适配:
这里layout-sw600dp为当屏幕宽度大于600dp时,会加载该目录下的activity_main.xml布局,Andoird会根据条件自己选择加载哪一个布局,不需要再有额外的代码操作。
布局实现:
首先来看两个activity_main.xml文件。
layout\activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/news_title_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.admin.fragmentbestpractice.MainActivity">
<fragment
android:id="@+id/news_title_fragment"
android:name="com.example.admin.fragmentbestpractice.NewsTitleFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
layout-sw600dp\activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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="match_parent"
android:orientation="horizontal"
tools:context="com.example.admin.fragmentbestpractice.MainActivity">
<fragment
android:id="@+id/news_title_fragment"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:name="com.example.admin.fragmentbestpractice.NewsTitleFragment"
/>
<FrameLayout
android:id="@+id/news_content_layout"
android:layout_width="0dp"
android:layout_weight="3"
android:layout_height="match_parent">
<fragment
android:id="@+id/news_content_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.admin.fragmentbestpractice.NewsContentFragment"
/>
</FrameLayout>
</LinearLayout>
对比两个文件,在layout-sw600dp文件下的布局文件中多了一个FrameLayout,其中的Fragment是用于加载新闻内容。这里使用到的都是静态的加载Fragment。
其中请记住FragLayout的id:news_content_layout,将用于之后的页面判断。
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"
android:orientation="vertical"
>
<fragment
android:id="@+id/news_content_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.admin.fragmentbestpractice.NewsContentFragment"
/>
</LinearLayout>
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/visibilityLayout"
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="#000"
/>
<TextView
android:id="@+id/news_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:textSize="15sp" />
</LinearLayout>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="#000"
/>
</RelativeLayout>
可以很容易的看出来,第一个news_content.xml是在单页面的时候使用的,第二个news_content_frag.xml为双页面时使用。可以先这么理解,㘝单页面上也同样是使用到news_content_frag.xml布局。
news_item.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="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/news_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="25sp"
android:maxLines="1"
android:ellipsize="end"
android:paddingLeft="20dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#afa9a9"
/>
</LinearLayout>
news_item_frag.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">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
news_item.xml为单个新闻标题的布局,news_item_frag.xml为整个新闻标题的布局,用RecyclerView展示。
逻辑实现:
其中的MainActivity与News文件没什么好说的,直接上代码。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
public class News {
private String title;
private String content;
public News(String title, String content) {
this.title = title;
this.content = 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;
}
}
首先先解决最为复杂的NewsTitleFragment.java文件。
public class NewsTitleFragment extends Fragment{
private boolean isTwoPane;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.news_title_frag, container, false);
RecyclerView recyclerView = view.findViewById(R.id.recycleView);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(linearLayoutManager);
NewsAdapter newsAdapter = new NewsAdapter(initNews());
recyclerView.setAdapter(newsAdapter);
return view;
}
private ArrayList<News> initNews() {
ArrayList<News> newsArrayList = new ArrayList<>();
for(int i = 0; i < 30; i++) {
News news = new News("News" + i, getRandomLength("This is contents" + i).toString());
newsArrayList.add(news);
}
return newsArrayList;
}
private StringBuilder getRandomLength(String s) {
Random random = new Random();
int counts = random.nextInt(30) + 1;
StringBuilder builder = new StringBuilder();
for(int i = 0; i < counts; i++) {
builder.append(s);
}
return builder;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(getActivity().findViewById(R.id.news_content_layout) != null) {
isTwoPane = true;
}
else {
isTwoPane = false;
}
}
class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder>{
private ArrayList<News> mNewsArrayList;
public NewsAdapter(ArrayList<News> newsArrayList) {
mNewsArrayList = newsArrayList;
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView newsTitle;
public ViewHolder(View view) {
super(view);
newsTitle = view.findViewById(R.id.news_title);
}
}
@Override
public 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 = mNewsArrayList.get(holder.getAdapterPosition());
if(isTwoPane) {
NewsContentFragment newsContentFragment = (NewsContentFragment)
getFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(news.getTitle(), news.getContent());
}
else {
NewsContentActivity.actionStart(getContext(), news.getTitle(), news.getContent());
}
}
});
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
News news = mNewsArrayList.get(position);
holder.newsTitle.setText(news.getTitle());
}
@Override
public int getItemCount() {
return mNewsArrayList.size();
}
}
}
(不知道如何使用RecyclerView的朋友们,可以看一下我之前对于RecyclerView的基础使用说明。)
首先我们先看onActivityCreated( )函数。我们知道该函数是在确保Fragment相关联的Activity已经创建完毕时调用,这里也就是说activity_main.xml已经被创建。通过是否存在R.id.news_content_layout这个控件id (此id只有在layout-sw600dp\activity_main.xml布局中出现过) 即可判断该设备是平板还是手机。同时将isTwoPane赋值,若为平板屏幕,则为true。若为手机屏幕,则为false。
剩下三个方法:onCreateView( ),initNews( ),getRandomLength( )不做解释。
接着来看定义了一个内部类NewsAdapter,其他的都是一些使用RecyclerView时的操作,只有在view.setOnClickListener中,当isTwoPane为true执行newsContentFragment.refresh()方法,isTwoPane为false执行NewContentActivity.actionStart()方法。
这里你可能对这两个方法有些疑惑,别着急,先记住这个调用,看完最后的两个文件NewsContentFragment.java与NewsContentActivity.java,你将会豁然开朗。
NewsContentFragment.java
public class NewsContentFragment extends Fragment{
private View view;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.news_content_frag, container, false);
return view;
}
public void refresh(String title, String content) {
View visibilityLayout = view.findViewById(R.id.visibilityLayout);
visibilityLayout.setVisibility(View.VISIBLE);
TextView newsTitle = visibilityLayout.findViewById(R.id.news_title);
TextView newsContent = visibilityLayout.findViewById(R.id.news_content);
newsTitle.setText(title);
newsContent.setText(content);
}
}
设置一个成员变量view,在OnCreatView( )函数中,加载入news_content_frag.xml布局。
新建的refresh( )方法中,将原本布局中不可见的LinearLayout设置为可见,同时将新闻标题与新闻内容赋值在两个TextView上。
不是特别明白的,再回头看一下layout-sw600dp\activity_main.xml文件。
这样在平板上的双页面就显示成功了。
NewsContentAcitivity.java
public class NewsContentActivity extends AppCompatActivity{
public static void actionStart(Context context, String title, String content) {
Intent intent = new Intent(context, NewsContentActivity.class);
intent.putExtra("news_Tiltle", title);
intent.putExtra("news_Content", content);
context.startActivity(intent);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_content);
String newsTiltle = getIntent().getStringExtra("news_Tiltle");
String newsContent = getIntent().getStringExtra("news_Content");
NewsContentFragment newsContentFragment = (NewsContentFragment)
getSupportFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(newsTiltle, newsContent);
}
}
首先定义了一个静态方法actionStart,实现跳转Activity的效果。也就是当你点击新闻标题的时候,将会跳转到NewsContentActivity中,显示新闻内容,此为手机屏幕上的效果。
先将得到的news_Tiltle与news_Content传入intent中,待Activity被创建时,显示到界面上。
在OnCreat( ) 方法中,得到news_Tiltle与news_Content,并新建对象newsContentFragment,同时调用refresh( )方法,刷新界面。
解释不正确,不清楚的地方,还请各位多多包涵。