关于 WheelView 组件的源码分析_Android开发_网络编程_码蚁之家_www.codes51.com
返回首页
专题
网络编程
ASP教程 .NET教程 PHP教程 JSP教程 C#教程 Java教程 Delphi教程 VB教程 C/C++教程 Android开发 IOS开发 Windows Phone开发 Python教程 Ruby教程 移动开发 其他编程教程
网页制作
HTML教程 CSS教程 Dreamweaver教程 FrontPages教程 Javascript教程 web前端
数据库
SqlServer MySql Oracle Access DB2 SQLite 其他数据库
图形设计
photoshop教程 Fireworks教程 CorelDraw教程 Illustrator教程 AutoCad教程 FLASH教程
操作系统
Windows xp教程 Windows 7教程 Windows 8教程 Windows 2003教程 Windows Server 2008教程 Linux教程 Windows 10
网站运营
建站经验 SEO优化 站长心得 网赚技巧 网站推广 站长故事
手机学院
手机速递 安卓教程 iphone教程 手机评测 手机技巧 手机知识 手机应用 手机游戏 手机导购
网店宝典
开店指导 开店经验 网店装修 网店推广 网店seo 网购技巧
软件教程
办公软件 系统工具 媒体工具 压缩工具 图文处理 文件管理
范文之家
自我介绍 自我鉴定 写作模板 合同范本 工作总结 贺词祝福语 演讲致辞 思想汇报 入党申请书 实习报告 心得体会 工作计划 简历模板 工作报告 导游词 评语寄语 口号大全 策划书范文
信息工程
软件工程 企业开发 系统运维 软件测试
移民之家
移民动态 移民政策 移民百科 移民生活 技术移民 投资移民
知识大全
母婴 数码 摄影 装修 美文 常识 时尚 婚嫁 美食 养生 旅游 兴趣 职场 教育 文学 健康
问答大全
电脑网络 手机数码 QQ专区 生活 游戏 体育运动 娱乐明星 休闲爱好 文化艺术 社会民生 教育科学 健康医疗 商业理财 情感家庭 地区问题 其他
编程问答
IOS Android .NET Java C/C++ Delphi VC/MFC 其他语言 PHP MSSQL MYSQL Oracle 其他数据库 Web开发 Windows Linux 硬件/嵌入开发 网络通信 移动开发 云计算 企业IT 游戏开发
笑话大全
幽默笑话 爱情笑话 成人笑话 校园笑话 爆笑笑话 综合笑话 古代笑话 现代笑话 国外笑话

关于 WheelView 组件的源码分析(1/4)

来源:互联网  时间:2014/11/29 23:40:08


我们都知道,在iOS里面有一种控件------滚筒控件(Wheel View),这通常用于设置时间/日期,非常方便,但Android SDK并没有提供类似的控件。这里介绍一下如何Android实现WheelView。


代码下载

-- GitHub : https://github.com/han1202012/WheelViewDemo.git 

-- CSDN : http://download.csdn.net/detail/han1202012/8208997 ;



博客总结 :

 

博文内容 : 本文完整地分析了 WheelView 所有的源码, 包括其适配器类型, 两种回调接口 (选中条目改变回调, 和开始结束滚动回调), 以及详细的分析了 WheelView 主题源码, 其中 组件宽高测量, 手势监听器添加, 以及精准的绘图方法是主要目的, 花了将近1周时间, 感觉很值, 在这里分享给大家;


WheelView 使用方法 : 创建 WheelView 组件 --> 设置显示条目数 --> 设置循环 --> 设置适配器 --> 设置监听器 ;


自定义组件宽高获取策略 : MeasureSpec 最大模式 取 默认值 和 给定值中较小的那个, 未定义模式取默认值, 精准模式取 给定值;


自定义组件维护各种回调监听器策略 : 维护集合, 将监听器置于集合中, 回调接口时遍历集合元素, 回调每个元素的接口方法;


自定义组件手势监听器添加方法 : 创建手势监听器, 将手势监听器传入手势探测器, 在 onTouchEvent() 方法中回调手势监听器的 onTouchEvent()方法;



