四大组件面试题

Fragment基本使用

2019-08-13  本文已影响0人  者文_

1. Fragment概述

1.1 介绍

Fragment是一种可以嵌入在活动中的UI片段,能够让程序更加合理和充分地利用大屏幕的空间,出现的初衷是为了适应大屏幕的平板电脑,可以将其看成一个小型Activity,又称作Activity片段。

使用Fragment可以把屏幕划分成几块,然后进行分组,进行一个模块化管理。Fragment不能够单独使用,需要嵌套在Activity中使用,其生命周期也受到宿主Activity的生命周期的影响

官方定义如下:

A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running.

从官方的定义可以得到:

Fragment的优势

1.2 Fragment基本生命周期

image

Fragment的一般生命周期如上图所示:

Fragment生命周期会经历:运行、暂停、停止、销毁

在介绍Fragment的具体使用时,先介绍一下Fragment的几个核心类

扩展子类

备注:开发Fragment不建议使用android.app下的Fragment而应是android:support.v4.app,因为support库是不断更新的。

2. Fragment使用

使用Fragment有两种方式,分别是静态加载和动态加载

2.1 静态加载

关于静态加载的流程如下:

静态加载一旦添加就不能在运行时删除

示例

2.2 动态加载Fragment

动态加载Fragment的流程如下:

简单示例:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
        replaceFragment(new RigthFragment());
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                replaceFragment(new AnotherRightFragment());
                break;
            default:
                break;
        }
    }

    private void replaceFragment(Fragment fragment) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();   // 开启一个事务
        transaction.replace(R.id.right_layout, fragment);
        transaction.commit();
    }
}

2.3 使用注意点

Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first.

示例

public class Fragment1 extends Fragment{
    private static String ARG_PARAM = "param_key";
    private String mParam;
    private Activity mActivity;
    public void onAttach(Context context) {
        mActivity = (Activity) context;
        mParam = getArguments().getString(ARG_PARAM);  //获取参数
    }
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_1, container, false);
        TextView view = root.findViewById(R.id.text);
        view.setText(mParam);
        return root;
    }
    public static Fragment1 newInstance(String str) {
        Fragment1 frag = new Fragment1();
        Bundle bundle = new Bundle();
        bundle.putString(ARG_PARAM, str);
        fragment.setArguments(bundle);   //设置参数
        return fragment;
    }
}

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

出现原因:commit()在onSaveInstanceState()后调用

由于onSaveInstanceState()onPause()之后onStop()之前调用。onRestoreInstanceState()在onStart()之后,onResume()之前,因此避免出现该异常的方案有:

3. Fragment与Activity联动

image

Fragment和Activity完整的生命周期如上图所示

下面给出Activity与Fragment联动时的执行顺序,最好通过log自我验证一下

  1. 当Fragment在Activity的onCreate()中被添加时
    • Activity super.onCreate执行完毕
    • Fragment onAttach
    • Fragment onCreate
    • Fragment onCreateView
    • Fragment onViewCreated
    • Activity.super.onStart()执行中
    • Fragment onActivityCreated
    • Fragment onViewStateRestored
    • Fragment onStart()
    • Activity super.onStart执行完毕
    • Activity super.onPostCreate()
    • Activity super.onResume()
    • Activity super.onPostResume()执行中
    • Fragment onResume()
    • Activity super.onPosResume()执行完毕
    • Activity onAttachedToWindow()
    • Activity onCreateOptionsMenu()
    • Fragment onCreateOptionsMenu()
    • Activity onPrepareOptionsMenu()
    • Fragment onPrepareOptionsMenu()
    • Activity onWindowFocusChanged()
  2. 暂停生命周期
    • Activity super.onPause()执行中
    • Fragment.onPause()
    • Activity super.onPause()执行完毕
    • Activity super.onSaveInstanceState()执行中
    • Fragment onSaveInstanceState()
    • Activity super.onSaveInstanceState()执行完毕
    • Activity super.onStop()执行中
    • Fragment onStop()
    • Activity super.onStop()执行完毕
  3. 销毁的生命周期
    • Activity super.onDestroy()执行中
    • Fragment onDestroyView()
    • Fragment onDestroy()
    • Fragment onDetach()
    • Activity super.onDestroy()执行完毕
  4. 重启的生命周期
    • Activity super.onRestart()
    • Activity super.onStart()执行中
    • Fragment onStart()
    • Activity super.onStart()执行完毕
    • Activity super.onResume()
    • Activity super.onPostResume()执行中
    • Fragment onResume()
    • Activity super.onPosResume()执行完毕
    • Activity onWindowFocusChanged()执行完毕

3.1 回退栈

类似Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。
一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity

添加一个Fragment事务到回退栈:

FragmentTransaction.addToBackStack(String)

