xUitls是一个第三方库,其就是一个工具类,是最初源于Afinal框架,进行了大量的重构,使得其能够支持大文件上传,以及更安全的Http请求协议支持。
它主要分为4个模块:DbUtils、HttpUtils、ViewUtils、BitmapUtils
DbUtils:
ViewUtils:
HttpUtils:
BitmapUtils:
由于xUtils已经很久不更新了,目前我们就使用它的升级版,也就是xUtils3
这是它的官方网站
xUtils3相对于旧版有了一些变化:
HTTP实现替换HttpClient为UrlConnection, 自动解析回调泛型, 更安全的断点续传策略;
支持标准的Cookie策略, 区分domain, path;
事件注解去除不常用的功能, 提高性能;
数据库api简化提高性能, 达到和greenDao一致的性能;
图片绑定支持gif(受系统兼容性影响, 部分gif文件只能静态显示), webp; 支持圆角, 圆形, 方形等裁剪, 支持自动旋转。
implementation 'org.xutils:xutils:3.8.9'
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!-- 可选 -->
// 在application的onCreate中初始化
@Override
public void onCreate() {
super.onCreate();
x.Ext.init(this);
x.Ext.setDebug(BuildConfig.DEBUG); // 是否输出debug日志, 开启debug会影响性能.
...
}
添加如下代码:
x.view().inject(this);
到此为止,xUitls3的初始化已经完成,接下来可以进行功能的使用了。
ViewUtils的使用无需findViewById和setClickListener等,它用注解进行了替代,大大简化了我们的开发。这里简单介绍几个常用注解
这是个什么注解呢?
很明显,通过名字我们便可得知,它就是用来替代**setContentView()**的
它怎么使用呢?
我们点开它的源代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
int value();
}
可以看到,在这个注解中,只需要Value填入你的布局文件id即可。然后将其标注在你的Activity上即可
这是个什么注解呢?
通过直译这个注解可以很明显的知道,它就是用来注入view的,也就是说,它替代了我们的findViewById()
它怎么使用呢?
源代码:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
int value();
/* parent view id */
int parentId() default 0;
}
可以看到,它也只需要注入value即可,并标注在TextView上即可。
例:
@ViewInject(R.id.queryname)
TextView tv;
这个注解主要用于事件的处理。
源代码:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Event {
/**
* 控件的id集合, id小于1时不执行ui事件绑定.
*/
int[] value();
/**
* 控件的parent控件的id集合, 组合为(value[i], parentId[i] or 0).
*/
int[] parentId() default 0;
/**
* 事件的listener, 默认为点击事件.
*/
Class<?> type() default View.OnClickListener.class;
/**
* 事件的setter方法名, 默认为set+type#simpleName.
*/
String setter() default "";
/**
* 如果type的接口类型提供多个方法, 需要使用此参数指定方法名.
*/
String method() default "";
}
这里有几点注意点:
举一个小例子:
@Event(value = {R.id.btn1,R.id.btn2,R.id.btn3})
private void onClick(View v) {
switch (v.getId()){
case R.id.btn1 :
Toast.makeText(getApplicationContext(),"btn1被点击了",Toast.LENGTH_LONG).show();
case R.id.btn2 :
Toast.makeText(getApplicationContext(),"btn2被点击了",Toast.LENGTH_LONG).show();
case R.id.btn3 :
Toast.makeText(getApplicationContext(),"btn3被点击了",Toast.LENGTH_LONG).show();
}
}
首先,我们在MainActivity中new一个DbManager.DaoConfig
这个DaoConfig都有什么东西呢?这里我总结一下:
public DaoConfig setDbDir(File dbDir) #设置数据库存储目录
public DaoConfig setDbName(String dbName) #设置数据库名
public DaoConfig setDbVersion(int dbVersion) #设置数据库版本
public DaoConfig setAllowTransaction(boolean allowTransaction) #设置是否开启事务(默认为true)
public DaoConfig setDbOpenListener(DbOpenListener dbOpenListener) #数据库打开监听
public DaoConfig setDbUpgradeListener(DbUpgradeListener dbUpgradeListener) #数据库更新监听
public DaoConfig setTableCreateListener(TableCreateListener tableCreateListener) #监听表创建
了解清楚后,我们可以开始配置了:
@ContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
DbManager db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
//创建并配置DaoConfig
DbManager.DaoConfig daoConfig = new DbManager.DaoConfig()
.setDbName("test1.db")
.setDbDir(new File("/sdcard"))
.setDbVersion(2)
.setDbOpenListener(new DbManager.DbOpenListener() {
@Override
public void onDbOpened(DbManager db) throws DbException {
//开启WAL,提升写入速度
db.getDatabase().enableWriteAheadLogging();
}
})
.setDbUpgradeListener(new DbManager.DbUpgradeListener() {
@Override
public void onUpgrade(DbManager db, int oldVersion, int newVersion) throws DbException {
//可进行建表等相关操作
}
});
try {
db = x.getDb(daoConfig);
} catch (DbException e) {
e.printStackTrace();
}
}
}
接下来创建一个User表作为数据库的实体类:(具体注释的用途已标明)
//声明其为一个表,给xutils解析 表名为user,onCreated处填写sql语句,则表创建时将执行(默认为空)
@Table(name="user",onCreated = "")
public class User {
//isId告诉xutils其为id,autoGen设置是否自增
@Column(name = "id",isId = true,autoGen = true,property = "NOT NULL")
private int id;
@Column(name = "name")
private String name;
//注意一定要保留这个无参构造,否则建表将会失败
public User(){
}
public User(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在MainActivity中使用DbManager的CRUD方法:
(这里使用到了**@Event**注解,这个注解只需传入按钮的id,即可在标注的方法中进行该按钮的监听事件,让我们省了不少功夫)
@Event(R.id.save)
private void save(View v) throws DbException {
ArrayList<User> users = new ArrayList<>();
users.add(new User("zhangsan"));
users.add(new User("lisi"));
//不仅仅可以进行单个用户的插入,还能实现List的插入
db.save(users);
Log.i("test","建表成功");
}
@Event(R.id.delete)
private void del(View v) throws DbException {
//whereBuilder即为一个条件类,在其中可以运行有条件的SQL语句
//传入id = 1
db.delete(User.class, WhereBuilder.b().and("id","=",1));
Log.i("test","删除成功");
}
@Event(R.id.update)
private void update(View v) throws DbException {
//将id=1的数据的name值改为Nick
db.update(User.class,WhereBuilder.b().and("id","=",1),new KeyValue("name","Nick"));
Log.i("test","修改成功");
}
@Event(R.id.query)
private void select(View v) throws DbException {
//查找出id=1的值
User user = db.selector(User.class).where("id","=",1).findFirst();
Log.i("test","用户名为:"+user.getName());
Toast.makeText(getApplicationContext(),user.getName(),Toast.LENGTH_LONG).show();
}
结果如下:
通常,我们的Get请求会有一长串的字符参数跟在请求地址后面,而通过HttpUtils,则可以让它像Post请求一样,组成一个字符串直接封装至请求方法中去,同时也可以设置自定义属性。
首先我们进行Get请求的学习:
在一个按钮的监听事件里添加如下代码:x.http().get(),我们点击进该方法查看需要什么参数。
/**
* 异步GET请求
*/
<T> Callback.Cancelable get(RequestParams entity, Callback.CommonCallback<T> callback);
它需要一个RequestParams和一个Callback,怎么办?new它。下面就是get请求的具体实现了:
@Event(R.id.get)
public void Get(View v){
RequestParams params = new RequestParams(url);
//添加参数
params.addQueryStringParameter("username","zahngsan");
params.addQueryStringParameter("password","1234");
x.http().get(params, new Callback.CacheCallback<String>() {
//成功
@Override
public void onSuccess(String result) {
}
//失败
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
//主动取消回调
@Override
public void onCancelled(CancelledException cex) {
}
//请求完成的回调
@Override
public void onFinished() {
}
//缓存
@Override
public boolean onCache(String result) {
return false;
}
});
}
类似的,我们也可以写出post请求:
@Event(R.id.post)
public void Post(View v){
RequestParams params = new RequestParams(url);
//添加参数
params.addBodyParameter("username","zahngsan");
params.addParameter("password","1234");
//请求头
params.addHeader("head","test");
x.http().get(params, new Callback.CacheCallback<String>() {
//成功
@Override
public void onSuccess(String result) {
}
//失败
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
//主动取消回调
@Override
public void onCancelled(CancelledException cex) {
}
//请求完成的回调
@Override
public void onFinished() {
}
});
}
同时,它也对上传文件和下载文件做了支持。
@Event(R.id.up)
private void up(View v){
String path="/mnt/sdcard/Download/nick.jpg";
RequestParams params = new RequestParams(url);
params.setMultipart(true);
params.addBodyParameter("file",new File(path));
x.http().post(params, new Callback.CommonCallback<String>() {
@Override
public void onSuccess(String result) {
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
});
}
@Event(R.id.down)
private void down(View v){
String url = "https://down.qq.com/qqweb/QQ_1/android_apk/Android_8.3.6.4590_537064458.apk";
RequestParams params = new RequestParams(url);
//定义存储的目录
params.setSaveFilePath(Environment.getExternalStorageDirectory()+"/test");
//设置文件自动命名
params.setAutoRename(true);
x.http().post(params, new Callback.ProgressCallback<File>() {
@Override
public void onSuccess(File result) {
//apk下载完成后,调用系统的安装方法
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(result), "application/vnd.android.package-archive");
startActivity(intent);
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
//网络请求之前的回调
@Override
public void onWaiting() {
}
//开始请求的回调
@Override
public void onStarted() {
}
//边请求边调用的方法
@Override
public void onLoading(long total, long current, boolean isDownloading) {
Log.i("test","current="+",total="+total);
}
});
}
xUtils3在xUtils的图片绑定基础上,增加了支持gif,以及圆角、方形等裁剪。这部分内容重点在于加载图片的4个bind方法,loadDrawable与loadFIle用法和ImageOptions用法。
首先,我可以打开**x.image().bind()**的接口感受一下它的源码,通过源码对它有所认识
public interface ImageManager {
void bind(ImageView view, String url);
void bind(ImageView view, String url, ImageOptions options);
void bind(ImageView view, String url, Callback.CommonCallback callback);
void bind(ImageView view, String url, ImageOptions options, Callback.CommonCallback callback);
Callback.Cancelable loadDrawable(String url, ImageOptions options, Callback.CommonCallback callback);
Callback.Cancelable loadFile(String url, ImageOptions options, Callback.CacheCallback callback);
void clearMemCache();
void clearCacheFiles();
}
可见,里面有4个重载的bind方法
我们再来看看它的ImageOptions都能做什么:
setFadeIn(true) //设置淡入效果
setCircular(true) //设置图片显示为圆形
setSquare(true) //设置图片显示为正方形
setCrop(true).setSize(100,100) //设置图片大小
setAnimation(animation) //设置动画
setFailureDrawable(Drawable failureDrawable) //设置加载失败的动画
setFailureDrawableId(int failureDrawable) //以资源id设置加载失败的动画
setLoardingDrawable(Drawable loardingDrawable) //设置加载中动画
setLoadingDrawableId(int loadingDrawable) //以资源id设置加载中的动画
setParamsBuilder(ParamsBuilder paramsBuilder) //在网络请求中添加参数
setIgnoreGif(false) //忽略Gif图片
setRaduis(int raduis) //设置拐角弧度
setUseMemCache(true) //设置使用MemCache
这里我主要就简单介绍一下其中一个bind方法的使用:
@ContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
String url = "https://i.picsum.photos/id/1/5616/3744.jpg";
@ViewInject(R.id.img)
ImageView iv;
@ViewInject(R.id.tv)
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
x.view().inject(this);
ImageOptions options = new ImageOptions.Builder()
.setFadeIn(true)
.setCrop(true)
.setSize(300,300).build();
x.image().bind(iv, url,options, new Callback.CacheCallback() {
@Override
//缓存
public boolean onCache(Drawable result) {
return false;
}
@Override
public void onSuccess(Drawable result) {
tv.setText("图片加载成功");
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
tv.setText("图片加载错误");
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
});
}
}
运行结果:
接着学习一下loadDrawable,使用它其实和bind效果一致,但是它会将加载的图片保存在本地
x.image().loadDrawable(url,options, new Callback.CommonCallback<Drawable>() {
@Override
public void onSuccess(Drawable result) {
iv.setImageDrawable(result);
tv.setText("图片加载成功");
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
tv.setText("图片加载错误");
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
});
最后,我们学习一下loadFile方法,在使用loadDrawable保存的图片,可以通过它来获取
x.image().loadFile(url, options, new Callback.CacheCallback<File>() {
@Override
public boolean onCache(File result) {
//使用本地图片
Log.i("test",result.getPath());
return true;
}
@Override
public void onSuccess(File result) {
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
});
最后,我们学习一下loadFile方法,在使用loadDrawable保存的图片,可以通过它来获取
x.image().loadFile(url, options, new Callback.CacheCallback<File>() {
@Override
public boolean onCache(File result) {
//使用本地图片
Log.i("test",result.getPath());
return true;
}
@Override
public void onSuccess(File result) {
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
});