Android之 fragment页面碎片详解

news2024/11/19 18:28:11

一 简介

1.1 Fragment是Android3.0新增的概念,中文意思是“碎片”,它与Activity非常相似,是用来描述一些行为或者一部分用户界面

1.2 可以在一个单独的Activity中建立多个Fragment面板,也可以在多个Activity中复用Fragment

1.3 Fragment拥有自己的生命周期和接收、处理用户的事件,可以动态的添加、替换和移除某个Fragment

1.4 Fragment必须总是被嵌入到一个Activity中,它的生命周期受宿主Activity生命周期影响,它的状态会随宿主的状态变化而变化

1.5 要创建一个Fragment 必须创建一个Fragment的子类,或者继承自另一个已经存在的Fragment的子类,并重写onCreateView()方法加载UI

二 Fragment生病周期

图示:

Fragment生命周期

  • onAttach():Fragment和Activity相关联时调用。可以通过该方法获取Activity引用,还可以通过getArguments()获取参数。
  • onCreate():Fragment被创建时调用。
  • onCreateView():创建Fragment的布局。
  • onActivityCreated():当Activity完成onCreate()时调用。
  • onStart():当Fragment可见时调用。
  • onResume():当Fragment可见且可交互时调用。
  • onPause():当Fragment不可交互但可见时调用。
  • onStop():当Fragment不可见时调用。
  • onDestroyView():当Fragment的UI从视图结构中移除时调用。
  • onDestroy():销毁Fragment时调用。
  • onDetach():当Fragment和Activity解除关联时调用。

三  Fragment 静态使用

3.1 使用步骤实战:

第一步:创建fragment

public class Fragment1 extends Fragment {
    private TextView tvText;

 
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment1, container, false);

        tvText = (TextView) view.findViewById(R.id.tv_text);
        return view;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    
    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
    }

    @Override
    public void onStart() {
        super.onStart();
    }

    @Override
    public void onResume() {
        super.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    @Override
    public void onStop() {
        super.onStop();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
    }

    @Override
    public void onDetach() {
        super.onDetach();
    }
}

 第二步:创建Activity

public class TestActivity extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
    }
}

第三步:在activity的布局文件xml里面设置fragment

<?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">
    
    <fragment
        android:id="@+id/fragment1"
        android:name="com.dinghe.servicetest.fragment.Fragment1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    
</LinearLayout>

3.2 注意:

  • 通过name属性将Fragment引入Activity布局,被引入的Fragment要写全路径
  • fragment必须指定id,否则报错

四 Fragment的动态使用

4.1 使用步骤

第一步:还是创建fragment

public class Fragment1 extends Fragment {
    private TextView tvText;

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment1, container, false);

        tvText = (TextView) view.findViewById(R.id.tv_text);
        return view;
    }
}

第二步: 创建Activity的布局文件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">

    <FrameLayout
        android:id="@+id/fl_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
		
</LinearLayout>

第三步:创建Activity,并在上面的fl_content里面添加fragment

public class TestActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        Fragment1 fragment1 = new Fragment1();
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.replace(R.id.fl_content, fragment1);
        transaction.commit();
    }
}

4.2 使用流程总结:

  • 获取到FragmentManager,在Activity中可以直接通过getSupportFragmentManager得到
  • 开启一个事务,通过调用getSupportFragmentManager().beginTransaction()方法开启
  • 向容器内加入Fragment,一般使用replace方法实现,需要传入容器的id和Fragment的实例
  • FragmentTransaction提交事务,调用commit方法提交

五 Fragment的add使用

5.1 除了上面的 transaction.replace() 添加Fragment方法,还有 transaction.add() 也可以添加,添加之后就可以用 transaction.show() 和 transaction.hide() 来控制Fragment显示和隐藏

5.2 那replace和add方法有什么区别呢?

replace是覆盖的意思,使用该方式会替换原有的Fragment,这就意味着原来的Fragment会结束其生命周期,缓存会被清理,会释放内存。

add是添加的意思,可以添加很多个Fragment,通过show和hide来控制显示和隐藏,所以该方式会缓存Fragment的全部内容,这也会导致内存会累加,占用大量内存。

