Android自定义View探索(二)—常用工具

简介: 本文转载自:自定义View系列教程01–常用工具介绍在自定义View的时候,常常会用到一些Android系统提供的工具。这些工具封装了我们经常会用到的方法,比如拖拽View,计算滑动速度,View的滚动,手势处理等等。

本文转载自:自定义View系列教程01–常用工具介绍

在自定义View的时候,常常会用到一些Android系统提供的工具。这些工具封装了我们经常会用到的方法,比如拖拽View,计算滑动速度,View的滚动,手势处理等等。如果我们自己去实现这些方法会比较繁琐,而且容易出一些bug。所以了解熟悉这些常用的工具,对我们后续的学习和工作有很大帮助。

Configuration:

Configuration用来描述设备的配置信息。比如用户的配置信息:locale和scaling等等 ,比如设备的相关信息:输入模式,屏幕大小, 屏幕方向等等。

我们可以采用如下方式来获取需要的相关信息:

Configuration configuration=getResources().getConfiguration();
//获取国家码
int countryCode=configuration.mcc;
//获取网络码
int networkCode=configuration.mnc;
//判断横竖屏
if(configuration.orientation==Configuration.ORIENTATION_PORTRAIT){
   } else {
}

ViewConfiguration:

ViewConfiguration提供了一些自定义控件用到的标准常量,比如尺寸大小,滑动距离,敏感度等等。

可以利用ViewConfiguration的静态方法获取一个实例

ViewConfiguration viewConfiguration=ViewConfiguration.get(context);

介绍ViewConfiguration的几个对象方法:

ViewConfiguration  viewConfiguration=ViewConfiguration.get(context);
//获取touchSlop。该值表示系统所能识别出的被认为是滑动的最小距离
int touchSlop = viewConfiguration.getScaledTouchSlop();
//获取Fling速度的最小值和最大值
int minimumVelocity = viewConfiguration.getScaledMinimumFlingVelocity();
int maximumVelocity = viewConfiguration.getScaledMaximumFlingVelocity();
//判断是否有物理按键
boolean isHavePermanentMenuKey=viewConfiguration.hasPermanentMenuKey();

//双击间隔时间.在该时间内是双击,否则是单击
int doubleTapTimeout=ViewConfiguration.getDoubleTapTimeout();
//按住状态转变为长按状态需要的时间
int longPressTimeout=ViewConfiguration.getLongPressTimeout();
//重复按键的时间
int keyRepeatTimeout=ViewConfiguration.getKeyRepeatTimeout();

GestureDetector:

GestureDetector是Android给我们提供的一个手势处理的工具,利用GestureDetector可以简化许多操作,轻松实现一些常用的功能。

一起看看怎么使用的:

第一步:实现OnGestureListener

private class GestureListenerImpl implements GestureDetector.OnGestureListener {
        //触摸屏幕时均会调用该方法
        @Override
        public boolean onDown(MotionEvent e) {
            System.out.println("---> 手势中的onDown方法");
            return false;
        }

        //手指在屏幕上拖动时会调用该方法
        @Override
        public boolean onFling(MotionEvent e1,MotionEvent e2, float velocityX,float velocityY) {
            System.out.println("---> 手势中的onFling方法");
            return false;
        }

        //手指长按屏幕时均会调用该方法
        @Override
        public void onLongPress(MotionEvent e) {
            System.out.println("---> 手势中的onLongPress方法");
        }

        //手指在屏幕上滚动时会调用该方法
        @Override
        public boolean onScroll(MotionEvent e1,MotionEvent e2, float distanceX,float distanceY) {
            System.out.println("---> 手势中的onScroll方法");
            return false;
        }

        //手指在屏幕上按下,且未移动和松开时调用该方法
        @Override
        public void onShowPress(MotionEvent e) {
            System.out.println("---> 手势中的onShowPress方法");
        }

        //轻击屏幕时调用该方法
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            System.out.println("---> 手势中的onSingleTapUp方法");
            return false;
        }
    }

第二步:生成GestureDetector对象

GestureDetector gestureDetector = new GestureDetector(context,new
GestureListenerImpl());

