开源中国项目,看懂了,就能得到很多启发,包括框架、设计模式等等。


http://www.oschina.net/question/213217_60071


大总结:

【网络框架】

   通过OSChinaApi、ApiHttpClient、AsyncHttpClient三个类来实现,哪么这三个类是如何

   进行关联呢?

   wKioL1Y3jYjwBnZRAANXhqQPvoM056.jpg

【开源中国中用到的自定义Widget,有可能是开源组件。】  

 1)BadgeView:主要是在代码里动态的标记在某个View上,如消息提示的个数等等。

/**
* A simple text label view that can be applied as a "badge" to any given
* {
@link android.view.View}. This class is intended to be instantiated at
* runtime rather than included in XML layouts.
*
*
@author Jeff Gilfelt
*/

  http://www.xuebuyuan.com/1964298.html

  部分核心源码————标记View是怎么绑定到目标View上去的。

private void applyTo(View target) {

    LayoutParams lp = target.getLayoutParams();
    ViewParent parent = target.getParent();
    FrameLayout container = new FrameLayout(context);

    if (target instanceof TabWidget) {
        // set target to the relevant tab child container
        target = ((TabWidget) target).getChildTabViewAt(targetTabIndex);
        this.target = target;

        ((ViewGroup) target).addView(container, new LayoutParams(
                LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

        this.setVisibility(View.GONE);
        container.addView(this);

    } else {
        // TODO verify that parent is indeed a ViewGroup
        ViewGroup group = (ViewGroup) parent;
        int index = group.indexOfChild(target);

        group.removeView(target);
        group.addView(container, index, lp);

        container.addView(target);

        this.setVisibility(View.GONE);
        container.addView(this);
        group.invalidate();

    }
}

 但是使用这个BadgeView需要注意如果applyto的这个view在RelativeLayout中,并且同级的其它View

参考了它的位置,applyto之后另外一个view的位置可能会发生错乱。



2)CircleImageView:圆形图片

  Android ImageView圆形头像 图片完全解析

  http://blog.csdn.net/feixiangdexin123087/article/details/42076987    

 3)MyQrodeDialog:自定义对话框,有点类似透明Activity的那种Dialog。

 wKioL1Zdt_mwcgeeAAAcrR93zxs341.png

  

【开源中国中用到的特殊数据类型】 

 1)枚举(enum):可列举性

 SimpleBackPage:对即将要放入SimpleBackActivity的Fragment的封装,通过value值来作为

             Fragment存取的唯一标识。

 2)Android中WebView与js及css交互

 wKioL1Zdyf3xRjcDAAAZVvk35jU420.png      

【开源中国中的工具类】   

 1)XmlUtils:对XStream的封装,将xml流转换成bean实体类,当然也可以将其它的类型转换成bean实

          体类。

 2)QrCodeUtils:根据字符串生成二维码,内部采用ZXING库中的MultiFormatWriter类根据字符串生

          成矩阵

 3)DialogHelp:得到不同格式的对话框,包括确认对话框、进度对话框等等。

 4)

 

【一般一个应用必须要有的一些类】     

 1)Application的子类:用于作一些全局的初始化操作,包括网络、登录

 2)AppConfig:保存一些配制信息,一般采用Properties进行封装。也可以采用其它的存储方式

 3)

 

2015年10月8日晚---核心基类

BaseApplication类

  继承了Application,在onCreate()方法里初始化了Context和Resources,其它的就是一些静态方法:sp存储与判断、获取屏幕的宽高、单例自定义视图的吐司。

  这个类可以应用于几乎所有的应用的全局Application类的父类。 


BaseActivity类

  继承自ActionBarActivity,并实现了DialogControl,BaseViewInterface等接口,DialogControl

用于控制进度状态的显示,BaseViewInterface用于初始化View和Data。

  wKiom1ZavU_x4-oSAAMxdyaUUcE287.png  