总结起来,一个Fragment两者基本没区别,多个Fragment:

  • replace可以节省内存,但多个fragment频繁创建和删除会造成性能的开销大。
  • 而add可以缓存Fragment,性能流畅,但也伴随着内存的增加,可能发生OOM风险。

5.3 使用:

第一步:创建两个Fragment

public class Fragment1 extends Fragment {
    private TextView tvText;



    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment1, container, false);

        tvText = (TextView) view.findViewById(R.id.tv_text);
        return view;
    }
}
public class Fragment2 extends Fragment {
    private TextView tvText;



    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment1, container, false);

        tvText = (TextView) view.findViewById(R.id.tv_text);
        return view;
    }
}

第二步: 一样创建Activity的布局文件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">


    <FrameLayout
        android:id="@+id/fl_content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/button1"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="50dp"
            android:layout_margin="10dp"
            android:text="页面1"/>
        <Button
            android:id="@+id/button2"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="50dp"
            android:layout_margin="10dp"
            android:text="页面2"/>
    </LinearLayout>
</LinearLayout>

第三步:Activity添加Fragment,并控制显示隐藏

public class TestActivity extends AppCompatActivity implements View.OnClickListener {
    private FrameLayout flContent;
    private Button button1;
    private Button button2;



    private Fragment1 fragment1;
    private Fragment2 fragment2;

    private  FragmentManager fragmentManager;
    private FragmentTransaction transaction;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        flContent = (FrameLayout) findViewById(R.id.fl_content);
        button1 = (Button) findViewById(R.id.button1);
        button2 = (Button) findViewById(R.id.button2);

        button1.setOnClickListener(this);
        button2.setOnClickListener(this);

    }


    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.button1:
                //先隐藏全部Fragment
                hideFragment();
                //添加并显示Fragment1
                fragmentManager = getSupportFragmentManager();
                transaction = fragmentManager.beginTransaction();
                if(fragment1==null){
                    fragment1 = new Fragment1();
                    transaction.add(R.id.fl_content, fragment1);
                }
                transaction.show(fragment1);
                transaction.commit();
                break;
            case R.id.button2:
                //先隐藏全部Fragment
                hideFragment();
                //添加并显示Fragment2
                fragmentManager = getSupportFragmentManager();
                transaction = fragmentManager.beginTransaction();
                if(fragment2==null){
                    fragment2 = new Fragment2();
                    transaction.add(R.id.fl_content, fragment2);
                }
                transaction.show(fragment2);
                transaction.commit();
                break;
        }
    }

    /**
     * 隐藏全部Fragment
     */
    private void hideFragment(){
        if(fragment1!=null){
            transaction.hide(fragment1);
        }

        if(fragment2!=null){
            transaction.hide(fragment2);
        }
        transaction.commit();
    }
}

5.4 使用注意:

  • 判断Fragment是否被添加过,没添加要先add,再show,避免重复添加
  • show之前要先隐藏其它Fragment,否则可能出现页面重叠
  •  每次提交commit()之后,再add,show,hide都需要新开始一个事务beginTransaction(),否则会报异常

六 Fragment与viewpager的使用

6.1 上面是通过按钮控制Fragment的显示和隐藏,还有一种配合ViewPager的使用,可以做左右滑动切换布局的效果,不用再手动控制添加,显示和隐藏,完全通过ViewPager的滑动状态来自动完成。

6.2 我们经常使用ListView,RecycleView等滑动控件,都有自己的Adapter适配器。ViewPager也是滑动控件,也有自己的适配器PagerAdapter,可以加载任何控件,比如VIew,ImageView,TextView等。如下PagerAdapte简单示例:

public class MyViewPagerActivity extends Activity {
    private ViewPager viewPager;


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

        viewPager = (ViewPager) findViewById(R.id.view_pager);

        List<View> viewList = new ArrayList<>();
        viewList.add(new View(this));
        viewList.add(new View(this));
        ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(viewList);
        //设置ViewPager适配器
        viewPager.setAdapter(viewPagerAdapter);
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                //监听滑动距离
            }

            @Override
            public void onPageSelected(int position) {
                //监听滑动页码
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                //监听滑动状态,滑动中还是滑动结束
            }
        });
    }


    /**
     * ViewPager适配器
     */
    private class ViewPagerAdapter extends PagerAdapter {
        private List<View> viewList;

        public ViewPagerAdapter(List<View> viewList) {
            this.viewList = viewList;
        }

        // 获取要滑动的控件的数量,在这里我们以滑动的广告栏为例,那么这里就应该是展示的广告图片的ImageView数量
        @Override
        public int getCount() {
            return viewList.size();
        }

        // 来判断显示的是否是同一张图片,这里我们将两个参数相比较返回即可
        @Override
        public boolean isViewFromObject(View arg0, Object arg1) {
            return arg0 == arg1;
        }

        // PagerAdapter只缓存三张要显示的图片,如果滑动的图片超出了缓存的范围,就会调用这个方法,将图片销毁
        @Override
        public void destroyItem(ViewGroup view, int position, Object object) {
            view.removeView(viewList.get(position));
        }

        // 当要显示的图片可以进行缓存的时候,会调用这个方法进行显示图片的初始化,我们将要显示的ImageView加入到ViewGroup中,然后作为返回值返回即可
        @Override
        public Object instantiateItem(ViewGroup view, int position) {
            view.addView(viewList.get(position));
            return viewList.get(position);
        }
    }

}

6.3 上面示例是ViewPager加载View控件,那ViewPager加载Fragment也是有适配器的,即FragmentPagerAdapter,它继承于上面说的PagerAdapter,专门用于加载Fragment。介绍如下:

  • FragmentPagerAdapter派生自PagerAdapter,它是专门用来呈现 Fragment页面的。
  • 该类中每一个生成的Fragment都将保存在内存中,所以这个适配器更适合那些数量相对较少,静态的页面。对于存在多个fragment的情况,一般推荐使用FragmentStatePagerAdapter。
  • FragmentPagerAdapter重载了几个必须实现的函数:getItem()、 getCount()。

6.4  我们常见的Activity种类有,Activity,FragmentActivity和AppCompatActivity,这对于Fragment的支持也不同,如下区别:

  • Activity是最基础的页面类,对应getFragmentManager方法来控制Activity和 Fragment之间的交互。
  • FragmentActivity间接继承自Activity,并提供了对v4包中support Fragment的支持,在FragmentActivity中必须使用getSupportFragmentManager方法来处理 support Fragment的交互。
  • AppCompatActivity继承自FragmentActivity,为Material Design风格控件提供 了便利。

6.5 示例:

第一步:还是创建两个Fragment

public class Fragment1 extends Fragment {
    private TextView tvText;



    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment1, container, false);

        tvText = (TextView) view.findViewById(R.id.tv_text);
        return view;
    }
}
public class Fragment2 extends Fragment {
    private TextView tvText;



    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment1, container, false);

        tvText = (TextView) view.findViewById(R.id.tv_text);
        return view;
    }
}

 第二步:创建ViewPager布局

<?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">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

第三步:Activity里面加载 FragmentPagerAdapter 适配器

public class MyFragmentViewPagerActivity extends AppCompatActivity {
    private ViewPager viewPager;


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

        viewPager = (ViewPager) findViewById(R.id.view_pager);

        List<Fragment> viewList = new ArrayList<>();
        viewList.add(new Fragment1());
        viewList.add(new Fragment2());
        MyFragmentPagerAdapter viewPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager(),viewList);
        //设置ViewPager适配器
        viewPager.setAdapter(viewPagerAdapter);
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                //监听滑动距离
            }

            @Override
            public void onPageSelected(int position) {
                //监听滑动页码
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                //监听滑动状态,滑动中还是滑动结束
            }
        });
    }

    /**
     * Fragment适配器
     */
    public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
        //1.创建Fragment数组
        private List<Fragment> mFragments;
        
        //2.接收从Activity页面传递过来的Fragment数组
        public MyFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments){
            super(fm);
            mFragments = fragments;
        }
        @Override
        public Fragment getItem(int position) {
            return mFragments.get(position);
        }
        @Override
        public int getCount() {
            return mFragments.size();
        }
    }

}

