a
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,130 @@
|
||||
def isBuildModule = rootProject.ext.module.isBuildModule
|
||||
if (Boolean.valueOf(isBuildModule)) {
|
||||
apply plugin: 'com.android.application'
|
||||
} else {
|
||||
apply plugin: 'com.android.library'
|
||||
}
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'com.google.dagger.hilt.android'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.android.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.android.buildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.android.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.android.targetSdkVersion
|
||||
versionCode rootProject.ext.android.versionCode
|
||||
versionName rootProject.ext.android.versionName
|
||||
multiDexEnabled true
|
||||
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments += [
|
||||
"room.schemaLocation":"$projectDir/schemas".toString(),
|
||||
"room.incremental":"true",
|
||||
"room.expandProjection":"true",
|
||||
AROUTER_MODULE_NAME: project.getName()
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion compose_version
|
||||
kotlinCompilerVersion kotlin_version
|
||||
}
|
||||
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
useIR = true
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
buildFeatures {
|
||||
compose true
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
if (Boolean.valueOf(isBuildModule)) {
|
||||
manifest.srcFile 'src/main/module/AndroidManifest.xml'
|
||||
} else {
|
||||
manifest.srcFile 'src/main/AndroidManifest.xml'
|
||||
java {
|
||||
//排除java/debug文件夹下的所有文件
|
||||
exclude '*module'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kapt {
|
||||
arguments {
|
||||
arg("AROUTER_MODULE_NAME", project.getName())
|
||||
}
|
||||
generateStubs = true
|
||||
useBuildCache = true
|
||||
javacOptions {
|
||||
option("-Xmaxerrs", 500)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation project(":common:common-base")
|
||||
implementation project(":common:common-service")
|
||||
|
||||
// compose
|
||||
implementation "io.coil-kt:coil-compose:1.3.1"
|
||||
implementation "androidx.compose.material:material:$compose_version"
|
||||
implementation 'androidx.activity:activity-compose:1.3.1'
|
||||
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
|
||||
implementation "androidx.compose.ui:ui:$compose_version"
|
||||
implementation "androidx.compose.ui:ui-tooling:$compose_version"
|
||||
implementation "androidx.compose.runtime:runtime:$compose_version"
|
||||
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07"
|
||||
implementation 'androidx.navigation:navigation-compose:2.4.0-alpha06'
|
||||
|
||||
kapt rootProject.ext.compiler["arouterCompiler"]
|
||||
|
||||
compileOnly(rootProject.ext.jetpack["hilt"])
|
||||
kapt rootProject.ext.compiler["hiltAndroidCompiler"]
|
||||
}
|
||||
|
||||
// 发布module到maven仓库
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.17.0'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
plugins.withId("com.vanniktech.maven.publish") {
|
||||
mavenPublish {
|
||||
sonatypeHost = "S01"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: "com.vanniktech.maven.publish"
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
-keep class com.bbgo.module_compose.bean.** {*;}
|
||||
@@ -0,0 +1,23 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-keep class com.bbgo.module_compose.bean.** {*;}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.bbgo.module_compose
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.bbgo.module_compose.test", appContext.packageName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.bbgo.module_compose">
|
||||
|
||||
<application>
|
||||
<activity android:name=".activity.ComposeActivity"/>
|
||||
</application>
|
||||
|
||||
|
||||
</manifest>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.bbgo.module_compose
|
||||
|
||||
import com.bbgo.common_base.BaseApplication
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
|
||||
/**
|
||||
* @Description:
|
||||
* @Author: wangyuebin
|
||||
* @Date: 2021/9/10 5:12 下午
|
||||
*/
|
||||
//@HiltAndroidApp
|
||||
class ComposeApp : BaseApplication() {
|
||||
}
|
||||
+394
@@ -0,0 +1,394 @@
|
||||
package com.bbgo.module_compose.activity
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.alibaba.android.arouter.facade.annotation.Route
|
||||
import com.bbgo.common_base.constants.RouterPath
|
||||
import com.bbgo.module_compose.R
|
||||
import com.bbgo.module_compose.bean.ArticleDetail
|
||||
import com.bbgo.module_compose.theme.*
|
||||
import com.bbgo.module_compose.util.InjectorUtil
|
||||
import com.bbgo.module_compose.viewmodel.ComposeViewModel
|
||||
|
||||
@Route(path = RouterPath.Compose.PAGE_COMPOSE)
|
||||
class ComposeActivity : AppCompatActivity() {
|
||||
|
||||
@ExperimentalFoundationApi
|
||||
val composeViewModel: ComposeViewModel by viewModels { InjectorUtil.getComposeViewModelFactory() }
|
||||
|
||||
@ExperimentalFoundationApi
|
||||
lateinit var context: Activity
|
||||
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
context = this
|
||||
composeViewModel.getArticles(0)
|
||||
setContent {
|
||||
WanAndroidTheme {
|
||||
RenderTopAppBar(composeViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalFoundationApi
|
||||
@Composable
|
||||
fun RenderTopAppBar(composeViewModel: ComposeViewModel) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.nav_my_collect),
|
||||
color = Color.White,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
}) {
|
||||
Icon(Icons.Filled.ArrowBack, null, tint = Color.White)
|
||||
}
|
||||
},
|
||||
backgroundColor = colorResource(id = R.color.colorPrimary)
|
||||
)
|
||||
},
|
||||
){
|
||||
Request(composeViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalFoundationApi
|
||||
@Composable
|
||||
fun Request(composeViewModel: ComposeViewModel) {
|
||||
val liveData by composeViewModel.articleLiveData.observeAsState()
|
||||
Logs.d("======================")
|
||||
liveData?.data?.let {
|
||||
RenderArticleList(it)
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalFoundationApi
|
||||
@Composable
|
||||
fun RenderArticleList(articles: List<ArticleDetail>) {
|
||||
LazyColumn {
|
||||
items(articles.size) {
|
||||
articles.forEach {
|
||||
ItemCard(articleDetail = it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*LazyColumn {
|
||||
items(articles) { articleDetail ->
|
||||
ItemCard(articleDetail = articleDetail)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@ExperimentalFoundationApi
|
||||
@Composable
|
||||
fun ItemCard(articleDetail: ArticleDetail) {
|
||||
val dp2 = dimensionResource(id = R.dimen.dp_2)
|
||||
val dp5 = dimensionResource(id = R.dimen.dp_5)
|
||||
val dp10 = dimensionResource(id = R.dimen.dp_10)
|
||||
Surface(elevation = dimensionResource(id = R.dimen.dp_1),
|
||||
) {
|
||||
Column() {
|
||||
Spacer(modifier = Modifier.padding(vertical = dp5))
|
||||
Row {
|
||||
Row(modifier = Modifier.weight(1f)) {
|
||||
if (articleDetail.top == "1") {
|
||||
Spacer(modifier = Modifier.padding(horizontal = dp5))
|
||||
Text(
|
||||
text = "置顶",
|
||||
color = Color.Red,
|
||||
fontSize = sp10,
|
||||
modifier = Modifier
|
||||
.border(dp05, Color.Red, shape = RoundedCornerShape(dp2))
|
||||
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
|
||||
)
|
||||
}
|
||||
if (articleDetail.fresh) {
|
||||
Spacer(modifier = Modifier.padding(horizontal = dp5))
|
||||
Text(
|
||||
text = "新",
|
||||
color = Color.Red,
|
||||
fontSize = sp10,
|
||||
modifier = Modifier
|
||||
.border(dp05, Color.Red, shape = RoundedCornerShape(dp2))
|
||||
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
|
||||
)
|
||||
}
|
||||
|
||||
if (articleDetail.tags.isNotEmpty()) {
|
||||
Spacer(modifier = Modifier.padding(horizontal = dp5))
|
||||
Text(
|
||||
text = articleDetail.tags[0].name,
|
||||
color = Color.Red,
|
||||
fontSize = sp12,
|
||||
modifier = Modifier
|
||||
.border(dp05, Color.Red, shape = RoundedCornerShape(dp2))
|
||||
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(horizontal = dp5))
|
||||
Text(
|
||||
text = articleDetail.author,
|
||||
color = colorResource(id = R.color.Grey600),
|
||||
fontSize = sp12,
|
||||
modifier = Modifier
|
||||
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
|
||||
)
|
||||
}
|
||||
|
||||
Row(modifier = Modifier.padding(end = dp5)) {
|
||||
Text(
|
||||
text = articleDetail.niceDate,
|
||||
color = colorResource(id = R.color.Grey600),
|
||||
fontSize = sp12,
|
||||
modifier = Modifier
|
||||
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(vertical = dp2))
|
||||
|
||||
Row(horizontalArrangement = Arrangement.Start) {
|
||||
Spacer(modifier = Modifier.padding(horizontal = dp5))
|
||||
Text(
|
||||
text = Html.fromHtml(articleDetail.title).toString(),
|
||||
color = colorResource(id = R.color.item_title),
|
||||
fontSize = sp16,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.padding(end = dp5),
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(vertical = dp5))
|
||||
|
||||
val chapterName = when {
|
||||
articleDetail.superChapterName.isNotEmpty() and articleDetail.chapterName.isNotEmpty() ->
|
||||
"${articleDetail.superChapterName} / ${articleDetail.chapterName}"
|
||||
articleDetail.superChapterName.isNotEmpty() -> articleDetail.superChapterName
|
||||
articleDetail.chapterName.isNotEmpty() -> articleDetail.chapterName
|
||||
else -> ""
|
||||
}
|
||||
|
||||
Row(verticalAlignment = Alignment.Bottom) {
|
||||
Text(
|
||||
text = chapterName,
|
||||
color = colorResource(id = R.color.item_title),
|
||||
fontSize = sp12,
|
||||
textAlign = TextAlign.Start,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = dp10)
|
||||
)
|
||||
|
||||
val imgId = if (articleDetail.collect) {
|
||||
R.drawable.ic_like
|
||||
} else {
|
||||
R.drawable.ic_like_not
|
||||
}
|
||||
|
||||
|
||||
Image(
|
||||
painter = painterResource(id = imgId),
|
||||
contentDescription = null,
|
||||
alignment = Alignment.CenterEnd,
|
||||
modifier = Modifier
|
||||
.weight(0.1f)
|
||||
.size(26.dp)
|
||||
.padding(end = dp5)
|
||||
.clickable {
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(vertical = dp5))
|
||||
|
||||
Divider(thickness = 0.2.dp)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TestItemCard() {
|
||||
val dp2 = dimensionResource(id = R.dimen.dp_2)
|
||||
val dp5 = dimensionResource(id = R.dimen.dp_5)
|
||||
Surface(shape = MaterialTheme.shapes.medium,
|
||||
elevation = dimensionResource(id = R.dimen.dp_1),
|
||||
) {
|
||||
Column() {
|
||||
Spacer(modifier = Modifier.padding(vertical = dp2))
|
||||
Row {
|
||||
Row(modifier = Modifier.weight(1f)) {
|
||||
Spacer(modifier = Modifier.padding(horizontal = dp5))
|
||||
Text(
|
||||
text = "置顶",
|
||||
color = Color.Red,
|
||||
fontSize = sp10,
|
||||
modifier = Modifier
|
||||
.border(dp05, Color.Red, shape = RectangleShape)
|
||||
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
|
||||
)
|
||||
Spacer(modifier = Modifier.padding(horizontal = dp5))
|
||||
Text(
|
||||
text = "新",
|
||||
color = Color.Red,
|
||||
fontSize = sp10,
|
||||
modifier = Modifier
|
||||
.border(dp05, Color.Red, shape = RectangleShape)
|
||||
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
|
||||
)
|
||||
Spacer(modifier = Modifier.padding(horizontal = dp5))
|
||||
Text(
|
||||
text = "玩安卓TAG",
|
||||
color = Color.Red,
|
||||
fontSize = sp10,
|
||||
modifier = Modifier
|
||||
.border(dp05, Color.Red, shape = RectangleShape)
|
||||
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
|
||||
)
|
||||
Spacer(modifier = Modifier.padding(horizontal = dp5))
|
||||
Text(
|
||||
text = "玩安卓",
|
||||
color = colorResource(id = R.color.Grey600),
|
||||
fontSize = sp12,
|
||||
modifier = Modifier
|
||||
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
|
||||
)
|
||||
}
|
||||
|
||||
Row {
|
||||
Spacer(modifier = Modifier.padding(horizontal = dp20))
|
||||
Text(
|
||||
text = "玩安卓",
|
||||
color = colorResource(id = R.color.Grey600),
|
||||
fontSize = sp12,
|
||||
modifier = Modifier
|
||||
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(vertical = dp2))
|
||||
|
||||
Row(horizontalArrangement = Arrangement.Start) {
|
||||
Spacer(modifier = Modifier.padding(horizontal = dp5))
|
||||
Text(
|
||||
text = "Compose 有效地处理嵌套布局,使其成为设计复杂UI的好方法。这是对 Android Views 的改进,在 Android Views 中,出于性能原因,您需要避免嵌套布局。你好呀陌生人,这是一个标题,不是很长,因为我想不出其他什么比较好的标题了",
|
||||
color = colorResource(id = R.color.item_title),
|
||||
fontSize = sp16,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.padding(end = dp5),
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(vertical = dp2))
|
||||
|
||||
/*Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = "问答 / 官方",
|
||||
color = colorResource(id = R.color.item_title),
|
||||
fontSize = sp8,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_like_not),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(end = 10.dp)
|
||||
)
|
||||
},
|
||||
backgroundColor = Color.White
|
||||
)
|
||||
},
|
||||
) {}*/
|
||||
|
||||
Row {
|
||||
Text(
|
||||
text = "问答 / 官方",
|
||||
color = colorResource(id = R.color.item_title),
|
||||
fontSize = sp12,
|
||||
textAlign = TextAlign.Start,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = dp10)
|
||||
)
|
||||
|
||||
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_like_not),
|
||||
contentDescription = null,
|
||||
alignment = Alignment.CenterEnd,
|
||||
modifier = Modifier
|
||||
.weight(0.1f)
|
||||
.size(20.dp)
|
||||
.padding(end = dp5)
|
||||
.clickable {
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun DefaultPreview() {
|
||||
// ArticleList(articleList = mutableListOf())
|
||||
TestItemCard()
|
||||
}
|
||||
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.bbgo.module_compose.bean
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
data class ArticleData(
|
||||
val curPage: Int,
|
||||
val datas: MutableList<ArticleDetail>,
|
||||
val offset: Int,
|
||||
val over: Boolean,
|
||||
val pageCount: Int,
|
||||
val size: Int,
|
||||
val total: Int
|
||||
)
|
||||
|
||||
@Keep
|
||||
data class ArticleDetail(
|
||||
val apkLink: String,
|
||||
val audit: Int,
|
||||
val author: String,
|
||||
val canEdit: Boolean,
|
||||
val chapterId: Int,
|
||||
val chapterName: String,
|
||||
var collect: Boolean,
|
||||
val courseId: Int,
|
||||
val desc: String,
|
||||
val descMd: String,
|
||||
val envelopePic: String,
|
||||
val fresh: Boolean,
|
||||
val host: String,
|
||||
val id: Int,
|
||||
val link: String,
|
||||
val niceDate: String,
|
||||
val niceShareDate: String,
|
||||
val origin: String,
|
||||
val prefix: String,
|
||||
val projectLink: String,
|
||||
val publishTime: Long,
|
||||
val realSuperChapterId: Int,
|
||||
val selfVisible: Int,
|
||||
val shareDate: Long,
|
||||
val shareUser: String,
|
||||
val superChapterId: Int,
|
||||
val superChapterName: String,
|
||||
val tags: List<Tag>,
|
||||
val title: String,
|
||||
val type: Int,
|
||||
val userId: Int,
|
||||
val visible: Int,
|
||||
val zan: Int,
|
||||
var top: String,
|
||||
)
|
||||
|
||||
@Keep
|
||||
data class Tag(
|
||||
val name: String,
|
||||
val url: String
|
||||
)
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.bbgo.module_compose.net.api
|
||||
|
||||
import com.bbgo.common_base.bean.HttpResult
|
||||
import com.bbgo.module_compose.bean.ArticleData
|
||||
import com.bbgo.module_compose.bean.ArticleDetail
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Path
|
||||
|
||||
/**
|
||||
* author: wangyb
|
||||
* date: 4/7/21 9:24 PM
|
||||
* description: http api
|
||||
*/
|
||||
interface HttpComposeApiService {
|
||||
|
||||
/**
|
||||
* 获取首页置顶文章列表
|
||||
* http://www.wanandroid.com/article/top/json
|
||||
*/
|
||||
@GET("article/top/json")
|
||||
fun getTopArticles(): Flow<HttpResult<List<ArticleDetail>>>
|
||||
|
||||
/**
|
||||
* 获取文章列表
|
||||
* http://www.wanandroid.com/article/list/0/json
|
||||
* @param pageNum
|
||||
*/
|
||||
@GET("article/list/{pageNum}/json")
|
||||
fun getArticles(@Path("pageNum") pageNum: Int): Flow<HttpResult<ArticleData>>
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.bbgo.module_compose.repository
|
||||
|
||||
|
||||
/**
|
||||
* author: wangyb
|
||||
* date: 3/30/21 2:36 PM
|
||||
* description: todo
|
||||
*/
|
||||
class ComposeLocalRepository private constructor(){
|
||||
|
||||
companion object {
|
||||
private var repository: ComposeLocalRepository? = null
|
||||
|
||||
fun getInstance(): ComposeLocalRepository {
|
||||
if (repository == null) {
|
||||
synchronized(ComposeLocalRepository::class.java) {
|
||||
if (repository == null) {
|
||||
repository = ComposeLocalRepository()
|
||||
}
|
||||
}
|
||||
}
|
||||
return repository!!
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.bbgo.module_compose.repository
|
||||
|
||||
import com.bbgo.common_base.bean.HttpResult
|
||||
import com.bbgo.common_base.net.ServiceCreators
|
||||
import com.bbgo.module_compose.bean.ArticleData
|
||||
import com.bbgo.module_compose.bean.ArticleDetail
|
||||
import com.bbgo.module_compose.net.api.HttpComposeApiService
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* author: wangyb
|
||||
* date: 3/30/21 2:35 PM
|
||||
* description: todo
|
||||
*/
|
||||
class ComposeRemoteRepository private constructor(){
|
||||
|
||||
private val service = ServiceCreators.create(HttpComposeApiService::class.java)
|
||||
|
||||
|
||||
fun getTopArticles() : Flow<HttpResult<List<ArticleDetail>>> {
|
||||
return service.getTopArticles()
|
||||
}
|
||||
|
||||
fun getArticles(pageNum: Int) : Flow<HttpResult<ArticleData>> {
|
||||
return service.getArticles(pageNum)
|
||||
}
|
||||
|
||||
|
||||
/********************静态内部类单例***************************/
|
||||
companion object {
|
||||
val instance = SingleTonHolder.holder
|
||||
}
|
||||
|
||||
private object SingleTonHolder {
|
||||
val holder = ComposeRemoteRepository()
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.bbgo.module_compose.repository
|
||||
|
||||
import com.bbgo.common_base.bean.HttpResult
|
||||
import com.bbgo.module_compose.bean.ArticleData
|
||||
import com.bbgo.module_compose.bean.ArticleDetail
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* author: wangyb
|
||||
* date: 3/29/21 9:32 PM
|
||||
* description: todo
|
||||
*/
|
||||
class ComposeRepository private constructor(private val remoteRepository: ComposeRemoteRepository, private val localRepository: ComposeLocalRepository) {
|
||||
|
||||
companion object {
|
||||
@Volatile
|
||||
private var repository: ComposeRepository? = null
|
||||
|
||||
fun getInstance(remoteRepository: ComposeRemoteRepository, localRepository: ComposeLocalRepository): ComposeRepository {
|
||||
if (repository == null) {
|
||||
synchronized(ComposeRepository::class.java) {
|
||||
if (repository == null) {
|
||||
repository = ComposeRepository(remoteRepository, localRepository)
|
||||
}
|
||||
}
|
||||
}
|
||||
return repository!!
|
||||
}
|
||||
}
|
||||
|
||||
fun getTopArticles() : Flow<HttpResult<List<ArticleDetail>>> {
|
||||
return remoteRepository.getTopArticles()
|
||||
}
|
||||
|
||||
fun getArticles(pageNum: Int) : Flow<HttpResult<ArticleData>> {
|
||||
return remoteRepository.getArticles(pageNum)
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.bbgo.module_compose.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple200 = Color(0xFFBB86FC)
|
||||
val Purple500 = Color(0xFF6200EE)
|
||||
val Purple700 = Color(0xFF3700B3)
|
||||
val Teal200 = Color(0xFF03DAC5)
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.bbgo.module_compose.theme
|
||||
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
val dp05 = 0.5.dp
|
||||
val dp2 = 2.dp
|
||||
val dp5 = 5.dp
|
||||
val dp4 = 4.dp
|
||||
val dp8 = 8.dp
|
||||
val dp10 = 10.dp
|
||||
val dp20 = 20.dp
|
||||
|
||||
val sp8 = 8.sp
|
||||
val sp10 = 10.sp
|
||||
val sp12 = 12.sp
|
||||
val sp16 = 16.sp
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.bbgo.module_compose.theme
|
||||
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Shapes
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
val Shapes = Shapes(
|
||||
small = RoundedCornerShape(4.dp),
|
||||
medium = RoundedCornerShape(4.dp),
|
||||
large = RoundedCornerShape(0.dp)
|
||||
)
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.bbgo.module_compose.theme
|
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.darkColors
|
||||
import androidx.compose.material.lightColors
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
private val DarkColorPalette = darkColors(
|
||||
primary = Purple200,
|
||||
primaryVariant = Purple700,
|
||||
secondary = Teal200
|
||||
)
|
||||
|
||||
private val LightColorPalette = lightColors(
|
||||
primary = Purple500,
|
||||
primaryVariant = Purple700,
|
||||
secondary = Teal200
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color.White,
|
||||
surface = Color.White,
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.Black,
|
||||
onBackground = Color.Black,
|
||||
onSurface = Color.Black,
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun WanAndroidTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) {
|
||||
val colors = if (darkTheme) {
|
||||
DarkColorPalette
|
||||
} else {
|
||||
LightColorPalette
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colors = colors,
|
||||
typography = Typography,
|
||||
shapes = Shapes,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.bbgo.module_compose.theme
|
||||
|
||||
import androidx.compose.material.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
body1 = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
button = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.W500,
|
||||
fontSize = 14.sp
|
||||
),
|
||||
caption = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 12.sp
|
||||
)
|
||||
*/
|
||||
)
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.bbgo.module_compose.util
|
||||
|
||||
import com.bbgo.module_compose.repository.ComposeLocalRepository
|
||||
import com.bbgo.module_compose.repository.ComposeRemoteRepository
|
||||
import com.bbgo.module_compose.repository.ComposeRepository
|
||||
import com.bbgo.module_compose.viewmodel.ComposeViewModelFactory
|
||||
|
||||
|
||||
/**
|
||||
* author: wangyb
|
||||
* date: 3/29/21 9:44 PM
|
||||
* description: todo
|
||||
*/
|
||||
object InjectorUtil {
|
||||
|
||||
fun getComposeViewModelFactory() = ComposeViewModelFactory(
|
||||
ComposeRepository.getInstance(
|
||||
ComposeRemoteRepository.instance, ComposeLocalRepository.getInstance()))
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.bbgo.module_compose.viewmodel
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bbgo.common_base.ext.Resource
|
||||
import com.bbgo.common_base.ext.logE
|
||||
import com.bbgo.module_compose.bean.ArticleDetail
|
||||
import com.bbgo.module_compose.repository.ComposeRepository
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.zip
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* author: wangyb
|
||||
* date: 3/29/21 9:31 PM
|
||||
* description: todo
|
||||
*/
|
||||
class ComposeViewModel(private val repository: ComposeRepository) : ViewModel() {
|
||||
|
||||
|
||||
val articleLiveData = MutableLiveData<Resource<MutableList<ArticleDetail>>>()
|
||||
|
||||
fun getArticles(pageNum: Int) {
|
||||
viewModelScope.launch {
|
||||
repository.getTopArticles()
|
||||
.zip(repository.getArticles(pageNum)) { topArticles, articles ->
|
||||
{
|
||||
val allArticles = mutableListOf<ArticleDetail>()
|
||||
topArticles.data.forEach {
|
||||
it.top = "1"
|
||||
}
|
||||
allArticles.addAll(topArticles.data)
|
||||
allArticles.addAll(articles.data.datas)
|
||||
allArticles
|
||||
}
|
||||
}
|
||||
.catch {
|
||||
Logs.e(TAG, it.message, it)
|
||||
}
|
||||
.collectLatest {
|
||||
articleLiveData.value = Resource.Success(it.invoke())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ComposeViewModel"
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.bbgo.module_compose.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.bbgo.module_compose.repository.ComposeRepository
|
||||
|
||||
/**
|
||||
* author: wangyb
|
||||
* date: 3/29/21 9:47 PM
|
||||
* description: todo
|
||||
*/
|
||||
|
||||
class ComposeViewModelFactory(private val repository: ComposeRepository) : ViewModelProvider.NewInstanceFactory() {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return ComposeViewModel(repository) as T
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.bbgo.module_compose">
|
||||
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- 配置权限,用来记录应用配置信息 -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 重力加速度传感器权限 -->
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- Android8.0安装apk需要请求安装权限 -->
|
||||
<uses-permission android:name="android.permission.VIBRATE" /> <!-- 小米push -->
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.REORDER_TASKS" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.hardware.sensor.accelerometer" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.GET_TASKS" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.FLASHLIGHT" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
|
||||
|
||||
<application
|
||||
android:name=".ComposeApp"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true">
|
||||
<activity
|
||||
android:name=".activity.ComposeActivity"
|
||||
android:theme="@style/SplashTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="@color/list_divider" />
|
||||
|
||||
</shape>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<stroke android:color="@color/Red"
|
||||
android:width="@dimen/dp_05"/>
|
||||
|
||||
<corners android:radius="@dimen/dp_2" />
|
||||
|
||||
</shape>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<stroke
|
||||
android:width="@dimen/dp_05"
|
||||
android:color="@color/colorAccent" />
|
||||
|
||||
<corners android:radius="@dimen/dp_2" />
|
||||
|
||||
</shape>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="@color/colorAccent"/>
|
||||
|
||||
<corners android:radius="@dimen/dp_2" />
|
||||
|
||||
</shape>
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<solid android:color="#A6000000" />
|
||||
<padding android:left="8dp" android:top="4dp" android:right="8dp" android:bottom="4dp" />
|
||||
<corners android:radius="2dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="#99000000" />
|
||||
<padding android:left="8dp" android:top="4dp" android:right="8dp" android:bottom="4dp" />
|
||||
<corners android:radius="2dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</selector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#ffe5acbf"
|
||||
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:strokeColor="#ffA8A8A8"
|
||||
android:strokeWidth="1.0"
|
||||
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
|
||||
</vector>
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/viewBackground">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
style="@style/RecyclerViewStyle"
|
||||
tools:listitem="@layout/item_home_list" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/app_bar_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
android:theme="@style/AppTheme.AppBarOverlay"
|
||||
app:elevation="0dp">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay"
|
||||
app:titleTextAppearance="@style/Toolbar.TitleText">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/viewBackground"
|
||||
android:textSize="@dimen/sp_18" />
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<cn.bingoogolapple.bgabanner.BGABanner
|
||||
android:id="@+id/banner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_230"
|
||||
android:scaleType="fitXY"
|
||||
app:banner_indicatorGravity="bottom|right"
|
||||
app:banner_placeholderDrawable="@drawable/placeholder_banner"
|
||||
app:banner_tipTextColor="@color/color_title_bg"
|
||||
app:banner_tipTextSize="@dimen/textSize_titleSmall"
|
||||
app:banner_transitionEffect="alpha" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,152 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView 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/cardView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
app:cardBackgroundColor="@color/viewBackground"
|
||||
app:cardCornerRadius="@dimen/dp_1"
|
||||
app:cardElevation="@dimen/dp_1">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/item_content_padding"
|
||||
android:paddingRight="@dimen/item_content_padding"
|
||||
android:paddingBottom="@dimen/item_content_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_article_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp_10"
|
||||
android:background="@drawable/bg_fresh"
|
||||
android:paddingLeft="@dimen/dp_4"
|
||||
android:paddingTop="@dimen/dp_2"
|
||||
android:paddingRight="@dimen/dp_4"
|
||||
android:paddingBottom="@dimen/dp_2"
|
||||
android:text="@string/top_tip"
|
||||
android:textColor="@color/Red"
|
||||
android:textSize="@dimen/sp_10"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_article_fresh"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp_10"
|
||||
android:layout_toRightOf="@+id/tv_article_top"
|
||||
android:background="@drawable/bg_fresh"
|
||||
android:paddingLeft="@dimen/dp_4"
|
||||
android:paddingTop="@dimen/dp_2"
|
||||
android:paddingRight="@dimen/dp_4"
|
||||
android:paddingBottom="@dimen/dp_2"
|
||||
android:text="@string/new_fresh"
|
||||
android:textColor="@color/Red"
|
||||
android:textSize="@dimen/sp_10"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_article_tag"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp_10"
|
||||
android:layout_toRightOf="@+id/tv_article_fresh"
|
||||
android:background="@drawable/bg_tag"
|
||||
android:paddingLeft="@dimen/dp_4"
|
||||
android:paddingTop="@dimen/dp_2"
|
||||
android:paddingRight="@dimen/dp_4"
|
||||
android:paddingBottom="@dimen/dp_2"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:textSize="@dimen/sp_10"
|
||||
android:visibility="gone"
|
||||
tools:text="@string/app_name"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_article_author"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBaseline="@+id/tv_article_top"
|
||||
android:layout_marginLeft="@dimen/dp_10"
|
||||
android:layout_toRightOf="@+id/tv_article_tag"
|
||||
android:textColor="@color/item_author"
|
||||
android:textSize="@dimen/item_tv_author"
|
||||
tools:text="@string/app_name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_article_date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBaseline="@+id/tv_article_top"
|
||||
android:layout_alignParentRight="true"
|
||||
android:textColor="@color/item_date"
|
||||
android:textSize="@dimen/item_tv_date"
|
||||
tools:text="@string/app_name" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_article_thumbnail"
|
||||
android:layout_width="@dimen/item_img_width"
|
||||
android:layout_height="@dimen/item_img_height"
|
||||
android:layout_below="@+id/tv_article_author"
|
||||
android:layout_marginLeft="@dimen/dp_10"
|
||||
android:layout_marginTop="@dimen/dp_8"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_article_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/tv_article_author"
|
||||
android:layout_marginLeft="@dimen/dp_10"
|
||||
android:layout_marginTop="@dimen/dp_8"
|
||||
android:layout_toRightOf="@+id/iv_article_thumbnail"
|
||||
android:ellipsize="end"
|
||||
android:gravity="top|start"
|
||||
android:lineSpacingExtra="2dp"
|
||||
android:maxLines="2"
|
||||
android:paddingBottom="@dimen/dp_6"
|
||||
android:textColor="@color/item_title"
|
||||
android:textSize="@dimen/item_tv_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_article_chapterName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/tv_article_title"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginStart="@dimen/dp_10"
|
||||
android:layout_marginLeft="@dimen/dp_10"
|
||||
android:layout_marginTop="@dimen/dp_10"
|
||||
android:layout_marginEnd="@dimen/dp_10"
|
||||
android:layout_marginRight="@dimen/dp_10"
|
||||
android:layout_toRightOf="@+id/iv_article_thumbnail"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/item_chapter"
|
||||
android:textSize="@dimen/item_tv_tag"
|
||||
tools:text="@string/app_name" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_like"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_like_not" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/colorPrimary"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,8 @@
|
||||
<resources>
|
||||
<string name="app_name">ComposeUI模块</string>
|
||||
<string name="nav_my_collect">我的收藏</string>
|
||||
<string name="new_fresh">新</string>
|
||||
<string name="top_tip">置顶</string>
|
||||
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,146 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="BaseAppTheme" parent="@style/Theme.AppCompat.DayNight">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="textColorPrimary">@color/textColorPrimary</item>
|
||||
<item name="viewBackground">@color/viewBackground</item>
|
||||
<!--关闭启动窗口-->
|
||||
<item name="android:windowDisablePreview">true</item>
|
||||
<item name="android:listDivider">@drawable/bg_divider</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme" parent="BaseAppTheme.NoActionBar"></style>
|
||||
|
||||
<style name="AppTheme.FullScreen" parent="@style/Theme.AppCompat.DayNight">
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:windowBackground">@null</item>
|
||||
<item name="android:windowDisablePreview">true</item>
|
||||
</style>
|
||||
|
||||
<style name="SplashTheme" parent="BaseAppTheme.NoActionBar">
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
</style>
|
||||
|
||||
<style name="BaseAppTheme.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="BaseAppTheme.NoActionBar.Slidable" parent="BaseAppTheme.NoActionBar">
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
<style name="MyTabLayoutStyle" parent="Base.Widget.Design.TabLayout">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:background">@color/colorPrimary</item>
|
||||
<item name="tabIndicatorColor">@color/viewBackground</item>
|
||||
<item name="tabIndicatorHeight">@dimen/dp_1</item>
|
||||
<item name="tabSelectedTextColor">@color/viewBackground</item>
|
||||
<item name="tabTextAppearance">@style/MyTabTextAppearance</item>
|
||||
</style>
|
||||
|
||||
<style name="MyTabTextAppearance">
|
||||
<item name="android:textSize">@dimen/sp_14</item>
|
||||
<item name="textAllCaps">false</item>
|
||||
<item name="android:textColor">?android:textColorSecondary</item>
|
||||
</style>
|
||||
|
||||
<style name="WindowAnimationFadeInOut">
|
||||
<item name="android:windowEnterAnimation">@anim/fade_in</item>
|
||||
<item name="android:windowExitAnimation">@anim/fade_out</item>
|
||||
</style>
|
||||
|
||||
<style name="ToolbarStyle" parent="AppTheme">
|
||||
<item name="android:textColorSecondary">@android:color/white</item>
|
||||
<item name="searchHintIcon">@null</item>
|
||||
</style>
|
||||
|
||||
<!-- 设置Toolbar标题字体的大小 -->
|
||||
<style name="Toolbar.TitleText" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
|
||||
<item name="android:textSize">@dimen/sp_18</item>
|
||||
</style>
|
||||
|
||||
<!-- 浮动窗口动画 -->
|
||||
<style name="anim_float_view">
|
||||
<item name="android:windowEnterAnimation">@anim/push_up_in</item>
|
||||
<item name="android:windowExitAnimation">@anim/push_up_out</item>
|
||||
</style>
|
||||
|
||||
<style name="MenuLabelsStyle">
|
||||
<item name="android:background">@drawable/fab_label_background</item>
|
||||
<item name="android:textColor">#FFFFFF</item>
|
||||
<item name="android:textSize">@dimen/sp_14</item>
|
||||
<item name="android:maxLines">2</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
</style>
|
||||
|
||||
<style name="RecyclerViewStyle">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">match_parent</item>
|
||||
<item name="android:scrollbarSize">@dimen/dp_2</item>
|
||||
<item name="android:scrollbars">vertical</item>
|
||||
</style>
|
||||
|
||||
<style name="TabLayout" parent="Base.Widget.Design.TabLayout">
|
||||
<item name="tabIndicatorColor">@color/colorPrimary</item>
|
||||
<item name="tabIndicatorHeight">2dp</item>
|
||||
<item name="tabPaddingStart">12dp</item>
|
||||
<item name="tabPaddingEnd">12dp</item>
|
||||
<item name="tabBackground">@color/colorPrimary</item>
|
||||
<item name="tabTextAppearance">@style/TextAppearance.Design.Tab</item>
|
||||
<item name="tabSelectedTextColor">@color/colorPrimary</item>
|
||||
<item name="tabTextColor">@color/colorPrimary</item>
|
||||
</style>
|
||||
|
||||
<declare-styleable name="CircleProgressView">
|
||||
<attr name="cpv_progressNormalColor" format="color"/>
|
||||
<attr name="cpv_progressReachColor" format="color"/>
|
||||
<attr name="cpv_progressTextColor" format="color"/>
|
||||
<attr name="cpv_progressTextSize" format="dimension"/>
|
||||
<attr name="cpv_progressTextOffset" format="dimension"/>
|
||||
<attr name="cpv_progressNormalSize" format="dimension"/>
|
||||
<attr name="cpv_progressReachSize" format="dimension"/>
|
||||
<attr name="cpv_radius" format="dimension"/>
|
||||
<attr name="cpv_progressTextVisible" format="boolean"/>
|
||||
<attr name="cpv_progressStartArc" format="integer"/>
|
||||
<attr name="cpv_progressTextSkewX" format="dimension"/>
|
||||
<attr name="cpv_progressTextPrefix" format="string"/>
|
||||
<attr name="cpv_progressTextSuffix" format="string"/>
|
||||
<attr name="cpv_innerBackgroundColor" format="color"/>
|
||||
<attr name="cpv_progressStyle" format="enum">
|
||||
<enum name="Normal" value="0"/>
|
||||
<enum name="FillInner" value="1"/>
|
||||
<enum name="FillInnerArc" value="2"/>
|
||||
</attr>
|
||||
<attr name="cpv_innerProgressColor" format="color"/>
|
||||
<attr name="cpv_innerPadding" format="dimension"/>
|
||||
<attr name="cpv_outerColor" format="color"/>
|
||||
<attr name="cpv_outerSize" format="dimension"/>
|
||||
<attr name="cpv_reachCapRound" format="boolean"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="CircleImageView">
|
||||
<attr name="type" format="enum">
|
||||
<enum name="none" value="0" />
|
||||
<enum name="circle" value="1" />
|
||||
<enum name="rect_rounded" value="2" />
|
||||
</attr>
|
||||
<attr name="borderColor" format="color" />
|
||||
<attr name="borderWidth" format="dimension" />
|
||||
<attr name="rectRoundRadius" format="dimension" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.bbgo.module_compose
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user