android自定义控件步骤(android如何自定义控件)

干货文章,第一时间送达!

android自定义控件步骤(android如何自定义控件)

项目中很多地方,使用到了自定义控件。

简单点的,如个性控件的定制,多个组件的组合封装等。我们需要了解自定义控件的基础知识,即可快速实现;

复杂点的,如各种图形报表(例如:股票K线图、分时图控件)。我们除了自定义控件的基础知识,还需要掌握控件事件的拦截传递机制,事件回调、手势识别、画图、 动画、架构设计等技术。

关于自定义控件,我们逐步深入讲解:今天,我们先来实现一个简单的自定义控件,后期找时间再讲解股票K线图、分时图控件如何自定义。

需求

分析

1,差异项应可配置:

自定义控件的以下内容应设计成可以配置的属性:

是否有动画效果; hint文本; hint移动到上部显示的文本等;此处的重点:1. 自定义属性如何配置?如何使用?

2,自定义控件被调用(使用)

应支持在代码中直接new 我们的控件 应支持在布局xml中直接使用我们的控件,可配置自定义属性

3,动画

平移效果:Tween动画、属性动画均可实现;字体伸缩:应使用属性动画,根据字号去伸缩,宽高也会自动变化(注意:Tween动画无法做字号差值变化)综上所述,应统一使用属性动画实现平移和伸缩的效果,而多个动画同时触发,会用到动画集合;此处的重点:1. 平移需要原始点、目标点两个坐标(x,y),自定义控件中如何获得对应的值?2. 字体伸缩,需要伸缩前后的两个字号值,代码中默认获得的字号是px格式,如何与sp转换?3. 设计点:要实现文本移动和字号伸缩的动画效果,我们可以在布局中放置2个文本控件,tv_message:作为hint占位,不显示,仅用于获得坐标和字号;tv_to_message: 作为顶部消息显示,作为hint显示,动画执行在该控件上;当然,如何设计这种动画效果,还有很多其他的方式,大家使用时,可以根据自己的需要,合理设计。

技术实现分析

属性的定义,需要单独定义在res下的文件中:res/values目录中,创建attrs.xml文件

name可以自定义,规范即可;showTopMessage //自定义属性:是否显示顶端提示信息(true:显示,false:不显示)topMessage//自定义属性:顶端提示信息内容hint //自定义属性:输入框提示信息

2 . 自定义控件的布局

使用相对布局,内容包括:输入框 et_phone(需设置成无背景色,因UI人员已固定输入线的颜色)、输入框的底部线 View、输入框的清空按钮 iv_phone_clear、输入框上面的文本控件 tv_message(用于作为hint位置、字号的占位)输入框顶部的文本控件 tv_to_message(用于显示提示信息)

3 . 创建自定义控件类(定义成可new ,可直接在xml中使用的控件)

构造函数:自定义控件,必须使用特定的构造函数:1. 一个参数的构造函数,可用于其他代码中直接new 当前控件 UserPhoneEditText(Context context)2. 两个以上参数的构造函数,可用于直接在布局xml中使用当前控件,使用AttributeSet 可获得我们在xml中设置的属性;(后面有讲解)UserPhoneEditText(Context context, AttributeSet attrs)更多构造函数相关的信息,请自行查找资料!!!

代码中解析获得自定义参数:

//获得 在attrs.xml UserPhoneEditText中已定义的属性集合TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.user_phone_edittext);showTopMessage = typedArray.getBoolean(R.styleable.user_phone_edittext_showTopMessage, false);topMessage = typedArray.getString(R.styleable.user_phone_edittext_topMessage);hint = typedArray.getString(R.styleable.user_phone_edittext_hint);//释放typedArray.recycle();1. R.styleable.user_phone_edittext是我们在res/attrs.xml中定义的名称,对应自动生成的id2. 获得参数后,一定记得把TypedArray 释放掉,切记!!!

创建自定义控件并获得自定义参数的详细代码:

4 , 动画的执行

动画1:平移动画

需要获得两个坐标点:坐标点1:hint文本的位置(tv_message)坐标点2:消息文本的位置( tv_top_message)咱们先定义两个数值,用于存放坐标点坐标private int[] position1 = new int[2];//tv_message的默认位置坐标private int[] position2 = new int[2];//tv_to_message的默认位置坐标x要移动、y也要移动,所以使用动画集合AnimatorSet如果仅是移动,代码如下:AnimatorSet set = new AnimatorSet();set.playTogether( ObjectAnimator.ofFloat(tv_to_message , “TranslationX” , startX , endX), ObjectAnimator.ofFloat(tv_to_message , “TranslationY” ,startY, endY));set.setDuration(duration).start();

动画2:字号变化

