Android自定义控件基本原理详解(一)

前言:

在日常的Android开发中会经常和控件打交道,有时Android提供的控件未必能满足业务的需求,这个时候就需要我们实现自定义一些控件,今天先大致了解一下自定义控件的要求和实现的基本原理。

自定义控件要求:

1. 应当遵守Android标准的规范(命名,可配置,事件处理等)。
     2. 在XML布局中科配置控件的属性。
     3. 对交互应当有合适的反馈,比如按下,点击等。
     4. 具有兼容性, Android版本很多,应该具有广泛的适用性。

自定义控件学习步骤:

  1 .View的工作原理
  2 .编写View类
  3.为View类增加属性
  4 .绘制屏幕
  5. 响应用户消息
  6 .自定义回调函数

自定义控件两种方式:
1. 继承ViewGroup 
例如:ViewGroup、LinearLayout、FrameLayout、RelativeLayout等。
2. 继承View
例如:View、TextView、ImageView、Button等。

自定义控件基本绘制原理:
 View的绘制基本上由measure()、layout()、draw()这个三个函数完成 
1.)测量-Measure过程是计算视图大小,View measure过程相关方法主要有三个: 
public final void measure(int widthMeasureSpec, int heightMeasureSpec) 
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
measure调用onMeasure,onMeasure测量宽度、高度然后调用setMeasureDimension保存测量结果,measure,setMeasureDimension是final类型,view的子类不需要重写,onMeasure在view的子类中重写。

关于MeasureSpec: 
(1)UPSPECIFIED :父容器对于子容器没有任何限制,子容器想要多大就多大. 
(2) EXACTLY父容器已经为子容器设置了尺寸,子容器应当服从这些边界,不论子容器想要多大的空间. 
(3) AT_MOST子容器可以是声明大小内的任意大小.

2.)布局-Layout过程用于设置视图在屏幕中显示的位置,View layout过程相关方法主要要三个: 
public void layout(int l, int t, int r, int b)
protected boolean setFrame(int left, int top, int right, int bottom)
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
layout通过调用setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,onLayout一般只会在自定义ViewGroup中才会使用

3.)绘制-draw过程主要用于利用前两步得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。 
public void draw(Canvas canvas)
protected void onDraw(Canvas canvas)
通过调用draw函数进行视图绘制,在View类中onDraw函数是个空函数,最终的绘制需求需要在自定义的onDraw函数中进行实现,比如ImageView完成图片的绘制,如果自定义ViewGroup这个函数则不需要重载。

自定义控件示例:
这里先介绍继承View的方式为例,其实ViewGroup最终的继承的也是View。这里 模拟一个需求场景,需要一个圆形显示百分比。

public class PercentView extends View {
 private final static String TAG = PercentView.class.getSimpleName();
 private Paint mPaint;

 public PercentView(Context context) {
 super(context);
 init();
 }

 public PercentView(Context context, AttributeSet attrs) {
 super(context, attrs);
 init();
 }

 public PercentView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 init();
 }

 private void init(){
 mPaint = new Paint();
 mPaint.setAntiAlias(true);
 }
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
 Log.e(TAG, "onMeasure--widthMode-->" + widthMode);
 switch (widthMode) {
 case MeasureSpec.EXACTLY:

 break;
 case MeasureSpec.AT_MOST:

 break;
 case MeasureSpec.UNSPECIFIED:

 break;
 }
 Log.e(TAG, "onMeasure--widthSize-->" + widthSize);
 Log.e(TAG, "onMeasure--heightMode-->" + heightMode);
 Log.e(TAG, "onMeasure--heightSize-->" + heightSize);
 }

 @Override
 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
 super.onLayout(changed, left, top, right, bottom);
 Log.e(TAG, "onLayout");
 }

 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 mPaint.setColor(Color.GRAY);
 // FILL填充, STROKE描边,FILL_AND_STROKE填充和描边
 mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
 int with = getWidth();
 int height = getHeight();
 Log.e(TAG, "onDraw---->" + with + "*" + height);
 float radius = with / 4;
 canvas.drawCircle(with / 2, with / 2, radius, mPaint);
 mPaint.setColor(Color.BLUE);
 RectF oval = new RectF(with / 2 - radius, with / 2 - radius, with / 2
 + radius, with / 2 + radius); //用于定义的圆弧的形状和大小的界限
 canvas.drawArc(oval, 270, 120, true, mPaint); //根据进度画圆弧
 }
}

在布局中如何使用

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">

 <com.whoislcj.views.PercentView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_margin="10dp" />

</LinearLayout>

显示效果:

如果布局文件改成

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">

 <com.whoislcj.views.PercentView
 android:layout_width="150dp"
 android:layout_height="150dp"
 android:layout_margin="10dp" />

</LinearLayout>

显示效果变成

