Browse Source

add(course): home course page

zhaoyadi 1 year ago
parent
commit
a42e06284c
32 changed files with 701 additions and 80 deletions
  1. 1 0
      .idea/gradle.xml
  2. 3 1
      app/build.gradle.kts
  3. 5 32
      app/src/main/java/com/zaojiao/app/ui/home/HomeCourseFragment.kt
  4. 0 40
      app/src/main/res/layout/fragment_course.xml
  5. 0 6
      app/src/main/res/layout/item_course.xml
  6. 3 0
      component/common/build.gradle.kts
  7. 4 1
      component/common/src/main/AndroidManifest.xml
  8. 8 0
      component/common/src/main/java/com/zaojiao/component/common/Colors.kt
  9. 67 0
      component/common/src/main/java/com/zaojiao/component/common/StatePage.kt
  10. 95 0
      component/common/src/main/java/com/zaojiao/component/common/Swiper.kt
  11. 41 0
      component/course/build.gradle.kts
  12. 2 0
      component/course/src/main/AndroidManifest.xml
  13. 42 0
      component/course/src/main/java/com/zaojiao/component/course/HomeCourseCategory.kt
  14. 186 0
      component/course/src/main/java/com/zaojiao/component/course/HomeCourseGroupBuy.kt
  15. 51 0
      component/course/src/main/java/com/zaojiao/component/course/HomeCoursePage.kt
  16. 38 0
      component/course/src/main/java/com/zaojiao/component/course/HomeCourseRecommend.kt
  17. 70 0
      component/course/src/main/java/com/zaojiao/component/course/HomeCourseTopBar.kt
  18. 34 0
      component/course/src/main/java/com/zaojiao/component/course/HomeCourseTopSwiper.kt
  19. 50 0
      component/course/src/main/java/com/zaojiao/component/course/HomeCourseZone.kt
  20. BIN
      component/course/src/main/res/mipmap-xhdpi/course_group_buy_bg.png
  21. BIN
      component/course/src/main/res/mipmap-xhdpi/course_learn.png
  22. BIN
      component/course/src/main/res/mipmap-xhdpi/course_zone0.png
  23. BIN
      component/course/src/main/res/mipmap-xhdpi/course_zone1.png
  24. BIN
      component/course/src/main/res/mipmap-xxhdpi/course_group_buy_bg.png
  25. BIN
      component/course/src/main/res/mipmap-xxhdpi/course_learn.png
  26. BIN
      component/course/src/main/res/mipmap-xxhdpi/course_zone0.png
  27. BIN
      component/course/src/main/res/mipmap-xxhdpi/course_zone1.png
  28. BIN
      component/course/src/main/res/mipmap-xxxhdpi/course_group_buy_bg.png
  29. BIN
      component/course/src/main/res/mipmap-xxxhdpi/course_learn.png
  30. BIN
      component/course/src/main/res/mipmap-xxxhdpi/course_zone0.png
  31. BIN
      component/course/src/main/res/mipmap-xxxhdpi/course_zone1.png
  32. 1 0
      settings.gradle.kts

+ 1 - 0
.idea/gradle.xml

@@ -14,6 +14,7 @@
             <option value="$PROJECT_DIR$/app" />
             <option value="$PROJECT_DIR$/component" />
             <option value="$PROJECT_DIR$/component/common" />
+            <option value="$PROJECT_DIR$/component/course" />
             <option value="$PROJECT_DIR$/component/personal" />
             <option value="$PROJECT_DIR$/http" />
           </set>

+ 3 - 1
app/build.gradle.kts

@@ -48,9 +48,11 @@ android {
 }
 
 dependencies {
-    implementation(project(":component:personal"))
     implementation(project(":http"))
 
+    implementation(project(":component:course"))
+    implementation(project(":component:personal"))
+
     implementation("androidx.core:core-ktx:1.10.1")
     implementation("androidx.appcompat:appcompat:1.6.1")
     implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")

+ 5 - 32
app/src/main/java/com/zaojiao/app/ui/home/HomeCourseFragment.kt

@@ -1,54 +1,27 @@
 package com.zaojiao.app.ui.home
 
-import android.graphics.Color
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
 import androidx.fragment.app.Fragment
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
 import com.zaojiao.app.R
+import com.zaojiao.component.course.HomeCoursePage
 
 class HomeCourseFragment : Fragment() {
-
     override fun onCreateView(
         inflater: LayoutInflater,
         container: ViewGroup?,
         savedInstanceState: Bundle?
     ): View? {
-        return inflater.inflate(R.layout.fragment_course, container, false)
+        return inflater.inflate(R.layout.fragment_compose, container, false)
     }
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
-
-        val listview = view.findViewById<RecyclerView>(R.id.view_course)
-        listview.layoutManager =
-            LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
-        listview.adapter = HomeCourseAdapter()
-    }
-
-    class HomeCourseAdapter : RecyclerView.Adapter<HomeCourseViewHolder>() {
-        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeCourseViewHolder {
-            val view =
-                LayoutInflater.from(parent.context).inflate(R.layout.item_course, parent, false)
-            return HomeCourseViewHolder(view)
+        if (view is ComposeView) {
+            view.setContent { HomeCoursePage() }
         }
-
-        override fun getItemCount(): Int = 100
-
-        override fun onBindViewHolder(holder: HomeCourseViewHolder, position: Int) {
-            if (position % 2 == 0) {
-                holder.itemView.setBackgroundColor(Color.BLACK)
-            } else {
-                holder.itemView.setBackgroundColor(Color.WHITE)
-            }
-        }
-
-    }
-
-    class HomeCourseViewHolder(view: View) : RecyclerView.ViewHolder(view) {
-
     }
 }

+ 0 - 40
app/src/main/res/layout/fragment_course.xml

@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"
-    android:fitsSystemWindows="true">
-
-    <LinearLayout
-        android:id="@+id/course_header_layout"
-        android:layout_width="match_parent"
-        android:layout_height="60dp"
-        android:layout_marginHorizontal="16dp"
-        android:gravity="center_vertical"
-        app:layout_constraintTop_toTopOf="parent">
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="探索空间"
-            android:textColor="#ff333333"
-            android:textSize="20sp" />
-
-        <View
-            android:layout_width="16dp"
-            android:layout_height="0dp" />
-
-        <androidx.appcompat.widget.SearchView
-            android:layout_width="0dp"
-            android:layout_height="34dp"
-            android:layout_weight="1" />
-    </LinearLayout>
-
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/view_course"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/course_header_layout" />
-
-</androidx.constraintlayout.widget.ConstraintLayout>

+ 0 - 6
app/src/main/res/layout/item_course.xml

@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="200dp">
-
-</androidx.constraintlayout.widget.ConstraintLayout>

+ 3 - 0
component/common/build.gradle.kts

@@ -30,6 +30,9 @@ android {
 dependencies {
     implementation("androidx.core:core-ktx:1.10.1")
 
+    implementation("androidx.constraintlayout:constraintlayout-compose:1.0.1")
+    api("io.coil-kt:coil-compose:2.2.2")
+
     implementation(platform("androidx.compose:compose-bom:2023.05.01"))
     implementation("androidx.compose.ui:ui")
     implementation("androidx.compose.runtime:runtime")

+ 4 - 1
component/common/src/main/AndroidManifest.xml

@@ -1,2 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <uses-permission android:name="android.permission.INTERNET" />
+</manifest>

+ 8 - 0
component/common/src/main/java/com/zaojiao/component/common/Colors.kt

@@ -7,4 +7,12 @@ object Colors {
     val FF333333 = Color(0xFF333333)
     val FF666666 = Color(0xFF666666)
     val FF999999 = Color(0xFF999999)
+
+    fun from(value: String): Color {
+        return Color(android.graphics.Color.parseColor(value))
+    }
+
+    fun from(value: Int): Color {
+        return Color(value)
+    }
 }

+ 67 - 0
component/common/src/main/java/com/zaojiao/component/common/StatePage.kt

@@ -0,0 +1,67 @@
+package com.zaojiao.component.common
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.sp
+
+enum class PageState { Idle, Running, Success, Error }
+
+@Composable
+fun StatePage(state: PageState, onSuccess: @Composable () -> Unit) {
+    when (state) {
+        PageState.Idle -> {
+            Box(
+                modifier = Modifier.fillMaxSize(),
+            ) {
+                Text(
+                    text = "idle state",
+                    modifier = Modifier.align(Alignment.Center),
+                    style = TextStyle(
+                        fontSize = 18.sp,
+                        color = Color.Green,
+                    )
+                )
+            }
+        }
+
+        PageState.Running -> {
+            Box(
+                modifier = Modifier.fillMaxSize(),
+            ) {
+                Text(
+                    text = "running state",
+                    modifier = Modifier.align(Alignment.Center),
+                    style = TextStyle(
+                        fontSize = 18.sp,
+                        color = Color.Cyan,
+                    )
+                )
+            }
+        }
+
+        PageState.Success -> {
+            onSuccess.invoke()
+        }
+
+        PageState.Error -> {
+            Box(
+                modifier = Modifier.fillMaxSize(),
+            ) {
+                Text(
+                    text = "error state",
+                    modifier = Modifier.align(Alignment.Center),
+                    style = TextStyle(
+                        fontSize = 18.sp,
+                        color = Color.Red,
+                    )
+                )
+            }
+        }
+    }
+}

+ 95 - 0
component/common/src/main/java/com/zaojiao/component/common/Swiper.kt

@@ -0,0 +1,95 @@
+package com.zaojiao.component.common
+
+import android.graphics.pdf.PdfDocument.Page
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.PagerState
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.painter.ColorPainter
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.compose.ConstraintLayout
+import androidx.constraintlayout.compose.ConstraintSet
+import coil.compose.AsyncImage
+import kotlinx.coroutines.launch
+import java.util.Timer
+import java.util.TimerTask
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun <T> LJGBanner(
+    data: List<T>,
+    pagerState: PagerState? = null,
+    onImagePath: (index: Int, data: T) -> String,
+    modifier: Modifier = Modifier,
+    contentScale: ContentScale = ContentScale.Crop,
+    isLoopBanner: Boolean = true,
+    loopDelay: Long = 3000,
+    loopPeriod: Long = 3000,
+    desc: @Composable ((index: Int, data: T) -> String)? = null,
+    onBannerItemClick: ((index: Int, data: T) -> Unit)? = null,
+) {
+    val state: PagerState = pagerState ?: rememberPagerState(initialPage = 200 * data.size)
+
+    if (isLoopBanner) {
+        val coroutineScope = rememberCoroutineScope()
+        DisposableEffect(Unit) {
+            val timer = Timer()
+            timer.schedule(object : TimerTask() {
+                override fun run() {
+                    coroutineScope.launch {
+                        val next = state.currentPage + 1
+                        state.animateScrollToPage(next)
+                    }
+                }
+            }, loopDelay, loopPeriod)
+            onDispose {
+                timer.cancel()
+            }
+        }
+    }
+    val constraintSet = ConstraintSet {
+        val image = createRefFor("image")
+        val content = createRefFor("content")
+        constrain(image) {
+            start.linkTo(parent.start)
+            end.linkTo(parent.end)
+        }
+        constrain(content) {
+            bottom.linkTo(image.bottom)
+            start.linkTo(image.start)
+        }
+    }
+    HorizontalPager(
+        pageCount = Int.MAX_VALUE,
+        state = state,
+        pageSpacing = 16.dp,
+        modifier = modifier,
+    ) { index ->
+        val actualIndex = index % data.size
+        val item = data[actualIndex]
+
+        ConstraintLayout(constraintSet = constraintSet) {
+            AsyncImage(
+                model = onImagePath(actualIndex, item),
+                contentDescription = desc?.invoke(actualIndex, item),
+                modifier = Modifier
+                    .layoutId("image")
+                    .fillMaxSize()
+                    .clickable {
+                        onBannerItemClick?.invoke(actualIndex, item)
+                    },
+                contentScale = contentScale,
+//                placeholder = ColorPainter(Colors.FF999999),
+                error = ColorPainter(Colors.FF999999),
+            )
+        }
+    }
+}

+ 41 - 0
component/course/build.gradle.kts

@@ -0,0 +1,41 @@
+plugins {
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+
+android {
+    namespace = "com.zaojiao.component.course"
+    compileSdk = 33
+
+    defaultConfig {
+        minSdk = 27
+    }
+
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_11
+        targetCompatibility = JavaVersion.VERSION_11
+    }
+
+    composeOptions {
+        kotlinCompilerExtensionVersion = "1.4.7"
+    }
+
+    buildFeatures.compose = true
+
+    kotlinOptions {
+        jvmTarget = "11"
+    }
+}
+
+dependencies {
+    implementation(project(":component:common"))
+
+    implementation("androidx.core:core-ktx:1.10.1")
+
+    implementation(platform("androidx.compose:compose-bom:2023.05.01"))
+    implementation("androidx.compose.ui:ui")
+    implementation("androidx.compose.runtime:runtime")
+    implementation("androidx.compose.foundation:foundation")
+    implementation("androidx.compose.material3:material3")
+    implementation("androidx.compose.ui:ui-tooling-preview")
+}

+ 2 - 0
component/course/src/main/AndroidManifest.xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest />

+ 42 - 0
component/course/src/main/java/com/zaojiao/component/course/HomeCourseCategory.kt

@@ -0,0 +1,42 @@
+package com.zaojiao.component.course
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+
+@Composable
+fun HomeCourseCategory() {
+    LazyRow(
+        modifier = Modifier
+            .padding(vertical = 14.dp)
+            .height(64.dp)
+            .fillMaxWidth(),
+        contentPadding = PaddingValues(horizontal = 16.dp)
+    ) {
+        items(100) { index ->
+            Text(
+                text = "$index",
+                modifier = Modifier
+                    .background(color = Color.Red)
+                    .fillMaxWidth()
+                    .height(48.dp),
+                style = TextStyle(
+                    fontSize = 16.sp,
+                    color = Color.Yellow,
+                )
+            )
+        }
+    }
+}

+ 186 - 0
component/course/src/main/java/com/zaojiao/component/course/HomeCourseGroupBuy.kt

@@ -0,0 +1,186 @@
+package com.zaojiao.component.course
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import com.zaojiao.component.common.LJGBanner
+import com.zaojiao.component.common.Spacer
+
+@Composable
+internal fun HomeCourseGroupBuy() {
+    Row(
+        modifier = Modifier
+            .padding(horizontal = 16.dp)
+            .fillMaxWidth()
+            .wrapContentHeight()
+    ) {
+        HomeCourseGroupBuySwiper()
+        Spacer(width = 10.dp)
+        HomeCourseGroupBuyRecommend()
+    }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun RowScope.HomeCourseGroupBuySwiper() {
+    Box(
+        modifier = Modifier
+            .weight(1f)
+            .aspectRatio(165f / 220f)
+    ) {
+        val pagerState = rememberPagerState(initialPage = 200 * 4)
+
+        LJGBanner(
+            data = listOf(
+                "https://t7.baidu.com/it/u=1595072465,3644073269&fm=193&f=GIF",
+                "https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF",
+                "https://t7.baidu.com/it/u=1956604245,3662848045&fm=193&f=GIF",
+                "https://t7.baidu.com/it/u=2529476510,3041785782&fm=193&f=GIF",
+            ),
+            pagerState = pagerState,
+            onImagePath = { index, item ->
+                item
+            },
+            modifier = Modifier
+                .clip(RoundedCornerShape(20.dp))
+                .fillMaxSize()
+        )
+
+        Row(
+            modifier = Modifier
+                .padding(bottom = 5.dp)
+                .align(Alignment.BottomCenter)
+                .wrapContentHeight()
+                .height(7.dp),
+            horizontalArrangement = Arrangement.spacedBy(6.dp),
+        ) {
+            repeat(4) { index ->
+                if (index == (pagerState.currentPage % 4)) {
+                    Box(
+                        modifier = Modifier
+                            .clip(RoundedCornerShape(7.dp))
+                            .background(color = Color.White)
+                            .size(7.dp, 7.dp)
+                    )
+                } else {
+                    Box(
+                        modifier = Modifier
+                            .clip(RoundedCornerShape(7.dp))
+                            .background(color = Color.White.copy(alpha = 0.3f))
+                            .size(7.dp, 7.dp)
+                    )
+                }
+            }
+        }
+    }
+}
+
+@Composable
+private fun RowScope.HomeCourseGroupBuyRecommend() {
+    Box(
+        modifier = Modifier
+            .weight(1f)
+            .aspectRatio(165f / 220f)
+    ) {
+        Image(
+            painter = painterResource(id = R.mipmap.course_group_buy_bg),
+            contentDescription = null,
+        )
+        Column(
+            modifier = Modifier.fillMaxSize(),
+        ) {
+            HomeCourseGroupBuyRecommendTitle()
+            Spacer(height = 10.dp)
+            HomeCourseGroupBuyRecommendBody()
+        }
+    }
+}
+
+@Composable
+private fun ColumnScope.HomeCourseGroupBuyRecommendTitle() {
+    Row(
+        modifier = Modifier
+            .background(color = Color.Red)
+            .weight(1f)
+            .fillMaxWidth()
+    ) {
+
+    }
+}
+
+@Composable
+private fun ColumnScope.HomeCourseGroupBuyRecommendBody() {
+    Column(
+        modifier = Modifier
+            .padding(horizontal = 11.dp)
+            .wrapContentHeight()
+            .fillMaxWidth(),
+
+        ) {
+        Row(
+            modifier = Modifier
+                .wrapContentHeight()
+                .fillMaxWidth(),
+            horizontalArrangement = Arrangement.SpaceBetween,
+        ) {
+            Box(
+                modifier = Modifier
+                    .background(color = Color.Blue)
+                    .width(64.dp)
+                    .height(64.dp),
+            )
+            Box(
+                modifier = Modifier
+                    .background(color = Color.Blue)
+                    .width(64.dp)
+                    .height(64.dp),
+            )
+        }
+        Spacer(height = 15.dp)
+        Row(
+            modifier = Modifier
+                .wrapContentHeight()
+                .fillMaxWidth(),
+            horizontalArrangement = Arrangement.SpaceBetween,
+        ) {
+            Box(
+                modifier = Modifier
+                    .background(color = Color.Blue)
+                    .width(64.dp)
+                    .height(74.5.dp),
+            )
+            Box(
+                modifier = Modifier
+                    .background(color = Color.Blue)
+                    .width(64.dp)
+                    .height(74.5.dp),
+            )
+        }
+    }
+}

+ 51 - 0
component/course/src/main/java/com/zaojiao/component/course/HomeCoursePage.kt

@@ -0,0 +1,51 @@
+package com.zaojiao.component.course
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import com.zaojiao.component.common.PageState
+import com.zaojiao.component.common.StatePage
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun HomeCoursePage() {
+    Surface(
+        color = Color.White,
+        modifier = Modifier.fillMaxSize(),
+    ) {
+        StatePage(state = PageState.Success) {
+            LazyColumn(
+                modifier = Modifier.fillMaxWidth()
+            ) {
+                stickyHeader {
+                    HomeCourseTopBar()
+                }
+
+                item {
+                    HomeCourseTopSwiper()
+                }
+
+                item {
+                    HomeCourseCategory()
+                }
+
+                item {
+                    HomeCourseZone()
+                }
+
+                item {
+                    HomeCourseGroupBuy()
+                }
+
+                item {
+                    HomeCourseRecommend()
+                }
+            }
+        }
+    }
+}

+ 38 - 0
component/course/src/main/java/com/zaojiao/component/course/HomeCourseRecommend.kt

@@ -0,0 +1,38 @@
+package com.zaojiao.component.course
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.zaojiao.component.common.Spacer
+
+@Composable
+internal fun HomeCourseRecommend() {
+    LazyVerticalGrid(
+        columns = GridCells.Fixed(2),
+        horizontalArrangement = Arrangement.spacedBy(12.dp),
+        verticalArrangement = Arrangement.spacedBy(20.dp),
+        contentPadding = PaddingValues(horizontal = 16.dp),
+        userScrollEnabled = false,
+    ) {
+        items(100) { index ->
+            Column(
+                modifier = Modifier
+                    .background(color = Color.Red)
+                    .wrapContentHeight()
+                    .fillMaxWidth()
+            ) {
+                Spacer(height = 200.dp)
+            }
+        }
+    }
+}

+ 70 - 0
component/course/src/main/java/com/zaojiao/component/course/HomeCourseTopBar.kt

@@ -0,0 +1,70 @@
+package com.zaojiao.component.course
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.zaojiao.component.common.Colors
+import com.zaojiao.component.common.Spacer
+
+@Composable
+internal fun HomeCourseTopBar() {
+    Row(
+        modifier = Modifier
+            .statusBarsPadding()
+            .padding(horizontal = 16.dp, vertical = 14.dp)
+            .height(34.dp)
+            .fillMaxWidth(),
+        verticalAlignment = Alignment.CenterVertically,
+    ) {
+        Text(
+            text = "探索空间",
+            style = TextStyle(
+                fontSize = 20.sp,
+                lineHeight = 28.sp,
+                fontWeight = FontWeight.SemiBold,
+                color = Color(0xFF333333),
+            ),
+        )
+        Spacer(width = 16.dp)
+        HomeCourseTopSearchBar()
+        Spacer(width = 10.dp)
+        Image(
+            painter = painterResource(id = R.mipmap.course_learn),
+            contentDescription = "正在学习的课程",
+        )
+    }
+}
+
+@Composable
+private fun RowScope.HomeCourseTopSearchBar() {
+    Box(
+        modifier = Modifier
+            .background(
+                color = Colors.from("#FFF1F6FF"),
+                shape = RoundedCornerShape(100.dp),
+            )
+            .weight(1f)
+            .padding(horizontal = 10.dp)
+            .fillMaxHeight(),
+    ) {
+
+    }
+}

+ 34 - 0
component/course/src/main/java/com/zaojiao/component/course/HomeCourseTopSwiper.kt

@@ -0,0 +1,34 @@
+package com.zaojiao.component.course
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.unit.dp
+import com.zaojiao.component.common.LJGBanner
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun HomeCourseTopSwiper() {
+    LJGBanner(
+        data = listOf(
+            "https://t7.baidu.com/it/u=1595072465,3644073269&fm=193&f=GIF",
+            "https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF",
+            "https://t7.baidu.com/it/u=1956604245,3662848045&fm=193&f=GIF",
+            "https://t7.baidu.com/it/u=2529476510,3041785782&fm=193&f=GIF",
+        ),
+        onImagePath = { _, item ->
+            item
+        },
+        modifier = Modifier
+            .padding(horizontal = 16.dp, vertical = 14.dp)
+            .clip(RoundedCornerShape(20.dp))
+            .height(151.dp)
+            .fillMaxWidth()
+
+    )
+}

+ 50 - 0
component/course/src/main/java/com/zaojiao/component/course/HomeCourseZone.kt

@@ -0,0 +1,50 @@
+package com.zaojiao.component.course
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import com.zaojiao.component.common.Spacer
+
+@Composable
+internal fun HomeCourseZone() {
+    Row(
+        modifier = Modifier
+            .padding(vertical = 14.dp, horizontal = 16.dp)
+            .wrapContentHeight()
+            .fillMaxWidth(),
+    ) {
+        HomeCourseZoneOne()
+        Spacer(width = 10.dp)
+        HomeCourseZoneZero()
+    }
+}
+
+@Composable
+private fun RowScope.HomeCourseZoneOne() {
+    Image(
+        painter = painterResource(id = R.mipmap.course_zone1),
+        modifier = Modifier
+            .weight(1f)
+            .aspectRatio(166f / 72f),
+        contentDescription = "特惠启蒙",
+    )
+}
+
+@Composable
+private fun RowScope.HomeCourseZoneZero() {
+    Image(
+        painter = painterResource(id = R.mipmap.course_zone0),
+        modifier = Modifier
+            .weight(1f)
+            .aspectRatio(166f / 72f),
+        contentDescription = "0元免费学",
+    )
+}

BIN
component/course/src/main/res/mipmap-xhdpi/course_group_buy_bg.png


BIN
component/course/src/main/res/mipmap-xhdpi/course_learn.png


BIN
component/course/src/main/res/mipmap-xhdpi/course_zone0.png


BIN
component/course/src/main/res/mipmap-xhdpi/course_zone1.png


BIN
component/course/src/main/res/mipmap-xxhdpi/course_group_buy_bg.png


BIN
component/course/src/main/res/mipmap-xxhdpi/course_learn.png


BIN
component/course/src/main/res/mipmap-xxhdpi/course_zone0.png


BIN
component/course/src/main/res/mipmap-xxhdpi/course_zone1.png


BIN
component/course/src/main/res/mipmap-xxxhdpi/course_group_buy_bg.png


BIN
component/course/src/main/res/mipmap-xxxhdpi/course_learn.png


BIN
component/course/src/main/res/mipmap-xxxhdpi/course_zone0.png


BIN
component/course/src/main/res/mipmap-xxxhdpi/course_zone1.png


+ 1 - 0
settings.gradle.kts

@@ -21,3 +21,4 @@ include(":http")
 
 include(":component:personal")
 include(":component:common")
+include(":component:course")