这里的GestureListenerImpl就是GestureListener监听器的实现。

第三步:将Touch事件交给GestureDetector处理
比如将Activity的Touch事件交给GestureDetector处理

@Override  
public boolean onTouchEvent(MotionEvent event) {  
     return mGestureDetector.onTouchEvent(event);  
} 

比如将View的Touch事件交给GestureDetector处理

mButton=(Button) findViewById(R.id.button);  
mButton.setOnTouchListener(new OnTouchListener() {            
   @Override  
   public boolean onTouch(View arg0, MotionEvent event) {  
          return mGestureDetector.onTouchEvent(event);  
      }  
});

VelocityTracker :

VelocityTracker用于跟踪触摸屏事件(比如,Flinging及其他Gestures手势事件等)的速率。

第一步:开始速度追踪

private void startVelocityTracker(MotionEvent event) {  
    if (mVelocityTracker == null) {  
         mVelocityTracker = VelocityTracker.obtain();  
     }  
     mVelocityTracker.addMovement(event);  
} 

在这里我们初始化VelocityTracker,并且把要追踪的MotionEvent注册到VelocityTracker的监听中。

第二步:获取追踪到的速度

private int getScrollVelocity() {  
     // 设置VelocityTracker单位.1000表示1秒时间内运动的像素  
     mVelocityTracker.computeCurrentVelocity(1000);  
     // 获取在1秒内X方向所滑动像素值  
     int xVelocity = (int) mVelocityTracker.getXVelocity();  
     return Math.abs(xVelocity);  
    } 

同理可以获取1秒内Y方向所滑动像素值

第三步:解除速度追踪

private void stopVelocityTracker() {  
      if (mVelocityTracker != null) {  
          mVelocityTracker.recycle();  
          mVelocityTracker = null;  
      }  
}

Scroller:

Scroller挺常见的,用的比较多了。在此只强调几个重要的问题,别的就不再赘述了。

第一点:scrollTo()和scrollBy()的关系
先看scrollBy( )的源码

public void scrollBy(int x, int y) {   
       scrollTo(mScrollX + x, mScrollY + y);   
} 

这就是说scrollBy( )调用了scrollTo( ),最终起作用的是scrollTo( )方法。

第二点:scroll的本质
scrollTo( )和scrollBy( )移动的只是View的内容,而且View的背景是不移动的。

第三点:scrollTo( )和scrollBy( )方法的坐标说明

比如我们对于一个TextView调用scrollTo(0,25) ;那么该TextView中的content(比如显示的文字:Hello)会怎么移动呢?
向下移动25个单位?不!恰好相反!!这是为什么呢?
因为调用该方法会导致视图重绘,即会调用

public void invalidate(int l, int t, int r, int b)

此处的l,t,r,b四个参数就表示View原来的坐标.
在该方法中最终会调用:

tmpr.set(l - scrollX, t - scrollY, r - scrollX, b - scrollY);
p.invalidateChild(this, tmpr);

其中tmpr是一个Rect,this是原来的View;通过这两行代码就把View在一个Rect中重绘。
请注意第一行代码:
原来的l和r均减去了scrollX
原来的t和b均减去了scrollY
就是说scrollX如果是正值,那么重绘后的View的宽度反而减少了;反之同理
就是说scrollY如果是正值,那么重绘后的View的高度反而减少了;反之同理
所以,TextView调用scrollTo(0,25)和我们的理解相反

ViewDragHelper:

在项目中很多场景需要用户手指拖动其内部的某个View,此时就需要在onInterceptTouchEvent()和onTouchEvent()这两个方法中写不少逻辑了,比如处理:拖拽移动,越界,多手指的按下,加速度检测等等。
ViewDragHelper可以极大的帮我们简化类似的处理,它提供了一系列用于处理用户拖拽子View的辅助方法和与其相关的状态记录。比较常见的:QQ侧滑菜单,Navigation Drawer的边缘滑动,都可以由它实现。

ViewDragHelper的使用并不复杂,在此通过一个示例展示其常用的用法。


public class MyLinearLayout extends LinearLayout {
    private ViewDragHelper mViewDragHelper;

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

