您的位置:首页 > 理论基础 > 计算机网络

[置顶] RxJava+Retrofit+okhttp+mvp+butterknife实现的简易开源项目

2017-02-12 22:19 477 查看
首先介绍下该项目用到的技术点和亮点(大神请绕道,小弟菜鸡)

1. Rxjava+Retrofit+okhttp搭建的网络框架
2. mvp设计模式
3. butterknife注解方式查找控件,减少findViewById冗余代码
4. Glide图片加载框架
5. Recyclerview结合SwipeRefreshLayout实现列表和下拉刷新
6. 封装了ListView和GridView,RecycleView的通用数据适配器工具类


1. Rxjava+Retrofit+okhttp搭建的网络框架

代码我写的浅显易懂,就不一一解释了
主要是CacheHelper,OkHttpClientHelper,RetrofitHelper和HttpUtils这四个类,代码我们主要看RetrofitHelper和HttpUtils这两个

public class RetrofitHelper {

private final OkHttpClient mClient;
private Retrofit mRetrofit;

private RetrofitHelper(){

mClient = OkHttpClientHelper.getInstance().getOkHttpClient();
}

private static RetrofitHelper helper;

//单例 保证对象唯一
public static RetrofitHelper getInstance(){
if(helper==null){
synchronized (RetrofitHelper.class){
if(helper==null){
helper = new RetrofitHelper();
}
}
}
return helper;
}

//获取Retrofit对象
public Retrofit getRetrofit(String url){

if(mRetrofit==null) {
mRetrofit = new Retrofit.Builder()
.baseUrl(url + "/")
.addConverterFactory(GsonConverterFactory.create())  //添加Gson支持
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())  //添加RxJava支持
.client(mClient)                                            //关联okhttp
.build();
}
return mRetrofit;
}

/**
*获取服务对象   Rxjava+Retrofit建立在接口对象的基础上的
*泛型避免强制转换
*/
public static<T> T getService(String url,Class<T> classz){

return RetrofitHelper.getInstance()
.getRetrofit(url)
.create(classz);
}
}

网络请求工具类

public class HttpUtils {

//Post方式请求网络
public static<T> void requestNetByPost(Observable observable, final OnResultListener resultListener){

setSubscriber(observable, new Subscriber<T>() {
@Override
public void onCompleted() {
Log.e("onCompleted","读取完成");
}

@Override
public void onError(Throwable error) {

if(error!=null && resultListener!=null){
resultListener.onError(error,error.getMessage());
}else if(resultListener!=null){
resultListener.onError(new Exception("网络不给力"),"");
Toast.makeText(MyApplication.getContext(),"网络不给力",Toast.LENGTH_LONG).show();
return;
}

String e  = error.getMessage();
int code  =0;
if(!TextUtils.isEmpty(e)){
try {
e = e.substring(e.length()-3,e.length());
code = Integer.valueOf(e);
}catch (Exception e1){
Toast.makeText(MyApplication.getContext(),"网络不给力",Toast.LENGTH_LONG).show();
}
}
Log.e("code==:",code+"");
if(code>=300&&code<500){
Toast.makeText(MyApplication.getContext(),"您的请求迷路了,请稍后再试",Toast.LENGTH_LONG).show();
}else if(code>=500){
Toast.makeText(MyApplication.getContext(),"服务器异常,请稍后再试",Toast.LENGTH_LONG).show();
}else{
Toast.makeText(MyApplication.getContext(),"网络不给力",Toast.LENGTH_LONG).show();
}
}

@Override
public void onNext(T t) {

if(resultListener!=null){
resultListener.onSuccess(t);
}
}
});
}

//Get方式请求网络
public static void requestNetByGet(Observable observable,final OnResultListener resultListener){

requestNetByPost(observable,resultListener);
}

//订阅事件
public static<T> void setSubscriber(Observable<T> observable, Subscriber<T> subscriber){

observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}

//网络请求接口回调
public interface OnResultListener<T>{

void onSuccess(T t);
void onError(Throwable error, String msg);
}
}


2. mvp设计模式

因为这个项目没涉及数据库方面数据 所以modle我直接放到presenter层实现逻辑处理了,本项目设计三个列表,因为实现方式都差不多,所以我就只介绍其中一个

具体代码如下

bean对象:

public class MeiziInfo {

public List<MeiziBean> results;

public static class MeiziBean{

public String objectId;
public String url;
public String type;
public String desc;
public String who;
public boolean used;
public boolean hasFadedIn=false;
public int imageWidth;
public int imageHeight;
}
}