总结:
本篇主要介绍Android自定义控件的基本绘制原理,会在下一篇中介绍如何自定义属性,点击查看。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • android自定义控件创建翻页接口详细代码

    本文分享的这个类的目的是为在看书翻页时,需要进行的动作提供接口,利用android自定义控件创建翻页接口,具体内容如下 BookPage.java package com.horse.util; import java.text.DecimalFormat; import java.util.Vector; import com.horse.bean.Chapter; import android.graphics.Bitmap; import android.graphics.Canvas;

  • Android自定义控件实现边缘凹凸的卡劵效果

    前言 最近做项目的时候遇到一个卡劵的效果,由于自己觉得用图片来做的话可以会出现适配效果不好,再加上自己自定义view方面的知识比较薄弱,所以想试试用自定义View来实现.但是由于自己知识点薄弱,一开始居然想着用画矩形来设置边缘实现,后面一个哥们指导了我,在这里感谢他. 实现分析 上面的图片其实和普通的Linearlayout,RelativeLayout一样,只是上下两边多了类似于半圆锯齿的形状.那么只需要处理不同地方.可以在上下两条线上画一个个白色的小圆来实现这种效果. 假如我们上下线的半圆以

  • Android自定义控件实现可左右滑动的导航条

    先上效果图: 这个控件其实算是比较轻量级的,相信不少小伙伴都能做出来.因为项目中遇到了一些特殊的定制要求,所以就自己写了一个,这里放出来.  首先来分析下这个控件的功能:  •能够响应左右滑动,并且能响应快速滑动 •选择项和未选择项有不同的样式表现,比如前景色,背景色,字体大小变粗之内的 •在切换选项的时候,如果当前选项未完全呈现在界面前,则自动滚动直至当前选项完全暴露显示 前两条还有,简简单单就实现了,主要是第三点,这才是我自定义这个控件的原因!那么如果要实现这个控件,需要用到哪些知识呢? 

  • Android自定义控件之开关按钮学习笔记分享

    今天来讲讲自定义单个控件,就拿开关按钮来讲讲,相信大家见了非常多这样的了,先看看效果: 我们可以看到一个很常见的开关按钮,那就来分析分析. 首先: 这是由两张图片构成: ①一张为有开和关的背景图片 ②一张为控制开和关的滑动按钮 第一步: 写个类继承View,并重写几个方法: 第一个为构造函数,重写一个参数的函数和两个参数的函数就够了,因为两个参数的函数能够使用自定义属性 第二个为控制控件的大小–>protected void onMeasure(int widthMeasureSpec, int

  • Android自定义控件之广告条滚动效果

    在一些电子商务网站上经常能够看到一些滚动的广告条,许多软件在首次使用时也有类似的广告条,如图: 其实在github上有实现这种效果的控件,不过这东西做起来也是很简单,我们今天就来看看该怎么做. 先来看看布局文件: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" and

  • Android自定义控件实现手势密码

    Android手势解锁密码效果图 首先呢想写这个手势密码的想法呢,完全是凭空而来的,然后笔者就花了一天时间弄出来了.本以为这个东西很简单,实际上手的时候发现,还有很多逻辑需要处理,稍不注意就容易乱套.写个UI效果图大约只花了3个小时,但是处理逻辑就处理了2个小时!废话不多说,下面开始讲解.      楼主呢,自己比较自定义控件,什么东西都掌握在自己的手里感觉那是相当不错(对于赶工期的小伙瓣儿们还是别手贱了,非常容易掉坑),一有了这个目标,我就开始构思实现方式.      1.整个自定义控件是继承

  • Android自定义控件实现简单的轮播图控件

    最近要做一个轮播图的效果,网上看了几篇文章,基本上都能找到实现,效果还挺不错,但是在写的时候感觉每次都要单独去重新在Activity里写一堆代码.于是自己封装了一下.本篇轮播图实现原理原文出处:循环广告位组件的实现,这里只是做了下封装成一个控件,不必每次重复写代码了. 效果图: 实现分析 轮播图的功能就是实现左右滑动的广告.图片信息展示,那我们就用ViewPager来实现,由于考虑到用户体验,我们还需要在下面加一个指示器来标示滑动到了第几张轮播图.指示器我们可以用一个线性布局来根据要展示的轮播图

  • Android自定义控件实现滑动开关效果

    自定义开关控件 Android自定义控件一般有三种方式 1.继承Android固有的控件,在Android原生控件的基础上,进行添加功能和逻辑. 2.继承ViewGroup,这类自定义控件是可以往自己的布局里面添加其他的子控件的. 3.继承View,这类自定义控件没有跟原生的控件有太多的相似的地方,也不需要在自己的肚子里添加其他的子控件. ToggleView自定义开关控件表征上没有跟Android原生的控件有什么相似的地方,而且在滑动的效果上也没有沿袭Android原生的地方,所以我们的自定义

  • Android自定义控件简单实现侧滑菜单效果

    侧滑菜单在很多应用中都会见到,最近QQ5.0侧滑还玩了点花样~~对于侧滑菜单,一般大家都会自定义ViewGroup,然后隐藏菜单栏,当手指滑动时,通过Scroller或者不断的改变leftMargin等实现:多少都有点复杂,完成以后还需要对滑动冲突等进行处理~~今天给大家带来一个简单的实现,史上最简单有点夸张,但是的确是我目前遇到过的最简单的一种实现~~~ 1.原理分析 既然是侧滑,无非就是在巴掌大的屏幕,塞入大概两巴掌大的布局,需要滑动可以出现另一个,既然这样,大家为啥不考虑使用Android

  • Android自定义控件之仿优酷菜单

    去年的优酷HD版有过这样一种菜单,如下图: 应用打开之后,先是三个弧形的三级菜单,点击实体键menu之后,这三个菜单依次旋转退出,再点击实体键menu之后,一级菜单会旋转进入,点击一级菜单,二级菜单旋转进入,点击二级菜单的menu键,三级菜单旋转进入,再次点击二级菜单的旋转键,三级菜单又会旋转退出,这时再点击一级菜单,二级菜单退出,最后点击实体menu键,一级菜单退出. 总体来说实现这样的功能: (1)点击实体menu键时,如果界面上有菜单显示,不管有几个,全部依次退出,如果界面上没有菜单显示,

随机推荐