SimpleBackActivity 继承自BaseActivity(****************************

  用于展示一些Fragment,并且Activity是具有回退按钮。

  -------------------------------------------------

  复写了布局指定方法  

  解析Intent传过来的参数,得到SimpleBackPage封装的Fragment及其class等信息, 

  并且利用反射+枚举技术实例化Fragment并填充到容器中  

@Override
protected int getLayoutId() {
    return R.layout.activity_simple_fragment;
}

@Override
protected boolean hasBackButton() {
    return true;
}

@Override
protected void init(Bundle savedInstanceState) {
    super.init(savedInstanceState);
    if (mPageValue == -1) {
        mPageValue = getIntent().getIntExtra(BUNDLE_KEY_PAGE, 0);
    }
    initFromIntent(mPageValue, getIntent());
}

protected void initFromIntent(int pageValue, Intent data) {
    if (data == null) {
        throw new RuntimeException(
                "you must provide a page info to display");
    }
    SimpleBackPage page = SimpleBackPage.getPageByValue(pageValue);
    if (page == null) {
        throw new IllegalArgumentException("can not find page by value:"
                + pageValue);
    }

    setActionBarTitle(page.getTitle());

    try {
        Fragment fragment = (Fragment) page.getClz().newInstance();
                //传到Activity的数据再设置给参数中的Fragment实例
        Bundle args = data.getBundleExtra(BUNDLE_KEY_ARGS);
        if (args != null) {
            fragment.setArguments(args);
        }

        FragmentTransaction trans = getSupportFragmentManager()
                .beginTransaction();
        trans.replace(R.id.container, fragment, TAG);
        trans.commitAllowingStateLoss();

        mFragment = new WeakReference<Fragment>(fragment);
    } catch (Exception e) {
        e.printStackTrace();
        throw new IllegalArgumentException(
                "generate fragment error. by value:" + pageValue);
    }
}

   再来看看值是怎么传到Activity的

public static void showSimpleBack(Context context, SimpleBackPage page) {
    Intent intent = new Intent(context, SimpleBackActivity.class);
    intent.putExtra(SimpleBackActivity.BUNDLE_KEY_PAGE, page.getValue());
    context.startActivity(intent);
}

   枚举仅仅是作为传给Activity的参数而已,Activity通过 

public final static String BUNDLE_KEY_PAGE = "BUNDLE_KEY_PAGE";
public final static String BUNDLE_KEY_ARGS = "BUNDLE_KEY_ARGS";

   来判断传递的参数的类型,是枚举呢,还是一些额外的参数。

  wKiom1ZbFA_ANakWAAORq6-2uqE401.png

DetailActivity继承自BaseActivity(****************************

 详情页面Activity

//发表评论的Fragment
public KJEmojiFragment emojiFragment = new KJEmojiFragment();
//工具栏Fragment
public ToolbarFragment toolFragment = new ToolbarFragment();

 注意init方法的填充Fragment方式与SimpleBackActivity的区别,是用工厂模式创建,而

 SimpleBackActivity是通过反射的方式来生成Fragment的。 

@Override
protected void init(Bundle savedInstanceState) {
    super.init(savedInstanceState);
    int displayType = getIntent().getIntExtra(BUNDLE_KEY_DISPLAY_TYPE,
            DISPLAY_NEWS);
    BaseFragment fragment = null;
    int actionBarTitle = 0;
    switch (displayType) {
        case DISPLAY_NEWS:
            actionBarTitle = R.string.actionbar_title_news;
            fragment = new NewsDetailFragment();
            break;
       。。。
        default:
            break;
    }
    setActionBarTitle(actionBarTitle);
    FragmentTransaction trans = getSupportFragmentManager()
            .beginTransaction();
    trans.replace(R.id.container, fragment);
    trans.commitAllowingStateLoss();
    if (fragment instanceof OnSendClickListener) {
        currentFragment = (OnSendClickListener) fragment;
    } else {
        currentFragment = new OnSendClickListener() {
            @Override
            public void onClickSendButton(Editable str) {
            }

            @Override
            public void onClickFlagButton() {
            }
        };
    }
}


CacheManager:缓存管理类

  包括存储对象和读取对象,以及判断缓存是否存在,判断缓存是否失效。

  注意一下API:

  Context.openFileInput("文件名",存储模式) 得到data/data/包名/文件名的输入流

  Context.openFileOutput("文件名",存储模式) 得到data/data/包名/文件名的输出流 

  ObjectOutputStream、ObjectInputStream 对象序列化与反序列化流

  File getFileStreamPath(String name):得到用openFileOutput存储的文件的绝对路径

    Returns the absolute path on the filesystem where a file created with

  openFileOutput(String, int) is stored. 

      

2015年11月2日晚

  突然之间,好像思路全断了一样。只有从清单里挑着看了。

AppContext extends BaseApplication

  AppContext:全局应用程序类:用于保存和调用全局应用配置及访问网络数据

  定义了一个自身的引用instance,在onCreate方法中进行了初始化 instance = this;


  onCreate方法里做了4个操作

  1.init()方法:初始化网络请求

  -----------------------------------------------

      Android Asynchronous Http Client-Android异步网络请求客户端接口

      http://blog.csdn.net/hil2000/article/details/13949513 


       PersistentCookieStore  用于存储Cookie,持久化存储登录状态。和AsyncHttpClient同

       属于一个包中


       [原]Android持久化保存cookie

       http://www.tuicool.com/articles/IRBZjm


      ApiHttpClient.setHttpClient(client); //为ApiHttpClient设置了AsyncHttpClient实例

      

      TLog OSchina自带的带有开关的打印日志的工具类 

  2.initLogin()方法:初始化登录

  ---------------------------------------------------

  利用AppConfig类读取Properties文件,获得用户的配置信息(如id,名称,位置等等)

  

  3.指定异常处理器

  4.发送通知广播

  用户的登录注销操作方法也在这个类里 

    

AppConfig类 用于保护用户相关的消息和配置,保存的方式是通过Properties(继承于

              Hashtable,所以就是键值对)

          默认存放图片的路径     DEFAULT_SAVE_IMAGE_PATH   

          默认存放文件下载的路径   DEFAULT_SAVE_FILE_PATH 

        get和set方法

          Android中关于内部存储的一些重要函数

         http://blog.csdn.net/hudashi/article/details/8037076 

        登录与注销就是用的Properties来保存的   

————————————————————————

登录与注销的逻辑(***************)

————————————————————————

1)登录初始化

wKiom1ZTG7niMTBDAAgJiCgKgxE621.png

2)注销登录

