学校里面开了门安卓课,为了作业,就开始学习安卓的各种知识。UI是最能直观看到的东西,所以我就从自定义控件开始。
首先,我说一下,为什么要自定义控件呢?1.原生的控件不足以满足我们的要求,或者有些功能与我们的要求不太相同等,比如说,要用一个带删除按钮的编辑框,原生的里面就没有;2.有很多控件是可以复用的,像导航栏,标题栏这种可能需要多次使用的东东,如果每次都是靠原生控件堆叠起来,然后拷贝多份的话,那么不仅费时费力,而且可维护性太差,如果要改东西的话,很容易出差错。
那接下来,就该步入正题了,那就是如何来自定义控件。基本上,我们可以把它拆解为3个部分:1.设计需要的属性;2.实现自定义的控件;3.引用自定义的控件
一、设计需要的属性
在res/value目录下面新建一个xml文件,命名为attrs.xml。里面需要用到许多属性,来限制不同参数的类型,具体的含义可以在下面的链接中查看:
下面是我写的一个attrs.xml中的内容,定义了一个Detailbar的相关属性,之后可以像设置系统原生控件属性那样设置自定义控件的属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Detailbar">
<attr name="info" format="string"></attr>
<attr name="infoTextSize" format="dimension"></attr>
<attr name="infoTextColor" format="color"></attr>
<attr name="infoBackground" format="reference|color"></attr>
<attr name="leftText" format="string"></attr>
<attr name="leftTextSize" format="dimension"></attr>
<attr name="leftTextColor" format="color"></attr>
<attr name="leftBackground" format="reference|color"></attr>
<attr name="rightText" format="string"></attr>
<attr name="rightTextSize" format="dimension"></attr>
<attr name="rightTextColor" format="color"></attr>
<attr name="rightBackground" format="reference|color"></attr>
</declare-styleable>
</resources>
二、
实现自定义的控件
继承某个控件的类,然后再这个类里面将控件的布局,控件的行为等加以定义和调整,原则上是参照Android的系统控件的实现方法来完成。
上代码:
package com.wecall.contacts.view;
import com.wecall.contacts.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* 自定义个人详细信息tab
*
* @author xiaoxin
*
*/
public class DetailBar extends RelativeLayout {
//内部的控件
private Button leftBtn,rightBtn;
private TextView tvInfo;
//内部控件的信息
private String leftText;
private int leftTextColor;
private float leftTextSize;
private Drawable leftBackground;
private String rightText;
private int rightTextColor;
private float rightTextSize;
private Drawable rightBackground;
private String infoText;
private int infoTextColor;
private float infoTextSize;
private Drawable infoBackground;
private LayoutParams leftParams,rightParams,infoParams;
//定义接口
public interface DetailBarClickListener{
void leftClick();
void rightClick();
void infoClick();
}
private DetailBarClickListener listener;
//开放给外部的方法
public void setOnDetailBarClickListener(DetailBarClickListener listener){
this.listener = listener;
}
//实现两个参数的构造方法,其中第二个参数为传入的参数集合
public DetailBar(Context context, AttributeSet attrs) {
super(context, attrs);
//初始化惨呼
initAttrs(context,attrs);
//初始化内部控件
initView(context);
}
public String getInfo(){
return tvInfo.getText().toString();
}
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.Detailbar);
leftText = ta.getString(R.styleable.Detailbar_leftText);
leftTextColor = ta.getColor(R.styleable.Detailbar_leftTextColor, 0);
leftTextSize = ta.getDimension(R.styleable.Detailbar_leftTextSize, 0);
leftBackground = ta.getDrawable(R.styleable.Detailbar_leftBackground);
rightText = ta.getString(R.styleable.Detailbar_rightText);
rightTextColor = ta.getColor(R.styleable.Detailbar_rightTextColor, 0);
rightTextSize = ta.getDimension(R.styleable.Detailbar_rightTextSize, 0);
rightBackground = ta.getDrawable(R.styleable.Detailbar_rightBackground);
infoText = ta.getString(R.styleable.Detailbar_infoText);
infoTextColor = ta.getColor(R.styleable.Detailbar_infoTextColor, 0);
infoTextSize = ta.getDimension(R.styleable.Detailbar_infoTextSize, 0);
infoBackground = ta.getDrawable(R.styleable.Detailbar_infoBackground);
ta.recycle();
}
private void initView(Context context) {
leftBtn = new Button(context);
rightBtn = new Button(context);
tvInfo = new TextView(context);
leftBtn.setText(leftText);
leftBtn.setTextColor(leftTextColor);
leftBtn.setTextSize(leftTextSize);
leftBtn.setBackground(leftBackground);
rightBtn.setText(rightText);
rightBtn.setTextColor(rightTextColor);
rightBtn.setTextSize(rightTextSize);
rightBtn.setBackground(rightBackground);
tvInfo.setText(infoText);
tvInfo.setTextColor(infoTextColor);
tvInfo.setTextSize(infoTextSize);
tvInfo.setBackground(infoBackground);
tvInfo.setGravity(Gravity.CENTER);
setBackgroundColor(0xfff59563);
leftParams = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
leftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);
leftParams.addRule(RelativeLayout.CENTER_VERTICAL,TRUE);
addView(leftBtn,leftParams);
rightParams = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
rightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,TRUE);
rightParams.addRule(RelativeLayout.CENTER_VERTICAL,TRUE);
addView(rightBtn,rightParams);
infoParams = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.MATCH_PARENT);
infoParams.addRule(RelativeLayout.CENTER_IN_PARENT,TRUE);
addView(tvInfo,infoParams);
leftBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
listener.leftClick();
}
});
rightBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
listener.rightClick();
}
});
tvInfo.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
listener.infoClick();
});
}
}
三、使用自定义控件
在布局文件中使用自定义控件的时候,要申请一个xml的命名空间,就是布局文件第一行那个xmlns,ns是namespace的缩写。参照那个写法:xmlns:android="http://schemas.android.com/apk/res/android", 我们申请命名空间xmlns:custom="http://schemas.android.com/apk/res-auto"
格式是:xmlns:命名空间="http://schemas.android.com/apk/res/应用包名",注意,是应用的包名,不是自定义控件类的包名,系统允许将“res/应用包名”写出"res-auto",它会自动帮你找到参数定义
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_vertical">
<ImageButton android:id="@+id/back_to_homepage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_menu_back"
android:background="@null"
android:layout_gravity="center_vertical"/>
<TextView android:id="@+id/tv_contact_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="齐天大圣"
android:textSize="30sp"
android:layout_margin="20dp"/>
</LinearLayout>
<com.wecall.contacts.view.DetailBar
android:id="@+id/phone_num"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
custom:infoText="1234567890"
custom:infoTextColor="#ffffff"
custom:infoTextSize="20sp"
custom:leftBackground="@drawable/sym_action_call"
custom:rightBackground="@drawable/sym_action_email"
>
</com.wecall.contacts.view.DetailBar>
</LinearLayout>
</ScrollView>
在Activity中使用的时候,就是跟使用其他控件是一样的
package com.wecall.contacts;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.TextView;
import com.wecall.contacts.view.DetailBar;
import com.wecall.contacts.view.DetailBar.DetailBarClickListener;
public class ContactInfo extends Activity {
private TextView nameTV;
private ImageButton backIB;
private DetailBar phoneNumBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contact_info);
initView();
}
private void initView() {
nameTV = (TextView)findViewById(R.id.tv_contact_name);
backIB = (ImageButton)findViewById(R.id.back_to_homepage);
phoneNumBar = (DetailBar)findViewById(R.id.phone_num);
Bundle bundle = getIntent().getExtras();
nameTV.setText(bundle.getString("cname"));
backIB.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
finish();
}
});
phoneNumBar.setOnDetailBarClickListener(new DetailBarClickListener() {
@Override
public void leftClick() {
String phoneNumber = phoneNumBar.getInfo().toString();
Intent intent = new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+phoneNumber));
ContactInfo.this.startActivity(intent);
}
@Override
public void rightClick() {
String phoneNumber = phoneNumBar.getInfo().toString();
Intent intent = new Intent(Intent.ACTION_SENDTO,Uri.parse("smsto:"+phoneNumber));
ContactInfo.this.startActivity(intent);
}
@Override
public void infoClick() {
Log.v("TAG","info");
}
});
}
}
这个是我写的代码中选取的一部分,删了点东西,而且也不是MainActivity,所以直接拷贝,可能会有点错误,不过原理上是没有问题的,大家看一下,了解一下原理就OK。