说实话:
实习的时候,看到公司项目网络框架搭建真的是很羡慕(什么时候我自己也能搭建一个(≖‿≖)✧),所以在我屡战屡败的刻苦坚持下,终于肝出了这一篇秘籍,实现了普通的请求和单文件上传请求,第一篇文章先具体介绍一下mvp以及一些基类封装
目录:
一.项目结构
二.准备的Gradle
三.BaseView
四.BasePresenter
五.BaseActivity
六.Okhttp、Interceptor、Retrofit
七.BaseResponse与BaseObserver封装
八.上传请求解析
九.具体请求实例
讲道理:
MVP与MVC区别:
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。
项目结构:
Gradle
/*
apply plugin表示应用了一个插件,该插件一般有两种值可选:
com.android.application',表示该模块为应用程序模块,可以直接运行,打包得到的是.apk文件
com.android.library',表示该模块为库模块,只能作为代码库依附于别的应用程序模块来运行,打包得到的是.aar文件
*/
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
signingConfigs {// 自动化打包配置 想要debug 与release 版本互相切换 必须要 写这个配置
debug {
keyAlias 'key0'
keyPassword '123456'
storeFile file('yy.jks') //将它放在app目录下才可以这样引入
storePassword "123456"
}
release {
keyAlias 'key0'
keyPassword '123456'
storeFile file('yy.jks')
storePassword "123456"
}
}
compileSdkVersion 28 //设置编译时用的Android版本
defaultConfig {
applicationId "com.ycb.mvpdemo" //项目包名
minSdkVersion 19 //项目最低支持的版本
targetSdkVersion 28 //项目的目标使用版本
versionCode 1 // 版本号
versionName "1.0" //版本名称
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//表明要使用AndroidJUnitRunner进行单元测试
}
buildTypes { // 正式/测试环境配置
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
signingConfig signingConfigs.debug
buildConfigField("boolean", "LOG_DEBUG", "true")//配置Log日志
buildConfigField("String", "DOMAIN_URL", "\"http://v.juhe.cn/toutiao/\"")// 配置URL前缀
}
}
}
//总体分成三部分 apply android dependencies
dependencies {
//项目的依赖关系
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:design:28.0.0'
implementation 'com.yanzhenjie:permission:1.0.5'
implementation 'com.google.code.gson:gson:2.8.5'
//Retrofit2
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
//Rxjava
implementation 'io.reactivex.rxjava2:rxjava:2.2.3'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
//okhttp拦截器依赖
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.1'
//butterknife
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
//chuck
debugImplementation 'com.readystatesoftware.chuck:library:1.1.0'
releaseImplementation 'com.readystatesoftware.chuck:library-no-op:1.1.0'
//eventBus
implementation 'org.greenrobot:eventbus:3.0.0'
//Glide 4.6
implementation 'com.github.bumptech.glide:glide:4.6.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
//动态权限申请库
implementation 'pub.devrel:easypermissions:1.3.0'
}
BaseView
封装一些常用的基础方法
public interface BaseView {
/**
* 显示dialog
*/
void showLoading();
/**
* 显示下载文件dialog
*/
// void showLoadingFileDialog();
/**
* 隐藏下载文件dialog
*/
// void hideLoadingFileDialog();
/**
* 下载进度
*
* @param totalSize
* @param downSize
*/
//void onProgress(long totalSize, long downSize);
/**
* 隐藏 dialog
*/
void hideLoading();
void showError(String msg);
}
BasePresenter
public class BasePresenter<V extends BaseView> {
public CompositeDisposable compositeDisposable;
public V baseView;
public BasePresenter(V baseView) {
this.baseView = baseView;
}
/**
* 解除绑定
*/
public void detachView() {
baseView = null;
removeDisposable();
}
/**
* 返回 view
*
* @return
*/
public V getBaseView() {
return baseView;
}
//observer 观察者 observable是被观察者
//subscribeOn() 指定的就是发射事件的线程,observerOn 指定的就是订阅者接收事件的线程。
//四种线程
//Schedulers.io() 代表io操作的线程, 通常用于网络,读写文件等io密集型的操作;
//Schedulers.computation() 代表CPU计算密集型的操作, 例如需要大量计算的操作;
//Schedulers.newThread() 代表一个常规的新线程;
//AndroidSchedulers.mainThread() 代表Android的主线程
public void addDisposable(Observable<?> observable, BaseObserver observer) {
if (compositeDisposable == null) {
compositeDisposable = new CompositeDisposable();
}
compositeDisposable.add(observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(observer));
}
public void removeDisposable() {
if (compositeDisposable != null) {
compositeDisposable.dispose(); //在RxJava 2.x 中,新增的Disposable可以做到切断的操作,让Observer观察者不再接收上游事件
}
}
}
BaseActivity
BaseActivity里面有使用自定义注解显示布局和是否启用EventBus,
还有引入了EasyPermissions进行权限管理,Butterknife管理控件。
因为不是每个Activity都需要使用EventBus,所以通过反射加注解的方式决定是否需要使用,抛出handleEvent方法,同时关于MvP的Presenter的调用方法,以及toolbar等常用方法。
public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements BaseView, EasyPermissions.PermissionCallbacks {
public Context context;
public P presenter;
protected Unbinder unbinder;
private boolean mIsOpenEventBus = false;
private int mContentViewId ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//如果是指定类型注解类型则返回true
if (getClass().isAnnotationPresent(ActivityFragmentInject.class)) {
//通过反射获取 反射对象的方法等 返回该类中与参数类型匹配的公有注解对象
ActivityFragmentInject annotation = getClass().getAnnotation(ActivityFragmentInject.class);
mContentViewId = annotation.contentViewId();
mIsOpenEventBus = annotation.isOpenEventBus();
} else {
throw new RuntimeException("Class must add annotations of ActivityFragmentInitParams.class");
}
setContentView(mContentViewId);
presenter=createPresenter();
if (mIsOpenEventBus) {
EventBus.getDefault().register(this);
}
unbinder = ButterKnife.bind(this);
context = this;
initView();
}
protected P createPresenter(){
return presenter;
}
public void initView() {
}
@Subscribe //主线程执行
private void onEventMainThread(EventBean eventBean) {
if (eventBean != null) {
handleEvent(eventBean);
}
}
public void handleEvent(EventBean eventBean) {
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mIsOpenEventBus) {
EventBus.getDefault().unregister(this);
}
if (presenter != null) {
presenter.detachView();
}
if (unbinder != null) {
unbinder.unbind();
}
}
/**
* @param s
*/
public void showtoast(String s) {
Toast.makeText(context, s, Toast.LENGTH_LONG).show();
}
public void initToolbar(Toolbar toolbar, TextView textView, String title, boolean isBack) {
if (textView != null && title != null) {
textView.setText(title);
}
if (toolbar != null) {
toolbar.setTitle("");
if (isBack) {
toolbar.setNavigationIcon(R.drawable.ic_left);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
}
}
private void closeLoadingDialog() {
/* if (dialog!=null&&dialog.getDialog().isShowing()){
dialog.dismiss();
}*/
}
//建议用自定义控件形式或者在比较耗时的操作时使用
private void showLoadingDialog() {
/*dialog = AnimationDialog.newInstance();
//dialog.setOutCancel(false);
dialog.show(getSupportFragmentManager(),"show");
*/
}
//显示loading 这里的方法都是实现BaseView里面的
//目前可以通过
@Override
public void showLoading() {
showLoadingDialog();
}
//隐藏loading
@Override
public void hideLoading() {
closeLoadingDialog();
}
@Override
public void showError(String msg) {
showtoast(msg);
}
public void startActivity( Class<? extends Activity> cls) {
if (context!=null) {
Intent intent = new Intent();
intent.setClass(context, cls);
startActivity(intent);
}
}
public void startActivity( Class<? extends Activity> cls, Bundle bundle) {
if (context != null && cls != null && bundle != null) {
Intent intent = new Intent();
intent.putExtras(bundle);
intent.setClass(context, cls);
startActivity(intent);
}
}
// 将权限处理结果托管给EasyPermission
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
/**
* @param requestCode 请求的code
* @param permissions 一些列的请求权限
* @param grantResults 用户授权的结果
* @param receivers 监听的上下文,处理回调
*/
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
//权限申请成功
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
}
//权限申请失败时的回调
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
}
@Override
protected void onResume() {
super.onResume();
}
}
上面介绍了mvp和一些基础类的封装,这里讲一下Rxjava+Retrofit的封装以及上传文件,还有在具体情况下的使用
讲两件注意事项:1.安卓9.0不支持http请求,当然可以解决 2.安卓7.0开始要用FileProvider
其实前面也写过关于这方面的文章,一直都有用聚合的新闻数据 ,也有很多人看了我写的文章,在这里也非常感谢大家(偷偷的说,大家用我的新闻key,在聚合工作台上面看到我的接口被调用了几千次,非常感谢大家),闲话不多说了,下面开始讲解
Retrofit使用:
Retrofit的使用,自然少不了okhttp
client = new OkHttpClient.Builder()
//添加log拦截器 Http拦截器 Header拦截器(用于拦截token)
.addInterceptor(new LogInterceptor())
.addInterceptor(new ChuckInterceptor(BaseApplication.getAppContext()))
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.build();
这里隆重为大家推荐两个拦截器:ChuckInterceptor 、HeaderInterceptor
**
* Created by asus on 2019/4/23.
* 用户登陆状态保持思路
* 首先在欢迎界面在用户信息缓存模块查找是否有token
* 1.没有token 2.有token
* 解决思路
* 1.没有token 进入LoginActivity 登陆然后保存返回的用户信息
* 2.有token 进入 主界面(默认主界面是有很多fragment)
* 因为拦截器的原因 所以调用每个接口时 会添加token,所以每个接口的使用时成功的
* 然后你在使用展示用户信息的那个fragment时 从用户缓存模块里面将需要展示的信息取出
* 注意如果有什么地方对用户信息进行了修改 提交 则要对本地缓存的用户信息进行update
*
*
* token失效了怎么办??? 按照上面的逻辑还是会进入主界面的 但是任何接口操作 似乎都会报错
* 解决办法 一:在BaseException 里面定义token 失效 对用户进行提示 重新登陆
* 解决办法 二: 可不可以自动刷新 重新获取token
*/
public class HeaderInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request.Builder request; //Builder 建造者模式 request请求 辅助类
String token=""; //从本地用户缓存信息里面取得
if (token!=null&&!token.isEmpty()){
request= chain.request().newBuilder().addHeader("usertoken",""); //根据具体情况填写
}else {
request = chain.request().newBuilder();
}
// 将修改后的请求 获得的Response响应数据 返回
return chain.proceed(request.build());
/*
* 拦截器执行顺序 先后有序 从第一个添加的拦截器执行到最后一个拦截器 然后在把结果从最后一个一直返回到第一个 得到最终返回的Response
*/
}
}
下面就是整体的Retrofit代码
public class ApiRetrofit {
private static ApiRetrofit apiRetrofit;
private Retrofit retrofit;
private OkHttpClient client;
private String TAG = "ApiRetrofit";
public ApiRetrofit() {
client = new OkHttpClient.Builder()
//添加log拦截器 Http拦截器 动态切换BaseUrl拦截器 Header拦截器(用于拦截token)
.addInterceptor(new LogInterceptor())
.addInterceptor(new ChuckInterceptor(BaseApplication.getAppContext()))
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(Domain.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
//支持RxJava2
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build();
}
public static ApiRetrofit getInstance() {
if (apiRetrofit == null) {
synchronized (Object.class) {
if (apiRetrofit == null) {
apiRetrofit = new ApiRetrofit();
}
}
}
return apiRetrofit;
}
//封装泛型方法 普通的Retrofit写法是 ApiServer apiserver = retrofit.create(ApiServer.class);
//Class<T>代表获取这个类型的类 意思是获取class 这个类型的类
//T 代表一个类型
//这个方法的意思是 返回一个T 代表的类型,由Class<T>决定,
// 因为T 代表一个类型,你给它什么类型,它就代表什么类型,就返回什么类型
public <T> T setCreate(Class<T> reqServer) {
return retrofit.create(reqServer);
}
}
关于Rxjava的封装主要是体现在封装BaseObserver,继承Disposableobserver,可以做到切断Rxjava之间的连接,以防止内存泄漏
public abstract class BaseObserver<T> extends DisposableObserver<BaseResponse<T>> {
protected BaseView view;
private boolean isShowDialog;
public BaseObserver(BaseView view) {
this.view = view;
}
public BaseObserver(BaseView view, boolean isShowDialog) {
this.view = view;
this.isShowDialog = isShowDialog;
}
@Override
protected void onStart() {
if (view != null && isShowDialog) {
view.showLoading();
}
}
@Override
public void onNext(BaseResponse<T> response) {
if (response.getReason().equals("成功的返回")){
onSuccess(response.getResult());
}else {
onFailure(null,response.getError_code()+"");
}
}
@Override
public void onError(Throwable e) {
if (view != null && isShowDialog) {
view.hideLoading();
}
onFailure(e, RxExceptionUtil.exceptionHandler(e));
}
@Override
public void onComplete() {
if (view != null && isShowDialog) {
view.hideLoading();
}
}
public abstract void onSuccess(T result); //成功的返回
public abstract void onFailure(Throwable e,String errorMsg); //报错信息
}
项目实例—单文件上传
Retrofit上传文件(带圆形进度条):
上传使用接口:
@Multipart
@POST("member/avatar")
Observable<DetailsBean> uploadImage(@Part MultipartBody.Part file);
解释:
@Part("description") RequestBody description
@Part MultipartBody.Part file 作用是将数据封装成表单的形式提交给服务器
上传图片
OKHttp源码分析(二)RequestBody https://blog.csdn.net/luoj_616/article/details/80750144
RequestBody 用来提交文件和说明文件类型
RequestBody reqFile = RequestBody.create(MediaType.parse("image/*"), file); //说明要上传的数据类型 请求文件主体
MultipartBody.Part 作用是将数据封装成表单的形式提交给服务器
同时调用的这个方法就是 MultipartBody.Part 这个类型,肯定要创建这个类型啊 加上它的作用理解
createFormData 就是它的初始化创建方法 将数据封装成表单的形式提交给服务器
MultipartBody.Part body =
//第一个参数String类型 第二个参数 文件名 第三个参数 RequestBody
MultipartBody.Part.createFormData("file", file.getName(), reqFile);
理解完Retrofit上传的参数涵义,来实现我们的上传方法,但是Retrofit没有提供带进度的提示框哦,我们通过继承RequestBody类,重写writeTo方法即可获取上传进度!
public class ProgressRequestBody extends RequestBody {
private File mFile;
private String mPath;
private String mMediaType;
private UploadCallbacks mListener;
private int mEachBufferSize = 1024;
/* RequestBody 三个核心方法 可以看看源码哦,内容不多,挺好理解的
1,public abstract MediaType contentType()//数据类型
2,public long contentLength()//数据长度
3,public abstract void writeTo(BufferedSink sink)//写操作*/
public interface UploadCallbacks {
//正在进行
void onProgressUpdate(int percentage);
//出错
void onError();
//结束
void onFinish();
}
public ProgressRequestBody(final File file, String mediaType, final UploadCallbacks listener) {
mFile = file;
mMediaType = mediaType;
mListener = listener;
}
public ProgressRequestBody(final File file, String mediaType, int eachBufferSize, final UploadCallbacks listener) {
mFile = file;
mMediaType = mediaType;
mEachBufferSize = eachBufferSize;
mListener = listener;
}
@Override
public MediaType contentType() {
return MediaType.parse(mMediaType); //说明提交文件类型
}
//这个方法执行的是写操作
@Override
public void writeTo(BufferedSink sink) throws IOException {
long fileLength = mFile.length();
byte[] buffer = new byte[mEachBufferSize];
FileInputStream in = new FileInputStream(mFile);
long uploaded = 0;
int read;
try {
Handler handler = new Handler(Looper.getMainLooper());
while ((read = in.read(buffer)) != -1) { //为-1时说明文件已经读完
//一次传进去一个1024长度的 buffer 而不是往buffer里边传
handler.post(new ProgressUpdater(uploaded, fileLength)); //post 传一个Runnable 到主线程进行处理
uploaded += read;
sink.write(buffer, 0, read); //将写入buffer 个数据,从offset开始 到 read
}
}catch (Exception e){
e.printStackTrace();
}finally {
in.close();
}
}
private class ProgressUpdater implements Runnable {
private long mUploaded;
private long mTotal;
public ProgressUpdater(long uploaded, long total) {
mUploaded = uploaded;
mTotal = total;
}
@Override
public void run() {
mListener.onProgressUpdate((int) (100 * mUploaded / mTotal));
}
}
}
AffairUtil 查询本地图片类:用Rxjava做这些异步操作挺好用的,还可以用于数据库存大量数据时的子线程操作
public class AffairUtil {
public void SelectImage(final Context mContext) {
final List<String> mPhotoPathList = new ArrayList<>();
//第一步初始化被观察者
Observable.create(new ObservableOnSubscribe<List<String>>() {
@Override //被观察者通过这个发送消息给
public void subscribe(ObservableEmitter<List<String>> emitter) throws Exception {
//String path= PictureUtil.getImagePath(context,uri,null);
Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver mContentResolver = mContext.getContentResolver();
//只查询jpeg和png的图片
Cursor mCursor = mContentResolver.query(mImageUri, null,
MediaStore.Images.Media.MIME_TYPE + "=? or "
+ MediaStore.Images.Media.MIME_TYPE + "=?",
new String[]{"image/jpeg", "image/png"}, MediaStore.Images.Media.DATE_MODIFIED);
if (mCursor == null) {
return;
}
while (mCursor.moveToNext()) {
//获取图片的路径
String path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DATA));
if (!TextUtils.isEmpty(path)) {
mPhotoPathList.add(path);
}
}
mCursor.close();
emitter.onNext(mPhotoPathList);
}
}).subscribeOn(Schedulers.newThread()) //被观察者所在线程
.observeOn(AndroidSchedulers.mainThread()) //观察者所在线程
.subscribe(new Observer<List<String>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(List<String> list) {
if (lisenter != null) {
lisenter.onSelect(list);
}
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
private onSelectLisenter lisenter;
public void setOnSelectLisenter(onSelectLisenter mLisenter) {
this.lisenter = mLisenter;
}
public interface onSelectLisenter {
void onSelect(List<String> list);
}
}
PhotoListActivit.kt :界面代码使用kotlin编写
@ActivityFragmentInject(contentViewId = R.layout.activity_photo_list)
class PhotoListActivity : BaseActivity<FilePersenter>(), View.OnClickListener,FileBaseView {
val affair = AffairUtil()
var permissions: Array<String> = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA)
var mPhotoListAdapter: PhotoListAdapter? = null
var mPath: String = ""
override fun createPresenter(): FilePersenter {
return FilePersenter(this)
}
override fun initView() {
mPhotoListAdapter = PhotoListAdapter(context)
initPermission()
}
private fun initPermission() {
// 检查是否有权限 实际建议这些获取权限操作还是放在欢迎页时比较好
if (EasyPermissions.hasPermissions(this, permissions.toString())) {
initData()
} else {
//不要用EasyPermissions 会飞快闪过 个人觉得和初始化加生命周期有关
// 用了ActivityCompat后面会在onRequestPermissionsResult 托管给EasyPermissions
ActivityCompat.requestPermissions(this, permissions, 1)
}
}
//获取失败
override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
super.onPermissionsDenied(requestCode, perms)
//onPermissionsDenied(int requestCode, List perms)方法:当权限申请失败的时候执行的回调,
// 在这个方法里面,官方还建议用EasyPermissions.somePermissionPermanentlyDenied(this, perms)方法来判断是否有权限被勾选了不再询问并拒绝,
// 还提供了一个AppSettingsDialog来给我们使用,在这个对话框里面解释了APP需要这个权限的原因,用户按下是的话会跳到APP的设置界面,
// 可以去设置权限(是不是很不要脸^_^),这个Dialog可以使用默认的样式new AppSettingsDialog.Builder(this).build().show(),
//原文:https://blog.csdn.net/qq_38414907/article/details/76535559
}
//获取成功
override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
super.onPermissionsGranted(requestCode, perms)
initData()
}
private fun initData() {
affair.SelectImage(context)
affair.setOnSelectLisenter(object : AffairUtil.onSelectLisenter {
override fun onSelect(list: MutableList<String>) {
mPhotoListAdapter?.addData(list)
rv_photo_list.layoutManager = GridLayoutManager(context, 3)
rv_photo_list.adapter = mPhotoListAdapter
rv_photo_list.addItemDecoration(ThreeGridDecoration(SizeUtils.dp2px(context, 2F), SizeUtils.dp2px(context, 2F)))
mPhotoListAdapter?.setPhotoAlbumListener(object : PhotoAlbumListener<String> {
//选择照片
override fun onSelected(t: String) {
mPath = t
Glide.with(context).load(t).into(iv_preview)
iv_preview.setOnClickListener(this@PhotoListActivity)
tv_preview.setOnClickListener(this@PhotoListActivity)
tv_next.setOnClickListener(this@PhotoListActivity)
tv_preview.setTextColor(resources.getColor(R.color.title))
tv_next.setTextColor(resources.getColor(R.color.title))
//关于Recyclerview 实现多选,单选,全选,反选,批量删除的功能的实现
//https://blog.csdn.net/guohaosir/article/details/72403724
}
//这个是调用相机的
override fun onClickCamera() {
}
})
}
})
}
override fun onResume() {
super.onResume()
tv_preview.setTextColor(resources.getColor(R.color.sub_title))
tv_preview.setClickable(false)
tv_next.setTextColor(resources.getColor(R.color.sub_title))
tv_next.setClickable(false)
mPhotoListAdapter?.changeCheckState()
}
override fun onClick(v: View) {
when (v.id) {
R.id.iv_preview ->
iv_preview.visibility = View.GONE
R.id.tv_preview ->
iv_preview.visibility = View.VISIBLE
R.id.tv_next ->
if (!TextUtils.isEmpty(mPath)) {
uploadPicture();
} else {
showtoast("请选择一张图片,亲")
}
}
}
//上传方法
private fun uploadPicture() {
presenter.UploadImage("image/*", mPath)
}
//启动loading
override fun StartLoading() {
fl_circle_progress.visibility = View.VISIBLE
}
override fun ShowLoading(percentage: Int) {
circle_progress.setProgress(percentage)
}
override fun onSucceed(bean: DetailsBean) {
circle_progress.visibility = View.GONE
//正常情况下 接口里面会有返回成功的消息
showtoast("上传成功")
}
override fun showError(msg: String) {
circle_progress.visibility=View.GONE
showtoast(msg)
}
}