6.6 可以看到 PagerAdapter 和 FragmentPagerAdapter 的用法几乎一样,只是把集合View换成了Fragment

七 Fragment事务Transaction详解

7.1 add()

添加一个Fragment到Activity中。

7.2 remove()

从Activity中移除一个Fragment。如果Fragment没有被加入回退栈中,则该Fragment会进行销毁。

7.3 replace()

用另一个Fragment替换当前的Fragment,本质上时先remove再add。

7.4 hide()

隐藏当前的Fragment,设置为不可见,不会销毁,与之对应的是show。本质是对View进行调用了View.GONE。

7.5 show()

显示之前隐藏的Fragment,与之对应的是hide。本质是对View进行调用了View.VISIBLE。

7.6 detach()

将视图进行移除,但是并不销毁Fragment,Fragment依旧由FragmentManager进行管理。

7.7 addToBackStack()

将Fragment加入返回栈。当移除或替换一个Fragment并向返回栈添加事务时,系统会停止(而非销毁)移除的Fragment。如果没有addToBackStack,则被替换的Fragment会直接进行销毁。

//示例,如果用户执行回退操作进行Fragment的恢复,被替换的Fragment将重新启动。
transaction.replace(R.id.fl_content, LearnFragment()).addToBackStack(null).commitAllowingStateLoss()

7.8 setMaxLifecycle()

设定Fragment生命周期最高上限。如果设置的生命周期上限低于当前的生命周期上限,则会进行回退设定的生命周期。

比如,当前生命周期为RESUME,如果设置MaxLifecycle为START,则生命周期则会回到RESUME。

说一个例子,在调用hide和show函数的时候,Frgament的生命周期并不会进行变化。但是我就是想要在show的时候让Fragment走到RESUME呢?此时可以进行如下操作。

//隐藏
transaction.setMaxLifecycle(fragment,Lifecycle.State.STARTED).hide(fragment)
//显示
transaction.setMaxLifecycle(fragment,Lifecycle.State.RESUMED).show(fragment)

7.9 commit/commitAllowingStateLoss()

提交一个事务。commit如果状态丢失则会抛异常,commitAllowingStateLoss则不会。 

八 Fragment提交事务种类区分

8.1 commit()

异步的操作。对应的同步方法为commitNow()。安排当前事务FragmentTransaction进行提交。但是提交后Fragment不会立即创建,而是由主线程异步来创建。也就是说使用commit()之后,你的Fragment不会被立即加入到Activity中。
本次提交,必须在Activity的onSaveInstanceState调用之前提交。否则会抛异常。

8.2 commitAllowingStateLoss()

和commit类似。但是如果本次是在Activity的onSaveInstanceState调用之后,那么本次提交记录在Activity恢复的时候,可能不被保存。

8.3 commitNow()

同步的操作。对应的异步方法为commit()。将事务立即提交。所有添加的Fragment会被立即初始化,并开始生命周期。所有被移除的Fragment将会被立即移除。调用这个方法,相当于调用commit,然后调用FragmentManager的executePendingTransactions()。

8.4 commitNowAllowingStateLoss()

和commitNow类似。但是如果在在Activity的onSaveInstanceState调用之后,那么本次提交记录在Activity恢复的时候,可能不被保存。

8.5 总结:

  • 每个FragmentTransaction只能提交commit一次。包括commit、commitNow、commitAllowingStateLoss、commitNowAllowingStateLoss。
  • 提交完一次之后,再commit就会抛出异常,也就是说不要每进行一个操作就commit一次,应该把多个操作都在FragmentTransact

九 Fragment通信和Fragment数据更新

9.1 在Fragment初始化的时候通过setArguments传值如下:

Activity新建Fragemnt的时候设置数据

public static QianggouChildFragment newInstance(int type, String categoryId) {
    QianggouChildFragment newFragment = new QianggouChildFragment();
    Bundle bundle = new Bundle();
    bundle.putInt("type", type);
    bundle.putString("categoryId", categoryId);
    newFragment.setArguments(bundle);
    return newFragment;
}

Fragment里面接收数据:

public class Fragment1 extends Fragment {

