欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

《第一行代码》阅读笔记(八)——自定义控件

程序员文章站 2022-03-12 19:47:03
在开发中我们经常会需要一些重复使用的控件或者控件组合,就比如安卓系统的三大金刚键,或者一些APP的顶部栏。如果我们为每个Activity都编写,不仅费时费力,而且维护性比较低。学过Java的朋友都应该知道,遇到这种情况,我们都是封装和抽取,然后再调用,所以安卓也不例外,下面就让我们用一个顶部栏作为例子展开学习吧。引入布局首先先设计共用布局。因为书上面是用了背景图片的,而笔者没有。所以做了一些调整。结果如下源代码为...

在开发中我们经常会需要一些重复使用的控件或者控件组合,就比如安卓系统的三大金刚键,或者一些APP的顶部栏。如果我们为每个Activity都编写,不仅费时费力,而且维护性比较低。学过Java的朋友都应该知道,遇到这种情况,我们都是封装和抽取,然后再调用,所以安卓也不例外,下面就让我们用一个顶部栏作为例子展开学习吧。

引入布局

首先先设计共用布局。因为书上面是用了背景图片的,而笔者没有。所以做了一些调整。结果如下

《第一行代码》阅读笔记(八)——自定义控件

源代码为

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

    <Button
        android:id="@+id/title_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top"

        android:text="Back"
        android:textColor="#fff" />

    <TextView
        android:id="@+id/title_text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="top"
        android:layout_weight="1"
        android:layout_marginTop="3dp"
        android:background="#504E4E"
        android:gravity="center"
        android:text="Title Text"
        android:textColor="#fff"
        android:textSize="27sp" />

    <Button
        android:id="@+id/title_edit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Edit"
        android:textColor="#fff" />


</LinearLayout>

这里笔者有一个疑问,就是书中对所有控件都加了android:layout_gravity=“center”,但是这显然不对啊,添加后控件会变到屏幕中间,所有笔者将其改成了top。而且为了让三个控件可以对齐,笔者取消了按钮控件的margin,同时给textview加了一个margin。并且把textview的底色调成灰色了。

这里
android:background是添加背景的,可以是图片,也可以是颜色。
android:layout_margin是可以指定控件在上下左右方向上偏移的距离,同时可以android:layout_marginTop、android:layout_marginLeft等

在以后还会遇到一个和边界有关的属性,就是padding。

margin:
     需要在border外侧添加空白时;
     空白处不需要背景(色)时;
    上下相连的两个盒子之间的空白,需要相互抵消时。
padding:
    需要在border内测添加空白时;
    空白处需要背景(色)时;
    上下相连的两个盒子之间的空白,希望等于两者之和时。
————————————————
版权声明:本文为CSDN博主「你与温柔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41155191/article/details/82798631

《第一行代码》阅读笔记(八)——自定义控件

因为我们是在外部编写的布局文件,所以在使用时只需要调用即可,这里很好理解,就像封装的函数的调用。这里有两步,第一步引入,只需要一行代码

<include layout="@layout/title" />

第二步,因为我们想添加顶部栏,所以需要隐藏之前系统自动生成的

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.hide();
        }
    }

可以通过getSupportActionBar()来获得的ActionBar的实例,然后通过hide()来进行隐藏。

如何全局隐藏ActionBar

其实ActionBar也是一个非常实用控件,如果想了解的话可以看看下面的这篇文章
https://www.cnblogs.com/mjsn/p/6150824.html

自定义布局

引入布局可以解决很多问题,但是我们不能再为每个引入的布局绑定事件,这样还是不满足我们的需求。所有我们需要再封装,把事件也封装进入。

先创建一个类TitleLayout继承LinearLayout,并重写构造函数

public class TitleLayout extends LinearLayout {

    public TitleLayout(Context context, AttributeSet attrs){
        super(context,attrs);
        LayoutInflater.from(context).inflate(R.layout.title, this);
        
    }
}

