一、前言

<font face = 黑体>对于 Android 开发者来说,原生控件往往无奈满足要求,须要开发者自定义一些控件,因而,须要去理解自定义 view 的实现原理。这样即便碰到须要自定义控件的时候,也能够熟能生巧。

1.1、自定义控件的长处

  1. <font face = 黑体>多个中央复用;
  2. <font face = 黑体>对立的逻辑款式;
  3. <font face = 黑体>精简代码。

1.2、自定义控件的形式

  1. <font face = 黑体>继承已有控件;
  2. <font face = 黑体>继承布局;
  3. <font face = 黑体>继承根 View。

二、继承已有控件

<font face = 黑体>咱们要自定义的控件和已有控件十分类似,那么咱们就能够在已有控件的根底上,继承组件,减少属性,来实现本人想要的成果。

<font face = 黑体>案例一: 自定义提交按钮(按钮点击变色),成果如下所示:


<font face = 黑体>具体实现步骤如下所示:

  1. <font face = 黑体>新建资源文件 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. <font face = 黑体>新建 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. <font face = 黑体>应用

    <?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>

三、继承布局

<font face = 黑体>当单个控件不好实现咱们须要的成果的时候,即须要有多个已有控件来实现咱们的成果,这时候咱们能够利用继承布局的形式来实现,能够继承线性布局或者绝对布局等。

<font face = 黑体>案例二: 自定义 Item,成果如下所示:



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

<font face = 黑体>咱们能够看到第一个 Item 上三个因素都是全的,右边的内容、左边的内容和左边的箭头,然而第二个和第三个则是只有箭头和只有内容,这个是依据咱们写的办法设置的。

<font face = 黑体>具体实现步骤如下所示:

    1. <font face = 黑体>新建单个 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>
    1. <font face = 黑体>新建 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);        }    }}```
    1. <font face = 黑体>应用

      <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 的内容

    <font face = 黑体>自定义属性个别有以下三个步骤:

    1. <font face = 黑体>在 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. <font face = 黑体>在自定义的控件中绑定属性;

      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);        }    }}```
    1. <font face = 黑体>在 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

    <font face = 黑体>当咱们须要实现一些非凡款式并且继承已有控件和布局都无奈实现,那么咱们就须要通过继承根 View 的形式本人去画了,即自定义 View。

    <font face = 黑体>案例三: 自定义 View,实现折线图,成果如下所示:


    <font face = 黑体>具体代码如下所示:

    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);    }}

    <font face = 黑体>这里的自定义 View 案例很简略,然而 Android 外面的自定义 View 所波及的常识还是很多的,也算是 Android 进阶的一个内容,然而这篇博客并不是讲自定义 View 的,所以这里就不开展讲了。

    五、小结

    <font face = 黑体>这篇博客次要讲了 Android 自定义控件的三种形式,每一种形式都给出了一个简略的案例来解说。