简单示例

private void replaceFragment(Fragment fragment) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.replace(R.id.right_layout, fragment);
    transaction.addToBackStack(null);   //添加进回退栈
    transaction.commit();
}

replace是remove和add的合体,并且如果不添加事务到回退栈,前一个Fragment实例会被销毁。这里很明显,我们调用transaction.addToBackStack(null);将当前的事务添加到了回退栈,所以FragmentOne实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryView和onCreateView

如果不希望视图重绘,可以将原来的Fragment隐藏:

private void replaceFragment(Fragment fragment) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.hide(this);
    transaction.add(R.id.right_layout, fragment);
    transaction.addToBackStack(null);   //添加进回退栈
    transaction.commit();
}

4. Fragment与Activity通信

image

Fragment与Activity的通信交互如上图所示:

备注

考虑Fragment的重复使用问题,降低与Activity的耦合,Fragment操作应该由它的管理者Activity决定

4.1 传递数据给Fragment

步骤流程

简单示例:

//创建Fragment对象,并通过Bundle对象传递值(在onCreate方法中)
MyFragment fragment = new MyFragment();
Bundle bundle = new Bundle();
bundle.putString("key", values);
fragment.setArguments(bundle);
//(在Fragment类中的onCreateView方法中)
Bundle bundle = this.getArguments();
    if (bundle != null)
    {
        String str = bundle.getString("key");
    }
    TextView textView = new TextView(getActivity());
    textView.setText("上上下下的享受");//是电梯,别误会

4.2 传递数据给Activity

步骤流程

简单示例

/*接口*/  
public interface Mylistener{
    public void thanks(String code);
}
private Mylistener listener;
@Override
    public void onAttach(Activity activity) {
        // TODO Auto-generated method stub
        listener=(Mylistener) activity;
        super.onAttach(activity);
    }
@Override
    public void thanks(String code) {
        // TODO Auto-generated method stub
        Toast.makeText(this, "已收到Fragment的消息:--"+code+"--,客气了", Toast.LENGTH_SHORT).show();;
    }

除了接口回调,还可以使用EventBus进行交互通信。

5. Fragment间通信

5.1 setArguments()

示例:

    public static Fragment2 newInstance(String text) {
        Fragment2 fragment = new Fragment2();
        Bundle args = new Bundle();
        args.putString("param", text);
        fragment.setArguments(args);
        return fragment;
    }
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view =  inflater.inflate(R.layout.fragment2, container, false);
        if (getArguments() != null) {
            String mParam1 = getArguments().getString("param");
            TextView tv =  (TextView)view.findViewById(R.id.textview);
            tv.setText(mParam1);
        }
        return view;
    }
public class Fragment1 extends Fragment {
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment1, container, false);
        Button btn = (Button)view.findViewById(R.id.load_fragment2_btn);
        btn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(final View view) {
                Fragment2 fragment2 = Fragment2.newInstance("从Fragment1传来的参数");
 
                FragmentTransaction transaction = getFragmentManager().beginTransaction();
                transaction.add(R.id.main_layout, fragment2);
                transaction.addToBackStack(null);
                transaction.commit();
            }
        });
        return view;
    }
}

5.2 同Activity不同Container的Fragment交互

这种情况有三中方法解决:

方法一直接在Activity中操作

​ 直接在Activity中找到对应控件的实例,然后直接操控即可

方法二直接在Fragment中操作

​ 这里有两个问题:如何获取自己控件的引用?如何获取其他Fragment页控件的引用?

总的实现示例如下:

public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
 
    mFragment2_tv = (TextView) getActivity().findViewById(R.id.fragment2_tv);//获取其它fragment中的控件引用的唯一方法!!!
    listView = (ListView) getView().findViewById(R.id.list);//获取自己视图里的控件引用,方法二
 
    ArrayAdapter arrayAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mStrings);
    listView.setAdapter(arrayAdapter);
    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            String str = mStrings[position];
            mFragment2_tv.setText(str);
       }
    });
}

方法三在各自Fragment中操作

方法二在Fragment A中操作了Fragment B,违背模块分离思想,应通过Activity将其分离

在Activity中可以直接通过FragmentManager.findFragmentById()获取Fragment实例

示例

public class Fragment2 extends Fragment {
    private TextView mTv;
    …………
    public void setText(String text) {
        mTv.setText(text);
    }
}
public class MainActivity extends FragmentActivity implements Fragment1.titleSelectInterface {
 
    ……
    
    @Override
    public void onTitleSelect(String title) {
        FragmentManager manager = getSupportFragmentManager();
        Fragment2 fragment2 = (Fragment2)manager.findFragmentById(R.id.fragment2);
        fragment2.setText(title);
    }
}
上一篇下一篇

猜你喜欢

热点阅读