在开发中我们经常会需要一些重复使用的控件或者控件组合,就比如安卓系统的三大金刚键,或者一些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也是一个非常实用控件,如果想了解的话可以看看下面的这篇文章
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标签就是属性的名字。
第四步:编写构造函数
- 首先声明一个paint类
paint = new Paint();
- 实例化TypedArray
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.circleProgressBar);
- 给每个属性赋值
roundColor = mTypedArray.getColor( R.styleable.circleProgressBar_circleColor, Color.RED);
- 回收
mTypedArray.recycle();
第五步:重写onDraw
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
其中canvas就是一个画布工具,可以对图形做出很多操作。在onDraw中需要对每个属性进行赋值,同时还可以进行一些逻辑的操作。
第六步:使用自定义控件
<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就是自定义的属性。注意使用全类名!