无名之辈的Android之路

Android 实战:美食项目(二)

2021-09-09  本文已影响0人  搬码人

创建导航和底部菜单栏

创建几个页面的fragment和添加navigation

有关fragment的创建可参考https://www.jianshu.com/p/5974e52a024d
暂时添加标记的fragment,其他的后边再提及

image.png
将所有fragment对应的xml文件的约束改为ConstraintLayout
并且将所有的fragment都换成binding的形式访问控件
模板

在res中创建navigation并且将刚创建的fragment添加到navigation中


image.png

去掉ActionBar
本项目中用不到ActionBar,也就是头部的导航信息
res -> themes -> themes.xml(两个都改)

image.png

在activity_main.xml中添加两个控件
同时进行约束布局

image.png

添加menu菜单

使用同创建navigation一样的方式创建menu


menu

id就是fragment对应的id,每个菜单按钮绑定一个对应的fragment使点击时能完成页面切换
icon为按钮图标,可以自己添加到drawable中,如果懒得设计图片的话可以在drawable右键添加Vector Asset添加系统提供的图片资源
showAsAction为底部菜单栏什么时候展示:小编本来是选择always的,不过系统建议使用ifRoom

这里小编来解释一下shoAsAction的三种不同状态:
always:表示一直显示在容器中(这里的容器指的是BottomNavigationBar)
ifRoom:表示在屏幕足够大的情况下显示在容器中,不够的话显示在菜单中
never:表示永远显示在菜单中
至于每种的效果具体如何,大家试过就知道

iconTint设置默认颜色


详细介绍
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/recipeFragment"
        android:icon="@drawable/ic_book"
        android:title="Recipes"
        app:showAsAction="ifRoom"
        android:iconTint="#7C7B7E"
        tools:targetApi="o" />
    <item
        android:id="@+id/favoriteFragment"
        android:icon="@drawable/ic_star"
        android:title="Favorite"
        app:showAsAction="ifRoom"
        android:iconTint="#7C7B7E"
        tools:targetApi="o"/>
    <item
        android:id="@+id/otherFragment"
        android:icon="@drawable/ic_other"
        android:title="Other"
        app:showAsAction="ifRoom"
        android:iconTint="#7C7B7E"
        tools:targetApi="o"/>
</menu>

设置按钮点击状态颜色改变
设置BottomNavigaitonView的menu属性,将刚创建的bottom_menu设置到menu上。

image.png
在res中创建color文件并且创建item_color.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="#F5FC" android:state_checked="true"/>
    <item android:color="#7C7b7E" android:state_checked="false"/>
</selector>

fragment_recipe.xml界面搭建

image.png

ShapeableImageView:通过shapeAppearanceOverlay设置圆形图案


image.png
<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/frameLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".fragment.recipe.RecipeFragment">


    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        android:src="@drawable/main_bg"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="Welcome to Food App"
        android:textColor="@color/white"
        android:textSize="26sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Ready to cook launch?"
        app:layout_constraintStart_toStartOf="@+id/textView2"
        app:layout_constraintTop_toBottomOf="@+id/textView2"
        android:textStyle="bold"
        android:textColor="#817F7C"
        android:textSize="20sp"/>

    <com.google.android.material.imageview.ShapeableImageView
        android:id="@+id/shapeableImageView"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginEnd="16dp"
        android:scaleType="centerCrop"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="@+id/textView2"
        app:shapeAppearanceOverlay="@style/roundedCornerImageStyle"
        app:srcCompat="@drawable/myhead" />

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:src="@drawable/top"
        app:layout_constraintEnd_toEndOf="@+id/shapeableImageView"
        app:layout_constraintStart_toStartOf="@+id/textView3"
        app:layout_constraintTop_toBottomOf="@+id/textView3"
        android:scaleType="centerCrop"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/typeRecyclerView"
        android:layout_width="383dp"
        android:layout_height="50dp"
        android:layout_marginTop="30dp"
        app:layout_constraintEnd_toEndOf="@+id/imageView2"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="@+id/imageView2"
        app:layout_constraintTop_toBottomOf="@+id/imageView2" />

    <com.todkars.shimmer.ShimmerRecyclerView
        android:id="@+id/foodRecyclerView"
        android:layout_width="0dp"
        android:layout_height="272dp"
        android:layout_marginTop="15dp"
        app:shimmer_recycler_layout="@layout/food_item_shimmer_layout"
        app:shimmer_recycler_item_count="4"
        app:layout_constraintBottom_toBottomOf="@+id/imageView"
        app:layout_constraintEnd_toEndOf="@+id/typeRecyclerView"
        app:layout_constraintStart_toStartOf="@+id/typeRecyclerView"
        app:layout_constraintTop_toBottomOf="@+id/typeRecyclerView"
        app:layout_constraintVertical_bias="0.355" />
