Android 控件架构与自定义控件(四)
来源:互联网 发布:软件编程是什么 编辑:程序博客网 时间:2024/06/07 10:43
Android 控件架构与自定义控件(一)
Android 控件架构与自定义控件(二)
Android 控件架构与自定义控件(三)
Android 控件架构与自定义控件(四)
自定义ViewGroup存在的目的就是为了对其子View进行管理,为其子View添加显示、响应的规则。因此,自定义ViewGroup通常需要重写onMeasure() 方法来对子View进行测量,重onLayout() 方法来确定子View的位置,重写onTouchEvent() 方法增加响应事件。
实现一个Android原生控件ScrollView 的自定义ViewGroup,自定义ViewGroup可以实现ScrollView所具有的上下滑动的功能,但是在滑动的过程中,增加一个粘性效果,即当一个子view向上滑动大于一定的距离后,松开手指,它将自动向上滑动,显示下一个View。同理,如果滑动距离小于一定的距离,松开手指,它将自动滑动到开始的位置
MyScrollView.java
package kotlindemo.zhoujian.com.customeview;import android.content.Context;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.view.WindowManager;import android.widget.Scroller;/** * Created by zhoujian on 2017/6/17. */public class MyScrollView extends ViewGroup{ private int mScreenHeight; private Scroller mScroller; private int mLastY; private int mStart; private int mEnd; public MyScrollView(Context context) { super(context); initView(context); } public MyScrollView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } private void initView(Context context) { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(dm); mScreenHeight = dm.heightPixels; mScroller = new Scroller(context); } /** * 使用遍历的方式来通知子View对自身进行测量 * * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int count = getChildCount(); for (int i = 0; i < count; i++) { View childView = getChildAt(i); measureChild(childView,widthMeasureSpec,heightMeasureSpec); } } @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(l, i * mScreenHeight, r, (i + 1) * mScreenHeight); } } } @Override public boolean onTouchEvent(MotionEvent event) { int y = (int) event.getY(); switch (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: int dScrollY = checkAlignment(); 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; } private int checkAlignment() { int mEnd = getScrollY(); boolean isUp = ((mEnd - mStart) > 0) ? true : false; int lastPrev = mEnd % mScreenHeight; int lastNext = mScreenHeight - lastPrev; if (isUp) { //向上的 return lastPrev; } else { return -lastNext; } } @Override public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()) { scrollTo(0, mScroller.getCurrY()); postInvalidate(); } }}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <kotlindemo.zhoujian.com.customeview.MyScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="fitXY" android:src="@mipmap/test1"/> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="fitXY" android:src="@mipmap/test2"/> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="fitXY" android:src="@mipmap/test3"/> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="fitXY" android:src="@mipmap/test4"/> </kotlindemo.zhoujian.com.customeview.MyScrollView></LinearLayout>
显示效果:
事件拦截机制分析
一个技术总监- ViewGroupA,最外层的ViewGroup
一个项目经理 - ViewGroupB,中间的ViewGroup
一个干活的程序员 - View,最底层的View
ViewGroup 的分发事件有三个方法:
dispatchTouchEvent(MotionEvent ev);
onInterceptTouchEvent(MotionEvent ev);
onTouchEvent(MotionEvent event);
View的分发事件只有两个方法:
dispatchTouchEvent(MotionEvent ev);
onTouchEvent(MotionEvent event);
事件的传递顺序是:
技术总监(ViewGroupA) -> 项目经理 (ViewGroupB) -> 程序员(View)。事件传递的时候,先执行dispatchTouchEvent()方法,再执行onInterceptTouchEvent()方法。
true:拦截 false:不拦截,继续传递
事件处理的顺序是:
程序员(View)-> 项目经理 (ViewGroupB) -> 技术总监(ViewGroupA)。事件处理执行onTouchEvent()方法
true:表示自己处理
false:自己不处理,交给上级处理
- Android 控件架构与自定义控件(四)
- Android控件架构与自定义控件详解
- Android控件架构与自定义控件(一)
- Android控件架构与自定义控件(二)
- Android控件架构与自定义控件详解
- Android控件架构与自定义控件详解
- Android控件架构与自定义控件
- Android控件架构与自定义控件
- Android 控件架构与自定义控件详解
- Android 控件架构与自定义控件(一)
- Android 控件架构与自定义控件(二)
- Android 控件架构与自定义控件(三)
- Android控件架构与自定义控件
- 3.5.Android控件架构与自定义控件详解之自定义View(四)
- Android控件架构与自定义控件详解(四)——事件拦截机制分析
- Android控件架构与自定义控件详解(四)事件拦截机制分析
- Android群英传之Android控件架构与自定义控件
- Android控件架构与自定义控件详解(一)
- 错误中学习--ssh整合问题
- Mac系统启动MySQL的federated引擎
- 6.17学习总结 Android Studio去除头部标题
- Spring Cloud构建微服务架构(六)高可用服务注册中心
- Script
- Android 控件架构与自定义控件(四)
- java swing编程学习
- php5.3+apache2.2配置要点
- Spring Cloud构建微服务架构(七)消息总线
- Activity的生命周期和Activity间
- 【转】Qt中mouseMoveEvent和mousePressEvent实现鼠标滑动换label颜色
- bitcoin软件安装
- 执行环境及作用域
- 在Linux上配置发布网站以及使用nginx搭建集群