Presenter接口和实现类代码

妹子接口
public interface IMeiziPrestener{

void getMeiziInfo(String url,int page);
}

妹子接口实现类
public class MeiziPresenterImpl implements IMeiziPrestener{

private IMeiziModel meiziModel; //没什么用 只是为了好看 哈哈
private IMeiziFragment iMeiziFragment;   //View接口 后面实现
public MeiziPresenterImpl(IMeiziFragment iMeiziFragment){
meiziModel = new MeiziModelImpl();
this.iMeiziFragment = iMeiziFragment;
}

//通过调用网络工具类 获取数据 View接口调用方法 在相应activity或者fragment中实现
@Override
public void getMeiziInfo(String url,int page) {

Observable<MeiziInfo> observable = RetrofitHelper.getService(url, IMeiziService.class).getMeizhiData(page);
HttpUtils.requestNetByGet(observable, new HttpUtils.OnResultListener<MeiziInfo>() {
@Override
public void onSuccess(MeiziInfo meiziInfo) {
if(meiziInfo!=null)
iMeiziFragment.getMeiziDataList(meiziInfo.results);
}

@Override
public void onError(Throwable error, String msg) {
Log.e("妹子info error",msg);
}
});

}
}


View接口代码

public interface IMeiziFragment {

void getMeiziDataList(List<MeiziInfo.MeiziBean> meiziInfos);
}


3.Activity和 Fragment中实现

MainActivity实现,通过toolbar实现标题栏 实现三fragment切换

注解方式查找控件

public class MainActivity extends BaseActivity {

@InjectView(R.id.toolbar)
Toolbar toolbar;
@InjectView(R.id.nav_view)
NavigationView navView;
@InjectView(R.id.drawer)
DrawerLayout drawer;
Fragment currentFragment;
SimpleArrayMap<Integer, String> mTitleArryMap = new SimpleArrayMap<>();
private ZhihuFragment zhihuFragment;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

ButterKnife.inject(this);  //注解初始化这个是必须的
initData();

setSupportActionBar(toolbar);
toolbar.setOnMenuItemClickListener(onMenuItemClick);

switchFragment(zhihuFragment = new ZhihuFragment());
currentFragment = zhihuFragment;
navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem item) {
switchFragment(getFragmentById(item.getItemId()));
Log.e("条目内容===",item.toString());
drawer.closeDrawer(GravityCompat.END);
return true;
}
});

}

private void initData() {

}

private Fragment getFragmentById(int id) {
Fragment fragment = null;
switch (id) {
case R.id.zhihuitem:
fragment = new ZhihuFragment();
break;
case R.id.topnewsitem:
fragment=new NewsFragment();
break;
case R.id.meiziitem:
fragment=new MeiziFragment();
break;

}
return fragment;
}

private void switchFragment(Fragment fragment) {

if (currentFragment == null || !currentFragment.getClass().getName().equals(fragment.getClass().getName()))
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment)
.commit();
currentFragment = fragment;

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

private Toolbar.OnMenuItemClickListener onMenuItemClick = new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.menu_open:
drawer.openDrawer(GravityCompat.END);
break;
}
return true;
}};
}


妹子对应的Fragment实现,注解初始化我在BaseFragment中实现了,在Fragment中注意在onDestroyView方法中调用ButterKnife.reset(this);

