Fragment基础(一)
-
Fragment的历史
-
在Android3.0的时候产生
- 让activity更好的配置界面
-
使用Fragment配置UI
- 例如常见应用中的底部导航栏
-
图示:
- 左侧(平板示例),红色大框是一个activity,里面的两个红框为fragment
- 右侧(手机端的新闻应用),两个界面都是fragment
-
-
Fragment特点
-
具有独立生命周期
-
可以在一个activity中组合多个Fragment,形成多窗口界面
-
一个Fragment可以在activity中重复使用
- 可以将Fragment看做activity中的一个模块
- 可以独立接收输入事件
- 可以activity正在运行的时候,动态增加或者删除Fragment
- 可以将Fragment看做子activity
-
-
必须有一个activity作为宿主
-
Fragment生命周期受宿主限制
- 当activity暂定/销毁,里面的Fragment的暂定/销毁
- 当activity运行时,Fragment可以执行它自己的操作(创建,暂定,销毁)
-
-
-
Fragment的简单使用
-
实现目标:
- 使用Fragment配合宿主activity,当点击按钮后实现对界面(TextView)进行更新
-
运行截图
-
点击前:
-
点击后
-
-
实现思路
-
创建一个宿主activity(MainActivity)
-
创建BasicFragment(继承Fragment,删除多余代码)
- 使用解析器解析fragment对应的布局文件
- 拿到布局中的控件
- 为响应控件添加监听
-
在res/layout中添加fragment对应的布局文件
- 添加控件(指定id,方便监听)
-
-
实现细节:
-
解析布局:
- activity使用setContentView解析布局
- fragment使用布局解析器解析布局
- 采用静态管理时,需要在activity_main.xml中绑定fragment
- 工程规范:在对控件的text进行赋值的时候,一般不使用明文赋值,而是在res/string.xml中进行处理
-
-
代码示例
-
工程结构(BlankFragment2暂时不用的)
-
MainActivity.java
package com.learningtogether.fragmentlearn; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);//解析xml } } -
BlankFragment1.java
package com.learningtogether.fragmentlearn; import android.os.Bundle; import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; public class BlankFragment1 extends Fragment { private View root; private TextView textView; private Button button; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { //因为onCreateView 是一个生命周期函数,所以在这个地方需要判断一下root是否为空;不为空,就没有必要重新加载了 if (root == null) {//这个地方为什么不能写成!root //在Fragment中调用解析器加载xml布局文件 root = inflater.inflate(R.layout.fragment_blank1, container, false); } //从root中拿到这两个控件 textView = root.findViewById(R.id.textview); button = root.findViewById(R.id.btn); //这个地方可以使用Lamda表达式进行简化 button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { textView.setText("这是按钮点击后的效果"); } }); return root; } } -
activity_main.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与activity通过name属性进行绑定--> <fragment android:id="@+id/fragment1" android:name="com.learningtogether.fragmentlearn.BlankFragment1" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" ></fragment> </LinearLayout> -
fragment_blank1.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="vertical" tools:context=".BlankFragment1"> <!-- TODO: Update blank fragment layout --> <TextView android:id="@+id/textview" android:layout_width="match_parent" android:layout_height="40dp" android:text="@string/hello_blank_fragment" /> <Button android:id="@+id/btn" android:layout_width="match_parent" android:layout_height="40dp" android:text="@string/greeting" ></Button> </LinearLayout>
-
-
-
在一个activity中添加两个Fragment
- 将那个fragment1复制一份命名为fragment2,然后在activity_main.xml中再添加一个fragment,并使用name属性将这个fragment2与MainActivity进行绑定
-
Fragment中需要添加id,这个fragment作为一个特殊的组件,如果想在activity中使用的话,就一定要搞一个id
-
动态替换fragment
-
实现效果:
-
主界面展示
-
点击CHANGE
-
点击REPLACE
-
-
实现思路
-
在main_activity .xml中添加两个按钮(进行页面切换),一个FragmentLayout(作为容器,用于存放动态添加的fragment)
-
在MainActivity中
-
onCreate方法中
-
MainActivity实现监听接口
-
获取控件实例并设置监听
-
添加点击事件onClick()
-
使用switch-case根据不同的控件id执行相应的逻辑
- 执行替换函数replaceFragment,将这个BlankFragment1传进去
- 执行替换函数replaceFragment,将新创建的ItemFragment传进去
-
-
-
onCreate方法外
-
创建replaceFragment函数(模拟Fragment切换)
- 实例化FragmentManager
- 通过FragmentManager获取触发器
- 使用触发器进行替换操作
- 提交事件
-
-
-
-
实现细节
-
触发器
- 对应的额事件:hide,remove,add,show
-
fragment有自己的管理栈
- 入栈:当点击上述两个按钮时,每点击一次,会创建一个Fragment并扔到这栈里面
- 出栈:点击手机的返回按钮
- 细节:当两个按钮各点击三次时,有六个fragment在管理栈中,点击六次返回键即可退回至主界面
-
-
代码示例
-
工程结构
-
MainActivity.java
package com.learningtogether.fragmentmanagers; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; public class MainActivity extends AppCompatActivity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //在java代码中创建控件实例并添加监听 Button button = findViewById(R.id.btn); button.setOnClickListener(this); Button button1 = findViewById(R.id.btn_2); button1.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.btn: //将这个绑好信息的Fragment进行传递 replaceFragment(new BlankFragment1()); break;//switch 一定要搭配这个break; case R.id.btn_2: replaceFragment(new ItemFragment()); break; } } //实现动态管理Fragment private void replaceFragment(Fragment fragment) { FragmentManager fragmentManager = getSupportFragmentManager(); //拿到fragmentManager触发器 FragmentTransaction transation = fragmentManager.beginTransaction(); transation.replace(R.id.framelayout,fragment);//创建了一个replace事件;将fragment添加到R.id.framelayout这个容器里面 transation.addToBackStack(null);//将Fragment放到同一个栈里面 transation.commit();//提交事件 } } -
BlankFragment.java(就使用默认的,创建出来就行了)
-
MyItemRecycleViewAdapter.java(就用默认的,创建出来就行了)
-
activity_main.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" > <Button android:id="@+id/btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/change" ></Button> <Button android:id="@+id/btn_2" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/replace" ></Button> <!--这个里面放布局--> <FrameLayout android:id="@+id/framelayout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/teal_200" ></FrameLayout> </LinearLayout> -
其余的三个布局就用自带的
-
-
-
Activity与fragment的通信
-
概述:
-
Activity与Fragment是独立的类,但是他们承担相同职责:展示UI
-
通信分类:
- Activity与Fragment
- Fragment与Fragment
- Activity与Activity
- 原生方案:Bundle通信
-
-
通信实例(以fragment为载体)
-
步骤:
-
实例化Bundle对象
-
对象调用方法(put函数,以键值对的形式传递数据)
-
实例化Fragment
-
将数据绑定在Fragment对象上
-
调用函数,使用Fragment对象
-
-
实现细节:
-
Bundle对象的put方法:
- 当需要将一个java Bean绑定在fragment上进行数据传输时,这个时候就需要调用putParcelable,将java Bean作为一个key进行传输,这个涉及到java序列化
-
-
-
-
实现Activity向fragment传递信息(使用Bundle通信机制)
-
实现效果
- 当用户点击屏幕按钮时,触发相应监听事件,在日志中打印信息
-
运行截图
-
主界面
-
点击CHANGE后对应的Logcat日志
-
-
实现思路
-
创建MainActivity,创建Fragment,(使用的是动态添加Fragment,不需要在activity.xml中通过name 属性绑定fragment)
-
在activity_main.xml中,搞一个按钮(触发监听的),搞一个FragmentLayout(装动态添加进来的Fragment)
-
在MainActivity中实例化控件并添加监听
-
监听器:
- 实例化Bundle
- 调用put族方法,以键值对传递信息
- 实例化BlankFragment
- 将数据绑定在BlankFragment对象上
- 将BlankFragment对象作为参数传入replaceFragment中,实现动态添加fragment
-
实现动态添加Fragment
- 实例化FragmentManager
- 获取触发器
- 触发器调用函数,创建事件
- 触发器提交事件
-
-
-
实现细节
-
代码示例
-
工程结构
-
MainActivity.java
package com.learningtogether.test; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; public class MainActivity extends AppCompatActivity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //在java代码中实例化控件并添加监听 Button button = findViewById(R.id.btn); button.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.btn: //实例化Bundle Bundle bundle = new Bundle(); //bundle以键值对的形式传递信息 bundle.putString("message", "这个是从MainActivity中传过来的信息"); //实例化Fragment BlankFragment1 bf = new BlankFragment1(); //将数据绑定在Fragment 对象上 bf.setArguments(bundle); //调用函数,实现动态添加Fragment replaceFragment(bf); } } //实现动态管理Fragment private void replaceFragment(Fragment fragment) { //实例化 FragmentManager FragmentManager fragmentManager = getSupportFragmentManager(); //实例化FragmentTransaction,拿到fragmentManager触发器 FragmentTransaction transation = fragmentManager.beginTransaction(); //触发器调用相应函数,创建事件 transation.replace(R.id.framelayout, fragment); transation.commit();//提交事件 } } -
BlankFragment1.java
package com.learningtogether.test; import android.annotation.SuppressLint; import android.os.Bundle; import androidx.fragment.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.Toast; public class BlankFragment1 extends Fragment { private static final String TAG = "BlankFragment1"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //获取的是伴随Fragment产生的参数 Bundle bundle = this.getArguments();//返回activity中传入的bundle //获取bundle中的数据,需要传入key String str = bundle.getString("message"); //打印信息 Log.d(TAG,"onCreate "+str); } } -
activity_main.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" > <Button android:id="@+id/btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/change" ></Button> <!--这个里面放布局--> <FrameLayout android:id="@+id/framelayout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/teal_200" ></FrameLayout> </LinearLayout> -
fragment_balnk1.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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".BlankFragment1"> <!-- TODO: Update blank fragment layout --> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="@string/hello_blank_fragment" /> </FrameLayout>
-
-
-
动态添加Fragment
-
步骤
-
-
通过接口实现通信(java的类间通信)
-
实现activity与fragment进行通信
-
定义一个接口
-
设计原则(面向接口编程):不应该有多余的接口
- activity向fragment发送信息
- fragment向activity发送信息
-
-
eventBus,LiveData(其他的通信模式,fragment或者activity只关心其感兴趣的方式)
- 这个里面会有发布订阅模式,LifeCycle模式
-
-
工程结构
-
实现效果:
- 从Fragment向Activity发送消息
- 在Fragment接收Activity的消息
-
运行截图:
-
从Fragment向Activity发送消息
-
在Fragment接收Activity的消息
-
-
代码实现:
-
BlankFragment1.java
package com.learningtogether.fragmentmanagers; import android.annotation.SuppressLint; import android.nfc.Tag; import android.os.Bundle; import androidx.fragment.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.Toast; public class BlankFragment1 extends Fragment implements View.OnClickListener{ private static final String TAG = "BlankFragment1"; /* 优化思路1: * 在解析fragment布局时为什么要使用全局变量? * 因为可能出现BalankFragment中的onCreate方法调用多次的可能 * 如果使用局部变量,那么每调用一次onCreate方法时就会重新解析一遍fragment布局文件 * 使用全局变量+判空即可实现,只在首次调用onCreate方法的时候会进行布局文件的解析*/ private View rootView; public BlankFragment1() { // Required empty public constructor } /*为什么要在Fragment中去声明接口对象? 结合这个接口中的方法名,要想实现activity与fragment双向通信等价于 实现fragment向activity发送数据 实现fragment可以从activity拿数据*/ private IFragmentCallBack fragmentCallBack; /* 承接来自Activity中的匿名内部类--->就是这个接口实例 在Fragment中声明这个方法,即可将其绑定在Fragment上, 在Activity中仅需实例化Fragment对象即可调用这个方法, 配合接口的匿名内部类即可在activity中重写接口方法 此时就形成了一个很牛逼的现象,就是在Fragment与Activity中都可以处理这个接口对象; */ public void setFragmentCallBack(IFragmentCallBack callBack) { fragmentCallBack=callBack; } @SuppressLint("LongLogTag") @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //获取的是伴随Fragment产生的参数 Bundle bundle = this.getArguments();//返回activity中传入的参数 bundle.getString("message"); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (rootView == null){ rootView = inflater.inflate(R.layout.fragment_blank1, container, false); } //实例化fragment布局文件中的控件,并添加监听 Button btn = rootView.findViewById(R.id.btn_getMsgFromActivity); btn.setOnClickListener(this); Button btn1 = rootView.findViewById(R.id.btn_sendMsgToActivity); btn1.setOnClickListener(BlankFragment1.this); return rootView; } @Override public void onClick(View view) { switch (view.getId()){ case R.id.btn_getMsgFromActivity: String msg = fragmentCallBack.getMsgFromActivity("null"); Toast.makeText(BlankFragment1.this.getContext(),msg,Toast.LENGTH_SHORT).show(); break; case R.id.btn_sendMsgToActivity: fragmentCallBack.sendMsgToActivity("hello,I am from fragment"); break; } } //一般都是在onResume函数中去启动Button或者在onCreateView后面弄 @Override public void onResume() { super.onResume(); } } -
IFragmentCallBack.java
package com.learningtogether.fragmentmanagers; /* * 设计原则: * 面向接口编程;用得到才搞接口; * 接口的默认修饰符,通过接口实现弱关联,实现通信*/ public interface IFragmentCallBack { void sendMsgToActivity(String string); String getMsgFromActivity(String msg); } -
MainActivity.java
package com.learningtogether.fragmentmanagers; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; public class MainActivity extends AppCompatActivity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //在java代码中创建控件实例并添加监听 Button button = findViewById(R.id.btn); button.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.btn: //实例化Bundle对象 Bundle bundle = new Bundle(); //bundle以键值对的形式传递信息 bundle.putString("message","I LOVE YOU"); //实例化Fragment BlankFragment1 bf = new BlankFragment1(); //将数据绑定在Fragment上 bf.setArguments(bundle); //在activity中实例化了一个fragment对象,因为fragment中有接口对象的实例和方法,等效于activity也有了 //在activity中对接口对象赋值,配合接口匿名内部类; bf.setFragmentCallBack(new IFragmentCallBack() { @Override public void sendMsgToActivity(String msg) { //这里第一个参数写成this会出现问题的,this,代表的是这个匿名内部类了 Toast.makeText(MainActivity.this,msg,Toast.LENGTH_LONG).show(); } @Override public String getMsgFromActivity(String msg) { return "hello,I`m from Activity "; } }); //将这个绑好信息的Fragment进行传递 replaceFragment(bf); break;//switch 一定要搭配这个break; } } //实现动态管理Fragment,好好研究一下fragment的触发器 private void replaceFragment(Fragment fragment) { FragmentManager fragmentManager = getSupportFragmentManager(); //拿到fragmentManager触发器 FragmentTransaction transation = fragmentManager.beginTransaction(); transation.replace(R.id.framelayout,fragment);//创建了一个replace事件 transation.addToBackStack(null);//将Fragment添加到同一任务栈中 transation.commit();//提交事件 } } -
activity_main.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" > <Button android:id="@+id/btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/change" ></Button> <!--这个里面放布局--> <FrameLayout android:id="@+id/framelayout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/teal_200" ></FrameLayout> </LinearLayout> -
fragment_blank1.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" > <Button android:id="@+id/btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/change" ></Button> <!--这个里面放布局--> <FrameLayout android:id="@+id/framelayout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/teal_200" ></FrameLayout> </LinearLayout>
-
\
-
as使用
-
恢复误删除的文件(红色框指定的包下的文件)
-
选中需要恢复的包
-
file->LocalHistory->show history
-
选中删除事件,点击左上角撤销即可恢复对应的删除事件(一个一个恢复)
-
恢复后的工程结构
-
-