如何在Android中自定义View?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
一、拓展谷歌提供的系统控件
假如我们要对Textview控件进行拓展,首先我们要定义一个类继承TextView,选择性的重写它的onDraw()、onMeasure()、onTouchEvent()等方法。其中,onDraw()负责对图像的绘制,onMeasure()负责测量位置,onTouchEvent()负责设置触摸的事件。当我们想直接绘制出有背景颜色的TextView时,可以在类中定义画笔,在onDraw()进行绘制。代码如下:
Paint paint1=new Paint(); //定义画笔 paint1.setColor(Color.YELLOW); paint1.setStyle(Paint.Style.FILL);
然后,通过以下的代码,就可以绘制出一个带矩形框的Textview,但是需要在绘制完成后在调用父类的onDraw(),因为是在系统控件上拓展,所以,还要有其原来的功能。
@Override protected void onDraw(Canvas canvas) { canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),paint1);//绘制矩形 canvas.save(); super.onDraw(canvas); canvas.restore(); }
使用canvas对象就可以进行绘图了,对canvas的讲解,我将会在下一篇博客讲解。
然后,我们只需要在布局文件中加入自定义的控件即可,在布局文件中,自定义view的名字就是自定义控件类的包名加上类名,假设定义CustomTextview类继承TextView,例子如下:
<com.example.myapplication.View.CustomTextView android:layout_width="wrap_content" android:layout_height="match_parent"></com.example.myapplication.View.Buttonbtn>
二、将系统提供的控件组合在一起
除了拓展原有的控件以外,我们还可以将控件组合成一个新的控件使用。首先,我们先定义一个新的布局文件,并把Imageview和Textview加入,代码如下。
<ImageView android:id="@+id/iv" android:layout_width="20dp" android:layout_height="20dp" android:src="@mipmap/ic_launcher" /> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="2dp" android:text="消息" android:textSize="13sp" />
然后我们定义一个类继承LinearLayout,在类的构造方法中对控件和布局进行初始化。
public void init(Context context) { //指定线性布局的显示方式,垂直 setOrientation(VERTICAL); //设置用户期望的布局方式 LayoutParams mLayoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); setLayoutParams(mLayoutParams); setGravity(Gravity.CENTER); setPadding(4, 4, 4, 4); //设置其布局文件 View mButtonbtnView = LayoutInflater.from(context).inflate(layout.botton_btn_view, this, true); mImageView = mButtonbtnView.findViewById(id.iv); mTextView = mButtonbtnView.findViewById(id.tv); }
接下来,它的使用方法就和拓展控件的方法一样了,直接在布局文件中,加入控件即可。
<com.example.myapplication.View.Buttonbtn android:layout_width="wrap_content" android:layout_height="match_parent"></com.example.myapplication.View.Buttonbtn>
三、重写View来实现全新的控件
当系统原生的控件无法满足我们需求时,我们就可以定义一个新的控件来完成需要的功能。创建一个新的控件,需要继承View类,其难点主要在于绘制控件和实现交互。在继承View类时,我们还需要重写它的onDraw(),onMeasure()、onTouchEvent()来实现绘制、测量和触摸事件。
onDraw()绘制就是在canvas对象上调用其一系列方法进行绘图,绘制控件的形状。
onMeasure()
下面,我来讲讲onMeasure()。在绘制View之前,我们需要告诉系统我们需要画一个多大的View以及他的位置,这就是onMeasure()进行的了。首先,我们来了解一下测量的三种模式:
EXACTLY:精确值模式,在指定view具体数值的时候会用到。
AT_MOST:最大值模式,将控件设置为"wrap_content"用到,它会根据子控件或者内容变化而变化。
UNSPECIFIED:绘制控件想要多大就可以多大。
根据以上三种模式,我们就可以在测量的时候判断和使用了。首先,我们重写一个view的onMeasure()方法。再通过使用MeasureSpec类获得控件的测量模式。MeasureSpec使用的是位运算,其高2位为测量的模式,剩下的30位为测量的大小。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); if (widthMode == MeasureSpec.EXACTLY) { } else if (widthMode == MeasureSpec.AT_MOST) { } else if (widthMode == MeasureSpec.UNSPECIFIED) { } }
以上代码就是通过判断测量模式来给定义控件的大小,这里只是测量了控件的宽度,控件高度的测量也是类似的,就不在做详解。
前面说过,ViewGroup是用来管理控件的,当ViewGroup的大小为"wrap_content"时,它就会遍历其所有子View,来获得子View的大小,再来设置自身的大小。我们使用过的布局,像RelativeLayout,LinearLayout都是继承ViewGroup的,所以他们也是使用这种方法来获得自己的大小的。
onTouchEvent()
onTouchEvent()就是我们所说的触摸事件,由于Android手机是触屏的,所以我们自定义View在触摸屏幕的时候,也需要有一定的处理来完成交互。当重写onTouchEvent方法的时候,我们可以看到,需要传入MotionEvent的对象。我们可以通过这个类来设置触摸的事件,也可以获得触摸点的位置。我们可以通过getAction()来获取触摸事件的行动,来判断是否按下屏幕或者移动。在Android的坐标系中,我们都知道Android的屏幕在竖屏的时候,以左上角的位置为原点,向右为x轴的正方向,向下为y轴的正方向,知道了这个后,我们就可以通过调用getX()和getY()方法可以获取触摸点的坐标,来完成一些交互操作。
public boolean onTouchEvent(MotionEvent event) { float x; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { x=event.getX(); } break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: break; } return true; }
以上就是自定义控件常用重写的方法,通过了重写这几个方法,我们基本就可以实现一个简易的自定义控件了。下面,我们来了解下控件的事件拦截机制的原理。
事件拦截机制分析
我们前面讲过,控件结构是树形结构,一个ViewGroup中可能有多个ViewGroup或者View,那么,触摸事件是怎么准确的分配给每个View和ViewGroup的呢。我们假设有一个ViewGroupA,在他的里面嵌套着ViewGroupB,而在ViewGroupB的里面,又嵌套着一个View。当我们重写ViewGroupA类的时候,就需要重写里面的这三个方法:
dispatchTouchEvent()
onInterceptTouchEvent()
onTouchEvent()
而在重写View的时候,需要重写两个方法:
dispatchTouchEvent()
onTouchEvent()
可以根据名字看出,ViewGroup中比View多了onInterceptTouchEvent()方法,这个方法就是事件拦截的核心。在每一个方法中Log一下,再点击View的时候,就会发现方法调用的顺序:
首先,调用了ViewGroupA类的dispatchTouchEvent()和onInterceptTouchEvent()。
再调用了ViewGroupB类的dispatchTouchEvent()和onInterceptTouchEvent()。
再到View的dispatchTouchEvent()方法。
这个调用的顺序就是事件传递的顺序,而事件处理的顺序则是:
View的onTouchEvent()。
ViewGroupB的onTouchEvent()。
ViewGroupA的onTouchEvent()。
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。