    //初始化ViewDragHelper
    private void initViewDragHelper() {
        mViewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
            @Override
            public boolean tryCaptureView(View child, int pointerId) {
                return true;
            }

            //处理水平方向的越界
            @Override
            public int clampViewPositionHorizontal(View child, int left, int dx) {
                int fixedLeft;
                View parent = (View) child.getParent();
                int leftBound = parent.getPaddingLeft();
                int rightBound = parent.getWidth() - child.getWidth() - parent.getPaddingRight();

                if (left < leftBound) {
                    fixedLeft = leftBound;
                } else if (left > rightBound) {
                    fixedLeft = rightBound;
                } else {
                    fixedLeft = left;
                }
                return fixedLeft;
            }

            //处理垂直方向的越界
            @Override
            public int clampViewPositionVertical(View child, int top, int dy) {
                int fixedTop;
                View parent = (View) child.getParent();
                int topBound = getPaddingTop();
                int bottomBound = getHeight() - child.getHeight() - parent.getPaddingBottom();
                if (top < topBound) {
                    fixedTop = topBound;
                } else if (top > bottomBound) {
                    fixedTop = bottomBound;
                } else {
                    fixedTop = top;
                }
                return fixedTop;
            }

            //监听拖动状态的改变
            @Override
            public void onViewDragStateChanged(int state) {
                super.onViewDragStateChanged(state);
                switch (state) {
                    case ViewDragHelper.STATE_DRAGGING:
                        System.out.println("STATE_DRAGGING");
                        break;
                    case ViewDragHelper.STATE_IDLE:
                        System.out.println("STATE_IDLE");
                        break;
                    case ViewDragHelper.STATE_SETTLING:
                        System.out.println("STATE_SETTLING");
                        break;
                }
            }

            //捕获View
            @Override
            public void onViewCaptured(View capturedChild, int activePointerId) {
                super.onViewCaptured(capturedChild, activePointerId);
                System.out.println("ViewCaptured");
            }

            //释放View
            @Override
            public void onViewReleased(View releasedChild, float xvel, float yvel) {
                super.onViewReleased(releasedChild, xvel, yvel);
                System.out.println("ViewReleased");
            }
        });
    }

    //将事件拦截交给ViewDragHelper处理
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }


    //将Touch事件交给ViewDragHelper处理
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        mViewDragHelper.processTouchEvent(ev);
        return true;
    }
}

从这个例子可以看出来ViewDragHelper是作用在ViewGroup上的(比如LinearLayout)而不是直接作用到某个被拖拽的子View。其实这也不难理解,因为子View在布局中的位置是其所在的ViewGroup决定的。

在该例中ViewDragHelper做了如下主要操作:

(1) ViewDragHelper接管了ViewGroup的事件拦截
(2) ViewDragHelper接管了ViewGroup的Touch事件
(3) ViewDragHelper处理了拖拽子View时的边界越界
(4) ViewDragHelper监听拖拽子View时的状态变化

除了这些常见的操作,ViewDragHelper还可以实现:抽屉拉伸,拖拽结束松手后子View自动返回到原位等复杂操作。

接下来会带来几个自定义View的实战,下一篇文章见~~~