一. WheelView 简介



1. WheelView 效果


在 Android 中实现类似与 IOS 的 WheelView 控件 : 如图 




2. WheelView 使用流程



(1) 基本流程简介

 

获取组件 --> 设置显示条目数 --> 设置循环 --> 设置适配器 --> 设置条目改变监听器 --> 设置滚动监听器


a. 创建 WheelView 组件 : 使用 构造方法 或者 从布局文件获取 WheelView 组件;

b. 设置显示条目数 : 调用 WheelView 组件对象的 setVisibleItems 方法 设置;

c. 设置是否循环 : 设置 WheelView 是否循环, 调用 setCyclic() 方法设置;

d. 设置适配器 : 调用 WheelView 组件的 setAdapter() 方法设置;

e. 设置条目改变监听器 : 调用 WheelView 组件对象的 addChangingListener() 方法设置;

f. 设置滚动监听器 : 调用 WheelView 组件对象的 addScrollingListener() 方法设置;



(2) 代码实例


a. 创建 WheelView 对象

        //创建 WheelView 组件
        final WheelView wheelLeft = new WheelView(context);

b. 设置 WheelView 显示条目数

        //设置 WheelView 组件最多显示 5 个元素
        wheelLeft.setVisibleItems(5);

c. 设置 WheelView 是否滚动循环

        //设置 WheelView 元素是否循环滚动
        wheelLeft.setCyclic(false);

d. 设置 WheelView 适配器

        //设置 WheelView 适配器
        wheelLeft.setAdapter(new ArrayWheelAdapter<String>(left));

e. 设置条目改变监听器

        //为左侧的 WheelView 设置条目改变监听器
        wheelLeft.addChangingListener(new OnWheelChangedListener() {
            @Override
            public void onChanged(WheelView wheel, int oldValue, int newValue) {
            	//设置右侧的 WheelView 的适配器
                wheelRight.setAdapter(new ArrayWheelAdapter<String>(right[newValue]));
                wheelRight.setCurrentItem(right[newValue].length / 2);
            }
        });

f. 设置滚动监听器

        wheelLeft.addScrollingListener(new OnWheelScrollListener() {
			
			@Override
			public void onScrollingStarted(WheelView wheel) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void onScrollingFinished(WheelView wheel) {
				// TODO Auto-generated method stub
				
			}
		});




二. WheelView  适配器 监听器 相关接口分析



1. 适配器 分析



这里定义了一个适配器接口, 以及两个适配器类, 一个用于任意类型的数据集适配, 一个用于数字适配;


适配器操作 : 在 WheelView.java 中通过 setAdapter(WheelAdapter adapter) 和 getAdapter() 方法设置 获取 适配器;

-- 适配器常用操作 : 在 WheelView 中定义了 getItem(), getItemsCount(), getMaxmiumLength() 方法获取 适配器的相关信息;

    /**
     * 获取该 WheelView 的适配器
     * 
     * @return 
     * 		返回适配器
     */
    public WheelAdapter getAdapter() {
        return adapter;
    }

    /**
     * 设置适配器
     * 
     * @param adapter
     *            要设置的适配器
     */
    public void setAdapter(WheelAdapter adapter) {
        this.adapter = adapter;
        invalidateLayouts();
        invalidate();
    }




(1) 适配器接口 ( interface WheelAdapter )


适配器接口WheelAdapter;

-- 接口作用 : 该接口是所有适配器的接口, 适配器类都需要实现该接口;


接口抽象方法介绍

-- getItemsCount() : 获取适配器数据集合中元素个数;

    /**
     * 获取条目的个数
     * 
     * @return 
     * 		WheelView 的条目个数
     */
    public int getItemsCount();


-- getItem(int index) : 获取适配器集合的中指定索引元素;

    /**
     * 根据索引位置获取 WheelView 的条目
     * 
     * @param index
     *            条目的索引
     * @return 
     * 		WheelView 上显示的条目的值
     */
    public String getItem(int index);


