前言
因为工作经历的关系,做框架搭建工作至今已经7年了,进过一轮轮的演变和优化,才有现在QAndroid的原型。之前一直觉得不完美,针对性太强所以一直没有开源。后来了解到飞信、苏宁、优信他们的框架,感觉还不如QAndroid,所以才有了这篇文章。
简介
QAndroid特别适合中小项目,目前已在多个项目中应用;他是一个简单小巧、开发快速并且维护简单的android app开发框架。基于DataBinding,使用kotlin开发跟迅捷。 框架已经集成okhttp、glide、fastjson和友盟统计。
推荐使用mvvm模式,只需要开发维护model和xml两个文件即可。
github地址:https://github.com/ddoolcg/QAndroid
集成
将其添加到存储库末尾的根build.gradle中:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
添加依赖项
// kotlin android插件
apply plugin: 'kotlin-android'
// kotlin的apt(编译时生成代码)
apply plugin: 'kotlin-kapt'
// 通过解析xml直接使用view,感兴趣的可以自行百度
//apply plugin: 'kotlin-android-extensions'
android {
...
//开启dataBinding
dataBinding {
enabled true
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
// QAndroid依赖
api 'com.github.ddoolcg:QAndroid:1.4.1'
// databinding注解依赖,编译时生成代码
kapt 'com.android.databinding:compiler:3.1.4'
}
UIUtils工具类需要初始化、自定义http认证(也可以继承BaseApplication实现)
@Override
public void onCreate() {
super.onCreate();
if (UIUtils.init(this)) onInitMainProcesses();
Token.INSTANCE.init("token", new Function1<Boolean, Unit>() {
@Override
public Unit invoke(Boolean aBoolean) {
gotoLoin(aBoolean);
return null;
}
});
}
联网调用
//lambda不支持泛型套泛型的解析方式
DataEntry("url").joinProgressDialog(activity).formBody(map).post<T> {TODO()}
//抽象方法实现的方式调用,支持泛型套泛型的解析方式
DataEntry("url").joinProgressDialog(activity).formBody(map).post<T>(OnSuccessListener)
//默认统一处理接口调用失败
DataEntry.failDefault={code,data-> }
SharedPreferences操作
操作类
PreferenceKTX
多次put采用Any扩展
preferenceEdit {
putBoolean()
putString()
}
view的一些实用扩展
doOnGlobalLayout
view.doOnGlobalLayout {
if (Boolean) {
action()
true
} else {
false
}
}
doOnPreDraw
view.doOnPreDraw {
action()
}
ViewGroup
viewGroup += view//addView
viewGroup -= view//removeView
页面实现demo(登陆)
activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityLoginBinding>(this, R.layout
.activity_login)
val login = Login(this).apply {
titleText = "登陆"
isShowBack = false
}
binding.login = login
}
xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.text.TextUtils"/>
<variable
name="login"
type="com.lcg.comment.model.Login"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_cover">
<include
android:id="@+id/i_title"
layout="@layout/common_title"
app:data="@{login}"/>
<ImageView
android:id="@+id/iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/i_title"
android:layout_centerHorizontal="true"
android:layout_margin="@dimen/distance_item"
android:src="@mipmap/ic_launcher"/>
<EditText
android:id="@+id/et_username"
style="@style/edit"
android:layout_below="@+id/iv"
android:hint="用户名(手机号码)"
android:inputType="number"
android:text="@={login.username}"/>
<EditText
android:id="@+id/et_password"
style="@style/edit"
android:layout_below="@+id/et_username"
android:ems="10"
android:hint="密码(3-10位)"
android:inputType="textPassword"
android:text="@={login.password}"/>
<Button
android:id="@+id/btn_login"
style="@style/btn_single"
android:layout_below="@+id/et_password"
android:layout_margin="@dimen/activity_horizontal_margin"
android:enabled="@{!TextUtils.isEmpty(login.username) && !TextUtils.isEmpty(login.password)}"
android:onClick="@{login.login}"
android:text="登录"/>
<TextView
android:id="@+id/tv_forget_password"
style="@style/text_content"
android:layout_height="48dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/btn_login"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:gravity="center"
android:onClick="@{login::gotoResetPassword}"
android:text="忘记密码?"
android:visibility="invisible"/>
<TextView
android:id="@+id/tv_regist"
style="@style/text_content"
android:layout_height="48dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/tv_forget_password"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:gravity="center"
android:onClick="@{login::gotoRegister}"
android:text="立即注册"
android:textColor="@color/blue"/>
</RelativeLayout>
</layout>
model:
class Login(activity: BaseActivity) : BaseObservableMe(activity) {
@get:Bindable
var username: String = ""
set(username) {
field = username
notifyPropertyChanged(BR.username)
}
@get:Bindable
var password: String = ""
set(password) {
field = password
notifyPropertyChanged(BR.password)
}
fun login(view: View) {
if (!check())
return
val map = hashMapOf("username" to username, "password" to MD5.GetMD5Code(password))
DataEntry(HttpUrl.login).joinProgressDialog(activity).formBody(map).post<AuthUser> {
saveToken(it.token)
PreferenceKTX.setConfig(it)
try {
val clazz: Class<Activity> = Class.forName("com.lcg.expressbus.MainActivity") as Class<Activity>
activity.startActivity(clazz)
activity.finish()
} catch (e: Exception) {
throw RuntimeException("com.lcg.expressbus.MainActivity is error!")
}
}
}
/**去注册*/
fun gotoRegister(view: View) {
RegisterActivity.start(activity)
}
/**去密码重置*/
fun gotoResetPassword(view: View) {
TODO()
}
protected open fun check(): Boolean {
when {
TextUtils.isEmpty(this.username) -> {
UIUtils.showToastSafe("用户名不能为空")
return false
}
!this.username.isPhone() -> {
UIUtils.showToastSafe("请输入一个有效手机号码")
return false
}
TextUtils.isEmpty(this.password) -> {
UIUtils.showToastSafe("密码不能为空")
return false
}
this.password.length < 2 -> {
UIUtils.showToastSafe("密码必须大于两位")
return false
}
else -> return true
}
}
}
开启友盟统计
在AndroidManifest.xml文件中修改
<meta-data android:name="UMENG_APPKEY" android:value="您的UMENG_APPKEY"/>
DataEntry无法满足你的需求可参考:
正常和异常数据都是通过200请求返回的