目录
相关文章
|
18天前
|
供应链 物联网 区块链
未来触手可及:探索新兴技术的趋势与应用安卓开发中的自定义视图:从基础到进阶
【8月更文挑战第30天】随着科技的飞速发展,新兴技术如区块链、物联网和虚拟现实正在重塑我们的世界。本文将深入探讨这些技术的发展趋势和应用场景,带你领略未来的可能性。
|
2天前
|
XML 编解码 Android开发
安卓开发中的自定义视图控件
【9月更文挑战第14天】在安卓开发中,自定义视图控件是一种高级技巧,它可以让开发者根据项目需求创建出独特的用户界面元素。本文将通过一个简单示例,引导你了解如何在安卓项目中实现自定义视图控件,包括创建自定义控件类、处理绘制逻辑以及响应用户交互。无论你是初学者还是有经验的开发者,这篇文章都会为你提供有价值的见解和技巧。
|
3天前
|
前端开发 Android开发 开发者
安卓应用开发中的自定义视图基础
【9月更文挑战第13天】在安卓开发的广阔天地中,自定义视图是一块神奇的画布,它允许开发者将想象力转化为用户界面的创新元素。本文将带你一探究竟,了解如何从零开始构建自定义视图,包括绘图基础、触摸事件处理,以及性能优化的实用技巧。无论你是想提升应用的视觉吸引力,还是追求更流畅的交互体验,这里都有你需要的金钥匙。
|
6天前
|
缓存 搜索推荐 Android开发
安卓应用开发中的自定义View组件实践
【9月更文挑战第10天】在安卓开发领域,自定义View是提升用户体验和实现界面个性化的重要手段。本文将通过一个实际案例,展示如何在安卓项目中创建和使用自定义View组件,包括设计思路、实现步骤以及可能遇到的问题和解决方案。文章不仅提供了代码示例,还深入探讨了自定义View的性能优化技巧,旨在帮助开发者更好地掌握这一技能。
|
8天前
|
Android开发
Android中SurfaceView的双缓冲机制和普通View叠加问题解决办法
本文介绍了 Android 平台上的 SurfaceView,这是一种高效的图形渲染控件,尤其适用于视频播放、游戏和图形动画等场景。文章详细解释了其双缓冲机制,该机制通过前后缓冲区交换来减少图像闪烁,提升视觉体验。然而,SurfaceView 与普通 View 叠加时可能存在 Z-Order 不一致、同步问题及混合渲染难题。文中提供了使用 TextureView、调整 Z-Order 和创建自定义组合控件等多种解决方案。
39 9
|
12天前
|
Android开发 容器
Android经典实战之如何获取View和ViewGroup的中心点
本文介绍了在Android中如何获取`View`和`ViewGroup`的中心点坐标,包括计算相对坐标和屏幕上的绝对坐标,并提供了示例代码。特别注意在视图未完成测量时可能出现的宽高为0的问题及解决方案。
24 7
|
18天前
|
XML 搜索推荐 Android开发
安卓开发中的自定义View组件实践
【8月更文挑战第30天】探索Android世界,自定义View是提升应用界面的关键。本文以简洁的语言带你了解如何创建自定义View,从基础到高级技巧,一步步打造个性化的UI组件。
|
10天前
|
前端开发 搜索推荐 Android开发
探索安卓开发中的自定义视图##
【9月更文挑战第6天】 在安卓应用开发的世界里,自定义视图如同绘画艺术中的色彩,它们为界面设计增添了无限可能。通过掌握自定义视图的绘制技巧,开发者能够创造出既符合品牌形象又提升用户体验的独特界面元素。本文将深入浅出地介绍如何从零开始构建一个自定义视图,包括基础框架搭建、关键绘图方法实现、事件处理机制以及性能优化策略。准备好让你的安卓应用与众不同了吗?让我们开始吧! ##
|
Android开发
【Android 逆向】Android 逆向通用工具开发 ( Android 平台运行的 cmd 程序类型 | Android 平台运行的 cmd 程序编译选项 | 编译 cmd 可执行程序 )(二)
【Android 逆向】Android 逆向通用工具开发 ( Android 平台运行的 cmd 程序类型 | Android 平台运行的 cmd 程序编译选项 | 编译 cmd 可执行程序 )(二)
159 0
【Android 逆向】Android 逆向通用工具开发 ( Android 平台运行的 cmd 程序类型 | Android 平台运行的 cmd 程序编译选项 | 编译 cmd 可执行程序 )(二)
|
移动开发 Android开发 C++
【Android 逆向】Android 逆向通用工具开发 ( Android 平台运行的 cmd 程序类型 | Android 平台运行的 cmd 程序编译选项 | 编译 cmd 可执行程序 )(一)
【Android 逆向】Android 逆向通用工具开发 ( Android 平台运行的 cmd 程序类型 | Android 平台运行的 cmd 程序编译选项 | 编译 cmd 可执行程序 )(一)
139 0
【Android 逆向】Android 逆向通用工具开发 ( Android 平台运行的 cmd 程序类型 | Android 平台运行的 cmd 程序编译选项 | 编译 cmd 可执行程序 )(一)