Fragment使用相关总结

1、Fragment使用方式

》静态加载
》动态加载

静态加载

静态加载就是,在程序初始化的时候加载对应的Fragment,在运行过程中不能变化,其使用方法如下:

//第一个Fragment
 public class FirstFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
      @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }}

    //第二个Fragment
    public class SecondFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
     @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }}

    //布局文件
    <?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="horizontal"
    tools:context="com.example.administrator.myapplication.MainActivity">

    <fragment
        android:layout_width="0dp"
        android:layout_weight="1"
        android:name="com.example.administrator.myapplication.FirstFragment"
        android:layout_height="match_parent"/>

    <fragment
        android:layout_width="0dp"
        android:layout_weight="1"
        android:name="com.example.administrator.myapplication.SecondFragment"
        android:layout_height="match_parent"/>
    </LinearLayout>

    //其中name属性的值是相应引入的Fragment的全类名

动态加载

其使用方法如下:
》创建待添加的碎片实例
》获取FragmentManager,在活动中直接通过getSupportFragmentManager()获得,但是这个活
动应当是继承自AppCompatActivity
》在开启一个事务,通过调用beginTransaction()方法开启
》向容器中添加或者替换随拍呢,一般使用replace()方法实现,传入相应的容器id和碎片实例
》提交事务,调用commit()方法

//第三个Fragment
    public class ThirdFragment extends Fragment {
    @Nullable
    @Override
    @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }}  

    //布局文件代码如下:
    <?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="horizontal"
    tools:context="com.example.administrator.myapplication.MainActivity">

    <FrameLayout
        android:id="@+id/fragment"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"/>
    </LinearLayout>
    //注意这里我们使用FrameLayout替换了fragment,这种布局中不需要任何的定位,默认放在左上角,
    //刚好适合这里放一个碎片。

    //动态替换代码如下:
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.replace(R.id.fragment,new ThirdFragment());
    transaction.commit();

Fragment中实现activity的返回栈效果

》效果描述:加入当前的活动中显示的事FragmentA,当触发点击事件的时候,替换为FragmentB,
可是当我们按压Back键,程序直接退出,没有返回到FragmentA,若是我们想要做到类似Activity的返回效果,
应该怎么处理呢?作法如下:

  FragmentManager fragmentManager = getSupportFragmentManager();
  FragmentTransaction transaction = fragmentManager.beginTransaction();
  transaction.replace(R.id.fragment,new ThirdFragment());
  transaction.addToBackStack(null);
  transaction.commit();
  /**既是在提交之前使用addToBackStack()方法,将此事务添加到任务栈中,它可以接受一个名   
   *  字用于描述返回栈的状态,一般传入null即可
   */

Fragment和Activity之间的通信

》Activity持有其所包含的Fragment的引用,可直接调用相应的public方法
》Activity若是没有持有相应的Fragment的引用,但是其包含的每一个Fragment都有其唯一的id
和tag,可以使用findFragmentByTag()和findFragmentById()来获取相应的Fragment
》Fragment中可以通过getActivity()方法得到当前绑定的Activity,从而进行相关操作

//findfragmentbytag使用方法
@Override  
    protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.activity_main);  
    FragmentManager fragmentManager = getFragmentManager();  
    FragmentTransaction fragmentTransaction = fragmentManager  
               .beginTransaction();  
    //FRAGMENT_TAG通过查找得到该Fragment,第一次还没添加时得到的fragment为null  
    fragment = (Fragment1) fragmentManager.findFragmentByTag(FRAGMENT_TAG);  
    if (fragment == null) {  
       fragment = new Fragment1();  
       fragment.setTargetFragment(fragment, 0);  
       //重新添加fragment,此时fragment与FRAGMENT_TAG绑定在一起了
       fragmentTransaction.add(fragment, FRAGMENT_TAG).commit();  
    }  
    }  

    //findFragmentById使用方法
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
      super.onCreate(savedInstanceState);  
      setContentView(R.layout.activity_main);  

      /*获取manager*/  
      manager = this.getSupportFragmentManager();   
      /*通过findFragmentById获取Fragment*/  
      fragment = (MyFragment) manager.findFragmentById(R.id.fragment1);        
      /*通过fragment.getView()获取视图,然后在获取fragment中的button*/  
      button = (Button) fragment.getView().findViewById(R.id.button1);  
      /*设置按钮的监听事件*/  
      button.setOnClickListener(new View.OnClickListener() {  
          @Override  
          public void onClick(View v) {  
            Toast.makeText(MainActivity.this, "hello world!!!", 1).show();  
          }  
      });  
  }  