wKiom1ZTHoSyY8h2AAF9JOkt6-g503.png

————————————————————————


               

AppException 应用程序异常:用于捕获异常和提示错误信息  

         定义异常的类型和状态码

          获取APP异常崩溃处理对象

        自定义异常处理:收集错误信息&发送错误报告(将异常存储到SD卡

        捕获未处理的异常信息

        UIHelper.sendAppCrashReport(context);【为什么这行代码放在Looper.prepare()

                           和Looper.loop()之间???】

         |

        通过DialogHelp(对话框辅助类)来提示用户程序发生了异常

        )

        应用有自己的异常处理框架,显得十分地专业。   


AppStart   应用启动界面

  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  

  Android应用开发框架 KJFrameForAndroid

  http://www.androidchina.net/1663.html (强烈推荐看看!)

  在《开源中国》项目中,用到的类如下:

     PreferenceHelper 就是对原生SP的封装,可以读写任意的SP文件。

     KJAsyncTask  也是这个库中一个异步类,使用了线程池管理线程,弥补了原生的AsyncTask的

               缺陷

     FileUtils   和文件操作相关的类

      

  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    

  TDevice 和Android设备相关的工具类

     如获取手机屏幕的信息、判断手机是否有相机、有wifi以及网络类型等功能、px与dp单位互换

    、获取应用的版本信息、判断设备有哪些应用(Manager层的封装)

   

  AppStart所做的操作:

   1.比对缓存(sp)里的应用版本号与应用当前版本号,如果缓存的版本号小于当前版本号,则将当

     前版本号覆盖缓存版本号,并且清空图片缓存(异步操作使用KJAsyncTask)。

   2.动画结束的时候,重定向操作:

     1)创建并启动Service,如果BUG信息存储文件不为空,上传BUG信息到服务器,上传成功,删

      除BUG信息存储文件。

       无论哪种情况,最后都要关闭Service。

     2)跳转到主界面

     

