0%

Fragment入门实战收获

今天又是写了一个下午的bug。
这一次是要用Fragment实现如下界面,并且还要同时兼容手机和平板。抽象逻辑实在复杂(其实还好,只是我发现自己写的和书里的不同然后被吓回去了),我这样的菜狗挣扎了一会,还是得照着书敲了,确实有很多需要我学习的东西,所以这回就直接放代码,学习一下大佬的代码和逻辑。

MainActivity.java

1
2
3
4
5
6
7
8
9
10
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

News.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class News {
private String title;
private String content;

public String getTitle() {
return title;
}

public String getContent() {
return content;
}

public void setTitle(String title) {
this.title = title;
}

public void setContent(String content) {
this.content = content;
}
}

NewsContentActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import android.content.Context;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

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");
NewsContentFragment newsContentFragment = (NewsContentFragment) getSupportFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(newsTitle, newsContent);
}
}

NewsContentFragment.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class NewsContentFragment extends Fragment {

private View view;
TextView newsTitleText;
TextView newsContentText;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.news_content_frag, container, false);
newsTitleText = (TextView) view.findViewById(R.id.news_title);
newsContentText = (TextView) view.findViewById(R.id.news_content);
return view;
}
public void refresh(String newsTitle, String newsContent) {
View visibilityLayout = view.findViewById(R.id.visibility_layout);
visibilityLayout.setVisibility(View.VISIBLE);

newsTitleText.setText(newsTitle);
newsContentText.setText(newsContent);
}
}

NewsTitleFragment.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

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

public class NewsTitleFragment extends Fragment {

private boolean isTwoPane;
private List<News> list = new ArrayList<News>();
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveIntanceState) {

View view = inflater.inflate(R.layout.news_title_frag, container, false);

RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.news_title_recycler_view);
LinearLayoutManager manager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(manager);
initList();
NewsAdapter newsAdapter = new NewsAdapter(list);
recyclerView.setAdapter(newsAdapter);

return view;
}

public void initList(){
News news;
String title;
String content = "";
for(int i = 1; i <= 20 ;i++){
title = "title"+i;
for(int j = 0; j < 50;j++){
content = content + "title" + i + " ";
}
news = new News();
news.setTitle(title);
news.setContent(content);
list.add(news);
}
}

public void onActivityCreated(Bundle saveInstanceState) {
super.onActivityCreated(saveInstanceState);
if(getActivity().findViewById(R.id.news_content_layout) != null) {
isTwoPane = true;
}
else{
isTwoPane = false;
}
}


class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {
private List<News> list;

public NewsAdapter(List<News> list) {
this.list = list;
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView newsTitleText;

public ViewHolder(View v){
super(v);
newsTitleText = (TextView) v.findViewById(R.id.news_title);
}
}

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(){
public void onClick(View v){
News news = list.get(holder.getAdapterPosition());
if(isTwoPane){
NewsContentFragment newsContentFragment = (NewsContentFragment) getFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(news.getTitle(), news.getContent());
}
else{
NewsContentActivity.actionStart(getActivity(), news.getTitle(), news.getContent());
}
}
});
return holder;
}

public void onBindViewHolder(ViewHolder holder, int position){
News news = list.get(position);
holder.newsTitleText.setText(news.getTitle());
}
public int getItemCount(){
return list.size();
}

}


}


layout/activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
<?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.fragmentbestpractice.NewsTitleFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

layout-large/activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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.fragmentbestpractice.NewsTitleFragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/news_content_layout"
android:layout_weight="3"
android:layout_width="0dp"
android:layout_height="match_parent">
<fragment
android:id="@+id/news_content_fragment"
android:name="com.example.fragmentbestpractice.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
</LinearLayout>

news_content_frag.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?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:orientation="vertical"
android:id="@+id/visibility_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"

android:visibility="invisible">
<TextView
android:text="Title"
android:padding="10dp"
android:textSize="20sp"
android:gravity="center"
android:id="@+id/news_title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<View
android:background="#000"
android:layout_width="match_parent"
android:layout_height="1dp" />
<TextView
android:text="Content"
android:textSize="18sp"
android:id="@+id/news_content"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<View
android:layout_width="1dp"
android:layout_alignParentLeft="true"
android:layout_height="match_parent"/>

