这里主要用到两种自定义布局的方法,第一是使用inflate加载布局文件,也是最简单的,但是前提是要继承自ViewGroup,第二种是自定义View的方式是重写View的onMeasure(),onLayout(),onDraw方法,这种要稍微复杂一点。下面总结一下两种自定义View。
一、使用布局文件xml文件来自定义布局
首先定义定义一个View,继承自RelativeLayout,当然也可以是LieanerLayout。然后实现其三个构造方法,使加载布局文件的方式会调用有两个参数的构造方法。
这个举个例子,自定义一个标题栏,由于Android原生的toolbar或者actionBar有时候不能满足业务需求,那么就需要自定义View。来达到我们特定的需求。
TitleView.java代码如下
package com.pos.ui;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.pos.R;
public class TitleView extends RelativeLayout {
private TextView title;
private RelativeLayout iconBack;
private RelativeLayout iconMore;
private ImageView ivMore;
public void setBackIconOnClickListener(OnClickListener clickListener) {
if(iconBack != null)
iconBack.setOnClickListener(clickListener);
}
public void setMoreIconOnClickListener(OnClickListener clickListener) {
if(iconMore != null)
iconMore.setOnClickListener(clickListener);
}
public void setTitle(String text) {
title.setText(text);
}
public void init(Context context) {
View.inflate(context, R.layout.item_title, this);
iconMore = (RelativeLayout)findViewById(R.id.icon_more);
ivMore = (ImageView)findViewById(R.id.img_more);
title = (TextView)findViewById(R.id.tv_title);
}
public TitleView(Context context) {
super(context);
init(context);
}
public TitleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public TitleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.posView);
String myTitle = a.getString(R.styleable.posView_myTitle);
boolean backIconIsVisible = a.getBoolean(R.styleable.posView_backIconIsVisible, true);
//Log.e("TitleView",backIconIsVisible+"");
if (!backIconIsVisible) {
ImageView img_back = (ImageView)findViewById(R.id.img_back);
img_back.setImageResource(R.drawable.btn_nomal);
} else {
iconBack = (RelativeLayout)findViewById(R.id.icon_back);
}
int iconMoreId = a.getResourceId(R.styleable.posView_rightIcon, R.drawable.btn_nomal);
ivMore.setImageResource(iconMoreId);
title.setText(myTitle);
a.recycle();
}
}
布局文件item_title.xml的代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dip"
android:background="@color/red">
<RelativeLayout
android:id="@+id/icon_back"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@drawable/bg_button"
>
<ImageView
android:id="@+id/img_back"
android:layout_marginLeft="10dp"
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/icon_back"
android:layout_centerVertical="true"
/>
</RelativeLayout>
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="标题"
android:textSize="17sp"
android:textColor="#fff" />
<RelativeLayout
android:id="@+id/icon_more"
android:layout_width="50dp"
android:layout_height="match_parent"
android:background="@drawable/bg_button"
android:layout_alignParentRight="true" >
<ImageView
android:id="@+id/img_more"
android:layout_width="35dp"
android:layout_height="30dp"
android:layout_centerInParent="true"
/>
</RelativeLayout>
</RelativeLayout>
最后的样子为:
标题栏主要由三个部分,左边的回退,中间的标题,右边的更多选项,默认不显示,也可以设置显示,比如:
自定义的标签:在value/attrs.xml中
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="posView">
<attr name="myTitle" format="string" />
<attr name="myIcon" format="reference" />
<attr name="bakIcon" format="reference" />
<attr name="rightIcon" format="reference" />
<attr name="backIconIsVisible" format="boolean"/>
</declare-styleable>
</resources>
基本上通过布局文件自定义View的代码都粘贴完了,是不是感觉很简单呢,代码确实不多。
通过View.inflate方法将item_title.xml文件加载到TitleView这个ViewGroup上。这样TitileView就会显示item_title里面的布局了,这么也忽略了onDraw方法绘制View的繁琐。
View.inflate(context, R.layout.item_title, this);
iconMore = (RelativeLayout)findViewById(R.id.icon_more);
ivMore = (ImageView)findViewById(R.id.img_more);
title = (TextView)findViewById(R.id.tv_title);
代码中的自定义属性标签,通过TypedArray来获取,这里不多说。
使用的时候,
<com.pos.ui.TitleView
android:id="@+id/author_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:myTitle = "标题字样"
>
</com.pos.ui.TitleView>
其中还定义了左右两个按钮的点击事件,可以在Activity中进行相应的调用。
TitleView titleView = (TitleView) findViewById(R.id.author_title);
titleView.setBackIconOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AuthorizeContactActivity.this.finish();
}
});
这样就完成一个自定义View了,需要注意的是,这个TitleView继承了ViewGoup,而不能继承View,因为ViewGoup内部处理wrap_content这个属性,View内部对wrap_content的处理与match_content是一样的,所以不能继承View。如果继承了View,就需要在onLayout方法中对wrap_content这个属性就行处理。
二、通过重写View的onMeasure,onLayout,onDraw等方法自定义View。
下面以一个TextView为例,来看看如何拓展TextView从而创建新的空间,比如想让一个TextView的北京更加丰富,给其多绘制几层背景。比如
如果要实现这个效果,用布局文件可能很不好做,但是如果重写onDraw方法就会好很多,方法如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
程序调用super.onDraw(canvas)方法来实现原生空间的功能,但是在调用super.onDraw(canvas)方法之前和之后,我们都可以实现自己的逻辑,分别在系统绘制文字前后,完成自己的操作,即如下显示。
@Override
protected void onDraw(Canvas canvas) {
//绘制文本内容前
super.onDraw(canvas);
//绘制文本内容后
}
思路就是这样,首先在构造方法中完成必要的对象的初始化工作,这里是初始化画笔。代码如下:
private void initView() {
mPaint1 = new Paint();
mPaint1.setColor(getResources().getColor(
android.R.color.holo_blue_light));
mPaint1.setStyle(Paint.Style.FILL);
mPaint2 = new Paint();
mPaint2.setColor(Color.YELLOW);
mPaint2.setStyle(Paint.Style.FILL);
}
最后的onDraw为:
@Override
protected void onDraw(Canvas canvas) {
// 绘制外层矩形
canvas.drawRect(
0,
0,
getMeasuredWidth(),
getMeasuredHeight(),
mPaint1);
// 绘制内层矩形
canvas.drawRect(
10,
10,
getMeasuredWidth() - 10,
getMeasuredHeight() - 10,
mPaint2);
canvas.save();
// 绘制文字前平移10像素
canvas.translate(10, 0);
// 父类完成的方法,即绘制文本
super.onDraw(canvas);
canvas.restore();
}
最后也贴出整个代码
package com.pos.ui;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.widget.TextView;
public class MyTextView extends TextView {
private Paint mPaint1, mPaint2;
public MyTextView(Context context) {
super(context);
initView();
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public MyTextView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
mPaint1 = new Paint();
mPaint1.setColor(getResources().getColor(
android.R.color.holo_blue_light));
mPaint1.setStyle(Paint.Style.FILL);
mPaint2 = new Paint();
mPaint2.setColor(Color.YELLOW);
mPaint2.setStyle(Paint.Style.FILL);
}
@Override
protected void onDraw(Canvas canvas) {
// 绘制外层矩形
canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mPaint1);
// 绘制内层矩形
canvas.drawRect(10,10,getMeasuredWidth() - 10,getMeasuredHeight() - 10,mPaint2);
canvas.save();
// 绘制文字前平移10像素
canvas.translate(10, 0);
// 父类完成的方法,即绘制文本
super.onDraw(canvas);
canvas.restore();
}
}