PYBase -- 快速构建自己的Android项目

PYBase

Android基础项目,封装了BaseActivity、BaseFragment、BaseLazyFragment,使用MVP设计模式、RxJava + Retrofit作为网络框架,目前处于初步阶段,项目会逐步进行更新

记录自己学到的东西

MVP+RxJava+Retrofit的封装

首先是RetrofitService:

/**
 * 登录
 */
@GET("login/{param}")
Observable<BaseResponse<User>> login(@Path("param") String param,
                                     @Query("password") String password,
                                     @Query("userType") int userType,
                                     @Query("visitIp") String ip);

retrofit配合rxjava使用需要将login()返回类型从原来的Call<T>类型改为Observable<T>类型

BaseResponse:

class BaseResponse<T> {
  private String msg;
  private int code;
  private T data;
}

接着是RetrofitUtils得到Retrofit实例:

public static Retrofit getInstance(String url) {
  sRetrofit = new Retrofit.Builder().baseUrl(url)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(getClient())
                .build();
  return sRetrofit;
}
private static OkHttpClient getClient() {
    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    builder.retryOnConnectionFailure(true).connectTimeout(TIMEOUT_CONNECT, TimeUnit.SECONDS)
            .readTimeout(TIMEOUT_CONNECT, TimeUnit.SECONDS);

    if (L.getGlobalToggle())  // 控制是否需要打印调试
      builder.addInterceptor(new LogInterceptor());
    return builder.build();
}

接下来就是MVP的使用:

首先是一些基类:

IView:

public interface IView<T extends IPresenter> {
    /**
     * 设置presenter
     * @param presenter
     */
    void setPresenter(T presenter);

    /**
     * 显示加载动画
     */
    void showLoading();

    /**
     * 隐藏加载
     */
    void hideLoading();

    /**
     * 显示错误状态
     */
    void showError(StateLayout.OnRetryListener onRetryListener);

    /**
     * 显示提示
     */
    void showTip(String tip);
}

BasePresenter:

public class BasePresenter<M extends IModel, V extends IView> implements IPresenter {
    protected CompositeDisposable mCompositeDisposable;

    protected M mModel;
    protected V mView;

    public BasePresenter(String baseUrl, V view) {
        this.mView = view;
    }

    protected void addDispose(Disposable disposable) {
        if (mCompositeDisposable == null) {
            mCompositeDisposable = new CompositeDisposable();
        }
        // 将所有disposable放入,集中处理
        mCompositeDisposable.add(disposable);
    }

    @Override
    public void onDestroy() {
        unDispose();
        if (mModel != null)
            mModel.onDestroy();
        this.mModel = null;
        this.mView = null;
        this.mCompositeDisposable = null;
    }

    /**
     * 解除订阅
     */
    protected void unDispose() {
        if (mCompositeDisposable != null) {
            // 保证activity结束时取消所有正在执行的订阅
            mCompositeDisposable.clear();
        }
    }
}

BaseModel获取RetrofitService实例:

public class BaseModel implements IModel {
    protected ApiService mService;

    public BaseModel(String baseUrl) {
        mService = RetrofitUtils.getInstance(baseUrl).create(ApiService.class);
    }

    @Override
    public void onDestroy() {
        mService = null;
    }

ILoginContract:

public interface ILoginContract {
    interface Presenter extends IPresenter {
        /**
         * 登录
         */
        void login(String param, String password, int userType, String ip);
    }

    interface View extends IView<Presenter> {
        /**
         * 获取登录信息
         * @param user
         */
        void showLoginMsg(User user);
    }
}

LoginModelImpl省略...

LoginPresenter:

public class LoginPresenter extends BasePresenter<LoginModelImpl, ILoginContract.View> implements ILoginContract.Presenter {
    public LoginPresenter(String baseUrl, ILoginContract.View view) {
        super(baseUrl, view);
        mModel = new LoginModelImpl(Constant.BASE_URL);
        mView.setPresenter(this);
    }

    @Override
    public void login(String param, String password, int userType, String ip) {
        ModelFilteredFactory.compose(mModel.login(param, password, userType, ip))
                .compose(RxUtils.bindToLifecycle(mView))
                .subscribe(new BaseObserver<User>() {
                    @Override
                    protected void onHandleSuccess(User user) {
                        mView.showLoginMsg(user);
                    }

                    @Override
                    protected void onHandleError(String tip) {
                        mView.showTip(tip);
                    }
                });
    }
}

其中使用了RxLifecycle绑定了activity和fragment的生命周期,防止rxjava使用过程当中的内存泄漏(生命周期的解除订阅)

public static <T> LifecycleTransformer<T> bindToLifecycle(IView view) {
    if (view instanceof RxAppCompatActivity) {
        return ((RxAppCompatActivity) view).bindToLifecycle();
    } else if (view instanceof RxFragment) {
        return ((RxFragment) view).bindToLifecycle();
    } else {
        throw new IllegalArgumentException("view isn't activity or fragment");
    }
}

BaseObserver(简单封装了Observer):

@Override
public void onNext(@NonNull BaseResponse<T> response) {
    if (response.getCode() == Constant.RESPONSE_CODE_OK) {
        T data = response.getData();
        onHandleSuccess(data);
    } else {
        onHandleError(response.getMsg());
    }
}

@Override
public void onError(Throwable e) {
    L.e("error:" + e.toString());
    if (e instanceof APIException) {
        APIException exception = (APIException) e;
        onHandleError(exception.getMessage());
    } else if (e instanceof UnknownHostException) {
        onHandleError("请打开网络");
    } else if (e instanceof SocketTimeoutException) {
        onHandleError("请求超时");
    } else if (e instanceof ConnectException) {
        onHandleError("连接失败");
    } else if (e instanceof HttpException) {
        onHandleError("请求超时");
    } else {
        onHandleError("请求失败");
    }
    e.printStackTrace();
}

在activity(封装了BaseActivity简化代码)中使用:

public class MainActivity extends BaseActivity<ILoginContract.Presenter> implements ILoginContract.View
mPresenter = new LoginPresenter(Constant.BASE_URL, this);

发送网络请求就仅仅只需一行代码:

mPresenter.login("用户名", "123456", 0, "");

在showLoginMsg()中返回服务器返回的信息:

@Override
public void showLoginMsg(User user) {
  // DO SOMETHING
}

RecyclerView.Adapter的简单封装:

  • 极大简化了代码
  • 支持添加多个Header和Footer
  • 支持item的单击和长按监听
  • 支持RecyclerView多ViewType
  • RecyclerView数据源变动的封装
  • RecyclerView列表的单选和多选模式

使用方法:

class  PYAdapter extends BaseRvAdapter<String> {
    public PYAdapter(Context context, List<String> dataList, int layoutId) {
        super(context, dataList, layoutId);
    }

    @Override
    public void convert(BaseViewHolder holder, String item, int position, int viewType) {
        holder.setText(R.id.tv, item);
    }
}
rc.setAdapter(new PYAdapter(this, dataList, R.layout.item));

多布局使用方法:

class PYAdapter extends BaseRvAdapter<String> {
    public PYAdapter(Context context, List<String> dataList, IMultiItemViewType<String> multiItemViewType) {
        super(context, dataList, multiItemViewType);
    }

    @Override
    public void convert(BaseViewHolder holder, String item, int position, int viewType) {
        switch (viewType) {
            case 100:
                holder.setText(R.id.tv, item + " 100");
                break;
            case 200:
                holder.setText(R.id.tv, item + " 200");
                break;
        }
    }
}
PYAdapter pyAdapter = new PYAdapter(this, dataList, new IMultiItemViewType<String>() {
  @Override
  public int getItemViewType(int position, String s) {
      if (position % 2 == 0) {
          return 100;
      } else {
          return 200;
      }
  }

  @Override
  public int getLayoutId(int viewType) {
      return R.layout.item;
  }
});
rv.setAdapter(pyAdapter);

添加Header/Footer(支持多Header/Footer):

View headerView0 = LayoutInflater.from(this).inflate(R.layout.item, null);
View headerView1 = LayoutInflater.from(this).inflate(R.layout.item, null);
View footerView0 = LayoutInflater.from(this).inflate(R.layout.item, null);
View footerView1 = LayoutInflater.from(this).inflate(R.layout.item, null);

pyAdapter.addHeaderView(headerView0, headerView1);
pyAdapter.addFooterView(footerView0, footerView1);

RecyclerView item单击和长按监听:

pyAdapter.setOnItemClickListener((holder, position, item) -> {});
pyAdapter.setOnItemLongClickListener((holder, position, item) -> {});

RecyclerView数据源变动的封装:

void add(T item);

void add(int position, T item);

void addAll(List<T> items);

void addAll(int position, List<T> items);

void remove(T item);

void remove(int position);

void removeAll(List<T> items);

void retainAll(List<T> items);

void set(T oldItem, T newItem);

void set(int position, T item);

void replaceAll(List<T> items);

void clear();

RecyclerView列表的单选和多选模式:

使用SelectAdapter(继承自BaseRvAdapter)

// 单选模式
selectAdapter.setSelectedMode(ISelect.SINGLE_MODE);
// 多选模式
selectAdapter.setSelectedMode(ISelect.MULTIPLE_MODE);

创建实体类实现ISelect接口:

public class TestSelectBean implements ISelect {
    private boolean isSelected;
    private String text;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    @Override
    public boolean isSelected() {
        return isSelected;
    }

    @Override
    public void setSelected(boolean selected) {
        isSelected = selected;
    }
}

创建SelectAdapter:

class PyAdapter extends SelectAdapter<TestSelectBean> {
    public PyAdapter(Context context, List<TestSelectBean> dataList, int layoutId) {
        super(context, dataList, layoutId);
    }

    @Override
    public void convert(BaseViewHolder holder, TestSelectBean item, int position, int viewType) {
        CheckBox cb = holder.getView(R.id.cb);
        cb.setChecked(item.isSelected());
        holder.setText(R.id.tv, item.getText());
    }
}

设置选择模式下的监听:

mAdapter.setItemSelectedListener(new SelectAdapter.OnItemSelectedListener<TestSelectBean>() {
    @Override
    public void onItemSelected(BaseViewHolder viewHolder, int position, boolean isSelected, TestSelectBean testSelectBean) {
      if (isSelected) {
        // 如果被选中
      } else {
        // 如果没有被选中
      }
    }

    @Override
    public void onNothingSelected() {
      // 什么也没有选中
    }
});

封装BaseDialog,实现TitleDialog、BottomDialog、ListDialog(配合SelectAdapter实现单选、多选模式)

<img width="300" height="100" src="http://upload-images.jianshu.io/upload_images/1570601-4020c2cee50c77e3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"/>
<img width="320" height="360" src="http://upload-images.jianshu.io/upload_images/1570601-6105f9934b4f003b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"/><img width="320" height="360" src="http://upload-images.jianshu.io/upload_images/1570601-021004253263b075.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"/>
<img width="320" height="600" src="http://upload-images.jianshu.io/upload_images/1570601-e05e7c07786d0829.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"/>

使用方法:

TitleDialog:

new TitleDialog(this).setDialogTitle("标题")
        .setLeftButtonText("取消")
        .setRightButtonText("确定")
        .setOnLeftBtnClickListener(null)
        .setOnRightBtnClickListener((dialog, view) -> {
            // do something
            dialog.dismissDialog();
        }).showDialog();

ListDialog(默认单选模式):

ListDialog<MyAdapter, TestSelectBean> listDialog = new ListDialog<>(this);
listDialog.setSelectAdapter(new MyAdapter(this, dataList, R.layout.item), new SelectAdapter.OnItemSelectedListener<TestSelectBean>() {
    @Override
    public void onItemSelected(BaseViewHolder viewHolder, int position, boolean isSelected, TestSelectBean testSelectBean) {
      if (isSelected) {
        // 如果被选中
      } else {
        // 如果没有被选中
      }
    }

    @Override
    public void onNothingSelected() {
      // 什么也没有选中
    }
}).setSelectMode(ISelect.SINGLE_MODE).showDialog();

多选模式:

setSelectMode(ISelect.MULTIPLE_MODE)

BottomDialog:

new BottomDialog(this)
        .setFullWidth()
        .setFromBottom()
        .setLeftButtonText("拍照")
        .setRightButtonText("从相册中选择照片")
        .setOnLeftBtnClickListener((dialog, view) -> {})
        .setOnRightBtnClickListener((dialog, view) -> {})
        .showDialog();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容