IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> #阅读笔记 Android群英传-第3章-Android控件框架与自定义控件详解 -> 正文阅读

[移动开发]#阅读笔记 Android群英传-第3章-Android控件框架与自定义控件详解

学习目标:

了解Android框架与自定义控件


学习内容:

Android控件框架与自定义控件详解
3.1 Android控件架构
3.2 View的测量与绘制
3.3 ViewGroup的测量与绘制
3.4 自定义控件的三种方式
3.5 事件的拦截机制

3.1 Android控件架构

UI界面架构图
View树结构
程序在OnCreate()中通过调用setContentView()后,ActivityManagerService会回调onResume()方法,此时系统才会把整个DecorView添加到PhoneWindow中,让其显示出来从而完成绘制。

3.2 View的测量

该过程在onMeasure()方法中进行,通过调用MeasureSpec类来对View进行测量。MeasureSpec是一个32位的int值,其中高2位为测量的模式,低30位为测量的大小,在计算中使用位运算是为了提高并优化效率。测量模式有以下三种:

  1. EXACTLY:精确值模式。有具体的宽高值或match_parent
  2. AT_MOST:最大值模式。wrap_content
  3. UNSPECIFIED:不指定模式。自定义View中使用

以measureWidth为例,代码如下:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
	//重写setMeasuredDimension方法
	setMeasuredDimension(measuredWidth(widthMeasureSpec), measuredHeight(heightMeasureSpec));
}

private int measureWidth(int measureSpec){
	if result = 0;
	int specMode = MeasureSpec.getMode(measureSpec);//获取测量模式
	int specSize = MeasureSpec.getSize(measureSpec);//获取测量大小
	
	if(specMode == MeasureSpec.EXACTLY){
		result = specSize;
	}else if(specMode == MeasureSpec.AT_MOST){
		result = Math.min(200, specSize);
	}else{//UNSPECIFIED
		result = 200;
	}
	return result;
}

3.3 View的绘制

该过程在onDraw()中进行、通过重写该方法在Canvas上使用Paint实现View的绘制。

Canvas canvas = new Canvas(bitmap);//装在画布
canvas.drawBitmap(bitmap1, 0, 0, null);
canvas.drawBitmap(bitmap2, 0, 0, null);
Canvas mCanvas = new Canvas(bitmap2);//将bitmap2装载到另一个Canvas对象中
mCanvas.drawXXX
//通过该操作后、刷新View会发现在canvas中的bitmap2被改变,因为bitmap2同时承载了两个canvas的画图操作

3.4 ViewGroup的测量

遍历子View进行测量,调用子View的Measure方法来获得每一个子View的测量结果,测量完毕后调用Layout方法放置在具体显示的位置。

3.5 ViewGroup的绘制

通常不需要绘制,除非是指定了背景颜色否则不会调用onDraw()。ViewGroup会使用dispatchDraw()方法来绘制其子View,及遍历所有的子View,然后调用子View的方法来绘制。

3.6 自定义View

在View中通常有以下一些比较重要的回调。

  • onFinishInflate():从XML加载组件后回调
  • onSizeChanged():组件大小改变时回调
  • onMeasure():回调该方法来进行测量
  • onLayout():回调该方法来确定显示的位置
  • onTouchEvent:监听到触摸事件时回调

通常有以下三种方式来实现自定义控件:

  • 对现有的控件进行拓展
  • 通过组合来实现新的控件
  • 重写View来实现全新控件

3.6.1 对现有控件进行拓展

以“抛光”效果的TextView为例,实现一段文字的颜色渐变。需要对其onSizeChanged方法中设置LinearGradient和onDraw方法中对矩阵进行平移的操作来重写。

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

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (mViewWidth == 0) {
            mViewWidth = getMeasuredWidth();
            if (mViewWidth > 0) {
                mPaint = getPaint();
                mLinearGradient = new LinearGradient(-mViewWidth, 0, 0, 0,
                        new int[]{0xFF9EADD9, 0xffffffff, 0xFF9EADD9},
                        new float[]{0, 0.5f, 1}, Shader.TileMode.CLAMP);
                mPaint.setShader(mLinearGradient);
                mGradientMatrix = new Matrix();
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mAnimating && mGradientMatrix != null) {
            mTranslate += mViewWidth / 10;
            if (mTranslate > 2 * mViewWidth) {
                mTranslate = -mViewWidth;
            }
            mGradientMatrix.setTranslate(mTranslate, 0);
            mLinearGradient.setLocalMatrix(mGradientMatrix);
            postInvalidateDelayed(50);
        }else{//通过set mAnimating值决定该自定义View是否显示抛光效果
            setTextColor(0xFF9EADD9);
        }
    }

3.6.2 创建复合控件

该方法为通常需要继承一个合适的ViewGroup,并给它添加指定的功能的控件,从而组合成新的复合控件。通过该方式的创建的控件,我们一般人为给它指定一些可配置的属性,使其具有更强的拓展性。

3.6.2.1 定义属性

为一个View提供属性,只需要在res资源目录的values目录下创建一个attrs.xml的属性定义文件。然后再标签声明了使用自定义属性,并通过name属性来确定引用的字体大小等。确定属性之后即可创建一个自定义控件——TopBar,并让它继承ViewGroup。在构造方法中,通过如下代码来获取XML布局文件中自定义的那些属性。

