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