  @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        int type = getArguments().getInt("type");
        String categoryId = getArguments().getString("categoryId");
    }
}

9.2 获取Fragment实例更新数据

Fragment里面添加设置数据方法

public class Fragment1 extends Fragment {

    int type;
    String categoryId;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
         type = getArguments().getInt("type");
         categoryId = getArguments().getString("categoryId");
    }

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment1, container, false);

        return view;
    }

    //设置数据
    public void setCategoryId(String categoryId) {
        this.categoryId = categoryId;
    }

    //获取数据
    public String getCategoryId() {
        return categoryId;
    }
}

Activity里面获取Fragment实例,设置数据和获取数据

//设置数据
public void setData(){
	if(fragment1!=null){
		fragment1.setCategoryId("111");
	}
}
//获取数据
public void getData(){
	if(fragment1!=null){
		fragment1.getCategoryId();
	}
}

 完整Activity

public class TestActivity extends AppCompatActivity implements View.OnClickListener {
    private FrameLayout flContent;
    private Button button1;
    private Button button2;



    private Fragment1 fragment1;
    private Fragment2 fragment2;

    private  FragmentManager fragmentManager;
    private FragmentTransaction transaction;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        flContent = (FrameLayout) findViewById(R.id.fl_content);
        button1 = (Button) findViewById(R.id.button1);
        button2 = (Button) findViewById(R.id.button2);

        button1.setOnClickListener(this);
        button2.setOnClickListener(this);

    }


    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.button1:
                //先隐藏全部Fragment
                hideFragment();
                //添加并显示Fragment1
                fragmentManager = getSupportFragmentManager();
                transaction = fragmentManager.beginTransaction();
                if(fragment1==null){
                    fragment1 = new Fragment1();
                    transaction.add(R.id.fl_content, fragment1);
                }
                transaction.show(fragment1);
                transaction.commit();
                break;
            case R.id.button2:
                //先隐藏全部Fragment
                hideFragment();
                //添加并显示Fragment2
                fragmentManager = getSupportFragmentManager();
                transaction = fragmentManager.beginTransaction();
                if(fragment2==null){
                    fragment2 = new Fragment2();
                    transaction.add(R.id.fl_content, fragment2);
                }
                transaction.show(fragment2);
                transaction.commit();
                break;
        }
    }

    /**
     * 隐藏全部Fragment
     */
    private void hideFragment(){
        if(fragment1!=null){
            transaction.hide(fragment1);
        }

        if(fragment2!=null){
            transaction.hide(fragment2);
        }
        transaction.commit();
    }

    //设置数据
    public void setData(){
        if(fragment1!=null){
            fragment1.setCategoryId("111");
        }
    }

    //获取数据
    public void getData(){
        if(fragment1!=null){
            fragment1.getCategoryId();
        }
    }
}

 9.3 有种比较特殊,Fragment+ViewPager里面加载Fragment采用动态加载单例的方式,并不是Fragment的List集合,单例这种是获取不到Fragment实例的,如下实例:

public class MyPagerAdapter extends FragmentPagerAdapter {

	public MyPagerAdapter(FragmentManager fm) {
		super(fm);
	}

	@Override
	public Fragment getItem(int position) {
		return Fragment1.newInstance(type, categoryList.get(position).id);
	}

	@Override
	public int getCount() {
		return 2;
	}

}

通过id也获取不到,因为在ViewPager里Fragment是new出来动态加到ViewPager里的,上需的Id只是Fragment的布局文件的根Id,不是Fragment 的Id,故获取的Fragment为空。如下示例:

Fragment1 fragment1 =(Fragment1)getSupportFragmentManager().findFragmentById(R.id.fragment);

解决方法:通过tag获取

Fragemnt在创建的时候,系统会为Fragment分配tag,其格式如下:"android:switcher:" + R.id.viewpager + ":0"  其中0是Fragment在viewpager中的位置 

示例:

//获取当前fragment,android:switcher:2131364838:0
public Fragment getCurrentFragment(int position) {
	FragmentManager manager = getChildFragmentManager();
	Fragment fragment = manager.findFragmentByTag(makeFragmentName(mDataBinding.viewPager.getId(), position));
	return fragment;
}  

 使用:

viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
	@Override
	public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
	}

	@Override
	public void onPageSelected(int position) {


	}

	@Override
	public void onPageScrollStateChanged(int state) {
		//滑动结束,不是当前item,取消懒加载
		if (state == ViewPager.SCROLL_STATE_SETTLING) {
		    //遍历Fragment
			for (int i = 0; i < pagerAdapter.getCount(); i++) {
			    //获取Fragment实例
				Fragment1 fragment1 = (Fragment1) getCurrentFragment(i);
				if (fragment1 != null) {
				    //刷新数据
					mDataBinding.resetData();
				}
			}
		}
	}
});

 9.4 当然还有其它方式:比如Fragment集合获取索引实例,EventBus等消息机制来进行数据交互

十 Fragment懒加载

10.1 由于Fragment添加之后就会调用资源和请求数据,和ViewPager配合使用也是如此,可能刚初始化还未被用户可见就调用很多Fragment。为了解决这种问题,防止大量数据的加载,就有了懒加载的方案,所谓懒加载就是Fragment被用户可见的时候才去加载资源和请求数据。

10.2 利用setUserVisibleHint()实现懒加载,Fragment提供了界面被用户可见的监听,即setUserVisibleHint(),用户可见会返回true,不可见会返回false。如下示例:

public abstract class BaseFragment extends Fragment {
 

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        return null;
    }


    /**
     * Fragment数据的懒加载.
     */
    protected boolean isVisible;//是否可见

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (getUserVisibleHint()) {
            //可见
            isVisible = true;
            lazyLoad();
        } else {
            //不可见
            isVisible = false;
        }
    }

    protected void lazyLoad() {
	  //懒加载数据,比如请求接口
    }
	
}

10.3 也有利用onHiddenChanged方法进行懒加载的,但onHiddenChanged使用可能会出现问题,并不是所有Fragment每次切换都会调用,所以该方式有风险。想尝试的可以参考下面示例:

public class FragmentTest extends Fragment {
    //判断是否已进行过加载,避免重复加载
    private boolean isLoad=false;
    //判断当前fragment是否可见
    private boolean isHidden=true;

    @Override
    public void onResume() {
        super.onResume();
        lazyLoad();
    }
	
    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        //hidden==false 表示不可见
        isHidden=hidden;
        lazyLoad();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        isLoad=false; //注意当view销毁时需要把isLoad置为false
    }

    private void lazyLoad() {
        if (!isLoad&&!isHidden){
            //懒加载
            isLoad=true;
        }
    }
}

十一 Fragment 返回监听

11.1 由于Fragment依赖与Activity,所以Fragment的返回键会被Activity拦截,那怎样来监听Fragment的返回事件呢,下面介绍三种方法

11.2 在宿主Activity的返回监听里面循环赋予Fragment监听回退功能,如下:

第一步:建一个基类,增加退回监听方法

public class BaseFragment extends Fragment {

    /*
     * fragment中的返回键
     *
     * 默认返回flase,交给Activity处理
     * 返回true:执行fragment中需要执行的逻辑
     * 返回false:执行activity中的 onBackPressed
     * */
    public boolean onBackPressed() {
        //返回true拦截
        return false;
    }


}

第二步:在Activity的onBackPressed 返回监听里面循环调用Fragment的方法

   @Override
    public void onBackPressed() {
        List<Fragment> fragments = getSupportFragmentManager().getFragments();

        for (Fragment fragment : fragments) {
            //如果是自己封装的Fragment的子类  判断是否需要处理返回事件
            if (fragment instanceof Fragment) {
                if (((Fragment) fragment).onBackPressed()) {
                    //在Fragment中处理返回事件
                    return;
                }
            }
        }
        super.onBackPressed();
    }

 11.3 在Fragment的onResume里面监听View的点击事件

@Override
public void onResume() {
    super.onResume();
    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
                // handle back button
                return true;
            }
            return false;
        }
    });
}

但这种方法存在一定的问题:当Fragment中的某些控件获取到焦点后,下述方法则无法再监听到 KeyEvent 

11.4  使用Google提供的 OnBackPressedDispatcher 实现监听