——第一行代码
首先我们重写了LinearLayout中带有两个参数的构造函数,在布局中引入TitleLayout控件就会调用这个构造函数。然后在构造函数中需要对标题栏布局进行动态加载,这就要借助LayoutInflater来实现了。通过LayoutInflater的from ()方法可以构建出一个LayoutInflater对象,然后调用inflate()方法就可以动态加载一个布局文件,inflate( )方法接收两个参数,第一个参数是要加载的布局文件的id,这里我们传入R.layout.title, 第二个参数是给加载好的布局再添加一个父布局,这里我们想要指定为TitleLayout,于是直接传人this。

在主活动的布局文件中插入,和普通控件一样,只不过需要使用全类名。

<com.firstcode.uicustomviews.TitleLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />

修改TitleLayout类,添加响应事件。

public class TitleLayout extends LinearLayout {

    public TitleLayout(Context context, AttributeSet attrs){
        super(context,attrs);
        LayoutInflater.from(context).inflate(R.layout.title, this);
        Button titleBack = (Button) findViewById(R.id.title_back);
        Button titleEdit = (Button) findViewById(R.id.title_edit);
        titleBack.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                ((Activity)getContext()).finish();
            }
        });
        titleEdit.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getContext(),"You clicked Edit button",Toast.LENGTH_SHORT).show();
            }
        });
    }


}

这里我看书上使用了一个((Activity)getContext()).finish();不知道什么意思,书上也没解释,我推测是结束当前的环境,不是直接退出程序。但是这个demo只有一个主界面,所有我改成finish()并没有影响。

自定义控件

第一步:声明一个类,并继承View

public class RoundProgressBar extends View {

继承View是比较底层的控件了,其实也可以继承已有的控件,例如之前的LinearLayout或者Button。

第二步:构造函数

public RoundProgressBar(Context context) {
		this(context, null);
	}

	public RoundProgressBar(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public RoundProgressBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
     }

其实这种方法并不规范, 但是这样编写有个方便的地方,就是不管用哪种方法构造,都会到最后一个函数。

第三步:添加属性
在编写构造函数之前,还需要给这个控件添加一些私有属性。在Value中添加一个attr.xml文件

<declare-styleable name="circleProgressBar">
        <attr name="circleColor" format="color" />
        <attr name="circleProgressColor" format="color" />
        <attr name="circleWidth" format="dimension" />
        <attr name="textColor" format="color" />
        <attr name="textSize" format="dimension" />
        <attr name="max" format="integer" />
        <attr name="textIsDisplayable" format="boolean" />
        <attr name="newCircleMargin" format="dimension" />
        <attr name="style">
            <enum name="STROKE" value="0" />
            <enum name="FILL" value="1" />
        </attr>
    </declare-styleable>

声明一个这样的内容,declare-styleable name标签就是这个控件的名字,然后attr name标签就是属性的名字。

第四步:编写构造函数

  1. 首先声明一个paint类paint = new Paint();
  2. 实例化TypedArrayTypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.circleProgressBar);
  3. 给每个属性赋值roundColor = mTypedArray.getColor( R.styleable.circleProgressBar_circleColor, Color.RED);
  4. 回收 mTypedArray.recycle();

第五步:重写onDraw

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

其中canvas就是一个画布工具,可以对图形做出很多操作。在onDraw中需要对每个属性进行赋值,同时还可以进行一些逻辑的操作。

安卓中Paint类和Canvas类的方法汇总

第六步:使用自定义控件

<com.core.common.widget.RoundProgressBar
                    android:id="@+id/testlib_in_totalProgress"
                    android:layout_width="35dp"
                    android:layout_height="35dp"
                    android:layout_centerHorizontal="true"
                    android:layout_marginRight="25dp"
                    android:background="@drawable/ic_totalprogress"
                    android:clickable="true"
                    app:circleColor="#d8d8d8"
                    app:circleProgressColor="@color/blue"
                    app:circleWidth="2dp" />

其中android就是系统自带的属性,app就是自定义的属性。注意使用全类名!

本文地址:https://blog.csdn.net/SafeVidulInfo/article/details/107525628