Fragment与Activity通信的最佳方式

为了更好的使Fragment与Activity解耦,可以采用接口回调的方式去实现两者之间的通信。

//方式一:
public class FirstFragment extends Fragment {
  private View view;
  private Button button;
  public interface OnButtonClickListener {
      void onClickListener();
  }
  @Nullable
  @Override
  public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                           @Nullable Bundle savedInstanceState) {
      view = inflater.inflate(R.layout.first_fragment,container,false);
      button.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              /**
               * 进行判断依附的Activity是否实现了OnButtonClickListener接口,
               * 若是,就将点击事件的内容交由Activity去实现,而Activity只要
               * 实现此接口即可,这就实现了很好的解耦
               */
              if (getActivity() instanceof OnButtonClickListener) {
                  ((OnButtonClickListener)getActivity()).onClickListener();
              }
          }
      });
      return view;
      }
  }
 //方式二:
  public class SecondFragment extends Fragment {
  private View view;
  private Button button;
  private OnClickListener onClickListener;
  public interface OnClickListener{
      void onClickListener();
  }
  public void setClickListenr(OnClickListener onClickListener) {
      //需要我们手动的去set
      this.onClickListener = onClickListener;
  }
  @Nullable
  @Override
  public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
        @Nullable Bundle savedInstanceState) {
      view = inflater.inflate(R.layout.second_fragment,container,false);
      button.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              if (onClickListener != null) {
                  //判断若是不为空的时候就执行点击方法
                  onClickListener.onClickListener();
              }
          }
      });
      return view;
      }
  }
Fragment与Fragment之间的通信

所有的Fragment都是依附于Activity的,那么Activity就相当于是一个总线一样,所以理应是Activity去管理所有的Fragment的,并且Activity可以接受到Intent,并根据其中的参数不同,去响应不同的Fragment。在上面有说道,可以采用findFragmentById()和findFragmentByTag()方法去获取Fragment的实例,比如说在FirstFragment中获取SecondFragment的实例,此时就可以直接进行Fragment和Fragment之间的通信传值,但是一般来说,没有特别特别的需求的话,不要使用这种方法!那怎么办呢?我们思考,既然Fragment可以和Activity进行通信,那么可不可以将Activity当做一个中转站呢?即FirstFragment传值给Activity然后Activity又将值传递给SecondFragment?这种是可行的,作法如下:

 //FirstFragment中写法如下:
public class FirstFragment extends Fragment {
   private View view;
   private Button button;
   private EditText editText;
   public interface OnButtonClickListener {
       void onClickListener(String data);
   }
   private OnButtonClickListener onClickListener;

   @Override
   public void onAttach(Context activity) {
       super.onAttach(activity);
    //从Activity中获取实例,activity继承该接口
       onClickListener=(OnButtonClickListener) getActivity();
   }
   @Nullable
   @Override
   public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                            @Nullable Bundle savedInstanceState) {
       view = inflater.inflate(R.layout.first_fragment,container,false);
       button.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
            //将数据以接口参数的方式传递给Activity
               onClickListener.onClickListener(editText.getText().toString());
           }
       });
       return view;
       }
  }

  //Activity中写法如下:
  public class MainActivity extends AppCompatActivity implements
      FirstFragment.OnButtonClickListener {
      //实现该接口
   private Button button;
   FragmentManager fm;
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       fm= this.getSupportFragmentManager();
       FragmentTransaction transaction = fm.beginTransaction();
       transaction.replace(R.id.fragment,new FirstFragment());
       transaction.commit();
   }

   @Override
   public void onClickListener(String data) {
       //此时的data是FirstFragment界面中传过来的数值
       SecondFragment secondFragment=new SecondFragment();
       Bundle bundle=new Bundle();
       bundle.putString("mes",data);
       secondFragment.setArguments(bundle);
       FragmentTransaction transaction = fm.beginTransaction();
       transaction.replace(R.id.fragment,secondFragment);
       transaction.commit();
       }
  }

  //SecondFragment中的写法:
  public class SecondFragment extends Fragment {
   private View view;
   private Button button;
   private TextView textView;
   @Nullable
   @Override
   public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
        @Nullable Bundle savedInstanceState) {
       view = inflater.inflate(R.layout.second_fragment,container,false);
          //获得传递过来的值
       Bundle bundle=getArguments();
       //判断是否为空,之后设置相应的值,至此两个Fragment之间实现通信
       if(bundle!=null) {
           textView.setText(bundle.getString("mes"));
       }
       return view;
       }
  }

  /**
    其实可以使用EventBus,在传值的时候更加方便
   */