public abstract class BackFragment extends BaseFragment {

    /*
     * 执行放弃方法
     */
    abstract void abandon();

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        requireActivity().getOnBackPressedDispatcher().addCallback(this/*LifecycleOwner*/, mCallback);
    }

    private OnBackPressedCallback mCallback = new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            abandon();
        }
    };
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        mCallback.remove();
    }
}

这里OnBackPressedCallback的构造需要传一个参数enabled,标识fragment是否要处理这个返回键事件,我们可以动态设置这个值来决定fragment是否要处理用户的返回键事

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/537190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【OpenCV】C++红绿灯轮廓识别+ROS话题实现

目录 前言 一、背景知识 Opencv轮廓检测 ROS相关知识 二、环境依赖 三、具体实现 Step1&#xff1a;初始化ROS&#xff0c;订阅话题 Step2&#xff1a;接收话题&#xff0c;进入回调 1. 帧处理 2. 膨胀腐蚀处理 Step3&#xff1a;红绿特征处理 1. 提取绘制轮廓 2…

20230517提升cv1826的打印等级

20230517提升cv1826的打印等级 2023/5/17 17:43 https://www.xitongjiaocheng.com/linux/2017/53494.html Linux内核log等级与printk打印消息控制 时间&#xff1a;2017-03-13 出处&#xff1a;系统之家复制分享人气(206次) 【大中小】 printk打印消息控制 // linux/includ…

认养农业小程序开发 赋予农业发展新模式

传统农业发展到今天&#xff0c;无论是规模还是方式都发生了很大的改变&#xff0c;尤其是在信息化时代影响下&#xff0c;智慧农业一步步发展起来&#xff0c;通过认养这种新模式与都市中想要体验农场乐趣的人群联系起来&#xff0c;既满足了都市人群体验农场生活乐趣的目的也…

720vr全景线上看车帮助企业快速对接到意向客户

360VR全景看车可以高精度还原线下展厅和各类汽车车型&#xff0c;进入VR汽车3d展厅&#xff0c;实现360度无死角VR看车&#xff0c;可以任意的查看和缩放&#xff0c;消费者根据自己的喜好更换车身的颜色&#xff0c;一键对比不同车型的外观、性能、耗能等&#xff0c;不用出门…

小程序架构足够安全吗?数据安全如何保障?

小程序大家已经再熟悉不过了&#xff0c;就是一种在移动操作系统中运行的轻量级应用程序&#xff0c;小程序发展这么多年来&#xff0c;是中国 IT 行业里为数不多的能够真正影响到普通程序员的创新成果。 当然随着小程序的流行&#xff0c;小程序的各个方面都是开发者讨论的热…

Navicat 数据备份与恢复功能 | 有效预防误删误改、删库跑路、软硬件故障造成的数据丢失

当今社会已进入了数据爆炸的时代&#xff0c;数据成为企业最重要的资产之一。如果没有采取数据备份和数据恢复手段与措施&#xff0c;就会导致数据的丢失&#xff0c;有时造成的损失是无法弥补估量的。市场上有很多数据备份的方法&#xff0c;今天我们给大家分享广受业界好评的…

首届百度商业AI技术创新大赛重磅启动,以前沿科技革新生产力

随着生成式AI在全球范围的热议&#xff0c;你可以想象在不远的将来AI将与你的工作、生活、出行等各种场景紧密相连。正如百度创始人、董事长兼CEO李彦宏所说&#xff1a;“以深度学习、算法为代表的人工智能革命是第四次科技革命”。 作为拥有强大互联网基础的领先AI公司&…

神经网络:Zero2Hero 3 - Tanh、Gradient、BatchNormal

Zero2Hero : 3 - Tanh、Gradient、BatchNormal 接上篇&#xff0c;对MLP模型有进一步进行了修改&#xff0c;增加BatchNormal、和激活函数。深入研究深层网络的内部&#xff0c;激活、反向传递梯度以及随机初始化的陷阱。BatchNormal的作用。 import torch import torch.nn.f…

python:图形用户界面GUI(模拟登录、计算器...)

