Fragment
什么是Fragment
Fragment的设计是为了解决不同分辨率的终端适配问题,下面这个图是从官方文档截取过来的,体现了这一设计思想。
Fragment和Activity的对比
- Fragment是Android3.0以后才出现的;
- 一个Activity可以运行多个Fragment;
- Fragment不能脱离Activity而存在;
- Activity是屏幕的主体,而Fragment是Activity的一个组成元素;
Fragment的创建
静态创建
这里要注意的是在主布局文件中声明的fragment必须要加上id,否则会出现问题。第二fragment导入的包是android.app.Fragment,千万不要导入androidx.fragment.app.Fragment。
静态创建其实指的就是使用xml文件来创建fragment,然后通过创建类继承Fragment加载自己创建的fragment的布局文件,最终就可以在主界面上显示fragmetn了。
主布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="horizontal">
<fragment
android:id="@+id/fragment1"
android:name="com.example.fragmentdemo.MyFirstFragment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<fragment
android:id="@+id/fragment2"
android:name="com.example.fragmentdemo.MySecondeFragmment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"/>
</LinearLayout>
MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
fragment1布局文件
<?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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="this is first fragment"/>
</LinearLayout>
MyFirstFragment
public class MyFirstFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, null);
return view;
}
}
fragment2布局文件
<?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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="this is second fragment"/>
</LinearLayout>
MySecondFragment
public class MySecondeFragmment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment2, null);
return view;
}
}
动态创建
fragment的动态创建指的是在加载的时候动态地把fragment挂载到指定的ViewGroup上。
布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity2">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="这是一个TextView"
android:textSize="30sp"/>
<LinearLayout
android:id="@+id/ll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv"
android:orientation="vertical"></LinearLayout>
</RelativeLayout>
MainActivity2
在创建的时候首先会通过 getWindowManager().getDefaultDisplay()获取屏幕的宽度和高度,从而判断是横屏还是竖屏,如果是横屏就显示MyFirstFragment,如果是竖屏就显示MySecondeFragmment。
public class MainActivity2 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
int width = getWindowManager().getDefaultDisplay().getWidth();
int height = getWindowManager().getDefaultDisplay().getHeight();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
if (width > height) {
//横屏
//把fragment对象替换到viewgroup节点下
//第一个参数:用来放置fragment的viewgroup的id
//第二个参数:要显示的fragment对象
//这里也可以使用add()方法达到同样的效果,remove()方法则会移除这个fragment
transaction.replace(R.id.ll, new MyFirstFragment());
} else {
//竖屏
transaction.replace(R.id.ll, new MySecondeFragmment());
}
transaction.commit();
}
}
fragment添加点击事件的写法
fragment的生命周期
onAttach():建立Fragment和Activity之间的联系;
onCreateView必须重写,通过这个方法加载界面,进行初始化操作;
onDestory,如果gragment占用了某些资源,在onDestory方法中要释放资源;
onStop/onPause可以进行数据保存的操作;
fragment之间的通信
直接使用new fragment()创建的fragment对象是不会走onCreateView方法的。我们需要使用getActivity().getFragmentManager()来得到FragmentManager对象,然后通过对象的findFragmentByTag()方法去获取指定tag的Fragment对象。
MainActivity4.java
public class MainActivity4 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.ll1,new MyFirstFragment(),"first");
transaction.replace(R.id.ll2,new MySecondeFragmment(),"second");
transaction.commit();
}
}
fragment2.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">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="this is second fragment"/>
</LinearLayout>
fragment1.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">
<Button
android:id="@+id/btn_change"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40sp"
android:text="点击修改文字"/>
</LinearLayout>
MyFirstFragment.java
这里要注意的是不能直接使用findViewById()而要使用view.findViewById(),因为这个控件是在view这个视图里面的。
public class MyFirstFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, null);
Button button = view.findViewById(R.id.btn_change);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentManager fragmentManager = getActivity().getFragmentManager();
MySecondeFragmment secondeFragmment = (MySecondeFragmment)fragmentManager.findFragmentByTag("second");
secondeFragmment.changeText("hahahah");
}
});
return view;
}
}
MySecondeFragmment.java
public class MySecondeFragmment extends Fragment {
private TextView textView;
View view;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment2, null);
return view;
}
public void changeText(String str) {
textView = view.findViewById(R.id.tv);
textView.setText(str);
}
}
Fragment和Activity之间的传值
Activity向Fragment传值,这个时候需要在Fragment中提供一个静态的构造方法,然后利用setArguments()方法进行传值。
ListFragment 类
public class ListFragment extends Fragment {
private static final String TAG = "ListFragment";
public static final String TITLE = "title";
private String mTitle;
public static ListFragment newInstance(String title) {
ListFragment fragment = new ListFragment();
Bundle bundle = new Bundle();
bundle.putString(TITLE,title);
//设置参数
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
/**
* 创建视图
* @param inflater
* @param container
* @param savedInstanceState
* @return
*/
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_test, container, false);
TextView textView = view.findViewById(R.id.textView);
//将获取到的参数设置为TextView控件的text属性的值
textView.setText(mTitle);
return view;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//获取在newInstance()方法中设置的参数
if (getArguments() != null) {
mTitle = getArguments().getString(TITLE);
}
}
@Override
public void onStart() {
super.onStart();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onStop() {
super.onStop();
}
@Override
public void onDestroyView() {
super.onDestroyView();
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
fragment_test.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">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="this is a fragment"
android:textSize="20sp"/>
</RelativeLayout>
StaticLoadFragmentActivity类
public class StaticLoadFragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_static_load_fragment);
//动态创建Fragment
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.ll, ListFragment.newInstance("dafawawafaf"));
transaction.commit();
ListFragment listFragment = ListFragment.newInstance("wterhehe");
getFragmentManager().beginTransaction().
add(R.id.ll1,listFragment).
commit();
}
}
activity_static_load_fragment.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".StaticLoadFragmentActivity"
android:orientation="vertical">
<TextView
android:text="StaticLoadFragmentActivity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<fragment
android:id="@+id/listFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:name="com.example.fragment.ListFragment"/>
<LinearLayout
android:id="@+id/ll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000000"/>
<LinearLayout
android:id="@+id/ll1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>
Fragment向Acticity传值,这个时候需要在Fragment中创建接口,然后通过回调方法完成传值操作。
在ListFragment类中添加以下代码
//设置接口的方法
public void setOnTitleClickListener(OnTitleClickListener ClickListener) {
onTitleClickListener = ClickListener;
}
//定义变量
private OnTitleClickListener onTitleClickListener;
//定义接口
public interface OnTitleClickListener {
void onClick(String title);
}
同时修改onCreateView()方法,其实就是增加了给TextView设置点击事件监听的代码,设置监听的时候判断onTitleClickListener 是否为null,不为null就给TextView设置点击事件监听回调方法。
/**
* 创建视图
* @param inflater
* @param container
* @param savedInstanceState
* @return
*/
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_test, container, false);
TextView textView = view.findViewById(R.id.textView);
textView.setText(mTitle);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (onTitleClickListener != null) {
onTitleClickListener.onClick(mTitle);
}
}
});
return view;
}
修改StaticLoadFragmentActivity类
public class StaticLoadFragmentActivity extends AppCompatActivity implements ListFragment.OnTitleClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_static_load_fragment2);
//动态创建Fragment
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.ll, ListFragment.newInstance("dafawawafaf"));
transaction.commit();
ListFragment listFragment = ListFragment.newInstance("wterhehe");
getFragmentManager().beginTransaction().
add(R.id.ll1,listFragment).
commit();
listFragment.setOnTitleClickListener(StaticLoadFragmentActivity.this);
}
@Override
public void onClick(String title) {
setTitle(title);
}
}
Fragment和Fragment之间的传值和Fragment向Activity传值的方法是一样的,都是通过设置回调方法来实现。