Android基础篇-自定义组件

当有的设计布局使用的地方比较多,还涉及到需要变化视图数据内容,现有的组件无法满足设计需求的时候,常常需要自定义组件。本文介绍在Android日常开发中如何自定义组件,从使用原生代码到使用databinging绑定页面数据来定义自定义的View。

1.使用原生方法自定义组件

1.1 自定义属性

res/values文件中创建属性资源文件attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="AppComponent">
        <attr name="customSrc" format="integer" />
        <attr name="customText" format="string" />
    </declare-styleable>
</resources>

1.2 定义item布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />

    <TextView
        android:id="@+id/icon_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="10dp" />
        
</LinearLayout>

1.3.绑定属性到视图

提供java和kotlin的两种写法
java写法:

package com.tosmart.launcher.myfile.component;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.Nullable;

import com.tosmart.launcher.myfile.R;

public class AppComponent extends LinearLayout {
    private ImageView mIcon;
    private TextView mTitle;

    public AppComponent(Context context) {
        super(context);
    }

    public AppComponent(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView(attrs);
    }

    public AppComponent(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(attrs);
    }

    private void initView(AttributeSet attributeSet) {
        LayoutInflater.from(getContext()).inflate(R.layout.item_layout, this);
        TypedArray array = getContext().obtainStyledAttributes(attributeSet, R.styleable.AppComponent);

        mIcon = findViewById(R.id.icon);
        mIcon.setImageDrawable(array.getDrawable(R.styleable.AppComponent_customSrc));

        mTitle = findViewById(R.id.icon_title);
        mTitle.setText(array.getText(R.styleable.AppComponent_customText));

        array.recycle();
    }
}

kotlin写法:

package com.tosmart.launcher.myfile.component

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.tosmart.launcher.myfile.R

class AppComponent (context: Context,attrs: AttributeSet ?):
    LinearLayout(context,attrs){
    private val customSrc : ImageView
    private val customText : TextView

    init {
        LayoutInflater.from(getContext()).inflate(R.layout.item_layout, this)
        val array = context.obtainStyledAttributes(attrs, R.styleable.AppComponent)

        customSrc = findViewById(R.id.icon)
        customSrc.setImageDrawable(array.getDrawable(R.styleable.AppComponent_customSrc))

        customText = findViewById(R.id.icon_title)
        customText.text = array.getText(R.styleable.AppComponent_customText)

        array.recycle()

    }


}

可以看到两种写法的不同,本自定义布局都继承LinearLayout并使用了构造函数,而kotlin构造函数中有两个参数,事实上布局中有4种构造方法,实现继承式实现的自定义布局必须要使用至少2个参数的构造方法,也就是上面用的这种。

  1. Context context:这是 Android 应用程序环境的上下文对象。提供访问应用程序资源和类的方法。
  2. AttributeSet attrs:这是一个 AttributeSet 对象,用于传递 XML 布局文件中定义的属性集合。在创建自定义 View 时,系统会将 XML 文件中定义的属性转化为 AttributeSet 对象传递给构造函数。这样可以在构造函数中获取 XML 中定义的属性值。
  3. int defStyleAttr:这是指向一个默认风格资源的引用,用于应用到此组件上。当 XML 布局文件中没有明确指定样式,则会使用这个默认风格资源。
  4. int defStyleRes:功能同defStyleAttr,只是引用风格资源的类型不一样。
    ![PDWKRHS%KR$F]}@_DJ7M028.png](https://img-blog.csdnimg.cn/img_convert/4d7354f29b956e0c4ffd2c433e5d63e5.png)

1.4.主布局引入自定义组件布局

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.example.spotless.AppComponent
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:customSrc="@drawable/home"
        app:customText="Your Custom Text" />

</LinearLayout>

2.使用databing

Data Binding 是一种用于在 Android 应用程序中实现声明式数据绑定的库。它允许您将 UI 组件与应用程序的数据模型直接绑定,从而简化了 UI 的更新和管理过程。

为什么要使用Databinding,以下有几点对比可以看出Databinding的优势所在。

对比属性原始方式** **databinding
查找速度使用findViewById进行深度优先查找,需要消耗一定时间,对于深层复杂布局性能低Databinding在编译时生成绑定类,将视图和数据模型进行静态绑定。运行时无需重复查找视图,速度更快。
数据更新需手动查找视图后设置数据设置数据绑定表达式自动绑定数据到视图,并且支持双向绑定
代码简洁性存在大量样板代码查找和设置视图布局文件中设置绑定表达式即可
类型安全性手动设置数据容易运行时异常提供类型安全的绑定表达式,可以在编译时就能检测错误,减少运行出错

自定义组件类中使用databing绑定视图,这里使用的是单向的绑定,对于双向绑定后面的博客再详细补充其具体使用。

package com.tosmart.launcher.myfile
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.databinding.DataBindingUtil
import com.tosmart.launcher.myfile.databinding.ItemLayoutBinding

class BindAppComponent(context: Context, attrs: AttributeSet?) :
    LinearLayout(context, attrs) {
    private val customSrc: ImageView
    private val customText: TextView

    init {
        val binding: ItemLayoutBinding =
            DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.item_layout, this, true)
        val ta = context.obtainStyledAttributes(attrs, R.styleable.AppComponent)

        customSrc = binding.icon
        customSrc.setImageDrawable(ta.getDrawable(R.styleable.AppComponent_customSrc))

        customText = binding.iconTitle
        customText.text = ta.getText(R.styleable.AppComponent_customText)

        ta.recycle()

    }
}


对于单向绑定,自定义的布局和引用方式和原始的一样,延续使用之前的属性配置文件。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.tosmart.launcher.myfile.BindAppComponent
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:customSrc="@drawable/music"
        app:customText="Music"
        android:layout_centerInParent="true"/>

</RelativeLayout>

其实还可以通过组合的方式实现自定义组件,相比于继承方式会灵活一点,但是不能直接通过类名使用组件,感兴趣的话可以了解一下。
以上自定义实现的效果如下,主要实现可以设置不同的图标和标题的布局。
image.png

自定义绑定属性

可以通过注解@BindingAdapter创建自定义的绑定属性,可以设置一些绑定属性是全局可用的,就不再需要通过属性配置资源文件中设置属性。

public class CustomBindingAdapters {

    @JvmStatic
    @BindingAdapter("isCollapseGroup") //使用注解定义一个自定义属性
    public static void setIsCollapseGroup(View view, boolean isCollapse) {
        if (isCollapse) {
            view.setVisibility(View.GONE);
        } else {
            view.setVisibility(View.VISIBLE);
        }
    }
}

在视图中通过自定义数据绑定数据:注意的是使用该注解创建的属性只能用于绑定数据,也就是等号右边一定是"@{viewModel.isGroupCollapsed}"类似的绑定表达式,不能是固定值。

<LinearLayout
    android:id="@+id/myLinearLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:isCollapseGroup="@{viewModel.isGroupCollapsed}">

</LinearLayout>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值