-- getMaximumLength() : 获取 WheelView 在界面上的显示宽度;

    /**
     * 获取条目的最大长度. 用来定义 WheelView 的宽度. 如果返回 -1, 就会使用默认宽度
     * 
     * @return 
     * 		条目的最大宽度 或者 -1
     */
    public int getMaximumLength();



(2) 数组适配器 ( class ArrayWheelAdapter<T> implements WheelAdapter )


适配器作用 : 该适配器可以传入任何数据类型的数组, 可以是 字符串数组, 也可以是任何对象的数组, 传入的数组作为适配器的数据源;


成员变量分析

-- 数据源

    /** 适配器的数据源 */
    private T items[];


-- WheelView 最大宽度

    /** WheelView 的宽度 */
    private int length;



构造方法分析

-- ArrayWheelAdapter(T items[], int length) : 传入 T 类型 对象数组, 以及 WheelView 的宽度;

    /**
     * 构造方法
     * 
     * @param items
     *            适配器数据源 集合 T 类型的数组
     * @param length
     *            适配器数据源 集合 T 数组长度
     */
    public ArrayWheelAdapter(T items[], int length) {
        this.items = items;
        this.length = length;
    }


-- ArrayWheelAdapter(T items[]) : 传入 T 类型对象数组, 宽度使用默认的宽度;

    /**
     * 构造方法
     * 
     * @param items
     *            适配器数据源集合 T 类型数组
     */
    public ArrayWheelAdapter(T items[]) {
        this(items, DEFAULT_LENGTH);
    }


实现的父类方法分析 :

--  getItem(int index) : 根据索引获取数组中对应位置的对象的字符串类型;

    @Override
    public String getItem(int index) {
    	//如果这个索引值合法, 就返回 item 数组对应的元素的字符串形式
        if (index >= 0 && index < items.length) {
            return items[index].toString();
        }
        return null;
    }


-- getItemsCount() : 获取数据集广大小, 直接返回数组大小;

    @Override
    public int getItemsCount() {
    	//返回 item 数组的长度
        return items.length;
    }


-- getMaximumLength() : 获取 WheelView 的最大宽度;

    @Override
    public int getMaximumLength() {
    	//返回 item 元素的宽度
        return length;
    }



(3) 数字适配器 ( class NumericWheelAdapter implements WheelAdapter )


NumericWheelAdapter 适配器作用 : 数字作为 WheelView 适配器的显示值;



成员变量分析

-- 最小值 : WheelView 数值显示的最小值;

    /** 设置的最小值 */
    private int minValue;


-- 最大值 : WheelView 数值显示的最大值;

    /** 设置的最大值 */
    private int maxValue;

-- 格式化字符串 : 用于字符串的格式化;

    /** 格式化字符串, 用于格式化 货币, 科学计数, 十六进制 等格式 */
    private String format;


构造方法分析

-- NumericWheelAdapter() : 默认的构造方法, 使用默认的最大最小值;

    /**
     * 默认的构造方法, 使用默认的最大最小值
     */
    public NumericWheelAdapter() {
        this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE);
    }


-- NumericWheelAdapter(int minValue, int maxValue) : 传入一个最大最小值;

    /**
     * 构造方法
     * 
     * @param minValue
     *            最小值
     * @param maxValue
     *            最大值
     */
    public NumericWheelAdapter(int minValue, int maxValue) {
        this(minValue, maxValue, null);
    }


-- NumericWheelAdapter(int minValue, int maxValue, String format) : 传入最大最小值, 以及数字格式化方式;

    /**
     * 构造方法
     * 
     * @param minValue
     *            最小值
     * @param maxValue
     *            最大值
     * @param format
     *            格式化字符串
     */
    public NumericWheelAdapter(int minValue, int maxValue, String format) {
        this.minValue = minValue;
        this.maxValue = maxValue;
        this.format = format;
    }


实现的父类方法

