1. 导包:gson包,ImageLoader包
2. 添加依赖:
(1)butterknife自动生成控件id和点击事件:
compile 'com.jakewharton:butterknife:7.0.0'
(2)okhttp依赖: compile 'com.squareup.okhttp3:okhttp:3.9.0'
(3)recyclerview依赖(我自己的版本需要把SDK改为25版本):
compile 'com.android.support:recyclerview-v7:25.3.1'
compile 'com.yqritc:recyclerview-flexibledivider:1.4.0'
(4)springview依赖
compile 'com.liaoinstan.springview:library:1.3.0'
一 . 首先编写OKhttp的封装类
import java.io.File; import java.util.Map; import java.util.concurrent.TimeUnit; import okhttp3.FormBody; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; /** * Okhttp 单例 范型的封装 */ public class OkhttpUtils { private static OkhttpUtils okhttpUtils = null ; private OkhttpUtils(){ } public static OkhttpUtils getInstance(){ if(okhttpUtils == null){ okhttpUtils = new OkhttpUtils(); client = new OkHttpClient.Builder() .readTimeout(20, TimeUnit.SECONDS) .writeTimeout(20,TimeUnit.SECONDS) .connectTimeout(20,TimeUnit.SECONDS) .addInterceptor(new LoggingInterceptor()) .build(); } return okhttpUtils ; } private static OkHttpClient client ; public void asy(Map<String,String> params,String url,AbstractUiCallBack callBack){ Request request = null ; if(params != null){ FormBody.Builder builder = new FormBody.Builder() ; for(Map.Entry<String,String> entry : params.entrySet()){ builder.add(entry.getKey(),entry.getValue()); } FormBody body = builder.build(); request = new Request.Builder() .url(url) .post(body) .build(); } else { request = new Request.Builder() .url(url) .build(); } client.newCall(request).enqueue(callBack); } private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png"); public static void postFile(Map<String,String> map, String url, File file,AbstractUiCallBack callBack) { String[] array = file.getAbsolutePath().split("\\/"); MultipartBody.Builder builder = new MultipartBody.Builder(); builder.setType(MultipartBody.FORM); for (Map.Entry<String, String> entry : map.entrySet()) { builder.addFormDataPart(entry.getKey(), entry.getValue()); } builder.addFormDataPart("imageFileName", array[array.length - 1]); if (file.exists() && file.length() > 0) { builder.addFormDataPart("image", array[array.length - 1], RequestBody.create(MEDIA_TYPE_PNG, file)); } MultipartBody body = builder.build(); Request request = new Request.Builder() .url(url) .post(body) .build(); client.newCall(request).enqueue(callBack); } }
OKhttp封装需实现的接口类
import android.os.Handler; import android.os.Looper; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Response; /** * * Okhttp 单例 范型的封装 */ public abstract class AbstractUiCallBack<T> implements Callback { /** * 成功回调 * @param t */ public abstract void success(T t); /** * 失败回调 * @param e */ public abstract void failure(Exception e); private Handler handler = null ; private Class clazz ; public AbstractUiCallBack(){ handler = new Handler(Looper.getMainLooper()); // 得到的是一个 AbstractUiCallBack<T> 的Type Type type = getClass().getGenericSuperclass() ; // 得到的是T的实际Type Type [] arr = ((ParameterizedType)type).getActualTypeArguments() ; clazz = (Class) arr[0] ; } @Override public void onFailure(Call call, IOException e) { failure(e); } @Override public void onResponse(Call call, Response response) throws IOException { try { String result = response.body().string(); System.out.println("result = " + result); Gson gson = new Gson(); final T t = (T) gson.fromJson(result,clazz); handler.post(new Runnable() { @Override public void run() { success(t); } }); } catch (IOException e) { e.printStackTrace(); failure(e); } catch (JsonSyntaxException e) { e.printStackTrace(); failure(e); } } }
二. 拦截器类
import java.io.IOException; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; //拦截器类 public class LoggingInterceptor implements Interceptor{ @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); //请求对象 /** * 返回最准确的可用系统计时器的当前值,以毫微秒为单位 * 返回值表示从某一固定但任意的时间算起的毫微秒数 */ long time1 = System.nanoTime(); Response response = chain.proceed(request); //响应对象 long time2 = System.nanoTime(); System.out.println("响应时长 = "+(time2 - time1)); return response; } }
三. model层
import com.bwie.secondweek.bean.MyDataBean; import com.bwie.secondweek.okhttp.AbstractUiCallBack; import com.bwie.secondweek.okhttp.OkhttpUtils; /** * model层,数据的存取 */ public class DataModel { //上拉刷新下拉加载更多 public void onRefresh(boolean b, final DataModelCallBack modelCallBack){ OkhttpUtils.getInstance().asy(null, "http://v.juhe.cn/toutiao/index?type=toutiao&key=c4479ad58f41e7f78a8fa073d0b1f1b5", new AbstractUiCallBack<MyDataBean>() { @Override public void success(MyDataBean dataBean) { modelCallBack.success(dataBean); } @Override public void failure(Exception e) { modelCallBack.failure(e); } }); } //定义接口 public interface DataModelCallBack{ public void success(MyDataBean dataBean); //成功获取数据 public void failure(Exception e); //数据获取失败 } }
四. view层
import com.bwie.secondweek.bean.MyDataBean; /** * view层,UI界面的搭建 */ public interface DataView { public void success(MyDataBean dataBean); //成功获取数据 public void failure(Exception e); //数据获取失败 }
五. presenter层
import com.bwie.secondweek.bean.MyDataBean; import com.bwie.secondweek.model.DataModel; import com.bwie.secondweek.view.DataView; /** * presenter层,进行model和view层之间数据的交互 */ public class DataPresenter { private DataView dataView; private DataModel dataModel; //构造方法中声明view层,初始化model层数据 public DataPresenter(DataView dataView) { this.dataView = dataView; dataModel = new DataModel(); } //调用model层接口,上拉加载下拉刷新 public void onRefresh(final boolean b){ dataModel.onRefresh(b, new DataModel.DataModelCallBack() { @Override public void success(MyDataBean dataBean) { dataView.success(dataBean); } @Override public void failure(Exception e) { dataView.failure(e); } }); } }
六. imageloader工具类及其全局初始化配置
(1)全局初始化配置类:
package com.bwie.secondweek.util; import android.app.Application; //全局初始化Application类 public class BaseApplication extends Application { @Override public void onCreate() { super.onCreate(); //配置imageLoader ImageLoaderUtil.init(this); } }
(2)imageloader工具类:
package com.bwie.secondweek.util; import android.content.Context; import android.graphics.Bitmap; import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache; import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator; import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.assist.QueueProcessingType; import com.nostra13.universalimageloader.core.decode.BaseImageDecoder; import com.nostra13.universalimageloader.core.display.RoundedBitmapDisplayer; import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; import com.nostra13.universalimageloader.core.download.BaseImageDownloader; import com.nostra13.universalimageloader.utils.StorageUtils; import java.io.File; public class ImageLoaderUtil { /** * 初始化imageLoader * @param context */ public static void init(Context context) { //1.获取配置config对象 File cacheDir = StorageUtils.getCacheDirectory(context); //缓存文件夹路径 ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context) .threadPoolSize(3) // default 线程池内加载的数量 .threadPriority(Thread.NORM_PRIORITY - 2) // default 设置当前线程的优先级 .tasksProcessingOrder(QueueProcessingType.FIFO) // default .denyCacheImageMultipleSizesInMemory() .memoryCache(new LruMemoryCache(2 * 1024 * 1024)) //可以通过自己的内存缓存实现 .memoryCacheSize(2 * 1024 * 1024) // 内存缓存的最大值 .memoryCacheSizePercentage(13) // default .diskCache(new UnlimitedDiscCache(cacheDir)) // default 可以自定义缓存路径 .diskCacheSize(50 * 1024 * 1024) // 50 Mb sd卡(本地)缓存的最大值 .diskCacheFileCount(100) // 可以缓存的文件数量 // default为使用HASHCODE对UIL进行加密命名, 还可以用MD5(new Md5FileNameGenerator())加密 .diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) .imageDownloader(new BaseImageDownloader(context)) // default .imageDecoder(new BaseImageDecoder(true)) // default .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default .writeDebugLogs() // 打印debug log .build(); //开始构建 //2.初始化配置...ImageLoader.getInstance()图片加载器的对象,单例模式 ImageLoader.getInstance().init(config); } /** * imageLoader加载图片的默认选项 * @return */ public static DisplayImageOptions getDefaultOption(){ DisplayImageOptions options = new DisplayImageOptions.Builder() .showImageOnLoading(R.mipmap.ic_launcher) // 设置图片下载期间显示的默认图片 .showImageForEmptyUri(R.mipmap.ic_launcher) // 设置图片Uri为空或是错误的时候显示的图片 .showImageOnFail(R.mipmap.ic_launcher) // 设置图片加载或解码过程中发生错误显示的图片 .resetViewBeforeLoading(true) // default 设置图片在加载前是否重置、复位 .delayBeforeLoading(1000) // 下载前的延迟时间 .cacheInMemory(true) // default 设置下载的图片是否缓存在内存中 .cacheOnDisk(true) // default 设置下载的图片是否缓存在SD卡中 .considerExifParams(true) // default .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default 设置图片以如何的编码方式显示 .bitmapConfig(Bitmap.Config.RGB_565) // default 设置图片的解码类型 .displayer(new SimpleBitmapDisplayer()) // default 还可以设置圆角图片new RoundedBitmapDisplayer(20) .build(); return options; } /** * imageLoader加载圆角图片....指定圆角的大小 * @return */ public static DisplayImageOptions getRoundedOption(int corner){ DisplayImageOptions options = new DisplayImageOptions.Builder() .showImageOnLoading(R.mipmap.ic_launcher) // 设置图片下载期间显示的图片 .showImageForEmptyUri(R.mipmap.ic_launcher) // 设置图片Uri为空或是错误的时候显示的图片 .showImageOnFail(R.mipmap.ic_launcher) // 设置图片加载或解码过程中发生错误显示的图片 .resetViewBeforeLoading(true) // default 设置图片在加载前是否重置、复位 .delayBeforeLoading(1000) // 下载前的延迟时间 .cacheInMemory(true) // default 设置下载的图片是否缓存在内存中 .cacheOnDisk(true) // default 设置下载的图片是否缓存在SD卡中 .considerExifParams(true) // default .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default 设置图片以如何的编码方式显示 .bitmapConfig(Bitmap.Config.RGB_565) // default 设置图片的解码类型 .displayer(new RoundedBitmapDisplayer(corner)) // default 还可以设置圆角图片new RoundedBitmapDisplayer(20) .build(); return options; } }
七. 数据接口封装的bean类(根据自己需要定义)
八. 主功能代码MainActivity类
import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.widget.Toast; import com.bwie.secondweek.adapter.DataAdapter; import com.bwie.secondweek.bean.MyDataBean; import com.bwie.secondweek.presenter.DataPresenter; import com.bwie.secondweek.view.DataView; import com.liaoinstan.springview.container.DefaultFooter; import com.liaoinstan.springview.container.DefaultHeader; import com.liaoinstan.springview.widget.SpringView; import butterknife.Bind; import butterknife.ButterKnife; //主功能代码类,实现view层,进行数据UI的获取 public class MainActivity extends AppCompatActivity implements DataView { @Bind(R.id.recyclerView) RecyclerView recyclerView; @Bind(R.id.springview) SpringView springview; private DataPresenter dataPresenter; private DataAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); //声明presenter层 dataPresenter = new DataPresenter(this); //设置布局适配器,,,线性布局管理器 adapter = new DataAdapter(this); LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); recyclerView.setLayoutManager(layoutManager); recyclerView.setAdapter(adapter); //设置SpringView进行多条目加载的头布局和尾布局 springview.setHeader(new DefaultHeader(this)); springview.setFooter(new DefaultFooter(this)); //设置SpringView的刷新监听事件 springview.setListener(new SpringView.OnFreshListener() { @Override public void onRefresh() { dataPresenter.onRefresh(true); } @Override public void onLoadmore() { dataPresenter.onRefresh(false); } }); dataPresenter.onRefresh(true); //设置一直刷新数据 } @Override public void success(MyDataBean dataBean) { //只要有数据就一直加载 if (springview != null){ springview.onFinishFreshAndLoad(); } adapter.addData(dataBean.getResult().getData()); } @Override public void failure(Exception e) { runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this,"数据出错",Toast.LENGTH_SHORT).show(); } }); } }
九. 自定义适配器类
import android.content.Context; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.bwie.secondweek.R; import com.bwie.secondweek.bean.MyDataBean; import com.bwie.secondweek.util.ImageLoaderUtil; import com.nostra13.universalimageloader.core.ImageLoader; import java.util.ArrayList; import java.util.List; import butterknife.Bind; import butterknife.ButterKnife; /** * 条目布局适配器 */ public class DataAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private Context context; private List<MyDataBean.ResultBean.DataBean> list; public DataAdapter(Context context) { this.context = context; } //声明数据来源,添加数据 public void addData(List<MyDataBean.ResultBean.DataBean> list) { if (this.list == null) { this.list = new ArrayList<>(); } this.list.addAll(list); notifyDataSetChanged(); } //创建条目布局 @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == 0) { View view01 = LayoutInflater.from(context).inflate(R.layout.item1, parent, false); return new ViewHolder01(view01); } else if (viewType == 1) { View view02 = LayoutInflater.from(context).inflate(R.layout.item2, parent, false); return new ViewHolder02(view02); } else { View view03 = LayoutInflater.from(context).inflate(R.layout.item3, parent, false); return new ViewHolder03(view03); } } //绑定数据 @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { //判断加载布局的类型 if (holder instanceof ViewHolder01) { ViewHolder01 holder01 = (ViewHolder01) holder; ImageLoader.getInstance().displayImage(list.get(position).getThumbnail_pic_s(), holder01.listItem1Imageview, ImageLoaderUtil.getDefaultOption()); } else if (holder instanceof ViewHolder02) { ViewHolder02 holder02 = (ViewHolder02) holder; ImageLoader.getInstance().displayImage(list.get(position).getThumbnail_pic_s(), holder02.listItem2Imageview1, ImageLoaderUtil.getDefaultOption()); ImageLoader.getInstance().displayImage(list.get(position).getThumbnail_pic_s02(), holder02.listItem2Imageview2, ImageLoaderUtil.getDefaultOption()); } else { ViewHolder03 holder03 = (ViewHolder03) holder; ImageLoader.getInstance().displayImage(list.get(position).getThumbnail_pic_s(), holder03.listItem3Imageview1, ImageLoaderUtil.getDefaultOption()); ImageLoader.getInstance().displayImage(list.get(position).getThumbnail_pic_s02(), holder03.listItem3Imageview2, ImageLoaderUtil.getDefaultOption()); ImageLoader.getInstance().displayImage(list.get(position).getThumbnail_pic_s03(), holder03.listItem3Imageview3, ImageLoaderUtil.getDefaultOption()); } } @Override public int getItemViewType(int position) { //判断布局类型,加载不同数据 if (!TextUtils.isEmpty(list.get(position).getThumbnail_pic_s03())) { return 2; }else if (!TextUtils.isEmpty(list.get(position).getThumbnail_pic_s02())) { return 1; }else { return 0; } } //首先加载三种图片布局 @Override public int getItemCount() { return list == null ? 0 : list.size(); } static class ViewHolder01 extends RecyclerView.ViewHolder { @Bind(R.id.list_item1_textview) TextView listItem1Textview; @Bind(R.id.list_item1_imageview) ImageView listItem1Imageview; ViewHolder01(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } } static class ViewHolder02 extends RecyclerView.ViewHolder { @Bind(R.id.list_item2_textview) TextView listItem2Textview; @Bind(R.id.list_item2_imageview1) ImageView listItem2Imageview1; @Bind(R.id.list_item2_imageview2) ImageView listItem2Imageview2; ViewHolder02(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } } static class ViewHolder03 extends RecyclerView.ViewHolder { @Bind(R.id.list_item3_textview) TextView listItem3Textview; @Bind(R.id.list_item3_imageview1) ImageView listItem3Imageview1; @Bind(R.id.list_item3_imageview2) ImageView listItem3Imageview2; @Bind(R.id.list_item3_imageview3) ImageView listItem3Imageview3; ViewHolder03(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } } }
十. 页面布局
1. activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" > <com.liaoinstan.springview.widget.SpringView android:id="@+id/springview" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent"></android.support.v7.widget.RecyclerView> </com.liaoinstan.springview.widget.SpringView> </RelativeLayout>
2. 条目一:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:layout_gravity="center_horizontal" android:id="@+id/list_item1_imageview" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_marginTop="18dp" android:src="@mipmap/ic_launcher" /> </LinearLayout>
3. 条目二:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="18dp" android:orientation="horizontal"> <ImageView android:id="@+id/list_item2_imageview1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:src="@mipmap/ic_launcher" /> <ImageView android:id="@+id/list_item2_imageview2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:src="@mipmap/ic_launcher" /> </LinearLayout> </LinearLayout>
4. 条目三:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="18dp" android:orientation="horizontal"> <ImageView android:id="@+id/list_item3_imageview1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:src="@mipmap/ic_launcher" /> <ImageView android:id="@+id/list_item3_imageview2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:src="@mipmap/ic_launcher" /> <ImageView android:id="@+id/list_item3_imageview3" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:src="@mipmap/ic_launcher" /> </LinearLayout> </LinearLayout>
注:
最后在 清单文件中 AndroidManifest.xml中配置权限,全局配置imageloader类
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET"/>
android:name=".util.BaseApplication"