关于新闻类应用快速开发框架的思考

hrlj1816 5年前
   <h2><strong>流程</strong></h2>    <p>目前我主要开发的是新闻资讯类的应用,所以这个框架也主要是针对这类应用设计的,在这一类应用中,最重要的就是内容展示,一般都是以列表的形式展示数据,比如下图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/989745677f34c8ddd08b9564acaa67fe.png"></p>    <p style="text-align:center">这里写图片描述</p>    <p>而这类页面还要有下拉刷新,上滑加载下一页,加载进度,错误重试等功能,但是这些功能都是公有的,而每个页面所不同的就是展示的内容的不同(废话),而这些内容又是以不同的item为基础的。例如下面不同的item:</p>    <p><strong>Item1</strong></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/eff2fa33cd60575268e9b5b6c8a1eafc.png"></p>    <p style="text-align:center">这里写图片描述</p>    <p><strong>item2</strong></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/0d26b4c3fa8750c676274f5d52f5c7aa.png"></p>    <p style="text-align:center">这里写图片描述</p>    <p>而在软件开发中,所不同的就是JSON数据的不同,JavaBean的不同,Layout的不同。大体的对应关系如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/3eca6dc67e15146c21503793d5a237b2.png"></p>    <p style="text-align:center">这里写图片描述</p>    <p>因此在实际开发中,最小的粒度就是一个Object。我的框架工作流程就是找到这些JsonObject然后转化为对应的JavaBean传统,实例对应的ViewHolder,然后将数据传递给对应的ViewHolder,ViewHolder绑定数据,最终一个新闻列表就显示出来了,大概的流程如下:</p>    <p><img src="https://simg.open-open.com/show/dcfb5c5a599e72855309bfdf0b92db04.png"></p>    <p style="text-align:center">这里写图片描述</p>    <h2><strong>实现</strong></h2>    <h3><strong>1. JSON数据的解析</strong></h3>    <p>在一般的开发流程中,我们会将服务器返回的Json数据直接转换成一个JavaBean,然后再操作。在界面不是很复杂的情况下这么做无可厚非,但是在多个界面有相同的Item,又有部分不同的Item。这就会造成,每一个界面的数据都要有一个对应的JavaBean,而且这些解析都会固化在类中,或许可以通过继承实现部分的复用,但是对于扩展性并不优化,三大设计原则中就有,组合优于继承,因此我所做的就是降低粒度,即针对JsonObject,而不是全部的数据。对于如何定位,我自己设计了一种定位的描述符:</p>    <p>比如要定位到下面这个数组:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/2593fb737ca994998274e6b627cab74a.png"></p>    <p style="text-align:center">这里写图片描述</p>    <p>我在配置文件中是这么写的</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d0a64adae16ed4cc49cec13e0c4e4522.png"></p>    <p style="text-align:center">这里写图片描述</p>    <p>大框中括起来的最终会转换为JsonConfig对象的素组,交给JsonAnalysisEngine 处理,而JsonAnalysisEngine 会循环遍历然后处理每一个JsonConfig,首先会更具jsonLocation的位置对原来的数据进行过滤,找到需要的数据,最后自动转化为对应的Javabean,关于使用大家看注释吧,目前来看可能性能还不是很好,我在后续的版本可能会更换实现方式。</p>    <pre>  <code class="language-java">package com.zgh.smartlibrary.json;    import android.text.TextUtils;    import com.google.gson.Gson;  import com.zgh.smartlibrary.config.JsonAnalysisConfig;  import com.zgh.smartlibrary.util.GsonUtil;      import org.json.JSONArray;  import org.json.JSONException;  import org.json.JSONObject;    import java.util.ArrayList;  import java.util.List;    /**   * Created by zhuguohui on 2016/8/17.   */  public class JsonAnalysisEngine {      List<JsonAnalysisConfig> configItems = new ArrayList<>();      Gson gson = new Gson();        public JsonAnalysisEngine(List<JsonAnalysisConfig> configItems) {          this.configItems = configItems;      }        public JsonAnalysisEngine(JsonAnalysisConfig... configItems) {          this.configItems.clear();          for (JsonAnalysisConfig config : configItems) {              this.configItems.add(config);          }        }        private int listDataSize = 0;        /**       * 获取数据       *       * @param jsonStr       * @return       */      public List<Object> getData(String jsonStr) {          listDataSize = 0;          List<Object> data = new ArrayList<>();          //处理每一个JsonAnalysisConfig          for (JsonAnalysisConfig item : configItems) {              Object o = getDataFromJson(jsonStr, item);              if (o != null) {                  //如果过JsonAnalysisConfig设置isListData为true则代表,其为List的主要数据,直接解析为List                  if (item.isListData()) {                      List list = (List) o;                      data.addAll(list);                      //记录List的数据的数量,对分页来说需要这个数量判断是否还有下一页                      listDataSize += list.size();                  } else {                      data.add(o);                  }              }          }          return data;      }        public int getListDataSize() {          return listDataSize;      }        private Object getDataFromJson(String jsonStr, JsonAnalysisConfig item) {          Object o = null;          String jsonLocation = item.getJsonLocation();          try {              String jsonData = getJsonStringFromLocation(jsonStr, jsonLocation);              if (!TextUtils.isEmpty(jsonData)) {                  //如果是[开头的代表是一个数组,解析为对应的javabean的List                  if (jsonData.startsWith("[")) {                      o = GsonUtil.jsonToBeanList(jsonData, Class.forName(item.getType()));                  } else {                      o = GsonUtil.jsonToBean(jsonData, Class.forName(item.getType()));                  }              }          } catch (Exception e) {              e.printStackTrace();          }          return o;      }        /**       * 返回jsonLocation 所描述的String       *       * @param jsonStr      需要处理的jsonStr       * @param jsonLocation 描述的jsonLocation       * @return       * @throws JSONException       */      private String getJsonStringFromLocation(String jsonStr, String jsonLocation) throws JSONException {          //这个方法会递归调用,每一次调用jsonLocation就会减少一层,当解析结束的时候jsonLocation就为空          if (TextUtils.isEmpty(jsonLocation)) {              return jsonStr;          }          char a;          //记录接下来的操作,如果是{开头代表解析一个对象,以[开头代表解析一个数组          char op = 0;          boolean haveFoundOP = false;          int nameStart = 0, nameEnd = 0;          //记录需要解析的对象的名字,例如{news 代表解析一个叫做news的json对象,[news代表解析一个叫news的数组          String name;          for (int i = 0; i < jsonLocation.length(); i++) {              a = jsonLocation.charAt(i);              if ('{' == a || '[' == a) {                  if (!haveFoundOP) {                      op = a;                      haveFoundOP = true;                      nameStart = i + 1;                  } else {                      nameEnd = i - 1;                      break;                  }              }          }          if (nameStart != 0 && nameEnd != 0) {              name = jsonLocation.substring(nameStart, nameEnd + 1);              jsonLocation = jsonLocation.substring(nameEnd + 1);          } else {              name = jsonLocation.substring(nameStart);              jsonLocation = "";          }          jsonStr = jsonStr.trim();          int index = -1;          //如果name中包含:表示需要解析一个数组指定的部分,比如[news:0 代表解析名叫news的Json数组下的第一条数据。          if (name.indexOf(":") != -1) {              String[] split = name.split(":");              name = split[0];              try {                  index = Integer.valueOf(split[1]);              } catch (Exception e) {              }            }          //如果原来的json字符串是以{开头代表解析一个对象,否则解析一个数组          if (jsonStr.startsWith("{")) {              JSONObject jsonObject = new JSONObject(jsonStr);              if ('{' == op && jsonObject.has(name)) {                  return getJsonStringFromLocation(jsonObject.getJSONObject(name).toString(), jsonLocation);              } else if ('[' == op) {                    if (index == -1) {                      return getJsonStringFromLocation(jsonObject.getJSONArray(name).toString(), jsonLocation);                  } else {                      return getJsonStringFromLocation(jsonObject.getJSONArray(name).getJSONObject(index).toString(), jsonLocation);                  }              }          } else {              try {                  if (index != -1) {                      JSONArray array = new JSONArray(jsonStr);                      return array.getJSONObject(index).toString();                  } else {                      return jsonStr;                  }              } catch (Exception e) {                  e.printStackTrace();                  return "";              }              }          return "";      }      }</code></pre>    <h3><strong>2.下拉刷新,上拉加载更多</strong></h3>    <p>为了以后的可扩展性,我把下拉刷新,上拉加载更多抽象成一个接口</p>    <pre>  <code class="language-java">package com.zgh.smartlibrary.manager;    import android.view.View;  import android.widget.BaseAdapter;  import android.widget.ListView;    import com.zgh.smartlibrary.page.IPagePolicy;      /**   * 能提供下拉刷新,上拉加载更多的接口   * Created by zhuguohui on 2016/9/5.   */  public interface ListViewUpdateManger {        /**       * 返回ListView       * @return       */      ListView getListView();        /**       * 返回view       * @return       */      View getView();        /**       * 相应不同的状态,比如没有分页信息就不显示,加载更多等。       * @param state       */      void setState(IPagePolicy.PageState state);          void setAdapter(BaseAdapter adapter);        /**       * 设置回调接口       * @param listener       */      void setUpdateListener(UpdateListener listener);        interface UpdateListener {          void pullUp();          void pullDown();      }        /**       * 更新完成时回调,实现者可在这个方法中结束动画       */      void updateComplete();        /**       * 手动更新       * @param showAnimation 是否显示动画       */      void update(boolean showAnimation);    }</code></pre>    <p>有一个默认的实现</p>    <pre>  <code class="language-java">package com.zgh.smartlibrary.manager.impl;    import android.content.Context;  import android.view.View;  import android.view.ViewGroup;  import android.widget.BaseAdapter;  import android.widget.LinearLayout;  import android.widget.ListView;  import android.widget.TextView;    import com.handmark.pulltorefresh.library.PullToRefreshBase;  import com.handmark.pulltorefresh.library.PullToRefreshListView;  import com.zgh.smartlibrary.R;  import com.zgh.smartlibrary.manager.ListViewUpdateManger;  import com.zgh.smartlibrary.page.IPagePolicy;  import com.zgh.smartlibrary.util.AppUtil;  import com.zgh.smartlibrary.util.TimeUtil;      /**   * Created by zhuguohui on 2016/9/5.   */  public class PullToRefreshManger implements ListViewUpdateManger {        private final TextView footerView;      protected PullToRefreshListView mPullToRefreshView;      protected ListView listView;      private String mStrNextPageRetry = "加载失败 点击重试";      protected int LAYOUT_ID = R.layout.fragment_smart_list;      private long mLastRefreshTime = 0;      private boolean isUpdate = false;      private View mBaseView = null;        public PullToRefreshManger(Context context) {          mBaseView = View.inflate(context, LAYOUT_ID, null);          mPullToRefreshView = (PullToRefreshListView) mBaseView.findViewById(R.id.refreshView);          mPullToRefreshView.setMode(PullToRefreshBase.Mode.BOTH);          //配置加载更多文字          mPullToRefreshView.getLoadingLayoutProxy(false, true).setPullLabel("上拉加载更多");          mPullToRefreshView.getLoadingLayoutProxy(false, true).setRefreshingLabel("正在加载");          mPullToRefreshView.getLoadingLayoutProxy(false, true).setReleaseLabel("释放加载更多");          //加载更多的借口          mPullToRefreshView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener2<ListView>() {              @Override              public void onPullDownToRefresh(final PullToRefreshBase<ListView> refreshView) {                  if (listener != null) {                      isUpdate = true;                      listener.pullUp();                  }              }                @Override              public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) {                    if (listener != null) {                      isUpdate = false;                      listener.pullDown();                  }              }          });          mPullToRefreshView.setOnPullEventListener(new PullToRefreshBase.OnPullEventListener<ListView>() {              @Override              public void onPullEvent(PullToRefreshBase<ListView> refreshView,                                      PullToRefreshBase.State state,                                      PullToRefreshBase.Mode direction) {                  if ((state == PullToRefreshBase.State.PULL_TO_REFRESH ||                          state == PullToRefreshBase.State.REFRESHING || state == PullToRefreshBase.State.MANUAL_REFRESHING)                          && direction == PullToRefreshBase.Mode.PULL_FROM_START) {                      if (mLastRefreshTime != 0L) {                          mPullToRefreshView.getLoadingLayoutProxy(true, false)                                  .setLastUpdatedLabel(TimeUtil.format(mLastRefreshTime)                                          + "更新");                      }                  }              }          });            //配置ListView          listView = mPullToRefreshView.getRefreshableView();          listView.setFooterDividersEnabled(false);          listView.setOverScrollMode(View.OVER_SCROLL_NEVER);          listView.setDivider(null);              //添加footview          footerView = (TextView) View.inflate(context, R.layout.view_bottom, null);          footerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, AppUtil.dip2px(context, 35)));          //要隐藏footview其外部必须再包裹一层layout          LinearLayout footerViewParent = new LinearLayout(context);          footerViewParent.addView(footerView);          footerView.setVisibility(View.GONE);          footerView.setOnClickListener(new View.OnClickListener() {              @Override              public void onClick(View v) {                  if (footerView.getText().equals(mStrNextPageRetry)) {                      // footerView.setVisibility(View.GONE);                      mPullToRefreshView.setMode(PullToRefreshBase.Mode.PULL_FROM_END);                      mPullToRefreshView.setRefreshing(true);                  }              }          });          listView.addFooterView(footerViewParent);      }        @Override      public ListView getListView() {          return listView;      }        @Override      public View getView() {          return mBaseView;      }        @Override      public void setState(IPagePolicy.PageState state) {          switch (state) {              case NO_MORE:                  footerView.setText("没有更多了");                  footerView.setVisibility(View.VISIBLE);                  mPullToRefreshView.setMode(PullToRefreshBase.Mode.PULL_FROM_START);                  break;              case HAVE_MORE:                  footerView.setVisibility(View.GONE);                  mPullToRefreshView.setMode(PullToRefreshBase.Mode.BOTH);                  break;              case NO_PAGE:                  footerView.setVisibility(View.GONE);                  mPullToRefreshView.setMode(PullToRefreshBase.Mode.DISABLED);                  break;              case LOAD_ERROR:                  footerView.setText(mStrNextPageRetry);                  footerView.setVisibility(View.VISIBLE);                  break;              default:                  throw new IllegalArgumentException("Specified state is not supported state=" + state);          }      }          @Override      public void setAdapter(BaseAdapter adapter) {          //设置adapter          listView.setAdapter(adapter);      }        UpdateListener listener;        @Override      public void setUpdateListener(UpdateListener listener) {          this.listener = listener;      }        @Override      public void updateComplete() {          if (isUpdate) {              mLastRefreshTime = System.currentTimeMillis();          }          mPullToRefreshView.onRefreshComplete();      }        @Override      public void update(boolean showAnimation) {          mPullToRefreshView.setRefreshing(false);      }      }</code></pre>    <p>大家可以通过重写SmartListFragment中的这个方法实现自己的替换。</p>    <pre>  <code class="language-java">/**       * 获取具有下拉刷新,上拉加载更多功能的接口       * @param context       * @return       */      protected  ListViewUpdateManger getUpdateManager(Context context){          return new PullToRefreshManger(context);      }</code></pre>    <p>上述的接口只是提供上拉加载更多的功能,而具体的逻辑每一个应用都各有不同为此,我使用这个接口实现对分页逻辑的解耦</p>    <pre>  <code class="language-java">package com.zgh.smartlibrary.page;    import com.zgh.smartlibrary.net.NetRequest;    /**   * Created by zhuguohui on 2016/9/2.   */  public interface IPagePolicy {        /**       * 获取分页的状态       *       * @param dataSize 当前页的item数量       * @param data     需要解析的json数据       */      PageState getPageState(int dataSize, String data);          /**       * 更具index获取网络请求       *       * @param index       * @return       */      NetRequest getNetRequestByPageIndex(int index);        /**       * 设置一个基本url,例如第一页的url,下一页在此基础上改变就行了       * @param baseURL       */      void setBaseURL(String baseURL);        /**       * 分页的状态       */      enum PageState {          //没有分页信息,还有下一页,没有更多,加载下一页失败          NO_PAGE, HAVE_MORE, NO_MORE, LOAD_ERROR      }  }</code></pre>    <p>我的实闲是这样的</p>    <pre>  <code class="language-java">package com.zgh.smartdemo.page;    import com.zgh.smartdemo.bean.PageInfo;  import com.zgh.smartlibrary.config.JsonAnalysisConfig;  import com.zgh.smartlibrary.json.JsonAnalysisEngine;  import com.zgh.smartlibrary.net.NetRequest;  import com.zgh.smartlibrary.page.IPagePolicy;    import java.util.List;    /**   * Created by zhuguohui on 2016/9/10 0010.   */  public class DemoPagePolicy implements IPagePolicy {      //用于获取分页信息的JsonAnalysisEngine      protected JsonAnalysisEngine pageEngine;      private int mPageSize;      private String mBaseUrl = "";        public DemoPagePolicy() {          //每页数量为5          mPageSize = 5;          JsonAnalysisConfig config = new JsonAnalysisConfig();          config.setJsonLocation("{response{page_info");          config.setType(PageInfo.class.getName());          pageEngine = new JsonAnalysisEngine(config);      }          @Override      public PageState getPageState(int dataSize, String data) {          PageState state;          List<Object> data1 = pageEngine.getData(data);          PageInfo page_info = data1 != null && data1.size() > 0 ? (PageInfo) data1.get(0) : null;          //如果分页信息为空的话,表示不需要分页即NO_PAGE          if (page_info != null) {              try {                  //如果当前页的数量小于每页的数量,表示已到最后一页                  int count = Integer.valueOf(page_info.getPage_count());                  int page_index = Integer.valueOf(page_info.getPage_index());                  if (count == page_index + 1 || dataSize < mPageSize) {                      state = PageState.NO_MORE;                  } else {                      state = PageState.HAVE_MORE;                  }              } catch (Exception e) {                  e.printStackTrace();                  state = PageState.NO_MORE;              }          } else {              state = PageState.NO_PAGE;          }          return state;      }        @Override      public NetRequest getNetRequestByPageIndex(int index) {          //此处改变的是url地址,具体项目具体分析。          NetRequest request=new NetRequest();          String url = mBaseUrl;          if (index != 0) {                  url = mBaseUrl+"_"+index;          }          request.setUrl(url);          return request;      }        @Override      public void setBaseURL(String baseURL) {          mBaseUrl = baseURL;      }    }</code></pre>    <p>我的分页信息在这里</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/10504d12b7195efa2e33b943c5e45ebf.png"></p>    <p style="text-align:center">这里写图片描述</p>    <p>大家更具自己的项目情况来实现自己的分页策略,只需要覆盖SmartListFragment中的这个方法就行了</p>    <pre>  <code class="language-java">/**       * 获取分页策略       * @return       */      protected abstract IPagePolicy getPagePolicy();</code></pre>    <h3><strong>3.状态切换</strong></h3>    <p>此处使用的是张鸿洋的LoadingAndRetryManager,具体的用法如下参考这里 <a href="/misc/goto?guid=4959716681551279701" rel="nofollow,noindex">LoadingAndRetryManager</a> 。</p>    <p>我简单说一下用法,在Application的Oncreate中设置默认的样式</p>    <pre>  <code class="language-java">/**   * Created by zhuguohui on 2016/9/10 0010.   */  public class DemoApp extends Application {      @Override      public void onCreate() {          super.onCreate();          LoadingAndRetryManager.BASE_RETRY_LAYOUT_ID = R.layout.base_retry;          LoadingAndRetryManager.BASE_LOADING_LAYOUT_ID = R.layout.base_loading;          LoadingAndRetryManager.BASE_EMPTY_LAYOUT_ID = R.layout.base_empty;      }  }</code></pre>    <p>如果某个Fragment有特殊需求,覆盖这个方法就行了</p>    <pre>  <code class="language-java">protected OnLoadingAndRetryListener createLoadingAndRetryListener() {          return null;      }</code></pre>    <h3><strong>4.网络请求</strong></h3>    <p>目前的网络框架有很多,但是我们不应该依赖于具体的某一个框架,而是采用面向接口编程的实现,把网络请求这块与具体的实现分开。于是我定义了两个接口,一个用于网络请求,一个用于网络请求的处理:</p>    <pre>  <code class="language-java">package com.zgh.smartlibrary.net;    import java.util.HashMap;  import java.util.Map;    /**   * 代表网路请求的接口   * Created by zhuguohui on 2016/9/6.   */  public class NetRequest {      //地址      String url;      //请求方式      METHOD method;      //缓存方式      CACHE cache;      //参数      Map<String, String> params;      //结果回调      NetResultListener resultListener;      //数据      Object data;        public Object getData() {          return data;      }        public void setData(Object data) {          this.data = data;      }        public NetResultListener getResultListener() {          return resultListener;      }        public NetRequest setResultListener(NetResultListener resultListener) {          this.resultListener = resultListener;          return this;      }        public String getUrl() {          return url;      }        public NetRequest setUrl(String url) {          this.url = url;          return this;      }        public METHOD getMethod() {          return method;      }        public NetRequest setMethod(METHOD method) {          this.method = method;          return this;      }        public CACHE getCache() {          return cache;      }        public NetRequest setCache(CACHE cache) {          this.cache = cache;          return this;      }        public Map<String, String> getParams() {          return params;      }        public void setParams(Map<String, String> params) {          this.params = params;      }        public NetRequest addParams(String key, String value) {          if (params == null) {              params = new HashMap<>();          }          params.put(key, value);          return this;      }        public enum METHOD {          GET, POST      }      public  enum CACHE {          //文件缓存,内存缓存,不需要缓存。          FILE, MEMORY, NO_CACHE      }        public interface NetResultListener {          void onSuccess(NetRequest netRequest);          void onError(String msg);      }        @Override      public boolean equals(Object o) {          return super.equals(o);      }  }</code></pre>    <p>一个用于处理请求:</p>    <pre>  <code class="language-java">package com.zgh.smartlibrary.net;    /**   * Created by zhuguohui on 2016/9/6.   */  public interface NetRequestHandler {      /**       * 处理网路请求       * @param netRequest       */      void handleNetRequest(NetRequest netRequest);        /**       * 取消网络请求       * @param netRequest       */      void cancelNetRequest(NetRequest netRequest);    }</code></pre>    <p>有一个采用张鸿洋的OkHttpUtil的默认实现</p>    <pre>  <code class="language-java">package com.zgh.smartlibrary.net.impl;    import android.content.Context;      import com.zgh.smartlibrary.net.NetRequest;  import com.zgh.smartlibrary.net.NetRequestHandler;  import com.zgh.smartlibrary.util.FileUtil;  import com.zhy.http.okhttp.OkHttpUtils;  import com.zhy.http.okhttp.builder.GetBuilder;  import com.zhy.http.okhttp.builder.HasParamsable;  import com.zhy.http.okhttp.builder.OkHttpRequestBuilder;  import com.zhy.http.okhttp.builder.PostFormBuilder;  import com.zhy.http.okhttp.callback.StringCallback;    import java.util.Map;    import okhttp3.Call;    /**   * Created by zhuguohui on 2016/9/6.   */  public class SmartNetRequestHandler implements NetRequestHandler {      private final Context mContext;      private String HTTP_HEAD = "http";      private String HTTPS_HEAD = "https";      private String RAW_HEAD="raw://";        public SmartNetRequestHandler(Context context){          mContext=context;      }        @Override      public void handleNetRequest(final NetRequest netRequest) {          String url = netRequest.getUrl();            boolean isHttpRequest = false;          if (url != null && url.length() > 5) {                if (url.toLowerCase().startsWith(HTTP_HEAD) || url.toLowerCase().startsWith(HTTPS_HEAD)) {                  isHttpRequest = true;              }          }          if(netRequest.getMethod()==null){              netRequest.setMethod(NetRequest.METHOD.GET);          }          if (isHttpRequest) {              GetBuilder getBuilder = null;              PostFormBuilder postFormBuilder = null;              OkHttpRequestBuilder requestBuilder;              HasParamsable hasParamsable;              switch (netRequest.getMethod()) {                  case GET:                      getBuilder = OkHttpUtils.get();                      break;                  case POST:                      postFormBuilder = OkHttpUtils.post();                      break;              }              requestBuilder = getBuilder != null ? getBuilder : postFormBuilder;              if (requestBuilder == null) {                  onError(netRequest, "不支持的协议!");                  return;              }              hasParamsable = getBuilder != null ? getBuilder : postFormBuilder;              requestBuilder.url(url);              Map<String, String> params = netRequest.getParams();              if (params != null && params.size() > 0) {                  for (String key : params.keySet()) {                      hasParamsable.addParams(key, params.get(key));                  }              }              requestBuilder.build().execute(new StringCallback() {                  @Override                  public void onError(Call call, Exception e, int id) {                      SmartNetRequestHandler.this.onError(netRequest, e.getMessage());                  }                    @Override                  public void onResponse(String response, int id) {                      onSuccess(netRequest,response);                  }              });          } else {              if(url.toLowerCase().startsWith(RAW_HEAD)){                  String rawName = url.substring(RAW_HEAD.length());                  String s = FileUtil.readRaw(mContext, rawName);                  onSuccess(netRequest, s);              }else{                  onError(netRequest,"不支持的协议!");                  return;              }          }        }        public void onError(NetRequest request, String msg) {          if (request != null && request.getResultListener() != null) {              request.getResultListener().onError(msg);          }      }        public void onSuccess(NetRequest request, Object data) {          if (request != null && request.getResultListener() != null) {              request.setData(data);              request.getResultListener().onSuccess(request);          }      }        @Override      public void cancelNetRequest(NetRequest netRequest) {        }  }</code></pre>    <p>如果以后有其他的网络框架出来了,大家可以实现自己的NetRequestHandler 并替换默的,覆盖这个方法就行了SmartListFragment。</p>    <pre>  <code class="language-java">/**       * 获取网络请求处理器       * @param context       * @return       */      protected NetRequestHandler getNetRequestHandler(Context context) {          return new SmartNetRequestHandler(context);      }</code></pre>    <p>另外,为了方便大家对网络请求的统一修改,我才用责任链的方式实现了一个网络请求修改器。</p>    <pre>  <code class="language-java">package com.zgh.smartlibrary.net;    /**   * 用于对网络请求就行修改   * Created by zhuguohui on 2016/9/6.   */  public interface NetRequestModifiers {      NetRequest modifyNetRequest(NetRequest request);  }</code></pre>    <p>使用的时候只需要在覆盖initNetRequestModifiers然后调用addNetRequestModifiers加入自己的NetRequestModifiers就行了,这个可以功能可以实现给所以的网络请求加统一的Token等等。</p>    <pre>  <code class="language-java">@Override      protected void initNetRequestModifiers() {          addNetRequestModifiers(new NetRequestModifiers() {              @Override              public NetRequest modifyNetRequest(NetRequest request) {                  return request;              }          });      }</code></pre>    <p>补充一下,要想刷新界面调用这个方法就行了</p>    <pre>  <code class="language-java">/**       * 加载页面数据       * @param pageIndex        */      protected void loadListData(int pageIndex) {          requestIndex = pageIndex;          isUpdate = pageIndex == FIRST_PAGE_INDEX;          NetRequest request  = pagePolicy.getNetRequestByPageIndex(pageIndex);          request.setResultListener(this);          if (isUpdate && adapterManager.getDataSize() == 0) {              showLoading();          }          if (isUpdate) {              //缓存首页              request.setCache(NetRequest.CACHE.FILE);          }          listDataRequest = request;          loadData(request);      }</code></pre>    <p>要想加载自己的网络请求,使用这个方法</p>    <pre>  <code class="language-java">/**       * 加载网络请求       * @param netRequest       */      protected void loadData(NetRequest netRequest) {          netRequest = modifyNetRequest(netRequest);          netRequestHandler.handleNetRequest(netRequest);      }</code></pre>    <h3><strong>5.数据展示</strong></h3>    <p>数据的展示关键点是Adapter,目前用的还是ListView,不过需求都能满足。</p>    <pre>  <code class="language-java">package com.zgh.smartlibrary.adapter;    import android.content.Context;  import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup;  import android.widget.BaseAdapter;    import java.lang.reflect.Field;  import java.util.ArrayList;  import java.util.Collection;  import java.util.HashMap;  import java.util.List;  import java.util.Map;    /**   * Created by 朱国辉   * Date: 2015/12/5   * Time: 22:26   *   */  public abstract class SmartAdapter<E> extends BaseAdapter {        protected Context mContext;      private List<E> data;        public Map<Integer, SmartViewHolder> holderMap = new HashMap<>();        public SmartAdapter(Context ctx, List<E> data,                          SmartViewHolder... holders) {          this.mContext = ctx;          this.data = data;          if (holders != null && holders.length > 0) {              for (int i = 0; i < holders.length; i++) {                  holders[i].setType(i);                  holderMap.put(holders[i].getViewType(), holders[i]);              }          } else {              throw new IllegalArgumentException("SmartViewHolder 不能为空");          }        }        public SmartAdapter(Context ctx, List<E> data,                          List<SmartViewHolder> holders) {          this.mContext = ctx;          this.data = data;          int i = 0;          if (holders != null && holders.size() > 0) {              for (SmartViewHolder holder : holders) {                  holder.setType(i++);                  holderMap.put(holder.getViewType(), holder);              }          } else {              throw new IllegalArgumentException("SmartViewHolder 不能为空");          }        }        @Override      public E getItem(int position) {          if (!isEmpty(data)) {              return data.get(position);          }          return null;      }        @Override      public int getViewTypeCount() {          return holderMap.size();      }          @Override      public long getItemId(int position) {          return position;      }        @Override      public int getCount() {          if (!isEmpty(data)) {              return data.size();          }          return 0;      }          @Override      public View getView(int position, View convertView, ViewGroup parent) {          SmartViewHolder holder;          if (convertView == null) {              int type = getItemViewType(position);              convertView = LayoutInflater.from(mContext).inflate(holderMap.get(type).getLayoutId(), parent, false);              holder = buildHolder(convertView, type);              convertView.setTag(holder);          } else {              holder = (SmartViewHolder) convertView.getTag();          }            // 避免Item在滚动中出现黑色背景          convertView.setDrawingCacheEnabled(false);          E item = getItem(position);          holder.setContentView(convertView);          holder.updateView(mContext, item);          return convertView;      }        /**       * 用于自动绑定view       * @param convertView       * @param type       * @return       */      private SmartViewHolder buildHolder(View convertView, int type) {          SmartViewHolder holder;          try {              holder = holderMap.get(type).getClass().newInstance();              List<Field> fields = getViewFields(holder.getClass());              for (Field f : fields) {                  String name = f.getName();                  f.setAccessible(true);                  // ViewHolder的属性,不论类型都初始化赋值                  f.set(holder, convertView.findViewById(getId(name)));              }          } catch (Exception e) {              throw new RuntimeException("holder初始化出错    " + e);          }          return holder;      }        /**       * ViewHolder中只有是View的子类的成员变量才会被初始化       * @param clazz       * @return       */      private List<Field> getViewFields(Class clazz) {          List<Field> fields = new ArrayList<>();          while (clazz != null && clazz != SmartViewHolder.class) {              Field[] declaredFields = clazz.getDeclaredFields();              for (Field f : declaredFields) {                  if (isViewField(f)) {                      fields.add(f);                  }              }              clazz = clazz.getSuperclass();          }          return fields;      }        private boolean isViewField(Field f) {          Class<?> fType = f.getType();          boolean isView = false;          Class sclass = fType;          while (sclass != null && sclass != View.class) {              sclass = sclass.getSuperclass();          }          if (sclass == View.class) {              isView = true;          }          return isView;      }          public void addItems(List<E> extras) {          if (isEmpty(extras)) {              return;          }          data.addAll(getCount(), extras);          notifyDataSetChanged();      }        @Override      public int getItemViewType(int position) {          Collection<SmartViewHolder> holders = holderMap.values();          for (SmartViewHolder holder : holders) {              if (holder.isMyType(data.get(position))) {                  return holder.getViewType();              }          }          throw new RuntimeException("没有对应的 SmartViewHolder position=" + position + " item=" + data.get(position));      }          /**       * Some General Functions       */      private boolean isEmpty(List<?> list) {          return (list == null || list.size() == 0);      }          public int getId(String name) {          try {              return mContext.getResources().getIdentifier(name, "id", mContext.getPackageName());          } catch (Exception e) {              throw new RuntimeException(e);          }      }        public static abstract class SmartViewHolder<E> {          int type;          private View contentView;            /**           * 获取该VIewHolder对应的Type           *           * @return           */          public final int getViewType() {              return type;          }            /**           * 判断是否是自己处理的数据类型           *           * @param item           * @return           */          public abstract boolean isMyType(E item);            public void setType(int type) {              this.type = type;          }            /**           * 获取对应的布局id           *           * @return           */          public abstract int getLayoutId();            public abstract void updateView(Context context, E item);            public void setContentView(View contentView) {              this.contentView = contentView;          }            public View getContentView() {              return this.contentView;          }        }  }</code></pre>    <h3><strong>6.界面定制</strong></h3>    <p>很多时候一个界面中并不是只需要一个ListView就能解决了,还需要有一个其他的内容,为了偷懒,我就通过使用占位符的信息来实现对自定义界面的需求。下面是我的占位符:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/610cf68c852579a9263051a24057702b.png"></p>    <p style="text-align:center">这里写图片描述</p>    <p>然后覆盖SmartListFragment这个方法,返回自定义布局的ID</p>    <pre>  <code class="language-java">/**       * 获取自定义布局的layoutID       * @return       */      protected int getLayoutID() {          return 0;      }</code></pre>    <p>最后的处理在这里,不难就是需要慢慢写。</p>    <pre>  <code class="language-java">package com.zgh.smartlibrary.util;    import android.content.Context;  import android.view.View;  import android.view.ViewGroup;  import android.widget.LinearLayout;  import android.widget.ListAdapter;  import android.widget.ListView;    import com.zgh.smartlibrary.R;    import java.util.ArrayList;  import java.util.List;    /**   * Created by zhuguohui on 2016/9/6.   */  public class LayoutUtil {      public static View getBaseView(Context context, int layoutID, View mView, ListView listView) {          if (layoutID != 0) {              View warpView = View.inflate(context, layoutID, null);              View holderView = warpView.findViewById(R.id.ListViewHolder);              //判断是否是LinearLayout              if (holderView instanceof LinearLayout) {                  LinearLayout holder = (LinearLayout) holderView;                  //获取id为ListViewContent的view,如果没有表示,子view全部都要添加为heardView                  View contentView = holder.findViewById(R.id.ListViewContent);                  List<View> headerViews = new ArrayList<>();                  List<View> footViews = new ArrayList<>();                  List viewList = headerViews;                  for (int i = 0; i < holder.getChildCount(); i++) {                      View childView = holder.getChildAt(i);                      if (childView == contentView) {                          viewList = footViews;                          continue;                      }                      viewList.add(childView);                  }                  handleHeaderAndFooterView(listView, context, headerViews, footViews);              }                ViewGroup parent = (ViewGroup) holderView.getParent();              if (parent != null) {                  int index = 0;                  for (int i = 0; i < parent.getChildCount(); i++) {                      if (parent.getChildAt(i) == holderView) {                          index = i;                          break;                      }                  }                  parent.removeView(holderView);                  ViewGroup.LayoutParams params = holderView.getLayoutParams();                  mView.setLayoutParams(params);                  parent.addView(mView, index);                  mView = parent;              }          }            return mView;      }        private static void handleHeaderAndFooterView(ListView listView, Context context, List<View> headerViews, List<View> footViews) {          for (View view : headerViews) {              LinearLayout ViewParent = new LinearLayout(context);              if (view.getParent() != null) {                  ViewGroup group = (ViewGroup) view.getParent();                  group.removeView(view);              }              ViewParent.addView(view);              listView.addHeaderView(ViewParent);          }            for (View view : footViews) {              LinearLayout ViewParent = new LinearLayout(context);              if (view.getParent() != null) {                  ViewGroup group = (ViewGroup) view.getParent();                  group.removeView(view);              }              ViewParent.addView(view);              listView.addFooterView(ViewParent);          }      }  }</code></pre>    <p>当自定义布局填充好了以后,通过这个方法可以拿到View的引用</p>    <pre>  <code class="language-java">@Override      protected void onViewInit() {          //记得使用父类的findViewById方法。          findViewById(R.id.tv_head1).setOnClickListener(this);          findViewById(R.id.tv_head2).setOnClickListener(this);          findViewById(R.id.tv_footer1).setOnClickListener(this);          findViewById(R.id.tv_fixed_head).setOnClickListener(this);          findViewById(R.id.tv_fload_view).setOnClickListener(this);      }</code></pre>    <h3><strong>7.数据修改</strong></h3>    <p>对于返回的数据在现实之前,是可以修改的,也是使用责任链的模式实现的。</p>    <pre>  <code class="language-java">interface DataModifiers {          List<Object> modifyData(List<Object> data, boolean update);      }</code></pre>    <p>使用的时候这样使用覆盖SmartListFragment的getDataModifiers方法返回自己的数据。</p>    <pre>  <code class="language-java">@Override      protected AdapterManager.DataModifiers[] getDataModifiers() {          //过滤数据          return new AdapterManager.DataModifiers[]{new NewsDataModifiers()};      }</code></pre>    <p>我的demo中有一个例子,因为第二页数据中包含有,banner数据等不需要的东西,所以过滤了一下</p>    <pre>  <code class="language-java">/**   * Created by yuelin on 2016/9/6.   */  public class NewsDataModifiers implements AdapterManager.DataModifiers {      @Override      public List<Object> modifyData(List<Object> data, boolean update) {          if (!update) {              List<Object> newsList = new ArrayList<>();              for (Object object : data) {                  if (object instanceof NewsItem) {                      newsList.add(object);                  }              }              data.clear();              data.addAll(newsList);          }          return data;      }  }</code></pre>    <h2><strong>总结</strong></h2>    <p>这个框架也许还有很多问题,但对于我来说确实是不小的提升,特别是用到许多学过的设计模式,也一直在思考怎么解耦,怎么对修改封闭,对拓展开放等原则。写完这个框架大概才有一点点软件设计师的感觉,终于是在设计一些东西了,前路怎样我不知道,但是希望我能用心的做好每一件事,用心的写程序,而不是为了图完成工作,既然都看到这里了,去GitHub上给我来个start。</p>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/3f9daa32ad98</p>    <p> </p>