Commit 754147f8 by LouisWang

feat(项目):添加列表Activity

parent 56dbe874
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
\ No newline at end of file
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.3.72' ext{
kotlin_version = '1.3.72'
arouter_register_version = '1.0.2'
}
repositories { repositories {
maven { url 'https://maven.aliyun.com/repository/public' } maven { url 'https://maven.aliyun.com/repository/public' }
google() google()
...@@ -12,7 +15,8 @@ buildscript { ...@@ -12,7 +15,8 @@ buildscript {
dependencies { dependencies {
classpath "com.android.tools.build:gradle:4.0.0" classpath "com.android.tools.build:gradle:4.0.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// 进行路由表的自动加载,可以缩短初始化时间和解决应用加固导致无法直接访问dex文件导致初始化失败的问题
classpath "com.alibaba:arouter-register:$arouter_register_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
} }
......
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.alibaba.arouter'
android { android {
compileSdkVersion 30 compileSdkVersion 30
buildToolsVersion "30.0.0" buildToolsVersion "30.0.0"
...@@ -31,7 +32,12 @@ android { ...@@ -31,7 +32,12 @@ android {
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
} }
kapt {
arguments {
// ARouter配置:配置module名称
arg("AROUTER_MODULE_NAME", project.getName())
}
}
dependencies { dependencies {
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.ext:junit:1.1.1'
...@@ -42,4 +48,6 @@ dependencies { ...@@ -42,4 +48,6 @@ dependencies {
api 'com.autocareai.lib:lifecycle:1.2.9' api 'com.autocareai.lib:lifecycle:1.2.9'
api 'com.autocareai.lib:net-retrofit:1.3.3' api 'com.autocareai.lib:net-retrofit:1.3.3'
api 'com.autocareai.lib:route-arouter:1.2.2' api 'com.autocareai.lib:route-arouter:1.2.2'
// ARouter注解处理器
kapt "com.alibaba:arouter-compiler:1.2.2"
} }
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".demo.list.ListActivity" />
</application> </application>
</manifest> </manifest>
\ No newline at end of file
package com.autocareai.mvvmdemo.common.http
import androidx.lifecycle.LifecycleOwner
import com.autocareai.lib.net.converter.IConverter
import com.autocareai.lib.net.exception.BusinessErrorException
import com.autocareai.lib.util.JsonUtil
import okhttp3.ResponseBody
import org.json.JSONObject
import retrofit2.Response
/**
* <pre>
* author : louis wang
* time : 2020/06/28
* desc : 将Http响应数据转换成一个列表,去除最外层封装
* version: 1.0
* </pre>
*/
class ResponseListConverter<D>(private val clazz: Class<D>) : IConverter<ArrayList<D>> {
override fun convertResponse(
owner: LifecycleOwner?,
response: Response<ResponseBody>
): ArrayList<D>? {
val json = response.body()?.string() ?: return null
val jsonObj = JSONObject(json)
val code = jsonObj.getInt("errorCode")
if (code == 0) {
return JsonUtil.parseArray(jsonObj.getJSONArray("data").toString(), clazz)
} else {
throw BusinessErrorException(code, jsonObj.getString("errorMsg"))
}
}
}
\ No newline at end of file
...@@ -3,6 +3,7 @@ package com.autocareai.mvvmdemo.common.http ...@@ -3,6 +3,7 @@ package com.autocareai.mvvmdemo.common.http
import com.autocareai.lib.net.observable.HttpObservable import com.autocareai.lib.net.observable.HttpObservable
import com.autocareai.lib.net.observable.HttpObservableImpl import com.autocareai.lib.net.observable.HttpObservableImpl
import com.autocareai.lib.net.request.BaseRequest import com.autocareai.lib.net.request.BaseRequest
import com.autocareai.mvvmdemo.common.tool.HttpTool
/** /**
* <pre> * <pre>
...@@ -18,4 +19,13 @@ import com.autocareai.lib.net.request.BaseRequest ...@@ -18,4 +19,13 @@ import com.autocareai.lib.net.request.BaseRequest
*/ */
inline fun <reified D : Any> BaseRequest<*>.asResponse(): HttpObservable<D> { inline fun <reified D : Any> BaseRequest<*>.asResponse(): HttpObservable<D> {
return HttpObservableImpl(this, ResponseConverter(D::class.java)) return HttpObservableImpl(this, ResponseConverter(D::class.java))
} }
\ No newline at end of file
/**
* 将Http响应数据转换成一个列表,去除最外层封装
*
* @param isAuth 是否需要添加认证参数,默认是true。
*/
inline fun <reified D : Any> BaseRequest<*>.asResponseList(): HttpObservable<ArrayList<D>> {
return HttpObservableImpl(this, ResponseListConverter(D::class.java))
}
package com.autocareai.mvvmdemo.common.view
import androidx.annotation.LayoutRes
import androidx.annotation.Nullable
import com.autocareai.lib.widget.recyclerview.LibBaseAdapter
/**
* <pre>
* author : louis wang
* time : 2020/06/28
* desc : RecyclerView适配器基类
* version: 1.0
* </pre>
*/
open class BaseAdapter<T> : LibBaseAdapter<T> {
constructor() : super(0, null)
constructor(@LayoutRes layoutResId: Int) : super(layoutResId, null)
constructor(@Nullable data: List<T>) : super(0, data)
constructor(@LayoutRes layoutResId: Int, @Nullable data: List<T>) : super(layoutResId, data)
}
\ No newline at end of file
package com.autocareai.mvvmdemo.demo package com.autocareai.mvvmdemo.demo
import com.autocareai.lib.application.LibApplication import com.autocareai.lib.application.LibApplication
import com.autocareai.lib.route.RouteUtil
import com.autocareai.lib.util.LogUtil import com.autocareai.lib.util.LogUtil
import com.autocareai.mvvmdemo.BuildConfig
import com.autocareai.mvvmdemo.common.tool.HttpTool import com.autocareai.mvvmdemo.common.tool.HttpTool
/** /**
...@@ -20,6 +22,9 @@ class Application : LibApplication() { ...@@ -20,6 +22,9 @@ class Application : LibApplication() {
HttpTool.init() HttpTool.init()
// 初始化路由框架
RouteUtil.init(this, BuildConfig.DEBUG)
return false return false
} }
} }
\ No newline at end of file
...@@ -3,6 +3,8 @@ package com.autocareai.mvvmdemo.demo.api ...@@ -3,6 +3,8 @@ package com.autocareai.mvvmdemo.demo.api
import com.autocareai.lib.net.HttpUtil import com.autocareai.lib.net.HttpUtil
import com.autocareai.lib.net.observable.HttpObservable import com.autocareai.lib.net.observable.HttpObservable
import com.autocareai.mvvmdemo.common.http.asResponse import com.autocareai.mvvmdemo.common.http.asResponse
import com.autocareai.mvvmdemo.common.http.asResponseList
import com.autocareai.mvvmdemo.demo.enity.ListEntity
import com.autocareai.mvvmdemo.demo.enity.UserEntity import com.autocareai.mvvmdemo.demo.enity.UserEntity
/** /**
...@@ -25,8 +27,16 @@ object DemoApi { ...@@ -25,8 +27,16 @@ object DemoApi {
*/ */
fun login(phone: String, password: String): HttpObservable<UserEntity> { fun login(phone: String, password: String): HttpObservable<UserEntity> {
return HttpUtil.postForm("user/login") return HttpUtil.postForm("user/login")
.param("username", phone) .param("username", phone)
.param("password", password) .param("password", password)
.asResponse() .asResponse()
}
/**
* 获取常用网站
*/
fun getPopularWebsites(): HttpObservable<ArrayList<ListEntity>> {
return HttpUtil.get("friend/json")
.asResponseList()
} }
} }
\ No newline at end of file
package com.autocareai.mvvmdemo.demo.enity
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
/**
* <pre>
* author : louis wang
* time : 2020/06/28
* desc : 常用网站
* version: 1.0
* </pre>
*/
@Parcelize
class ListEntity : Parcelable {
//网站id
var id = 0
//网站图片地址
var icon = ""
//网站链接
var link = ""
//网站名称
var name = ""
}
\ No newline at end of file
package com.autocareai.mvvmdemo.demo.list
import android.os.Bundle
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import com.autocareai.lib.route.Route
import com.autocareai.mvvmdemo.R
import com.autocareai.mvvmdemo.common.view.BaseLifecycleActivity
import com.autocareai.mvvmdemo.demo.route.DemoRoute
import kotlinx.android.synthetic.main.list_activity.*
/**
* <pre>
* author : louis wang
* time : 2020/06/28
* desc :
* version: 1.0
* </pre>
*/
@Route(path = DemoRoute.DEMO_LIST)
class ListActivity : BaseLifecycleActivity<ListViewModel>() {
private val adapter by lazy { ListAdapter() }
override fun getLayoutId(): Int = R.layout.list_activity
override fun getViewModelClass(): Class<ListViewModel> = ListViewModel::class.java
override fun initView(savedInstanceState: Bundle?) {
swipeRefresh.setOnRefreshListener { loadDataOnCreate() }
recycleView.layoutManager = LinearLayoutManager(this)
recycleView.adapter = adapter
}
override fun initLifecycleObserver() {
mViewModel.listDataEvent.observe(this, Observer {
swipeRefresh.isRefreshing = false
adapter.setNewData(it)
})
}
override fun loadDataOnCreate() {
mViewModel.getPopularWebsites(this)
}
}
\ No newline at end of file
package com.autocareai.mvvmdemo.demo.list
import android.widget.ImageView
import com.autocareai.lib.extension.load
import com.autocareai.lib.widget.recyclerview.BaseViewHolder
import com.autocareai.mvvmdemo.R
import com.autocareai.mvvmdemo.common.view.BaseAdapter
import com.autocareai.mvvmdemo.demo.enity.ListEntity
/**
* <pre>
* author : louis wang
* time : 2020/06/28
* desc : 常用网站适配器
* version: 1.0
* </pre>
*/
class ListAdapter : BaseAdapter<ListEntity>(R.layout.list_item) {
override fun convert(helper: BaseViewHolder, item: ListEntity) {
super.convert(helper, item)
helper.setText(R.id.tvTitle, item.name)
helper.setText(R.id.tvLink, item.link)
helper.getView<ImageView>(R.id.image).load(item.link, R.mipmap.ic_launcher, R.mipmap.ic_launcher)
}
}
\ No newline at end of file
package com.autocareai.mvvmdemo.demo.list
import androidx.lifecycle.LifecycleOwner
import com.autocareai.lib.lifecycle.extension.post
import com.autocareai.lib.lifecycle.livedata.SingleLiveEvent
import com.autocareai.mvvmdemo.common.view.BaseViewModel
import com.autocareai.mvvmdemo.demo.api.DemoApi
import com.autocareai.mvvmdemo.demo.enity.ListEntity
/**
* <pre>
* author : louis wang
* time : 2020/06/28
* desc :
* version: 1.0
* </pre>
*/
class ListViewModel : BaseViewModel() {
/**
* 常用网站数据
*/
var listDataEvent = SingleLiveEvent<ArrayList<ListEntity>>()
/**
* 获取常用网站
*/
fun getPopularWebsites(owner: LifecycleOwner) {
DemoApi.getPopularWebsites()
.attachLifecycle(owner)
.onSuccess {
listDataEvent.post(it)
}.onError { _, message ->
shortToast(message)
}.async()
}
}
\ No newline at end of file
...@@ -2,8 +2,10 @@ package com.autocareai.mvvmdemo.demo.main ...@@ -2,8 +2,10 @@ package com.autocareai.mvvmdemo.demo.main
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.autocareai.lib.extension.onClick import com.autocareai.lib.extension.onClick
import com.autocareai.lib.route.Route
import com.autocareai.mvvmdemo.R import com.autocareai.mvvmdemo.R
import com.autocareai.mvvmdemo.common.view.BaseLifecycleActivity import com.autocareai.mvvmdemo.common.view.BaseLifecycleActivity
import com.autocareai.mvvmdemo.demo.route.DemoRoute
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
/** /**
...@@ -14,6 +16,7 @@ import kotlinx.android.synthetic.main.activity_main.* ...@@ -14,6 +16,7 @@ import kotlinx.android.synthetic.main.activity_main.*
* version: 1.0.0 * version: 1.0.0
* </pre> * </pre>
*/ */
@Route(path = DemoRoute.LOGIN)
class MainActivity : BaseLifecycleActivity<MainViewModel>() { class MainActivity : BaseLifecycleActivity<MainViewModel>() {
override fun getLayoutId(): Int = R.layout.activity_main override fun getLayoutId(): Int = R.layout.activity_main
...@@ -26,7 +29,7 @@ class MainActivity : BaseLifecycleActivity<MainViewModel>() { ...@@ -26,7 +29,7 @@ class MainActivity : BaseLifecycleActivity<MainViewModel>() {
// TODO: 2020/6/24 如果防止用户短时间多次点击,使用onClick,如果用户可以多次点击,还是使用原生的setOnClickListener // TODO: 2020/6/24 如果防止用户短时间多次点击,使用onClick,如果用户可以多次点击,还是使用原生的setOnClickListener
btnLogin.onClick { btnLogin.onClick {
// 登录 // 登录
mViewModel.login(this, etPhone.text.toString(), etPwd.text.toString()) mViewModel.login(this, edPhone.text.toString(), edPwd.text.toString())
} }
} }
...@@ -35,7 +38,7 @@ class MainActivity : BaseLifecycleActivity<MainViewModel>() { ...@@ -35,7 +38,7 @@ class MainActivity : BaseLifecycleActivity<MainViewModel>() {
mViewModel.toListEvent.observe(this, Observer { mViewModel.toListEvent.observe(this, Observer {
// 跳转至列表界面 // 跳转至列表界面
// TODO: 2020/6/24 跳转至列表界面 DemoRoute.toList().navigation(this)
}) })
} }
} }
package com.autocareai.mvvmdemo.demo.route
import com.autocareai.lib.route.RouteNavigation
/**
* <pre>
* author : louis wang
* time : 2020/06/28
* desc : demo路由
* version: 1.0
* </pre>
*/
object DemoRoute {
const val LOGIN = "/demo/login"
const val DEMO_LIST = "/demo/list"
fun toList(): RouteNavigation {
return RouteNavigation(DEMO_LIST)
}
}
\ No newline at end of file
...@@ -7,16 +7,15 @@ ...@@ -7,16 +7,15 @@
android:paddingStart="25dp" android:paddingStart="25dp"
android:paddingEnd="25dp"> android:paddingEnd="25dp">
<!--todo: EditText的简写应该是et,不是ed-->
<EditText <EditText
android:id="@+id/etPhone" android:id="@+id/edPhone"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/main_input_phone_hint" android:hint="@string/main_input_phone_hint"
android:textSize="16sp" /> android:textSize="16sp" />
<EditText <EditText
android:id="@+id/etPwd" android:id="@+id/edPwd"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/main_input_password_hint" android:hint="@string/main_input_password_hint"
......
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
\ No newline at end of file
<?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:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/image"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="title"
android:textColor="#333333"
android:textSize="14dp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="12dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tvLink"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:ellipsize="end"
android:lines="1"
android:text="www.baidu.com"
android:textColor="#666666"
android:textSize="13dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment