从淘宝京东APP看Android编程实例分析新手如何从零搭建项目界面
嘿,朋友!是不是每天刷淘宝京东刷得不亦乐乎?你有没有想过,那些好看的界面、丝滑的交互,到底是怎么在手机上跑起来的?今天咱们就掰开揉碎了聊,新手怎么从零开始,搭出一个像淘宝京东那样的Android项目界面。
咱们先从”看”开始
先别急着打开Android Studio,咱先拿起手机,仔细看看淘宝首页长什么样。
你打开淘宝,第一眼看到的是顶部的搜索栏,下面是一排轮播图,再往下是各种商品图标入口,然后是”猜你喜欢”的瀑布流。京东也差不多,顶部是搜索框,下面是轮播广告,再往下是分类导航。
这些界面,在Android里是怎么搭起来的?咱们一步步来。
第一步:把手里的工具准备好
你电脑上得装好Android Studio,这是Google官方出的开发工具,免费用,下载安装就行。装好之后,咱们新建一个项目。
新建项目的时候,选个”Empty Activity”(空活动),这个选项的意思就是给你一个干干净净的舞台,你自己往上面搭东西。项目起个名字,比如叫”DemoShop”,然后选择语言,Java或者Kotlin都行,现在大多数新人都用Kotlin,咱们就用Kotlin来演示。
新建完之后,你会看到项目结构里有几个关键文件,最重要的就是res/layout/activity_main.xml,这个是界面的蓝图,咱们要在这里面画东西。
第二步:先把”骨架”搭起来
打开activity_main.xml,你会看到默认的代码。先别管那些默认的,咱们自己来搭一个淘宝首页的骨架。
淘宝首页最上面是什么?是一个搜索框,对吧?咱们先做这个。
<?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="match_parent"
android:orientation="vertical"
android:background="#FFFFFF">
<!-- 顶部搜索栏区域 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="56dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="8dp">
<!-- 搜索框 -->
<EditText
android:id="@+id/et_search"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:hint="搜索商品"
android:background="#F5F5F5"
android:padding="10dp"
android:textSize="14sp"
android:layout_marginEnd="8dp"
android:singleLine="true" />
<!-- 扫一扫按钮 -->
<ImageButton
android:id="@+id/btn_scan"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_scan"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="扫一扫" />
<!-- 二维码按钮 -->
<ImageButton
android:id="@+id/btn_qrcode"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_qrcode"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="二维码" />
</LinearLayout>
<!-- 内容区域 -->
<FrameLayout
android:id="@+id/fl_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
看懂了吗?用LinearLayout做整体布局,垂直排列,顶部是搜索栏,下面留给内容区域。EditText就是那个搜索框,ImageButton是两边的按钮。layout_weight="1"这个属性是让搜索框自动撑满剩余空间,这个在淘宝京东里很常见。
第三步:把轮播图加上去
淘宝首页那个会自己动的图片,专业名字叫”ViewPager”或者”Banner”,咱们用一个简单的方案来实现。
先在布局里加一个ViewPager:
<!-- 轮播图区域 -->
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="160dp"
android:layout_marginTop="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" />
然后新建一个布局文件,用来放每一张图片:
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/iv_banner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/banner_1" />
接着写一个Adapter,这个Adapter的作用就是把图片和ViewPager连接起来:
class BannerAdapter(private val imageResIds: List<Int>) :
RecyclerView.Adapter<BannerAdapter.BannerViewHolder>() {
inner class BannerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val imageView: ImageView = view.findViewById(R.id.iv_banner)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BannerViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_banner, parent, false)
return BannerViewHolder(view)
}
override fun onBindViewHolder(holder: BannerViewHolder, position: Int) {
holder.imageView.setImageResource(imageResIds[position % imageResIds.size])
}
override fun getItemCount(): Int = Int.MAX_VALUE
}
这里的Int.MAX_VALUE是个小技巧,让轮播可以无限循环。然后在MainActivity里初始化:
class MainActivity : AppCompatActivity() {
private lateinit var viewPager: ViewPager2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewPager = findViewById(R.id.viewPager)
// 轮播图片资源
val bannerImages = listOf(
R.drawable.banner_1,
R.drawable.banner_2,
R.drawable.banner_3,
R.drawable.banner_4
)
val adapter = BannerAdapter(bannerImages)
viewPager.adapter = adapter
// 自动轮播
val autoPlayDelay = 3000L
val handler = Handler(Looper.getMainLooper())
val runnable = object : Runnable {
override fun run() {
val currentItem = viewPager.currentItem
viewPager.currentItem = currentItem + 1
handler.postDelayed(this, autoPlayDelay)
}
}
handler.postDelayed(runnable, autoPlayDelay)
}
}
这样轮播图就动起来了,跟淘宝首页的效果差不多。
第四步:那些图标入口怎么摆
淘宝首页搜索栏下面有一排圆形的图标,什么”天猫”、”京东”、”淘宝直播”之类的。在Android里,这个通常用GridLayout或者RecyclerView来实现。
咱们用GridLayout来搭:
<!-- 图标入口区域 -->
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="5"
android:rowCount="2"
android:layout_marginTop="12dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp">
<!-- 第一行 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_tmall"
android:contentDescription="天猫" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="天猫"
android:textSize="12sp"
android:textColor="#333333"
android:layout_marginTop="4dp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_live"
android:contentDescription="直播" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="直播"
android:textSize="12sp"
android:textColor="#333333"
android:layout_marginTop="4dp" />
</LinearLayout>
<!-- 后面继续类似... -->
</GridLayout>
这样就是一个两行五列的网格,每个格子里放一个图标和文字说明。layout_columnWeight="1"让每个格子等宽。
第五步:商品列表怎么搞
淘宝京东最核心的部分是下面的商品列表,每一行显示一个商品的图片、标题、价格。这个在Android里用RecyclerView来实现,是最标准的做法。
先写一个商品Item的布局:
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardCornerRadius="8dp"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="12dp">
<!-- 商品图片 -->
<ImageView
android:id="@+id/iv_product"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:background="#F0F0F0" />
<!-- 商品信息 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginStart="12dp"
android:gravity="center_vertical">
<!-- 商品标题 -->
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#222222"
android:maxLines="2"
android:ellipsize="end" />
<!-- 价格 -->
<TextView
android:id="@+id/tv_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="#FF4444"
android:layout_marginTop="8dp"
android:fontFamily="sans-serif-medium" />
<!-- 销量 -->
<TextView
android:id="@+id/tv_sales"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textColor="#999999"
android:layout_marginTop="4dp" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
然后写一个商品数据模型:
data class Product(
val id: Int,
val title: String,
val price: Double,
val sales: String,
val imageUrl: Int
)
再写一个RecyclerView的Adapter:
class ProductAdapter(
private val productList: List<Product>,
private val onItemClick: (Product) -> Unit
) : RecyclerView.Adapter<ProductAdapter.ProductViewHolder>() {
inner class ProductViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
val ivProduct: ImageView = view.findViewById(R.id.iv_product)
val tvTitle: TextView = view.findViewById(R.id.tv_title)
val tvPrice: TextView = view.findViewById(R.id.tv_price)
val tvSales: TextView = view.findViewById(R.id.tv_sales)
init {
view.setOnClickListener(this)
}
override fun onClick(v: View?) {
val position = adapterPosition
if (position != RecyclerView.NO_POSITION) {
onItemClick(productList[position])
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_product, parent, false)
return ProductViewHolder(view)
}
override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
val product = productList[position]
holder.tvTitle.text = product.title
holder.tvPrice.text = "¥${product.price}"
holder.tvSales.text = "已售 ${product.sales}"
// 这里用Glide加载网络图片
Glide.with(holder.ivProduct.context)
.load(product.imageUrl)
.placeholder(R.drawable.placeholder)
.error(R.drawable.placeholder)
.into(holder.ivProduct)
}
override fun getItemCount(): Int = productList.size
}
最后在MainActivity里把RecyclerView接起来:
class MainActivity : AppCompatActivity() {
private lateinit var rvProducts: RecyclerView
private lateinit var productAdapter: ProductAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
rvProducts = findViewById(R.id.rv_products)
// 模拟数据
val products = generateMockProducts(20)
productAdapter = ProductAdapter(products) { product ->
Toast.makeText(this, "点击了: ${product.title}", Toast.LENGTH_SHORT).show()
}
rvProducts.apply {
layoutManager = LinearLayoutManager(this@MainActivity)
adapter = productAdapter
addItemDecoration(DividerItemDecoration(this@MainActivity, DividerItemDecoration.VERTICAL))
}
}
private fun generateMockProducts(count: Int): List<Product> {
return (1..count).map { i ->
Product(
id = i,
title = "这是第${i}件商品的标题,描述一下商品的特点和卖点",
price = (Math.random() * 500 + 10).toDouble(),
sales = "${(Math.random() * 10000).toInt()}",
imageUrl = "https://via.placeholder.com/200x200"
)
}
}
}
第六步:底部导航栏
淘宝京东下面都有一个导航栏,有”首页”、”分类”、”购物车”、”我的”这几个Tab。在Android里,最常用的是BottomNavigationView配合Fragment来实现。
先在布局里加上导航栏:
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#FFFFFF"
app:menu="@menu/bottom_nav_menu"
app:labelVisibilityMode="labeled" />
然后新建一个菜单资源文件res/menu/bottom_nav_menu.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/nav_home"
android:icon="@drawable/ic_home"
android:title="首页" />
<item
android:id="@+id/nav_category"
android:icon="@drawable/ic_category"
android:title="分类" />
<item
android:id="@+id/nav_cart"
android:icon="@drawable/ic_cart"
android:title="购物车" />
<item
android:id="@+id/nav_profile"
android:icon="@drawable/ic_profile"
android:title="我的" />
</menu>
然后在MainActivity里处理点击事件:
class MainActivity : AppCompatActivity() {
private lateinit var bottomNav: BottomNavigationView
private lateinit var fragmentContainer: FrameLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bottomNav = findViewById(R.id.bottom_nav)
fragmentContainer = findViewById(R.id.fl_content)
// 默认显示首页
if (savedInstanceState == null) {
loadFragment(HomeFragment())
}
bottomNav.setOnItemSelectedListener { item ->
when (item.itemId) {
R.id.nav_home -> {
loadFragment(HomeFragment())
true
}
R.id.nav_category -> {
loadFragment(CategoryFragment())
true
}
R.id.nav_cart -> {
loadFragment(CartFragment())
true
}
R.id.nav_profile -> {
loadFragment(ProfileFragment())
true
}
else -> false
}
}
}
private fun loadFragment(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.fl_content, fragment)
.commit()
}
}
这样底部导航就能切换不同的页面了,每个页面是一个Fragment,这样做的好处是每个页面的逻辑互不干扰,代码好维护。
第七步:把淘宝京东的样式学过来
光有结构还不够,淘宝京东好看的界面还有一些细节值得模仿。
圆角卡片:淘宝京东的商品卡片都有圆角,用CardView加上app:cardCornerRadius就能实现。
阴影效果:滑动的时候卡片会有轻微阴影,这个用cardElevation属性控制。
颜色规范:淘宝主色是橙色(#FF5000),京东主色是红色(#E1251B)。咱们做Demo的话可以选一个主色,然后通过res/values/colors.xml来统一管理:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="primary">#FF5000</color>
<color name="primary_dark">#CC4000</color>
<color name="accent">#FF9500</color>
<color name="background">#F5F5F5</color>
<color name="white">#FFFFFF</color>
<color name="text_primary">#222222</color>
<color name="text_secondary">#999999</color>
</resources>
然后在布局里引用:
<TextView
android:textColor="@color/text_primary"
android:textSize="14sp" />
这样改颜色只改一个地方就全部生效,别的地方不用动。
间距规范:淘宝京东的间距很有规律,通常是8dp的倍数,比如4dp、8dp、12dp、16dp、24dp。做界面设计的时候养成这个习惯,界面看起来会舒服很多。
第八步:用约束布局让界面更灵活
前面用的是LinearLayout,但对于复杂的界面,约束布局(ConstraintLayout)更灵活。咱们把搜索栏那块用ConstraintLayout重写一下:
<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="wrap_content"
android:padding="8dp">
<EditText
android:id="@+id/et_search"
android:layout_width="0dp"
android:layout_height="40dp"
android:hint="搜索商品"
android:background="#F5F5F5"
android:padding="10dp"
android:textSize="14sp"
android:singleLine="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/btn_scan"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<ImageButton
android:id="@+id/btn_scan"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_scan"
android:background="?attr/selectableItemBackgroundBorderless"
android:layout_marginStart="8dp"
app:layout_constraintStart_toEndOf="@id/et_search"
app:layout_constraintEnd_toStartOf="@id/btn_qrcode"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<ImageButton
android:id="@+id/btn_qrcode"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_qrcode"
android:background="?attr/selectableItemBackgroundBorderless"
android:layout_marginStart="8dp"
app:layout_constraintStart_toEndOf="@id/btn_scan"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
约束布局通过锚点(constraint)来定位每个元素,不管屏幕多大都能自适应。你在Android Studio的布局编辑器里可以直接拖拽来设置约束,比手写字要快得多。
第九步:把界面做精致
好界面和好代码一样,细节决定成败。
图片加载:前面提到了用Glide来加载图片,这是Android里最流行的图片加载库:
// 在build.gradle里添加依赖
// implementation 'com.github.bumptech.glide:glide:4.15.1'
// annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
Glide.with(context)
.load(url)
.placeholder(R.drawable.loading) // 加载中的占位图
.error(R.drawable.error) // 加载失败的错误图
.circleCrop() // 圆形裁剪(适合头像)
.into(imageView)
下拉刷新:淘宝京东首页都能下拉刷新,用Android官方的SwipeRefreshLayout:
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvProducts"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
swipeRefresh.setOnRefreshListener {
// 模拟刷新数据
fetchProducts { products ->
productAdapter.updateData(products)
swipeRefresh.isRefreshing = false
}
}
上拉加载更多:在RecyclerView的Adapter里监听滚动到底部:
rvProducts.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val layoutManager = recyclerView.layoutManager as? LinearLayoutManager
?: return
val totalItemCount = layoutManager.itemCount
val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
if (lastVisibleItem >= totalItemCount - 5 && !isLoading) {
loadMoreProducts()
}
}
})
第十步:整体结构搭建完整了
把所有部分组合在一起,最终的activity_main.xml大概是这样的:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
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:background="@color/background">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
app:layout_scrollFlags="scroll|enterAlways">
<!-- 搜索栏 -->
<EditText
android:id="@+id/et_search"
android:layout_width="0dp"
android:layout_height="40dp"
android:hint="搜索商品"
android:background="#F5F5F5"
android:padding="10dp"
android:textSize="14sp"
android:singleLine="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/btn_scan"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<ImageButton
android:id="@+id/btn_scan"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_scan"
android:background="?attr/selectableItemBackgroundBorderless"
android:layout_marginStart="8dp"
app:layout_constraintStart_toEndOf="@id/et_search"
app:layout_constraintEnd_toStartOf="@id/btn_qrcode"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<ImageButton
android:id="@+id/btn_qrcode"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_qrcode"
android:background="?attr/selectableItemBackgroundBorderless"
android:layout_marginStart="8dp"
app:layout_constraintStart_toEndOf="@id/btn_scan"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvProducts"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingTop="8dp"
android:paddingBottom="8dp" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#FFFFFF"
app:menu="@menu/bottom_nav_menu" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
这里用了CoordinatorLayout作为根布局,它能让各个组件之间配合得更默契。AppBarLayout里的app:layout_scrollFlags让搜索栏在RecyclerView滚动时自动收起,这个交互效果在淘宝京东里很常见。
一些新手容易踩的坑
别在UI线程做网络请求:加载图片、获取数据这些耗时操作,一定要放在子线程里,不然界面会卡死。用Coroutine或者RxJava来处理异步操作:
lifecycleScope.launch(Dispatchers.IO) {
val products = fetchProductsFromServer()
withContext(Dispatchers.Main) {
productAdapter.updateData(products)
}
}
图片尺寸要合适:别把高清大图直接加载到列表里,内存会爆。用Glide的override()方法来指定尺寸:
Glide.with(context)
.load(url)
.override(200, 200)
.into(imageView)
布局嵌套不要太深:每多一层嵌套,渲染性能就差一点。能用约束布局解决的,就别套好几个LinearLayout。
善用ViewBinding:传统的方式是用findViewById来找控件,现在推荐用ViewBinding,代码更安全:
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 直接访问控件,不用再findViewById
binding.etSearch.hint = "搜索"
binding.bottomNav.setOnItemSelectedListener { ... }
}
总结一下
从淘宝京东这类APP的界面结构来看,Android项目的界面搭建其实就这几个核心模块:顶部搜索栏、轮播广告、图标入口、内容列表、底部导航。对应到代码层面,就是LinearLayout/ConstraintLayout负责布局、ViewPager2负责轮播、RecyclerView负责列表、BottomNavigationView负责导航。
新手一开始可能会被各种组件搞懵,别急,先从最简单的开始,先把搜索栏+列表+底部导航这三个部分搭起来,然后逐步加功能。每完成一个功能,在真机或者模拟器上跑一跑,看到效果会有很大成就感。
记住,淘宝京东那些精美的界面,不是一天搭成的,也是由一行行代码、一个个布局堆出来的。你现在写的每一个TextView、每一行约束,都是在往那个方向靠近。动手写起来,比看一百篇教程都管用。
