通常情况下,有三种情况实现自定义控件:
- 对现有控件进行拓展
该方法主要是在原生控件的基础上进行拓展,增加新的功能,修改显示UI等,一般是通过onDraw方法进行拓展. - 通过组合来实现新的控件
该方法主要是通过继承合适的ViewGroup,比如,LinearLayout,RelativeLayout等等,再添加一些点击事件,监听事件等指定功能,从而合成复合控件,另外还可以指定一些它自己的属性,让其更具有拓展性.
- 重新View来实现全新的控件
该方法主要是通过继承View类,重写onMeasure(),onDraw()等方法进行绘制,并重新onTouchEvent,OnClick等触控事件来实现交互,另外也可以通过引入自定义属性,来拓展该控件的功能.
- 重新View来实现全新的控件
举例1 ,对TextView的控件进行拓展,通过绘制改变其背景,代码如下
// ViewTest继承TextView,进行初始化,并重写onDraw方法
public class ViewTest extends TextView {
private Paint mPaint1,mPaint2;
public ViewTest(Context context){
super(context);
}
private void init() {
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);
}
public ViewTest(Context context, AttributeSet attrs){
super(context,attrs);
init();
}
public ViewTest(Context context,AttributeSet attributeSet,int defStyleAttr){
super(context,attributeSet,defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mPaint1);
canvas.drawRect(10,10,getMeasuredWidth()-10,getMeasuredHeight()-10,mPaint2);
canvas.save();
canvas.translate(10,5);
super.onDraw(canvas);
canvas.restore();
}
}
// MainActivity 创建ViewTest的对象mViewText
public class MainActivity extends Activity {
private ViewTest mViewText;
@Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewText = (ViewTest) findViewById(R.id.tv1);
}
}
// activity_main.xml中装载ViewTest
<com.example.nft.myapplication.ViewTest //引入的时候 必须要加上packagename路径
android:layout_width="200dp"
android:layout_height="100dp"
android:layout_marginTop="50dp"
android:layout_marginHorizontal="50dp"
android:gravity="center"
android:text="ViewTest"
android:id="@+id/tv1"/>
举例2
组合控件举例 利用LinearLayout实现一个UI模板的标题栏,左右各一个button,中间是一个显示标题,后面再加一个logo图,并引入自己的属性,来定义按钮的背景 ,字体的大小和方向等等,通过对外暴露接口来实现交互.
/**
* Created by nft on 17-11-24.
*/
public class ViewTest1 extends LinearLayout {
private Button mLeftButton,mRightButton;
private TextView mContent;
private ImageView mImageView;
private OnViewClick mlistener;
private int mPainColor;
private float mContenSize;
private Drawable mBackground;
private boolean mFocus;
private float mApha;
private String mTextContent,mLeftButtonText,mRightButtonText;
private LinearLayout.LayoutParams mLeftParams,mRightParams,mTitleParams,mImageParams;
private int morientation;
public ViewTest1(Context context){
super(context);
}
public ViewTest1(Context context, AttributeSet attributeSet){
super(context,attributeSet);
Log.i("niuniu","ViewTest1");
mLeftButton = new Button(context);
mRightButton = new Button(context);
mContent = new TextView(context);
mImageView = new ImageView(context);
// 获取自定义的样式属性
TypedArray mTypedArray = context.obtainStyledAttributes(attributeSet,R.styleable.ViewTest1);
int n = mTypedArray.getIndexCount();
for (int i = 0;i<n;i++){
int attr = mTypedArray.getIndex(i);
switch (attr){
case R.styleable.ViewTest1_content_text:
mTextContent = mTypedArray.getString(R.styleable.ViewTest1_content_text);
break;
case R.styleable.ViewTest1_content_text_apha:
mApha = mTypedArray.getFloat(R.styleable.ViewTest1_content_text_apha,1);
break;
case R.styleable.ViewTest1_content_text_color:
mPainColor = mTypedArray.getColor(R.styleable.ViewTest1_content_text_color, Color.DKGRAY);
break;
case R.styleable.ViewTest1_content_text_focus:
mFocus = mTypedArray.getBoolean(R.styleable.ViewTest1_content_text_focus,false);
break;
case R.styleable.ViewTest1_content_text_size:
mContenSize = mTypedArray.getDimension(R.styleable.ViewTest1_content_text_size,10);
break;
case R.styleable.ViewTest1_contet_text_background:
mBackground = mTypedArray.getDrawable(R.styleable.ViewTest1_contet_text_background);
break;
case R.styleable.ViewTest1_orientation:
morientation = mTypedArray.getInt(R.styleable.ViewTest1_orientation,0);
break;
case R.styleable.ViewTest1_left_button_text:
mLeftButtonText = mTypedArray.getString(R.styleable.ViewTest1_left_button_text);
break;
case R.styleable.ViewTest1_right_button_text:
mRightButtonText = mTypedArray.getString(R.styleable.ViewTest1_right_button_text);
break;
}
}
mTypedArray.recycle();
mLeftButton.setText(mLeftButtonText);
mLeftButton.setBackgroundColor(mPainColor);
mLeftButton.setTextSize(mContenSize);
mLeftButton.setClickable(mFocus);
mRightButton.setTextSize(mContenSize);
mRightButton.setBackgroundColor(mPainColor);
mRightButton.setText(mRightButtonText);
mContent.setText(mTextContent);
mContent.setTextColor(mPainColor);
mContent.setTextSize(mContenSize);
mContent.setAlpha(mApha);
mImageView.setImageDrawable(mBackground);
mLeftParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
mLeftParams.leftMargin = 20;
addView(mLeftButton,mLeftParams);
mTitleParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
if (morientation == 0){
mTitleParams.leftMargin = 100;
}else {
mTitleParams.rightMargin =100;
}
addView(mContent,mTitleParams);
mRightParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
mRightParams.leftMargin = 500;
addView(mRightButton,mRightParams);
mImageParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
mImageParams.leftMargin = 100;
addView(mImageView,mImageParams);
mLeftButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mlistener.leftClick();
}
});
mRightButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mlistener.rightClick();
}
});
}
public interface OnViewClick{
void leftClick();
void rightClick();
}
public void setOnViewClickListener(OnViewClick listener){
this.mlistener = listener;
}
public ViewTest1(Context context,AttributeSet attributeSet,int defStyleAttr){
super(context,attributeSet,defStyleAttr);
}
//主页面
public class MainActivity extends Activity {
private ViewTest mViewText;
private ViewTest1 mViewTest1;
@Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewTest1 = (ViewTest1) findViewById(R.id.tv2);
mViewTest1.setOnViewClickListener(new ViewTest1.OnViewClick() {
@Override
public void leftClick() {
Toast.makeText(getApplicationContext(),"leftclick",Toast.LENGTH_LONG).show();
}
@Override
public void rightClick() {
Toast.makeText(getApplicationContext(),"rightclick",Toast.LENGTH_LONG).show();
}
});
}
}
// attrs.xml文件
<resources>
<declare-styleable name="ViewTest1">
<attr name="content_text_color" format="color"></attr>
<attr name="content_text" format="string"></attr>
<attr name="left_button_text" format="string"></attr>
<attr name="right_button_text" format="string"></attr>
<attr name="content_text_size" format="dimension"></attr>
<attr name="content_text_apha" format="float"></attr>
<attr name="content_text_focus" format="boolean"></attr>
<attr name="contet_text_background" format="color|reference"></attr>
<attr name="orientation">
<enum name="left" value="0" />
<enum name="right" value="1" />
</attr>
</declare-styleable>
</resources>
// activity_main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" // 引入该控件的命名空间
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.nft.myapplication.MainActivity">
<com.example.nft.myapplication.ViewTest1 //申明控件时,需要指定完整的包名
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="500dp"
android:layout_margin="50dp"
app:content_text="添加标题" // 引用自定义属性时,需要使用自定义的xmls的名字
app:content_text_apha="0.8"
app:orientation="left"
app:contet_text_background="@mipmap/ic_launcher"
app:left_button_text="点击"
app:right_button_text="添加"
app:content_text_size="30dp"
app:content_text_focus="true"
app:content_text_color="#ff661231"
/>
</LinearLayout>
举例3: 重写View实现全新的控件
实现一个点击后,数字发生变化的自定义View,其步骤:
1、自定义View的属性 同上的例子一样 在attrs.xml文件中定义属性
2、在View的构造方法中获得我们自定义的属性 同上例子一样 通过TypeArray 获取自定义属性
3、重写onMesure // 该步骤不是必须的
4、重写onDraw
public class ViewTest2 extends View {
private Paint mPaint;
private Rect mBound;
private TypedArray mTypeArray;
private int mTextColor;
private int mViewWidth =0,mTranslate;
private LinearGradient mLinearGradient;
private Matrix mGradientMatix;
private float mTextSize;
private String mText;
public ViewTest2(Context context){
super(context);
}
public ViewTest2(Context context, AttributeSet attributeSet){
super(context,attributeSet);
mTypeArray = context.obtainStyledAttributes(attributeSet,R.styleable.ViewTest2);
mText = mTypeArray.getString(R.styleable.ViewTest2_text);
mTextColor = mTypeArray.getColor(R.styleable.ViewTest2_text_color, Color.DKGRAY);
mTextSize = mTypeArray.getDimension(R.styleable.ViewTest2_text_size,10);
mTypeArray.recycle();
mBound = new Rect();
mPaint = new Paint();
mPaint.setTextSize(mTextSize);
mPaint.getTextBounds(mText,0,mText.length(),mBound);
this.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
mText = randomText();
postInvalidate();
}
});
}
private String randomText() {
Random random = new Random();
Set<Integer> set = new HashSet<Integer>();
while (set.size() < 4)
{
int randomInt = random.nextInt(10);
set.add(randomInt);
}
StringBuffer sb = new StringBuffer();
for (Integer i : set)
{
sb.append("" + i);
}
return sb.toString();
}
@Override
protected void onDraw(Canvas canvas) {
Log.i("niuniu"," onDraw");
mPaint.setColor(Color.YELLOW);
canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mPaint);
mPaint.setColor(mTextColor);
canvas.drawText(mText,getMeasuredWidth()/2-mBound.width()/4,getMeasuredHeight()/2-mBound.height()/4,mPaint);
canvas.save();
super.onDraw(canvas);
canvas.restore();
if(mGradientMatix !=null){
mTranslate += mViewWidth/5;
if(mTranslate>2*mViewWidth){
mTranslate = -mViewWidth;
}
mGradientMatix.setTranslate(mTranslate,0);
mLinearGradient.setLocalMatrix(mGradientMatix);
postInvalidateDelayed(100);
Log.i("niuniu"," postinvalidate");
}
}
public ViewTest2(Context context, AttributeSet attributeSet, int defStyleAttr){
super(context,attributeSet,defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = measureDimension(widthSize,widthMode);
int height = measureDimension(heightSize,heightMode);
setMeasuredDimension(width,height);
}
private int measureDimension(int size, int mode) {
Log.i("niuniu"," measureDimension");
int result =0;
if (mode == MeasureSpec.EXACTLY){
result = size;
}else {
result = 500;
if(mode ==MeasureSpec.AT_MOST){
result = Math.min(result,size);
Log.i("niuniu " ," result " +result);
}
}
return result;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Log.i("niuniu"," onSizeChanged");
super.onSizeChanged(w, h, oldw, oldh);
if (mViewWidth ==0){
mViewWidth = getMeasuredWidth();
if(mViewWidth>0){
mLinearGradient = new LinearGradient(0, 0, mViewWidth, 0, new int[]{Color.BLUE, 0x123321, Color.BLUE}, null, Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
mGradientMatix = new Matrix();
}
}
}
}
//log的输出是 measureDimension onSizeChanged onDraw onDraw onDraw ......