使用Fragment保存数据

在activity中屏幕旋转的时候,activity是会重新启动的,恢复其中的数据可以采用如下三种方法:
》其一: 当数据比较少的时候,可以采用onSaveInstanceState()和onRestoreInstanceState()进行保存数据和恢复数据
》其二: 当数据量比较大的时候,可设置android:configChanges属性,来保存真个Activity,但是此种方法,官方并不推 荐,这种方法简单设置属性,就可以禁止销毁和重建,但是配置此属性之后,就意味着要在代码中,要做更多的处理,去维护当前界面的中每一个配置,如尺寸,布局等,很容易就造成前后不一致,导致一些bug。同时,activity的销毁和重建,并不是只有横竖屏的切换,还有语言、字体的变更,都会导致界面重绘,所以这种方式并不合适。
》其三: 因此我们采用Fragment来保存相应的数据,因为使用onRestoreInstanceState()方法的Bundle参数来进行恢复是不现实的, Bundle不要用来携带大量数据的,比如说bitmap,又因为Bundle中携带的数据必须能够被序列化和反序列化,这样对于内存的消耗是有影响的,同时会使配置变化缓慢,因此我们使用Fragment来保存相应的数据,做法如下:

 //先创建一个没有UI的Fragment
  public class RecoverFragment extends Fragment {
      private Bitmap data;
   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       /**
        * 默认情况下Fragment会随着其依附的Activity的生命周期而变化,但是当我们设置
        * 此属性方法为true的时候,就允许我们跳过销毁和重新创建的周期,指定系统保留
        * 当前的fragment实例
        */
       setRetainInstance(true);
   }
   public void setData(Bitmap data) {
       this.data = data;
   }
   public Bitmap getData() {
       return data;
   }
  }                                     


  //在Activity中的使用方式:
  public class FragmentRetainDataActivity extends Activity {

   private static final String TAG = "FragmentRetainDataActivity";
   private RetainedFragment dataFragment;
   private DialogFragment mLoadingDialog;
   private ImageView mImageView;
   private Bitmap mBitmap;

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       FragmentManager fm = getFragmentManager();
       dataFragment = (RetainedFragment) fm.findFragmentByTag("data");
       if (dataFragment == null) {
        //获取Fragment的实例
           dataFragment = new RetainedFragment();
           fm.beginTransaction().add(dataFragment, "data").commit();
       }
       mBitmap = collectMyLoadedData();
       initData();
   }

   /**
    * 初始化数据
    */
   private void initData() {
       mImageView = (ImageView) findViewById(R.id.id_imageView);
       if (mBitmap == null) {
          //加载网络图片
       } else {
           mImageView.setImageBitmap(mBitmap);
       }
   }

   @Override
   public void onDestroy() {
       super.onDestroy();
       dataFragment.setData(mBitmap);
   }
   private Bitmap collectMyLoadedData() {
       return dataFragment.getData();
   }
  }

异步执行任务时,屏幕旋转导致的相关问题处理方法

》问题描述
a)当异步数据没有加载完成之前,进行屏幕的旋转,此时onCreate会重新执行,再次启动线程,但是上一个线程还在执行,那么会更新不存 在的控件,造成空指针异常
b) ProgressDialog是在上一个线程中关闭,但是此时上个线程被杀死,那么就无法关闭之前的ProgressDialog
》解决方案:
a) 考虑到上面所说的Fragment保存数据方法,此时用Fragment去持有“运行中的线程”,“AsyncTask”,“Socket”等对象
b) 改换ProgressDialog为DialogFragment来创建加载框,DialogFragment其实就是Fragment,其生命周期会受当前activity影响

 //保存数据的Fragment:
 public class OtherRecoverFragment extends Fragment {
  //保存一个异步的任务,在异步任务中保持数据的加载
  private MyAsyncTaskTest data;
  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setRetainInstance(true);
  }
  public void setData(MyAsyncTaskTest data) {
      this.data = data;
  }
  public MyAsyncTaskTest getData() {
      return data;
  }
 }

 //异步任务MyAsyncTaskTest的写法:
public class MyAsyncTaskTest extends AsyncTask<Void, Void, Void> {
  private FirstActivity activity;
  /**
   * 是否完成
   */
  private boolean isCompleted;
  /**
   * 进度框
   */
  private LoadingDialog mLoadingDialog;
  private List<String> items;
  public MyAsyncTaskTest(FirstActivity activity) {
      this.activity = activity;
  }

  /**
   * 开始的收,显示加载框
   */
  @Override
  protected void onPreExecute() {
      mLoadingDialog = new LoadingDialog();
      mLoadingDialog.show(activity.getFragmentManager(), "LOADING");
  }

  /**
   * 加载数据
   */
  @Override
  protected Void doInBackground(Void... voids) {
      items = loadingData();
      return null;
  }

  @Override
  protected void onPostExecute(Void aVoid) {
      super.onPostExecute(aVoid);
      isCompleted = true;
      //执行完成之后更新一下activity
      notifyActivityTaskCompled();
      if (mLoadingDialog != null) {
          mLoadingDialog.dismiss();
      }
  }

  private void notifyActivityTaskCompled() {
      if (null != activity) {
          activity.onTaskCompleted();
      }
  }

  public List<String> getItems() {
      return items;
  }

  /**
   * 设置Activity,因为在旋转的过程中,activity会不停的变
   * @param activity
   */
  public void setActivity(FirstActivity activity) {
      //若是上一个的Activity销毁的话,将与上一个Activity绑定的DialogFragment销毁
      if (activity == null) {
          mLoadingDialog.dismiss();
      }
      //设置为当前的activity
      this.activity = activity;
      //开启一个与当前Activity绑定的加载框
      if (activity != null && !isCompleted) {
          mLoadingDialog = new LoadingDialog();
          mLoadingDialog.show(activity.getFragmentManager(), "LOADING");
      }
      //如果完成,通知activity
      if (isCompleted) {
          notifyActivityTaskCompled();
      }
  }

//模拟耗时加载
  private List<String> loadingData() {
      try {
          Thread.sleep(5000);
      } catch (Exception e) {
      }
      return new ArrayList<String>(Arrays.asList("item1", "item2", "item3",
              "item4", "item5", "item6"));
  }
 }

 //Activity中的写法
 public class FirstActivity extends ListActivity {
  private ListAdapter mAdapter;
  private List<String> mDatas;
  private MyAsyncTaskTest mMyTask;
  private OtherRecoverFragment dataFragment;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_first);
      FragmentManager fm = getFragmentManager();
      dataFragment = (OtherRecoverFragment) fm.findFragmentByTag("data");

      if (dataFragment == null) {
          dataFragment = new OtherRecoverFragment();
          fm.beginTransaction().add(dataFragment, "data").commit();
      }

      mMyTask = dataFragment.getData();
      if (mMyTask != null) {
          mMyTask.setActivity(this);
      } else {
          mMyTask = new MyAsyncTaskTest(this);
          dataFragment.setData(mMyTask);
          mMyTask.execute();
      }
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
      //销毁的时候,置空防止内存泄漏
      mMyTask.setActivity(null);
      super.onSaveInstanceState(outState);
  }

  /**
   * 回调
   */
  public void onTaskCompleted() {
      mDatas = mMyTask.getItems();
      mAdapter = new ArrayAdapter<String>(FirstActivity.this,
              android.R.layout.simple_list_item_1, mDatas);
      setListAdapter(mAdapter);
  }
 }

参考文章:

https://blog.csdn.net/lmj623565791/article/details/37936275
https://blog.csdn.net/lmj623565791/article/details/37992017
https://blog.csdn.net/lmj623565791/article/details/42628537
http://www.cnblogs.com/kissazi2/p/4116456.html

猜你喜欢

转载自blog.csdn.net/qq_33768280/article/details/80238781