2015年11月22日晚  

  》》MainActivity类

  注意这个类是没有继承BaseActivity的,是直接继承自ActionBarActivity的。    

  ui设计

     wKiom1ZRwVKhD5O5AABsi22JpHs129.png

    onCreate()方法:1)判断sp,切换夜间或白天主题。

             wKiom1ZRwq7if2pWAABOrlBCd_g663.png

            2)initView()初始化控件(********************

                DoubleClickExitHelper双击退出帮助类

                 。。。

              广播、服务

                 是否第一次启动,检查更新。   

              3) 将当前Activity压入栈中

            4)handleIntent(getIntent());处理传进来的Intent

   MainTab Tab选项卡的枚举,管理主界面的四大模块的Fragment。

    DataCleanManager 数据删除工具类                                      UpdateManager   更新管理类

    主界面的代码实现

    wKioL1ZSkrCAqMUgAACgzwRfMHQ970.png 


   

  》》BaseFragment 碎片基类

     BaseViewPagerFragment    带有导航条的Fragment 

     PagerSlidingTabStrip     自定义的滑动选项卡(********************)

    wKiom1ZSlXiC5qV1AAGKCVcLfbU305.png   以NewsViewPagerFragment(综合模块)来说明作者的重构思路:

   wKioL1ZR7-aiRmNoAAQxWWTXtL8444.png

   对于“带有导航条的Fragment”这一小框架的抽取,我与作者的思路简单就是如出一辙,甚至毫

 不夸张的说,我的实现方式可能比作者的代码更加简单。

   其实很多时候不要一开始就想代码怎么写,而是去思考事物的本质。如“带有导航条的Fragment",

 很自然的想到ViewPager的item肯定要抽象化,而ViewPager的item是由Adapter来决定的。作者就将导

 航条基类的Adapter只是new了出来,进行了一些初始化,对Adapter的具体操作是由不同的子类作不同

 的实现(代码中表现的是addTab的个数和参数不同)。

   而我对“带有导航条的Fragment”小框架的实现,也是通过Adapter入手,只是将Adapter的抽象

 逐层向外转移,最终将Adapter的抽象转换成Fragment的抽象。这样用户在使用我这个框架的时候就

 不需要去了解Adapter的抽象,只需要关注Fragment的抽象即可。我突然感觉这像某一种设计模式?

   不知我的感觉对不对,我感觉重构其实就像是一种哲学。(大家也给评评我想法对吗?)



 》》EmptyLayout(维护加载中、加载数据失败等状态的自定义布局类)

   这种多个状态自定义布局类,是几乎每个应用都需要做的一种抽取。wKioL1ZSsbWT6lTPAANe4bZ_c0o416.png  

  然后通过两个方法来切换状态

  wKioL1ZSsoSTzY1fAAHgd1_PPnk733.png     


■2015年11月23日晚    

  》》主界面模块之“我”-MyInformationFragment

  ui设计(做Android,其实ui设计也是一门学问,不是吗?)

   原则1:复杂ui模块化

   wKiom1ZTCc-wTEjsAAN-CpUNYzE188.png   头像区ui设计,包括登录和未登录2种状态(一般都是采用FrameLayout来完成的)

   wKioL1ZTCpLC1MuLAAIzdI5rtQc723.png

  

  MyInformationFragment核心代码解析:

 在onCreate方法里注册广播 

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        IntentFilter filter = new IntentFilter(Constants.INTENT_ACTION_LOGOUT);
        filter.addAction(Constants.INTENT_ACTION_USER_CHANGE);
        getActivity().registerReceiver(mReceiver, filter);
    }

 在onDestroy方法里注销广播

@Override
public void onDestroy() {
    super.onDestroy();
    getActivity().unregisterReceiver(mReceiver);
}

 在onCreateView方法里填充View并初始化

@Override
public View onCreateView(LayoutInflater inflater,
        @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_my_information,
            container, false);
    ButterKnife.inject(this, view);
    initView(view);  //父类初始化View的接口
    return view;
}

 在onViewCreated方法里请求网络数据并设置给View

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    requestData(true);
    mInfo = AppContext.getInstance().getLoginUser();
    fillUI();
}

 initView(View view)方法详解:为各个子View添加点击监听

 wKiom1ZTLo7hGErqAAL4Yr29oWk036.png 

 requestData(true)方法详解:请求网络数据

 可见作者的判断是十分的准确的(注意要加深理解作者的数据存储及缓存是怎么设计的)

 wKiom1Za-qmhYavNAARCwpMdeAQ462.png

 作者将缓存的存储和读取都用AsyncTask来封装,读写文件属于耗时操作。

 缓存存储类:

private class SaveCacheTask extends AsyncTask<Void, Void, Void> {
    private final WeakReference<Context> mContext;
    private final Serializable seri;
    private final String key;

    private SaveCacheTask(Context context, Serializable seri, String key) {
        mContext = new WeakReference<Context>(context);
        this.seri = seri;
        this.key = key;
    }

    @Override
    protected Void doInBackground(Void... params) {
        CacheManager.saveObject(mContext.get(), seri, key);
        return null;
    }
}

 缓存读取类:

private class CacheTask extends AsyncTask<String, Void, User> {
    private final WeakReference<Context> mContext;

    private CacheTask(Context context) {
        mContext = new WeakReference<Context>(context);
    }

    @Override
    protected User doInBackground(String... params) {
        Serializable seri = CacheManager.readObject(mContext.get(),
                params[0]);
        if (seri == null) {
            return null;
        } else {
            return (User) seri;
        }
    }
    //读取数据成功之后就填充UI
    @Override
    protected void onPostExecute(User info) {
        super.onPostExecute(info);
        if (info != null) {
            mInfo = info;
            // mErrorLayout.setErrorType(EmptyLayout.HIDE_LAYOUT);
            // } else {
            // mErrorLayout.setErrorType(EmptyLayout.NETWORK_ERROR);
            fillUI();
        }
    }
}

■2015年11月30日晚  

 》》LoginActivity类

  wKiom1ZcSE2Sp92vAACoS7oqoWQ836.png 

 一般登录的逻辑:

 1)登录成功之后,为什么要发送广播?-----因为登录之后,要请求登录之后的数据刷新另外一个界

   面的UI,这时就要用到广播传递登录成功的消息及数据。

  

wKioL1ZcSfvR7piLAATmcGUtpAc966.png 关于第3方登录,不得不明白一个东西OpenId。

 什么是OpenID?OpenID概念、原理和案例http://www.uegeek.com/ue/openid_intro


 QQ第3方登录的逻辑: 

 ???????关于LoginActivity这一块,以后再仔细研究?????????

 WX第3方登录的逻辑: 


 》》BaseListFragment类

 带下拉刷新的Fragment,作者采用的是SwipRefreshLayout+ListView的组合方式。

 

 在类的成员位置,一开始作者就定义了一个静态常量,用来分类标识Fragment。

public static final String BUNDLE_KEY_CATALOG = "BUNDLE_KEY_CATALOG";

    

 填充的View:

@Override
protected int getLayoutId() {
  return R.layout.fragment_pull_refresh_listview;
}

 wKioL1ZccVaxtE_3AAAPKLxrgxY162.png 

 

 初始化View方法:

 @Override

 public void initView(View view) 

 

...
mAdapter = getListAdapter();   //不同的页面的List展示的数据不一样,需要抽取出来。
...

protected abstract ListBaseAdapter<T> getListAdapter();

 

 wKioL1Zdfx3R7GLiAABK5vnqM-0233.png  

 


 wKioL1ZdowLixjybAAGYTH6AE2o153.png

 解析任务类:

class ParserTask extends AsyncTask<Void, Void, String> {

    private final byte[] reponseData;
    private boolean parserError;
    private List<T> list;

    public ParserTask(byte[] data) {
        this.reponseData = data;
    }