需要获得两个文本的字号值(tv_message、tv_top_message)咱们先定义一个数组,用于存放两个文本的字号值private float[] fonts = new float[2];//tv_to_message的默认大小动画的执行,如果是边移动边伸缩字号,可以继续使用AnimatorSet,代码也就改造成:/** * 播放动画 * @param startX 开始X * @param endX 目标X * @param startY 开始Y * @param endY 目标Y * @param startFont 开始字号 * @param endFont 目标字号 */ private void startAnim(float startX, float endX, float startY, float endY, float startFont, float endFont){ AnimatorSet set = new AnimatorSet(); set.playTogether( ObjectAnimator.ofFloat(tv_to_message , “TranslationX” , startX , endX), ObjectAnimator.ofFloat(tv_to_message , “TranslationY” ,startY, endY), ObjectAnimator.ofFloat(tv_to_message , “TextSize” , startFont, endFont)); set.setDuration(duration).start(); }

重点:自定义控件中,如何获取到tv_message、tv_top_message的坐标和字号大小呢??

自定义控件有自己的函数周期,不同的函数做不同的事情,如onSizeChanged、onMeasure、onLayout、onDraw等。如果不明白这些方法是做什么的,请自行查找资料。

我们先来写个代码做个试验,先来看下自定义控件函数的执行顺序:自定义个简单的view,测试代码:

输出结果:

03-05 17:38:55.690 23189-23189/iwangzhe.testcustomview I/TestView: 构造函数TestView03-05 17:38:55.690 23189-23189/iwangzhe.testcustomview I/TestView: onFinishInflate03-05 17:38:55.770 23189-23189/iwangzhe.testcustomview I/TestView: onAttachedToWindow03-05 17:38:55.770 23189-23189/iwangzhe.testcustomview I/TestView: onWindowVisibilityChanged03-05 17:38:55.780 23189-23189/iwangzhe.testcustomview I/TestView: onMeasure03-05 17:38:55.780 23189-23189/iwangzhe.testcustomview I/TestView: onMeasure03-05 17:38:55.820 23189-23189/iwangzhe.testcustomview I/TestView: onSizeChanged03-05 17:38:55.820 23189-23189/iwangzhe.testcustomview I/TestView: onLayout03-05 17:38:55.830 23189-23189/iwangzhe.testcustomview I/TestView: onDraw03-05 17:38:55.860 23189-23189/iwangzhe.testcustomview I/TestView: onWindowFocusChanged03-05 17:38:55.880 23189-23189/iwangzhe.testcustomview I/TestView: onMeasure03-05 17:38:55.880 23189-23189/iwangzhe.testcustomview I/TestView: onMeasure03-05 17:38:55.880 23189-23189/iwangzhe.testcustomview I/TestView: onLayout03-05 17:38:55.880 23189-23189/iwangzhe.testcustomview I/TestView: onDraw……(重复onMeasure、onLayout、onDraw)

我们看到,页面加载自定义控件,准备完毕后,会执行onWindowFocusChanged方法,那么这个方法之前,已经执行了初始化、计算、布局和绘制显示,控件的位置等信息已经被赋值。所以在onWindowFocusChanged方法中,我们是可以获取到相应属性的,代码如下:

/** * 自定义控件准备完毕,获得各组件的位置等数据 */ @Override public void onWindowFocusChanged(boolean hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); //获得消息文本的位置信息 tv_message.getLocationInWindow(position1); tv_to_message.getLocationOnScreen(position2); //获得消息文本的字号信息 fonts[0] = PxUtils.px2sp(context,tv_message.getTextSize()); fonts[1] = PxUtils.px2sp(context,tv_to_message.getTextSize()); //初始化位置、字号(把tv_to_message设置的与tv_message显示一致) tv_to_message.setTextSize(fonts[0]); tv_to_message.setTranslationX(position1[0]-position2[0]); tv_to_message.setTranslationY(position1[1]-position2[1]); }

代码比较简单,如下:

类1:PxUtils px与sp转换的帮助类

类3:自定义控件类(核心类)

布局:user_phone_edittext.xml

测试类MainActivity(测试调用自定义控件).xml

自定义属性的使用,需要先设置命名控件:xmlns:userphoneedittext=”http://schemas.android.com/apk/res-auto”;userphoneedittext可以自己定义,其他格式固定。

public class MainActivity extends BaseActivity { Button btn1; Button btn2; Button btnPhone; UserPhoneEditText upet; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnPhone = (Button) findViewById(R.id.btnPhone); upet = (UserPhoneEditText) findViewById(R.id.upet1); //设置回调监听,获得输入完成的回调数据(被动回调) upet.setOnSuccessListener(new UserPhoneEditText.OnSuccessListener(){ @Override public void onSuccess(String phone) { Toast.makeText(MainActivity.this, phone, Toast.LENGTH_SHORT).show(); } }); //获得自定义控件文本信息(主动获取) btnPhone.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String text = upet.getPhone(); Toast.makeText(MainActivity.this,text,Toast.LENGTH_SHORT).show(); } }); }}

还有不明白的请看Demo,如果还是不明白请留言或者自行查询资料。本文章,主要是为了让大家了解自定义控件的过程,如果想在自己的项目中使用,请根据需要自行调整优化。

往期干货

1

MPAndroidChart 绘制曲线图总结

2

【需求解决系列一】之移动卡片实现答题功能

3

【年后第一篇】如何在复杂业务场景中优雅实现Android指纹验证?

如果你觉得本文对你有帮助,请分享给更多的人

发表评论

登录后才能评论