首先介绍下该项目用到的技术点和亮点(大神请绕道,小弟菜鸡)
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 getService(String url,Class classz){
return RetrofitHelper.getInstance()
.getRetrofit(url)
.create(classz);
}
}
网络请求工具类
public class HttpUtils {
//Post方式请求网络
public static void requestNetByPost(Observable observable, final OnResultListener resultListener){
setSubscriber(observable, new Subscriber() {
@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 void setSubscriber(Observable observable, Subscriber subscriber){
observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
//网络请求接口回调
public interface OnResultListener{
void onSuccess(T t);
void onError(Throwable error, String msg);
}
}
2. mvp设计模式
因为这个项目没涉及数据库方面数据 所以modle我直接放到presenter层实现逻辑处理了,本项目设计三个列表,因为实现方式都差不多,所以我就只介绍其中一个
具体代码如下
bean对象:
public class MeiziInfo {
public List 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 observable = RetrofitHelper.getService(url, IMeiziService.class).getMeizhiData(page);
HttpUtils.requestNetByGet(observable, new HttpUtils.OnResultListener() {
@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 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 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 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 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 mDatas;
protected View mView;
private CommonRecyclerViewHolder viewHolder;
public CommonRecyclerViewAdapter(Context context, List 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 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 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:[email protected]