    @Override
    protected String doInBackground(Void... params) {
        try {
            ListEntity<T> data = parseList(new ByteArrayInputStream(
                    reponseData));
            new SaveCacheTask(getActivity(), data, getCacheKey()).execute();
            list = data.getList();
            if (list == null) {
                ResultBean resultBean = XmlUtils.toBean(ResultBean.class,
                        reponseData);
                if (resultBean != null) {
                    mResult = resultBean.getResult();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();

            parserError = true;
        }
        return null;
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
        if (parserError) {
            readCacheData(getCacheKey());
        } else {
            executeOnLoadDataSuccess(list);
            executeOnLoadFinish();
        }
    }
}

 executeOnLoadDataSuccess(list);将解析后的List设置给子类的适配器,通过Adapter刷新到

 ListView上

 【理解父类与子类的MVC是怎么相互衔接起来!!!!!!!!!!

   其实还是逻辑的问题,到底哪些代码应该写在父类中,哪些代码要抽象让子类去实现。


   将父类的戏做足,那么子类的代码也就简洁了。   

 】

 写好了BaseListFragment,那么它的子类只需要指定Adapter,请求接口让父类Handler处理即可。

   

再来看作者的ListBaseAdapter类(********非常核心的一个类************)

 wKiom1Zdl8_AHhzFAAFQfDGD5Uo983.png 

 复写getCount方法,根据是否有脚布局来决定是否要在条目的数目之上加1:

 wKiom1ZdmPyAR9v-AAEnaBo1hZw706.png

 复写getItem和getItemId方法:

@Override
public T getItem(int arg0) {
    if (mDatas.size() > arg0) {
        return mDatas.get(arg0);
    }
    return null;
}

@Override
public long getItemId(int arg0) {
    return arg0;
}

 

 下面是对数据data的增删改查,同时进行刷新:

public int getDataSize() {
    return mDatas.size();
}

public void setData(ArrayList<T> data) {
    mDatas = data;
    notifyDataSetChanged();
}

public ArrayList<T> getData() {
    return mDatas == null ? (mDatas = new ArrayList<T>()) : mDatas;
}

public void addData(List<T> data) {
    if (mDatas != null && data != null && !data.isEmpty()) {
        mDatas.addAll(data);
    }
    notifyDataSetChanged();
}

public void addItem(T obj) {
    if (mDatas != null) {
        mDatas.add(obj);
    }
    notifyDataSetChanged();
}

public void addItem(int pos, T obj) {
    if (mDatas != null) {
        mDatas.add(pos, obj);
    }
    notifyDataSetChanged();
}

public void removeItem(Object obj) {
    mDatas.remove(obj);
    notifyDataSetChanged();
}

public void clear() {
    mDatas.clear();
    notifyDataSetChanged();
}

  动态设置脚布局Text的内容:

public void setLoadmoreText(int loadmoreText) {
    _loadmoreText = loadmoreText;
}

public void setLoadFinishText(int loadFinishText) {
    _loadFinishText = loadFinishText;
}

public void setNoDataText(int noDataText) {
    _noDateText = noDataText;
}

  复写适配器最重要的方法,将脚布局的情况判断出来,具体条目的getView方法抽取出来:

public View getView(int position, View convertView, ViewGroup parent) {
    if (position == getCount() - 1&&hasFooterView()) {// 最后一条
        // if (position < _data.size()) {
        // position = getCount() - 2; // footview
        // }
        if (getState() == STATE_LOAD_MORE || getState() == STATE_NO_MORE
                || state == STATE_EMPTY_ITEM
                || getState() == STATE_NETWORK_ERROR) {
            this.mFooterView = (LinearLayout) LayoutInflater.from(
                    parent.getContext()).inflate(R.layout.list_cell_footer,
                    null);
            if (!loadMoreHasBg()) {
                mFooterView.setBackgroundDrawable(null);
            }
            ProgressBar progress = (ProgressBar) mFooterView
                    .findViewById(R.id.progressbar);
            TextView text = (TextView) mFooterView.findViewById(R.id.text);
            switch (getState()) {
            case STATE_LOAD_MORE:
                setFooterViewLoading();
                break;
            case STATE_NO_MORE:
                mFooterView.setVisibility(View.VISIBLE);
                progress.setVisibility(View.GONE);
                text.setVisibility(View.VISIBLE);
                text.setText(_loadFinishText);
                break;
            case STATE_EMPTY_ITEM:
                progress.setVisibility(View.GONE);
                mFooterView.setVisibility(View.VISIBLE);
                text.setText(_noDateText);
                break;
            case STATE_NETWORK_ERROR:
                mFooterView.setVisibility(View.VISIBLE);
                progress.setVisibility(View.GONE);
                text.setVisibility(View.VISIBLE);
                if (TDevice.hasInternet()) {
                    text.setText("加载出错了");
                } else {
                    text.setText("没有可用的网络");
                }
                break;
            default:
                progress.setVisibility(View.GONE);
                mFooterView.setVisibility(View.GONE);
                text.setVisibility(View.GONE);
                break;
            }
            return mFooterView;
        }
    }
    if (position < 0) {
        position = 0; // 若列表没有数据,是没有footview/headview的
    }
    return getRealView(position, convertView, parent);
}

//将不确定的具体列表View抽取出来,部分抽象方法模块化。
protected View getRealView(int position, View convertView, ViewGroup parent) {
    return null;
}

 那么,任何一个BaseListFragment的子类的适配器,只要继承ListBaseAdapter并复写它的getView方

 法就可以了。           

 

 》》CommonDetailFragment类

 通用的详情Fragment

 ui设计:

@Override
protected int getLayoutId() {
    return R.layout.fragment_news_detail;
}

 wKiom1ZdwDPgvCSqAAFu8Ckx7CI469.png

 代码设计:

 结构设计和BaseFragment的直接子类BaseListFragment很像