</androidx.constraintlayout.widget.ConstraintLayout>

手机屏幕顶部背景色设置
为了使我们设置的图片的背景色和系统默认的顶部颜色不违和,我们需要在theme.xml中更改颜色

statusBarColor

搜索栏设计

xml

RecipeFragment中
注:在fragment中context不能使用this,而需要使用requireContext

 private fun initRecyclerView(){
        //配置类型选择的RecyclerView
        binding.typeRecyclerView.layoutManager = LinearLayoutManager(requireContext(),RecyclerView.HORIZONTAL,false)
        binding.typeRecyclerView.adapter = typeAdapter
        //处理回调事件
        typeAdapter.callBack= {current, last ->
            val currentHolder = binding.typeRecyclerView.findViewHolderForAdapterPosition(current) as TypeAdapter.MyViewHolder
            val lastHolder = binding.typeRecyclerView.findViewHolderForAdapterPosition(last)
            //选中当前
            currentHolder.changeSelectedStatus(true)
            //取消之前选中的
            if (lastHolder != null){
                val lastTypeHolder = lastHolder as TypeAdapter.MyViewHolder
                lastTypeHolder.changeSelectedStatus(false)
            }else{
                //重新把上一次选中的item刷新
                typeAdapter.notifyItemChanged(last)
            }
            //获取数据
            fetchData(typeAdapter.typeList[current])
        }

    }

适配器对应的布局


image.png

TypeAdaper中

package com.example.foodresp.fragment.recipe.adapter

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.foodresp.databinding.ItemTypeBinding

/**
 *@Description
 *@Author PC
 *@QQ 1578684787
 */
class TypeAdapter:RecyclerView.Adapter<TypeAdapter.MyViewHolder>() {
    //事件回调的lambda
    var callBack:((current:Int,last:Int)->Unit)?=null
    val typeList = listOf("main course","side dish","dessert","appetizer","salad","bread",
            "breakfast","beverage","sauce","marinade","finger food","snack","drink")
    private var lastSelectedPosition = 0
    class MyViewHolder(private val binding:ItemTypeBinding):RecyclerView.ViewHolder(binding.root) {
        //数据回调
        var callBack:((Int)->Unit)?=null
        companion object{
            fun from(parent: ViewGroup): MyViewHolder {
                //创建ViewHolder
                val inflater = LayoutInflater.from(parent.context)
                return MyViewHolder(ItemTypeBinding.inflate(inflater))
            }
        }
        //绑定数据
        fun bind(type:String,position: Int){
            binding.titleTextView.text = type
            binding.titleTextView.setOnClickListener {
                callBack?.let {it(position) }
                changeSelectedStatus(true)

            }
        }
        fun changeSelectedStatus(status:Boolean){
            binding.titleTextView.isSelected = status
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val holder = MyViewHolder.from(parent)
        //处理点击之后的回调事件
        holder.callBack = {
            //点的是不是同一个
            if (it!=lastSelectedPosition){
                callBack?.let { call ->
                    call(it,lastSelectedPosition)
                    //记录当前被选中的索引
                    lastSelectedPosition = it
                }
            }
        }
        return holder
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.bind(typeList[position],position)
        if (position == lastSelectedPosition){
            holder.changeSelectedStatus(true)
        }else{
            holder.changeSelectedStatus(false)
        }
    }

    override fun getItemCount(): Int {
        return typeList.size
    }

}

关于搜索栏的详细实现分析见下章

未完待续
项目完整代码

上一篇下一篇

猜你喜欢

热点阅读