文章目录 一、Tkinter简介1、第一个tkinter窗口2、在窗口内加入组件2.1 思考题&#xff08;问题与答案&#xff09; 3、坐标管理器 二、Tkinter组件及其属性1、Label组件和Entry组件2、计算器代码 引言&#xff1a;我们以QQ为例&#xff0c;当我们点击QQ图标时候&#xff0c;它…

SpringBoot整合MyBatis-Plus实现增删改查

简介 MyBatis-Plus (opens new window)的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 特性 无侵入&#xff1a;只做增强不做改变&#xff0c;引入它不会对现有工程产生影响&#xff0c;如丝般顺滑损耗小&#xff1a;启…

一个有趣的avs编码器(注意,是avs,而不是avs2噢)

本章附件是一个清华大学写的关于avs编解码器: https://download.csdn.net/download/weixin_43360707/87793302 该编码器遵循了stuffing bit: 打开文件夹后&#xff0c;如下&#xff1a; 可以看出这个是个跨平台的工程&#xff0c;提供了windows vs2015的工程文件sln&#x…

【数据结构】栈的详解

☃️个人主页&#xff1a;fighting小泽 &#x1f338;作者简介&#xff1a;目前正在学习C语言和数据结构 &#x1f33c;博客专栏&#xff1a;数据结构 &#x1f3f5;️欢迎关注&#xff1a;评论&#x1f44a;&#x1f3fb;点赞&#x1f44d;&#x1f3fb;留言&#x1f4aa;&…

pom里加依赖和把jar包放到lib文件夹下的区别

首先,什么是jar包,jar包其实就是一个a项目打成了a.jar包,然后b项目引入了a.jar包,然后b项目就能用到a项目里面的工具类了. b项目怎么引入a.jar包呢. 第一种:直接把a.jar包放到lib文件夹下(不推荐) 第二种:在pom里添加maven依赖,把a.jar包引过来(推荐) 在pom里加的依赖跟直接…

小学妹刚毕业没地方住想来借宿,于是我连夜用Python给她找了个好房子,我真是太机智了

事情是这样的&#xff0c;小学妹刚毕业参加工作&#xff0c;人生地不熟的&#xff0c;因为就在我附近上班&#xff0c;所以想找我借宿。。。 想什么呢&#xff0c;都不给住宿费&#xff0c;想免费住&#xff1f;于是我用Python连夜给她找了个单间&#xff0c;自己去住吧&#…

解决Linux普通用户无法使用Docker

目录 1.问题描述 2.解决方法 2.1 添加docker用户组 2.2 把当前用户加入docker用户组 2.3 查看是否添加成功 2.4 重启docker 2.5 更新用户组 2.6 测试docker命令是否可以使用 1.问题描述 当使用普通用户的时候&#xff0c;无法对Docker进行操作 [howlongbogon ~]$ dock…

性能优化的大致策略

平时多多少少在工作中会遇到性能问题相关的工作&#xff0c;记录一下大致的思路以及方法。 1. 指导思想 抓大放小&#xff0c;可以采用两种方向&#xff1a; 一种是自底向上&#xff0c;先从操作系统发现某一现象&#xff0c;例如内存过高&#xff0c;负载过高&#xff0c;i…

python值得学习么

python值得学习么&#xff0c;答案当然是毋庸置疑的~ 目前几乎所有大中型互联网企业都在使用 Python 完成各种各样的工作&#xff0c;比如Web应用开发、自动化运维、人工智能领域、网路爬虫、科学计算、游戏开发等领域均已离不开Python。 特别是在和数据相关的领域&#xff0…

【ArcGIS Pro二次开发】(29):村庄规划生成空间功能结构调整表

根据现在村规成果要求&#xff0c;【空间功能结构调整表】是必需的。 以福建省为例&#xff0c;它长这样&#xff1a; 下面就来实现从现状用地和规划用地导出这样的Excel表格。 一、要实现的功能 如上图所示&#xff0c;点击【汇总村庄空间功能结构调整表】工具&#xff0c;选…

Day1--ARM1

用for循环实现1~100相加

百度翻译可以翻译页面

百度翻译可以翻译页面 例如&#xff1a;输入网址 https://www.baidu.com&#xff0c;点击翻译即可。