</RelativeLayout>

news_content.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
<?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:name="com.example.fragmentbestpractice.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

</LinearLayout>

news_item.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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:maxLines="1"
android:ellipsize="end"
android:textSize="18sp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="15dp"
android:paddingBottom="15dp"/>

news_title_frag.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
<?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">

<android.support.v7.widget.RecyclerView
android:id="@+id/news_title_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

</LinearLayout>

首先这一次的MainActivity中没有任何东西,就只有onCreate和setContentView方法,那代码到底怎么运行。

  • 我发现,在activity_main中,用fragment的name属性指定了一个fragment包类NewsTitleFragment,而且基本所有的行为都是在这个类中执行调用的。
  • 在这个NewTitleFragment中,首先是调用onCreateView加载了我们打开应用时看到的第一个界面,即一个title列表。
  • 接着随即在onCreateView方法中数据进行初始化,这里就和我一般的操作不同,一般我在Fragment类中,只加载碎片布局,而初始化列表数据,更是放在MainActivity中。而且还在这里定义并调用了列表的Adapter配适器。
  • 这说明其实安卓代码的定义和调用其实都十分灵活,应该根据每次开发时的情况去定义,使代码更加简洁易懂就行。
    onActivityCreated中通过是否能找到new_content_layout标签来判断当前是单页界面还是双页界面。

  • 在Fragment类中加载fragment布局的时候,曾遇到个bug,是我在抄书的时候出了差错。就是加载布局后,我想顺便通过findViewById获取界面里的控件实例,但是一直报NullPoint空指针错误,想了半天一直觉得是id没对上。。
    想了一个下午无果,给大佬学长一看,一眼就断定是findViewById获取不到,看了一下,原来我的View是个类,而我应该用的是view的实例,才能获取到view里的控件,而刚好在同类的另一个方法中我加载了一个view,于是将view定义为类变量,用获取到的布局给view初始化。果然问题就解决了!

woc果然是大佬,我还是太太菜了。

  • 然后还有一个问题,就是Activity如果是自己定义的话,将不会自动在ActivityManifest注册(废话)。而一个活动如果没有被注册,则它在调用时将会报错,报错信息也很明显了,就是Do you 确定 Activity has been define? 然而四级都还没过的我并没有注意到这个问题,后来才被一个大佬解决,当然,大佬六级已经过了。。。

然后我觉得很该学习的,是他的逻辑,也就是他将一个需求抽象出来的思路。下面梳理一遍。

  • 启动程序以后,MainActivity加载一个activity_main,而主活动有两个,根据设备的具体参数决定加载普通的活动界面还是large界面。
    main_1中由一个fragment指定加载了NewsTitleFragment;main_2中第一个fragment也加载了NewsTitleFragment,第二个布局是FrameLayout,里边也有一个fragment,指定加载的是NewsContentFragment类。
  • NewsTitleFragment类首先是初始化了一下初始的数据。然后在onActivityCreated方法中getActivity().findViewById(R.id.news_content_layout)判断当前是否分屏,保证该判断在活动被启动时能被调用。方法中先用getActivity()获得当前view实例,再尝试用findViewById在该view中找到news_content_layout,这是large_main中那个FrameLayout的id名。也就是说,如果find到的对象是null,那么意味着当前应为分屏状态的界面。如果能够找到(即!null),则为单界面。判断结果赋给一个全局的布尔变量。
  • 由于在列表布局的必要方法onCreateViewHolder中,通过inflate获得了列表子项的布局,所以顺便就在这里设置了子项的点击事件:如果是分屏的话,就在右边加载Content;否则就启动另一个活动,显示content内容。这里就用到了刚刚的全局布尔变量。

基本就是这样子。

这样仔细地剖析,也是希望自己能在一次一次的学习中,get到一些大佬的抽象思维和一个需求功能落地的过程,让自己更快的体会开发的过程吧。。


完结 撒花 ฅ>ω<*ฅ