-- 获取条目 : 如果需要格式化, 先进行格式化;

    @Override
    public String getItem(int index) {
    	String result = "";
        if (index >= 0 && index < getItemsCount()) {
            int value = minValue + index;
            //如果 format 不为 null, 那么格式化字符串, 如果为 null, 直接返回数字
            if(format != null){
            	result = String.format(format, value);
            }else{
            	result = Integer.toString(value);
            }
            return result;
        }
        return null;
    }

-- 获取元素个数

    @Override
    public int getItemsCount() {
    	//返回数字总个数
        return maxValue - minValue + 1;
    }

-- 获取 WheelView 最大宽度

    @Override
    public int getMaximumLength() {
    	//获取 最大值 和 最小值 中的 较大的数字
        int max = Math.max(Math.abs(maxValue), Math.abs(minValue));
        //获取这个数字 的 字符串形式的 字符串长度
        int maxLen = Integer.toString(max).length();
        if (minValue < 0) {
            maxLen++;
        }
        return maxLen;
    }



2. 监听器相关接口



(1) 条目改变监听器 ( interface OnWheelChangedListener )


监听器作用 : 在 WheelView 条目改变的时候, 回调该监听器的接口方法, 执行条目改变对应的操作;


接口方法介绍

-- onChanged(WheelView wheel, int oldValue, int newValue) : 传入 WheelView 组件对象, 以及 旧的 和 新的 条目值索引;

    /**
     * 当前条目改变时回调该方法
     * 
     * @param wheel
     *            条目改变的 WheelView 对象
     * @param oldValue
     *            WheelView 旧的条目值
     * @param newValue
     *            WheelView 新的条目值
     */
    void onChanged(WheelView wheel, int oldValue, int newValue);



(2) 滚动监听器 ( interface OnWheelScrollListener )


滚动监听器作用 : 在 WheelView 滚动动作 开始 和 结束的时候回调对应的方法, 在对应方法中进行相应的操作;



接口方法介绍

-- 开始滚动方法 : 在滚动开始的时候回调该方法;

    /**
     * 在 WheelView 滚动开始的时候回调该接口
     * 
     * @param wheel
     *            开始滚动的 WheelView 对象
     */
    void onScrollingStarted(WheelView wheel);


-- 停止滚动方法 : 在滚动结束的时候回调该方法;

    /**
     * 在 WheelView 滚动结束的时候回调该接口
     * 
     * @param wheel
     *            结束滚动的 WheelView 对象
     */
    void onScrollingFinished(WheelView wheel);



三. WheelView 解析



1. 触摸 点击 手势 动作操作控制组件 模块



(1) 创建手势监听器


手势监听器创建及对应方法

-- onDown(MotionEvent e) : 在按下的时候回调该方法, e 参数是按下的事件;

-- onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) : 滚动的时候回调该方法, e1 滚动第一次按下事件, e2 当前滚动的触摸事件, X 上一次滚动到这一次滚动 x 轴距离, Y 上一次滚动到这一次滚动 y 轴距离;

-- onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) : 快速急冲滚动时回调的方法, e1 e2 与上面参数相同, velocityX 是手势在 x 轴的速度, velocityY 是手势在 y 轴的速度;

