Android AppAndroid开发经验谈Android知识

Android Fragment使用技巧

2017-10-20  本文已影响82人  肖丹晨

前言

笔记贴,记录平时开发遇到的一些值得总结的bug。欢迎加入学习小组QQ群: 193765960

版权归作者所有,如有转发,请注明文章出处:https://xiaodanchen.github.io/archives/

ViewPager + fragment: 生命周期

当viewpger和fragment搭配使用时,一定会遇到过各种各样的问题。
诸如,二次进入界面,数据丢失了,界面空白等等。
但凡viepager+fragment的种种问题,都可以从此种情况下fragment的生命周期得到解释和解决。
因为预加载的缘故,当现实当前fragment时,下一页fragment已预加载好(resume状态)。上一页fragment(如果当前页不是第一页)也处于resumed状态。
简单粗暴点说,viewpager同时有三个页处于onresume状态,其中中间那个页处于可见状态。
以现实页为中心的三页(三剑客)作为一个整体,这个整体的上一页和下一页都处于ondestroyview状态(之前已经加载过)。
“三剑客”的前一个或者下一个再次显示的时候,需要经oncreatview启动显示。所以,如果在oncreatview和ondestroyview中的UI和数据处理不合理,就会导致各种内存浪费,数据空指针等问题。

我建议:
在onAttach方法中来初始化context、adapter等,例如:

    @Override
    public void onAttach(Context context) {
        mContext = context;
        if(null == mAdapter){
            mAdapter = new HomePageListAdapter(mContext, data);
        }
        super.onAttach(context);
    }

在onDetach方法中来释放非view类资源,例如:

    @Override
    public void onDetach() {
        mContext = null;
        jump2activity = null;
        mAdapter = null;
        data = null;
        super.onDetach();
    }

在onCreateView方法中来初始化view类资源,例如:

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_xxxx, container,false);
        lv_orders = (ListView)view.findViewById(R.id.lv_orders);
        lv_orders.setAdapter(mAdapter);
        return view;
    }

在onCreateView方法中来初释放view类资源,例如:

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

Activity + fragment: 二次进入fragment 某个button点击效果没有了

Q: 通过replace方法,切换fragment,后来发现某个button的点击效果没有了。
**A: **通过private OnClickListener mListener = new OnClickListener{...}方式声明的对象在第二次进入fragment的时候成为了null。
没有搞懂为啥,但是先把解决办法记录下来吧,很傻的方案:

    private OnClickListener mListener  = null;
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        ......
        if(null == mListener){
            mListener = new OnClickListener() {
                
                @Override
                public void onClick(View v) {
                    //TODO
                }
            };
        }
        return view;
    }

TabLayout+ViewPager + fragment:

滑动page的时候不要将fragment 干掉,否则会存在异步刷新界面出现问题。
因为将fragment干掉后,再次创建fragment后,从视图上看不出区别来,但是fragment的对象已经变了,此时如果存在异步刷新界面的情况,就会存在问题。
正确的办法是,判断fragment是否存在,如果存在的话,就想办法让界面单纯的刷新数据。

//在界面的回调中需要刷新viewpager的数据时
//判断界面fragment已经初始化
if(null == mViewPager.getAdapter()){
    setupViewPager(mViewPager);
}

//通过广播通知fragment去刷新界面(fragment中注册reciver)
Intent intent = new Intent(CFTGlobal.HOME_UPDATE_MESSAGE_ACTION);
sendBroadcast(intent);

H5与Android,IOS的JS交互

1,定义接口

public class AndroidJsInterface {

    private Context mContext;

    public AndroidJsInterface(Context activity) {
        mContext = activity;
    }

    @JavascriptInterface//系统sdk 版本在v4.2以上时,必须加这个注解(安全性)
    public void fun1() {
        //点击H5的某个区域,实现APP的跳转(退出WebpageActivity)
        Intent intent = new Intent(mContext, MainActivity.class);
        mContext.startActivity(intent);
    }
}

2,activity处理

//Android端主要代码
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new AndroidJsInterface(context), "android");//注意:这里一定要让别名的首字母为小写,因为JS端实际调用中会将别名处理成小写。
//JS端主要代码
jsFun = new function(){
  if(app.isIOS()){
    ......
  }else if(app.isAndroid()){
    android.fun1();//别名首字母默认会被处理成小写,所以为了避免调用出错,最好统一采用小写字母
  }
}
//Html端主要代码
......(通过href或者onclick调用js的jsFun)
//Android端主要代码
mWebView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.length() != 0 && url.startsWith("app:")) {//和后台约定好移动端js交互接口重定向URL的前缀:例如“app:”
            // 获得方法名
            String methodName = url.substring("app:".length());
            // 通过方法名反射获得方法
            Method method;
            Class<?> c;
            try {
                c = Class.forName("com.base.web.AndroidJsInterface");
                Constructor<?>[] constructors = c.getDeclaredConstructors();
                Constructor<?> constructor = null;
                for (int i = 0; i < constructors.length; i++) {
                    constructor = constructors[i];
                    if (constructor.getGenericParameterTypes().length == 0)
                    break;
                }
                constructor.setAccessible(true);
                Object clazz = constructor.newInstance(context);

                method = clazz.getClass().getDeclaredMethod(methodName);
                // 执行该方法
                if(null != method){
                    method.setAccessible(true);
                    method.invoke(clazz);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }else if(url.length() != 0 && url.startsWith("tel:")){
            //打电话:H5直接重定向href=“tel:13120003456”
            Intent intent = new Intent(Intent.ACTION_DIAL,
            Uri.parse(url));
            startActivity(intent);
        }else{
            view.loadUrl(url);
        }
        return true;
    }
});
//JS端主要代码
jsFun1 = new function(){
  window.location.href="app:fun1";//"app:"是和服务器约定好的前缀,fun1是移动端定义的名为fun1的js回调接口
}

jsFun2 = new function(){
  window.location.href="app:fun2";
}
//Html端主要代码
......(通过href或者onclick调用js的jsFun*)

需要注意的几点:
1,采用方式一,一定要注意别名的首字母小写(是不是需要全部小写没有验证)
2,小米系列的手机可能对webview和Android的交互存在一些问题,如果发现交互怎么都不起作用,试试把APP卸载后重新安装(有缓存)。
3,使用方式二,一定要注意使用反射时,要调用反射类的构造函数实例化,否则会报告一堆的异常。

Activity: finish()与onDestroy()

activity调用finish()方法,可以关闭自己,在finish方法中国默认会调用onDestroy()方法。所以不要在finish中做清除view等操作,这样会导致activity退出的时候出现闪现空白界面的假象。

ViewGroup view = (ViewGroup) getWindow().getDecorView();
view.removeAllViews();

这段代码不要放在finish中。

适配地图显示所需要的设置

webSettings.setDomStorageEnabled(true); 
webSettings.setDatabaseEnabled(true);
String dir = getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
webSettings.setGeolocationEnabled(true);
webSettings.setGeolocationDatabasePath(dir);

笔记贴,持续更新记录......

上一篇下一篇

猜你喜欢

热点阅读