Android 自定义控件

一、前言

对于 Android 开发者来说,原生控件往往无法满足要求,需要开发者自定义一些控件,因此,需要去了解自定义 view 的实现原理。这样即使碰到需要自定义控件的时候,也可以游刃有余。

1.1、自定义控件的优点

  1. 多个地方复用;
  2. 统一的逻辑样式;
  3. 精简代码。

1.2、自定义控件的方式

  1. 继承已有控件;
  2. 继承布局;
  3. 继承根 View。

二、继承已有控件

我们要自定义的控件和已有控件非常相似,那么我们就可以在已有控件的基础上,继承组件,增加属性,来实现自己想要的效果。

案例一: 自定义提交按钮(按钮点击变色),效果如下所示:

在这里插入图片描述
具体实现步骤如下所示:

  1. 新建资源文件 R.drawable.btn_submit
    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:drawable="@mipmap/btn_submit_p" android:state_pressed="true"/>
        <item android:drawable="@mipmap/btn_submit_n" />
    </selector>
    
  2. 新建 SubmitButton 类继承 Button 类
    public class SubmitButton extends Button {
    
        public SubmitButton(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        private void init() {
            setBackgroundResource(R.drawable.btn_submit);
            setText("自定义提交按钮");
            setTextSize(18);
            setTextColor(0xffffffff);  // 白色
        }
    }
    
  3. 使用
    <?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="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/btn_normal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="45dp"
            android:layout_marginTop="30dp"
            android:layout_marginEnd="45dp"
            android:text="普通Button"
            android:textSize="15sp" />
    
        <com.zjgsu.customviewdemo.SubmitButton
            android:id="@+id/btn_custom"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="45dp"
            android:layout_marginTop="30dp"
            android:layout_marginEnd="45dp" />
    
    </LinearLayout>
    

三、继承布局

当单个控件不好实现我们需要的效果的时候,即需要有多个已有控件来实现我们的效果,这时候我们可以利用继承布局的方式来实现,可以继承线性布局或者相对布局等。

案例二: 自定义 Item,效果如下所示:

自定义Item

3.1、通过调用方法设置 Item 的内容

我们可以看到第一个 Item 上三个要素都是全的,左边的内容、右边的内容和右边的箭头,但是第二个和第三个则是只有箭头和只有内容,这个是根据我们写的方法设置的。

具体实现步骤如下所示:

  1. 新建单个 Item 的布局 item_layout
        <?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"
           tools:context=".MainActivity">
       
           <View
               android:layout_width="wrap_content"
               android:layout_height="1px"
               android:background="#898888" />
       
           <RelativeLayout
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:background="@drawable/selector_item"
               android:clickable="true"
               android:focusable="true"
               android:padding="10dp">
       
               <TextView
                   android:id="@+id/tv_left"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:layout_alignParentStart="true"
                   android:layout_centerVertical="true"
                   android:textColor="#333333"
                   android:textSize="15sp" />
       
               <LinearLayout
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:layout_alignParentEnd="true"
                   android:layout_centerVertical="true"
                   android:gravity="center_vertical">
       
                   <TextView
                       android:id="@+id/tv_right"
                       android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:layout_marginEnd="5dp"
                       android:textColor="#646464"
                       android:textSize="14sp" />
       
                   <ImageView
                       android:id="@+id/iv_arrow"
                       android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:src="@mipmap/arrow" />
       
               </LinearLayout>
       
           </RelativeLayout>
       
           <View
               android:layout_width="wrap_content"
               android:layout_height="1px"
               android:background="#898888" />
       
       </LinearLayout>
        ```
    
  2. 新建 ItemView 继承自 RelativeLayout,并写一个设置内容与是否显示箭头的方法
    public class ItemView extends RelativeLayout {
    
        private TextView tvLeft;
        private TextView tvRight;
        private ImageView ivArrow;
    
        public ItemView(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            LayoutInflater.from(context).inflate(R.layout.item_layout, this);
    
            tvLeft = findViewById(R.id.tv_left);
            tvRight = findViewById(R.id.tv_right);
            ivArrow = findViewById(R.id.iv_arrow);
        }
    
        /**
         * 设置内容
         *
         * @param tvLeftStr   左边内容
         * @param tvRightStr  右边内容
         * @param isShowArrow 是否显示箭头
         */
        public void setView(String tvLeftStr, String tvRightStr, boolean isShowArrow) {
            if (tvLeftStr != null) {
                tvLeft.setText(tvLeftStr);
            }
    
            if (tvRightStr != null) {
                tvRight.setText(tvRightStr);
            }
    
            if (isShowArrow) {
                ivArrow.setVisibility(VISIBLE);
            } else {
                ivArrow.setVisibility(GONE);
            }
        }
    }
    
    
  3. 使用
    <com.zjgsu.customviewdemo.ItemView
        android:id="@+id/item_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp" />
    
     item1 = findViewById(R.id.item_1);
     item2 = findViewById(R.id.item_2);
     item3 = findViewById(R.id.item_3);
    
     item1.setView("姓名", "Bob", true);
     item2.setView("年龄", null, true);
     item3.setView("学校", "中国人民大学", false);
    

3.2、通过自定义属性设置 Item 的内容

自定义属性一般有以下三个步骤:

  1. 在 attrs.xml 中添加属性;
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="ItemView">
            <attr name="leftText" format="string"/>
            <attr name="rightText" format="string"/>
            <attr name="showArrow" format="boolean"/>
        </declare-styleable>
    </resources>
    
  2. 在自定义的控件中绑定属性;
    public class ItemView extends RelativeLayout {
    
        private TextView tvLeft;
        private TextView tvRight;
        private ImageView ivArrow;
    
        public ItemView(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            LayoutInflater.from(context).inflate(R.layout.item_layout, this);
    
            tvLeft = findViewById(R.id.tv_left);
            tvRight = findViewById(R.id.tv_right);
            ivArrow = findViewById(R.id.iv_arrow);
    
    		// 获取自定义属性
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ItemView);
    
            String leftText = a.getString(R.styleable.ItemView_leftText);
            String rightText = a.getString(R.styleable.ItemView_rightText);
            boolean showArrow = a.getBoolean(R.styleable.ItemView_showArrow, true);
    
            setView(leftText, rightText, showArrow);
    
            a.recycle();
        }
    
        /**
         * 设置内容
         *
         * @param tvLeftStr   左边内容
         * @param tvRightStr  右边内容
         * @param isShowArrow 是否显示箭头
         */
        public void setView(String tvLeftStr, String tvRightStr, boolean isShowArrow) {
            if (tvLeftStr != null) {
                tvLeft.setText(tvLeftStr);
            }
    
            if (tvRightStr != null) {
                tvRight.setText(tvRightStr);
            }
    
            if (isShowArrow) {
                ivArrow.setVisibility(VISIBLE);
            } else {
                ivArrow.setVisibility(GONE);
            }
        }
    }
    
  3. 在 xml 中使用。
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:ItemView="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".ItemActivity">
    
        <com.zjgsu.customviewdemo.ItemView
            android:id="@+id/item_1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            ItemView:leftText="姓名"
            ItemView:rightText="Bob1"
            ItemView:showArrow="true" />
    
        <com.zjgsu.customviewdemo.ItemView
            android:id="@+id/item_2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            ItemView:leftText="年龄" />
    
        <com.zjgsu.customviewdemo.ItemView
            android:id="@+id/item_3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            ItemView:leftText="学校"
            ItemView:rightText="中国人民大学"
            ItemView:showArrow="false" />
    
    </LinearLayout>
    

四、继承根 View

当我们需要实现一些特殊样式并且继承已有控件和布局都无法实现,那么我们就需要通过继承根 View 的方式自己去画了,即自定义 View。

案例三: 自定义 View,实现折线图,效果如下所示:

折线图
具体代码如下所示:

public class LineView extends View {

    Paint linePaint;

    public LineView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        initPaint();
    }

	// 初始化画笔
    private void initPaint() {
        linePaint = new Paint();
        linePaint.setColor(Color.BLUE);
        linePaint.setStrokeWidth(3);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);


        // 原点
        int xPoint = 200;
        int yPoint = 400;

        // y 轴
        canvas.drawLine(xPoint, 400 - 300, xPoint, yPoint, linePaint);
        // x 轴
        canvas.drawLine(xPoint, yPoint, xPoint + 500, yPoint, linePaint);

        // 画线  上一条线的终点就是要画的线的起点
        canvas.drawLine(xPoint, yPoint, xPoint + 80, yPoint - 150, linePaint);
        canvas.drawLine(xPoint + 80, yPoint - 150, xPoint + 160, yPoint - 100, linePaint);
        canvas.drawLine(xPoint + 160, yPoint - 100, xPoint + 240, yPoint - 200, linePaint);
        canvas.drawLine(xPoint + 240, yPoint - 200, xPoint + 400, yPoint - 100, linePaint);
    }
}

这里的自定义 View 案例很简单,但是 Android 里面的自定义 View 所涉及的知识还是很多的,也算是 Android 进阶的一个内容,但是这篇博客并不是讲自定义 View 的,所以这里就不展开讲了。

五、小结

这篇博客主要讲了 Android 自定义控件的三种方式,每一种方式都给出了一个简单的案例来讲解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值