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>

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,您想了解有关自定义几何体的内容,我很乐意为您解答。首先,让我们了解一下什么是几何体。 在Cesium中,几何体是由一些点、线和三角形组成的图形。几何体可以在地球上显示各种形状的物体,如建筑、飞机、汽车等。Cesium提供了一些内置的几何体,如BoxGeometry、CylinderGeometry、SphereGeometry等,但是有时候我们需要展示一些特殊形状的物体,这时候就需要自定义几何体了。 下面是一个简单的自定义几何体的例子: ```javascript var geometry = new Cesium.Geometry({ attributes: { position: new Cesium.GeometryAttribute({ componentDatatype: Cesium.ComponentDatatype.DOUBLE, componentsPerAttribute: 3, values: new Float64Array([ 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0 ]) }) }, indices: new Uint16Array([ 0, 1, 2, 0, 2, 3 ]), primitiveType: Cesium.PrimitiveType.TRIANGLES }); ``` 这个例子中,我们创建了一个由四个点组成的矩形,并用这些点的索引定义了两个三角形。这个几何体可以用来在地球上显示一个矩形。 接下来,让我们逐步了解这个例子中的代码。首先是Cesium.GeometryAttribute。 Cesium.GeometryAttribute是几何体属性的容器。在这个例子中,我们定义了一个名为position的属性,它有三个分量:x、y和z。这个属性使用的数据类型是Cesium.ComponentDatatype.DOUBLE,表示每个分量有一个双精度浮点数。componentsPerAttribute表示每个属性有几个分量。在这个例子中,每个属性都有三个分量。最后,我们用一个Float64Array数组来定义这个属性的值。 接下来是indices,它定义了几何体使用哪些点来组成三角形。在这个例子中,我们定义了两个三角形,每个三角形使用三个顶点。在indices数组中,我们用顶点在attributes数组中的索引来定义每个三角形。 最后,我们定义了几何体的primitiveType,它表示几何体的类型。在这个例子中,我们使用的是三角形类型,所以primitiveType为Cesium.PrimitiveType.TRIANGLES。 希望这个例子可以帮助您更好地理解自定义几何体的实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值