-- 代码示例

        /*
         * 手势监听器监听到 滚动操作后回调
         * 
         * 参数解析 : 
         * MotionEvent e1 : 触发滚动时第一次按下的事件
         * MotionEvent e2 : 触发当前滚动的移动事件
         * float distanceX : 自从上一次调用 该方法 到这一次 x 轴滚动的距离, 
         * 				注意不是 e1 到 e2 的距离, e1 到 e2 的距离是从开始滚动到现在的滚动距离
         * float distanceY : 自从上一次回调该方法到这一次 y 轴滚动的距离
         * 
         * 返回值 : 如果事件成功触发, 执行完了方法中的操作, 返回true, 否则返回 false 
         * (non-Javadoc)
         * @see android.view.GestureDetector.SimpleOnGestureListener#onScroll(android.view.MotionEvent, android.view.MotionEvent, float, float)
         */
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        	//开始滚动, 并回调滚动监听器集合中监听器的 开始滚动方法
            startScrolling();
            doScroll((int) -distanceY);
            return true;
        }

        /*
         * 当一个急冲手势发生后 回调该方法, 会计算出该手势在 x 轴 y 轴的速率
         * 
         * 参数解析 : 
         * -- MotionEvent e1 : 急冲动作的第一次触摸事件;
         * -- MotionEvent e2 : 急冲动作的移动发生的时候的触摸事件;
         * -- float velocityX : x 轴的速率
         * -- float velocityY : y 轴的速率
         * 
         * 返回值 : 如果执行完毕返回 true, 否则返回false, 这个就是自己定义的
         * 
         * (non-Javadoc)
         * @see android.view.GestureDetector.SimpleOnGestureListener#onFling(android.view.MotionEvent, android.view.MotionEvent, float, float)
         */
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        	//计算上一次的 y 轴位置, 当前的条目高度 加上 剩余的 不够一行高度的那部分
            lastScrollY = currentItem * getItemHeight() + scrollingOffset;
            //如果可以循环最大值是无限大, 不能循环就是条目数的高度值
            int maxY = isCyclic ? 0x7FFFFFFF : adapter.getItemsCount() * getItemHeight();
            int minY = isCyclic ? -maxY : 0;
            /*
             * Scroll 开始根据一个急冲手势滚动, 滚动的距离与初速度有关
             * 参数介绍 : 
             * -- int startX : 开始时的 X轴位置
             * -- int startY : 开始时的 y轴位置
             * -- int velocityX : 急冲手势的 x 轴的初速度, 单位 px/s
             * -- int velocityY : 急冲手势的 y 轴的初速度, 单位 px/s
             * -- int minX : x 轴滚动的最小值
             * -- int maxX : x 轴滚动的最大值
             * -- int minY : y 轴滚动的最小值
             * -- int maxY : y 轴滚动的最大值
             */
            scroller.fling(0, lastScrollY, 0, (int) -velocityY / 2, 0, 0, minY, maxY);
            setNextMessage(MESSAGE_SCROLL);
            return true;
        }
    };



(2) 创建手势探测器


手势探测器创建 : 调用 其构造函数, 传入 上下文对象 和 手势监听器对象;

-- 禁止长按操作 : 调用 setIsLongpressEnabled(false) 方法, 禁止长按操作, 因为 长按操作会屏蔽滚动事件;

    	//创建一个手势处理
        gestureDetector = new GestureDetector(context, gestureListener);
        /*
         * 是否允许长按操作, 
         * 如果设置为 true 用户按下不松开, 会返回一个长按事件, 
         * 如果设置为 false, 按下不松开滑动的话 会收到滚动事件.
         */
        gestureDetector.setIsLongpressEnabled(false);



(3) 将手势探测器 与 组件结合


关联手势探测器 与 组件 : 在组件的 onTouchEvent(MotionEvent event) 方法中, 调用手势探测器的 gestureDetector.onTouchEvent(event) 方法即可;

    /*
     * 继承自 View 的触摸事件, 当出现触摸事件的时候, 就会回调该方法
     * (non-Javadoc)
     * @see android.view.View#onTouchEvent(android.view.MotionEvent)
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
    	//获取适配器
        WheelAdapter adapter = getAdapter();
        if (adapter == null) {
            return true;
        }

        /*
         * gestureDetector.onTouchEvent(event) : 分析给定的动作, 如果可用, 调用 手势检测器的 onTouchEvent 方法
         * -- 参数解析 : ev , 触摸事件
         * -- 返回值 : 如果手势监听器成功执行了该方法, 返回true, 如果执行出现意外 返回 false;
         */
        if (!gestureDetector.onTouchEvent(event) && event.getAction() == MotionEvent.ACTION_UP) {
            justify();
        }
        return true;
    }





上一篇nodejs常见的读取文件内容的方法
下一篇Android 使用Gradle动态生成多渠道的APK
明星图片
相关文章
《关于 WheelView 组件的源码分析》由码蚁之家搜集整理于网络,
联系邮箱:mxgf168#qq.com(#改为@)