public class MeiziFragment extends BaseFragment implements IMeiziFragment,SwipeRefreshLayout.OnRefreshListener {

@InjectView(R.id.id_meizi_recycle)
RecyclerView mMeiziRecycle;
@InjectView(R.id.id_meizi_swipe)
SwipeRefreshLayout mMeiziSwipe;
private MeiziPresenterImpl meiziPrestener;
private MeiziAdapter mMeiziAdapter;
//妹子地址
private String url = "http://gank.io";
private int page = 1;
private LinearLayoutManager linearLayoutManager;
private RecyclerView.OnScrollListener mloadmoreListener;
private boolean loading;
private List<MeiziInfo.MeiziBean> meiziInfos;

@Override
public View initView() {
mView = View.inflate(mActivity, R.layout.fragment_meizi, null);
return mView;
}

@Override
public void initData() {

SwipeRefreshUtil.setSiwpeLayout(mMeiziSwipe,mActivity,this);
meiziInfos = new ArrayList<>();
meiziPrestener = new MeiziPresenterImpl(this);
meiziPrestener.getMeiziInfo("",page);
setRecycleView();
}

private void setRecycleView() {

mMeiziRecycle.addItemDecoration(new GridItemDividerDecoration(getContext(), R.dimen.divider_height, R.color.divider_color));
mMeiziRecycle.setItemAnimator(new DefaultItemAnimator());
mMeiziRecycle.setLayoutManager(linearLayoutManager =new LinearLayoutManager(mActivity, LinearLayoutManager.VERTICAL, false));

}

@Override
protected void initListener() {
super.initListener();
mloadmoreListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0 && linearLayoutManager!=null) //向下滚动
{
int visibleItemCount = linearLayoutManager.getChildCount();
int totalItemCount = linearLayoutManager.getItemCount();
int pastVisiblesItems = linearLayoutManager.findFirstVisibleItemPosition();

if (!loading && (visibleItemCount + pastVisiblesItems) >= totalItemCount) {
loading = true;
page+=1;
loadMoreData();
}
}
}
};
}

private void loadMoreData() {
Log.e("当前页数==",page+"");
meiziPrestener.getMeiziInfo("",page);

}

@Override
public void getMeiziDataList(List<MeiziInfo.MeiziBean> meiziInfos) {

mMeiziSwipe.setRefreshing(false);
if(meiziInfos==null || meiziInfos.size()<=0)
return;
loading = false;
this.meiziInfos.addAll(meiziInfos);
Log.e("妹子数据集合==",meiziInfos.size()+"");
if(mMeiziAdapter==null) {
mMeiziRecycle.setAdapter(mMeiziAdapter = new MeiziAdapter(mActivity, this.meiziInfos));
}else{
mMeiziAdapter.notifyDataSetChanged();
}

mMeiziRecycle.addOnScrollListener(mloadmoreListener);
}

@Override
public void onRefresh() {

new Handler().postDelayed(new Runnable() {
@Override
public void run() {
meiziInfos.clear();
page = 1;
loading = false;
meiziPrestener.getMeiziInfo("",page);
}
},1000);
}
}


4. ListView和GridView,RecycleView的通用数据适配器工具类封装

ListView通用适配器在我的“你还在用第三方开源下拉刷新控件吗?试试google自带的下拉刷新控件SwipeRefreshLayout”这篇博客中有讲解

RecycleView通用适配器实现

public abstract class CommonRecyclerViewAdapter<T> extends RecyclerView.Adapter<CommonRecyclerViewHolder>{

protected Context mContext;
protected List<T> mDatas;
protected View mView;
private CommonRecyclerViewHolder viewHolder;

public CommonRecyclerViewAdapter(Context context, List<T> datas){
this.mContext = context;
this.mDatas = datas;
}

@Override
public CommonRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
mView = initView();
viewHolder = new CommonRecyclerViewHolder(mContext,mView);
return viewHolder;
}

@Override
public int getItemCount() {
return mDatas!=null && mDatas.size()>0 ? mDatas.size() : 0;
}

@Override
public  void onBindViewHolder(CommonRecyclerViewHolder holder, int position){
setData(holder,position);
}

public CommonRecyclerViewHolder getViewHolder() {
return viewHolder;
}

public abstract View initView();  //加载布局

public abstract void setData(CommonRecyclerViewHolder holder, int position);   //设置数据 处理逻辑

}

CommonRecyclerViewHolder 类实现

public class CommonRecyclerViewHolder extends RecyclerView.ViewHolder{

private final SparseArray<View> mViews;  //相当于Map集合 不过效率更高,key只能为Integer
private View mConvertView;
private int mPosition;
private Context mContext;
public CommonRecyclerViewHolder(Context context,View itemView) {
super(itemView);
this.mConvertView = itemView;
this.mContext = context;
this.mViews = new SparseArray<>();
}

/**
* 通过控件的Id获取对应的控件,如果没有则加入views
* @param viewId
* @return
*/
public <T extends View> T getView(int viewId){

View view = mViews.get(viewId);
if(view==null && mConvertView!=null){
view = mConvertView.findViewById(viewId);
mViews.put(viewId,view);
}
return (T) view;
}

/**
* 为TextView设置字符串
*
* @param viewId
* @param text
* @return
*/
public CommonRecyclerViewHolder setText(int viewId, String text)
{
TextView view = getView(viewId);
view.setText(text);
return this;
}

/**
* 为ImageView设置图片
*/
public CommonRecyclerViewHolder setImageResource(int viewId,int resId){

ImageView imageView = getView(viewId);
imageView.setImageResource(resId);
return this;
}

/**
* 为ImageView设置图片
*/
public CommonRecyclerViewHolder setImageBitmap(int viewId,Bitmap bitmap){

ImageView imageView = getView(viewId);
imageView.setImageBitmap(bitmap);
return this;
}

public void showImage(int viewId,String imageUrl){
ImageView view = getView(viewId);
ImageUtil.show(view,imageUrl);
}

}