TypeArray ta = context.obtainStyleAttributes(attrs,R.style.TopBar);

系统提供了TypedArray这样的数据结构来获取自定义属性集,后面引用的styleable的TopBar,就是我们在XML中通过所制定的name名。通过ta.getColor等方法可获取这些定义的属性值。需要注意的是,当获取完所有的饿属性值后,需要调用TypeaArray的recycle方法完成资源回收。

3.6.2.2 组合控件

也可由现成的多个控件进行组合,再通过接口调用来实现具体的实现,

3.6.2.3 引用UI模板

xmlns:android="http://schemas.android.com/apk/res/android"

指定引用的名字空间。使用系统属性时才可使用"android: "来引用Android的系统属性。若要使用自定义属性,就需要创建自己的名字空间,第三方控件都使用如下代码引入名字空间。

xmlns:custom="http://schemas.android.com/apk/res-auto"

之后在XML文件中使用自定义属性,都可以通过这个名字空间来引用。使用自定义View时需要指定完整的包名。而在引用自定义的属性时,需要使用自定义的xlms的名字。

3.7 自定义ViewGrop

自定义ViewGroup的修改实际上是通过改变其子View来实现、先onMeasure() 对子View大小进行测量、重写onLayout()确定子View的位置,重写onTouchEvent()方法增加响应事件。
例如:使用ViewGroup实现类似ScrollView的功能。但在滑动过程中增加一个黏性效果,即当一个子View向上滑动大于一定距离之后松开手指,它将自动向上滑显示下一个View.。下滑同理。

1、首先对ViewGroup的子类大小进行测量

@Overrrie
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
	super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	int count = getChildCount();//获取ViewGroup的子V
	for(int i = 0; i < count; i++){
		View  childView = getChildAt(i);
		measureChild(childView, widthMeasureSpec, heightMeasureSpec);
	}
}

2、对子View进行防治位置的设定。让每个子View都显示完整的一屏。因此整个ViewGroup的高度即子View的个数乘以屏幕的高度。获取之后通过遍历来设定每个子View需要放置的位置、直接调用layout()方法。代码如下所示

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b){
	int childCount = getChildCount();
	//设置ViewGroup的高度
	MarginLayoutParams mlp = (MarginLayoutParams)getLayoutParams();
	mlp.height = mScreenHeight * childCount;
	setLayoutParams(mlp);
	for(int i = 0; i < childCount; i++){
		View child = getChildAt(i);
		if(child.getVisibility()!= View.GONE){
			child.layout(1, i*mScreenHeight, r, (i+1)*mScreenHeight);
		}
	}
}

3、重写onTouchEvent()方法,为ViewGroup重写触摸事件。通常使用scrollBy()来辅助滑动。

case MotionEvent.ACTION_DOWN:
	mLastY = y;
	break;
case MotionEvent.ACYION_MOVE:
	if(!mScroller.isFinished()){
		mScroller.abortAnimation();
	}
	int dy = mLastY-y;
	if(getScrollY() < 0){
		dy = 0;
	}
	if(getScrollY() > getHeight() - mScreenHeight()){
		dy = 0;
	}
	scrollBy(0, dy);
	mLastY = Y;
	break;

4、实现ViewGroup黏性效果。在ACTION_UP事件中判断手指滑动的距离,如果超过一定距离,则使用Scroller类来平滑移动到下一个View,若小于一定距离,则回滚到原来的位置。

@Override
public boolean onTouchEvent(MotionEvent event){
	int y = (int)event.getY();
	swtich(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			mLastY = y;
			mStart = getScrollY();//记录触摸起点
			break;
		case MotionEvent.ACTION_MOVE:
			if(!mScroller.isFinished()){
				mScroller.abortAnimation();
			}
			int dy = mLastY - y;
			if(getScrollY() < 0){
				dy = 0;
			}
			if(getScrollY() > getHeight() - mScreenHeight()){
				dy = 0;
			}
			scrollBy(0, dy);
			mLastY = y;
			break;
		case MotionEvent.ACTION_UP:
			mEnd = getScrollY();//记录触摸终点
			if(dScrollY > 0){
				int dScrollY = mEnd - mStart;
				if(dScrollY > 0){//上滑
					if(dScrollY < mScreenHeight/3){
						mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
					}else{
						mScroller.startScroll(0, getScrollY(), 0, mScreenHeight - dScrollY);
					}
				}else{//下滑
					if(-dScrollY < mScreenHeight/3){
						mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
					}else{
						mScroller.startScroll(0, getScrollY(), 0, -mScreenHeight - dScrollY);
					}
				}
			}
			break;
	}
	postInvalidate();
	return true;
}

@Override
public void computeScroll(){
	super.computeScroll();
	if(mScroller.computeScrollOffset()){
		scrollTo(0,mScroller.getCurY());
		postInvalidate();
	}
}

3.8 事件拦截机制分析

方法与控件的对应关系如下:
在这里插入图片描述
事件传递过程:从上之下依次为父类与子类。父类通过dispatchTouchEvent()将事件传递给子类,其中onInterceptTouchEvent()决定是否拦截传递的事件,若不拦截则在子类通过onTouchEvent()实现具体的操作事件。
在这里插入图片描述


学习时间:

2021/1/27-2021/1/28


学习产出:

1、 技术博客 1 篇
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-01-30 19:02:44  更:2022-01-30 19:04:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/28 5:35:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码