zoukankan      html  css  js  c++  java
  • 下导航栏3-封装实例讲解

    效果图:

    1:先分析里面要的组合方式,是上面一个图片,下面一个文本,我们可以用自定义LInearLayout来实现是比较容易的:

    自定义的方法看上两篇前,

    我们需要的属性,但是系统又没有提供的,或者系统没能够满足我们的就可以自己去添加,添加的方法看上两篇前的,

    这个是对每一个按钮的自定义的属性:当点下一个按钮是上一个按钮的选中状态就需要恢复为未选中状态,在这我是用两

    组不同的照片来进行交换实现的,就是正常时的图片normalIcon,和选中时的图片selectedIcon,同样文本(title)的颜色的变化也有两种,

    misselected是记录按钮是否被点击时的状态,

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    <declare-styleable name="Bottom_bar">
    <attr name="normalIcon" format="reference"/>
    <attr name="selectedIcon" format="reference"/>
    <attr name="title" format="string"/>
    <attr name="hightlighttitle" format="string"/>
    <attr name="color" format="color"/>
    <attr name="hightlightcolor" format="color"/>
    <attr name="misselected" format="boolean"/>
    </declare-styleable>
    <declare-styleable name="myViewGroup">
    <attr name="number" format="integer"/>
    </declare-styleable>
    </resources>

    2:解析属性以得到自定义的属性,在解析属性之后就可以将属性取出应用了,我们需要xml的配置,

    也需要代码的配置,所以在两个构造方法中都需要初始化:方法看上两篇

    constructor(context: Context):super(context){
    Log.v("lfl","代码")
    initView() 初始化图片与文本
    }
    constructor(context: Context,attributeSet: AttributeSet?):super(context,attributeSet){

    //获取自定义的属性
    var typedArray = context.obtainStyledAttributes(attributeSet,
    R.styleable.Bottom_bar)
    //获取单个的属性
    normalIconID = typedArray.getResourceId(R.styleable.Bottom_bar_normalIcon,
    R.drawable.home)
    selectedIconID = typedArray.getResourceId(R.styleable.Bottom_bar_selectedIcon,
    R.drawable.home_selected)
    title = typedArray.getString(R.styleable.Bottom_bar_title).toString()
    Log.v("lfl","xml")
    //初始化视图
    initView()
    //要改变的值
    misselected = typedArray.getBoolean(R.styleable.Bottom_bar_misselected,false)
    textColor = typedArray.getColor(R.styleable.Bottom_bar_color,Color.BLACK)
    //释放
    typedArray.recycle()

    }

    3:将解析出来的各种属性用起来,初始化图片与文本

    用到的方法有:

    (1):转换像素:

    //获取手机的像素
    fun dpTopx(dp:Int):Int = dp*(context.resources.displayMetrics.density).toInt()

    (2):更新图片与文本 ,我们需要在多个地方更新,所以将其抽出

    private fun UpdateUI(){
        Log.v("lfl","******************************")
    if (misselected){
    //切换成没有被选中的状态
    image?.setImageResource(selectedIconID)
    textView?.setTextColor(Color.RED)
    }else{
    //选中状态
    image?.setImageResource(normalIconID)
    textView?.setTextColor(Color.BLACK)
    }
    }
    //初始化
    fun initView(){
    orientation = VERTICAL 设置内容的填充方向
    gravity = Gravity.CENTER_VERTICAL
    //添加图片
    image = ImageView(context).apply {
    var layoutParams = LinearLayout.LayoutParams(dpTopx(32),dpTopx(32)) 设置图片的大小,
    addView(this,layoutParams)
    }

    //添加文字
    textView = TextView(context).apply {
    //setText(title)
    //text = title
    }.also {
    val layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT,
    LayoutParams.WRAP_CONTENT)
    addView(it,layoutParams)
    }

    UpdateUI()
    }

    4:改变按钮的点击状态,当我们点击一个按钮时,若该按钮是点击状态则不响应,若该按钮没有被点击时

    则将该按钮设置为被点击状态,并且更新图片与文本,对于一个按钮而言只需要这样,点击事件可以在点下去时

    实现,也可以在手离开屏幕那一刻时实现,这是在点下去时实现的,并且这个事件已经被消费了,所以返回true

    override fun onTouchEvent(event: MotionEvent?): Boolean {
    if (event?.action == MotionEvent.ACTION_DOWN){
    if (!misselected){
    //改变状态
    misselected = !misselected
    selectCallback?.let {
    it(index) //调用高阶函数,点下去时改变状态的同时要将上一个被点击
    的按钮还原
    }
    }

    }
    //事件已经被消费了就返回true
    return true
    }

    只要状态改变,那么就更新数据,因此可以重写misselected的set方法,

    //记录按钮被点击的状态
    var misselected:Boolean = false
    set(value) {
    field = value
    Log.v("lfl","misselected")
    UpdateUI()
    }

    一个按钮实现,接下来就实现多个按钮,多个按钮也可以通过自定义linearLayout来实现,一个按钮里面初始化的是图片和文本,
    多个按钮的实现,就是将多个按钮放在一个容器里面,初始化按钮即可
    (1)xml里面的应用:
    <com.example.myapplication.myViewGroup
    android:id="@+id/myviewGroup"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:number="3" 有多少个按钮,是个自定义属性
    />
    (2)初始化按钮,代码,xml皆要初始化
     constructor(context: Context):super(context){
    //初始化
    initView()
    }
    constructor(context: Context,attributeSet: AttributeSet):super(context,attributeSet){
    //解析
    val typedArray = context.obtainStyledAttributes(attributeSet,
    R.styleable.myViewGroup)
    number = typedArray.getInteger(R.styleable.myViewGroup_number,4)
    typedArray.recycle()
    initView()
    }

    //初始化每一个视图
    private fun initView() {
    orientation = HORIZONTAL
    gravity = Gravity.CENTER_VERTICAL
    for (i in 0 until number){
    Bottom_bar(context).also {
    val lp = LinearLayout.LayoutParams(
    ViewGroup.LayoutParams.WRAP_CONTENT,
    ViewGroup.LayoutParams.WRAP_CONTENT
    ).apply {
    weight = 1f
    }
    addView(it,lp)
    Bottom_bar_array.add(it)

    //为每一个按钮添加监听事件
    it.selectCallback = {index->
    //还原之前选中的栏目的状态
    Bottom_bar_array[current].misselected = false
    //保存索引
    current = index

    }
    }
    }
    }
    (3):通过一个模型将数据从外部传进来
    a:需要先定义一个模型,模型里面放的全是属性,模型就作为传输数据的中间人,外部将数据给模型,模型在将数据传给对象
    data class Bottom_bar_model (
    var normalIconID:Int,
    var selectedIconID:Int,
    var title:String,
    var misselected:Boolean
    )

    b:将通过模型的传输进来的数据给到每一个按钮
    //更新每一个视图
    private fun initBottom_bar(){
    for ((i,iteam) in Bottom_bar_array.withIndex()){
    iteam.index = i
    iteam.title = model[i].title
    iteam.normalIconID = model[i].normalIconID
    iteam.selectedIconID = model[i].selectedIconID
    iteam.misselected = model[i].misselected
    }


    在外部调用时就只需要将数据给模型就好了
    myviewGroup.model = arrayOf(
    Bottom_bar_model(
    R.drawable.home,
    R.drawable.home_selected,
    "主页",
    true
    ),
    Bottom_bar_model(
    R.drawable.home,
    R.drawable.home_selected,
    "主页",
    false
    ),
    Bottom_bar_model(
    R.drawable.home,
    R.drawable.home_selected,
    "主页",
    false
    )
    )


    GitHub连接:https://github.com/luofangli/Bottom_bar


  • 相关阅读:
    python系列之
    php正则:匹配(),{},[]小括号,大括号,中括号里面的内容
    TCP、UDP以及HTTP的简单讲解
    Python Async/Await入门指南
    asyncio并发编程
    python中socket模块详解
    TCP和UDP的优缺点及区别
    网络通信 --> IO多路复用之select、poll、epoll详解
    异步IO和协程
    Python--多任务(多进程,多线程,协程)
  • 原文地址:https://www.cnblogs.com/luofangli/p/13881492.html
Copyright © 2011-2022 走看看