5. Glide实现的图片加载框架

public class ImageUtil {

/**===========================加载图片方式,可换其他框架加载===========================================================================*/
/**
* 以正常模式加载网络图片
*/

public static void show(ImageView mImageView, String imageUrl) {

Glide.with(MyApplication.getContext()).load(imageUrl)
.crossFade(0)
.into(mImageView);  //crossFade是个淡入淡出效果
}

/**
* 以拉伸模式加载网络图片
*/
public static void show1(ImageView mImageView, String imageUrl) {
Glide.with(MyApplication.getContext()).load(imageUrl)
.fitCenter()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.transform(new GlideRoundTransform(MyApplication.getContext(),8))
.crossFade(0)
.into(mImageView);  //crossFade是个淡入淡出效果
}

//加载正方形图片
public static void showSquare(ImageView mImageView, String imageUrl) {

Glide.with(MyApplication.getContext()).load(imageUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.centerCrop()
.into(mImageView);
}

//加载正方形图片(商品)
public static void showSquare2(ImageView mImageView, String imageUrl) {

Glide.with(MyApplication.getContext()).load(imageUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(mImageView);
}

/**
* 加载圆头像
*/
public static void showCircle(final ImageView mImageView, String imageUrl) {

Glide.with(MyApplication.getContext()).load(imageUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.fitCenter()
.bitmapTransform(new CropCircleTransformation(MyApplication.getContext()))
.crossFade(0)
.into(mImageView);
}

public static void showRoundedImage(ImageView imageView, String imageUrl) {

Glide.with(MyApplication.getContext())                                 //可以传getApplicationContext Activity Fragment
.load(imageUrl)                                  //图片地址
.diskCacheStrategy(DiskCacheStrategy.ALL)
.transform(new GlideRoundTransform(MyApplication.getContext(),4))
.centerCrop()
.into(imageView);
}

public static void showGoodsImage(ImageView imageView, String imageUrl){

Glide.with(MyApplication.getContext())                                 //可以传getApplicationContext Activity Fragment
.load(imageUrl)                                  //图片地址
.diskCacheStrategy(DiskCacheStrategy.ALL)
.transform(new GlideRoundTransform(MyApplication.getContext(),4))
.into(imageView);
}

public static void showRoundedImage2(ImageView imageView, String imageUrl,int radius) {

Glide.with(MyApplication.getContext())                                 //可以传getApplicationContext Activity Fragment
.load(imageUrl)                                  //图片地址
.diskCacheStrategy(DiskCacheStrategy.ALL)
.transform(new GlideRoundTransform(MyApplication.getContext(),radius))
.into(imageView);
}

/**
* 加载高斯模糊
*/
public static void show2(ImageView mImageView, String imageUrl) {

Glide.with(MyApplication.getContext())
.load(imageUrl)
.bitmapTransform(new BlurTransformation(MyApplication.getContext(), 25)).crossFade(0)
.into(mImageView);
}

//=========================Glide加载GIF图片配置==============================================================//

//加载本地GIF
public static void showGIF(ImageView imageView,int img){
Glide.with(MyApplication.getContext()).load(img).asGif()
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(imageView);
}

//加载网络GIF
public static void showGIF(ImageView imageView,String img){
Glide.with(MyApplication.getContext()).load(img).asGif().diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView);
}

}


6. 总结

我是读了网上的一个开源项目然后自己封装仿写的这个项目 不过我感觉比它写的好 至少代码可读性要强 哈哈(你个自恋狂)

7. 源代码下载

点击进入GitHub下载

如果觉得好,请随手star,如果不好,欢迎批评指点。Thanks you very much!

或者 下载需要2积分 sorry

8.效果展示

本来是想录制gif图片的,因为一些原因只能截图了





9. 联系方式

qq:1509815887

email:zlc921022@163.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: