网友通过本文主要向大家介绍了android滚轮控件,android滚轮选择器,android 仿ios滚轮,android 滚轮,android时间滚轮等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com
Android自定义实现循环滚轮控件WheelView
首先呈上效果图
现在很多地方都用到了滚轮布局WheelView,比如在选择生日的时候,风格类似系统提供的DatePickerDialog,开源的控件也有很多,不过大部分都是根据当前项目的需求绘制的界面,因此我就自己写了一款比较符合自己项目的WheelView。
首先这个控件有以下的需求:
1、能够循环滚动,当向上或者向下滑动到临界值的时候,则循环开始滚动
2、中间的一块有一块半透明的选择区,滑动结束时,哪一块在这个选择区,就选择这快。
3、继承自View进行绘制
然后进行一些关键点的讲解:
1、整体控件继承自View,在onDraw中进行绘制。整体包含三个模块,整个View、每一块的条目、中间选择区的条目(额外绘制一块灰色区域)。
2、通过动态设置或者默认设置的可显示条目数,在最上和最下再各加入一块,意思就是一共绘制showCount+2个条目。
3、当最上面的条目数滑动超过条目高度的一半时,进行动态条目更新:将最下面的条目删除加入第一个条目、将第一个条目删除加入最下面的条目。
4、外界可设置条目显示数、字体大小、颜色、选择区提示文字(图中那个年字)、默认选择项、padding补白等等。
5、在onTouchEvent中,得到手指滑动的渐变值,动态更新当前所有的条目。
6、在onMeasure中动态计算宽度,所有条目的宽度、高度、起始Y坐标等等。
7、通过当前条目和被选择条目的坐标,超过一半则视为被选择,并且滑动到对应的位置。
下面的是WheelView代码,主要是计算初始值、得到外面设置的值:
package cc.wxf.view.wheel; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import java.util.ArrayList; import java.util.List; /** * Created by ccwxf on 2016/3/31. */ public class WheelView extends View { public static final int FONT_COLOR = Color.BLACK; public static final int FONT_SIZE = 30; public static final int PADDING = 10; public static final int SHOW_COUNT = 3; public static final int SELECT = 0; //总体宽度、高度、Item的高度 private int width; private int height; private int itemHeight; //需要显示的行数 private int showCount = SHOW_COUNT; //当前默认选择的位置 private int select = SELECT; //字体颜色、大小、补白 private int fontColor = FONT_COLOR; private int fontSize = FONT_SIZE; private int padding = PADDING; //文本列表 private Listlists; //选中项的辅助文本,可为空 private String selectTip; //每一项Item和选中项 private List wheelItems = new ArrayList (); private WheelSelect wheelSelect = null; //手点击的Y坐标 private float mTouchY; //监听器 private OnWheelViewItemSelectListener listener; public WheelView(Context context) { super(context); } public WheelView(Context context, AttributeSet attrs) { super(context, attrs); } public WheelView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /** * 设置字体的颜色,不设置的话默认为黑色 * @param fontColor * @return */ public WheelView fontColor(int fontColor){ this.fontColor = fontColor; return this; } /** * 设置字体的大小,不设置的话默认为30 * @param fontSize * @return */ public WheelView fontSize(int fontSize){ this.fontSize = fontSize; return this; } /** * 设置文本到上下两边的补白,不合适的话默认为10 * @param padding * @return */ public WheelView padding(int padding){ this.padding = padding; return this; } /** * 设置选中项的复制文本,可以不设置 * @param selectTip * @return */ public WheelView selectTip(String selectTip){ this.selectTip = selectTip; return this; } /** * 设置文本列表,必须且必须在build方法之前设置 * @param lists * @return */ public WheelView lists(List lists){ this.lists = lists; return this; } /** * 设置显示行数,不设置的话默认为3 * @param showCount * @return */ public WheelView showCount(int showCount){ if(showCount % 2 == 0){ throw new IllegalStateException("the showCount must be odd"); } this.showCount = showCount; return this; } /** * 设置默认选中的文本的索引,不设置默认为0 * @param select * @return */ public WheelView select(int select){ this.select = select; return this; } /** * 最后调用的方法,判断是否有必要函数没有被调用 * @return */ public WheelView build(){ if(lists == null){ throw new IllegalStateException("this method must invoke after the method [lists]"); } return this; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //得到总体宽度 width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); // 得到每一个Item的高度 Paint mPaint = new Paint(); mPaint.setTextSize(fontSize); Paint.FontMetrics metrics = mPaint.getFontMetrics(); itemHeight = (int) (metrics.bottom - metrics.top) + 2 * padding; //初始化每一个WheelItem initWheelItems(width, itemHeight); //初始化WheelSelect wheelSelect = new WheelSelect(showCount / 2 * itemHeight, width, itemHeight, selectTip, fontColor, fontSize, padding); //得到所有的高度 height = itemHeight * showCount; super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); } /** * 创建显示个数+2个WheelItem * @param width * @param itemHeight */ private void initWheelItems(int width, int itemHeight) { wheelItems.clear(); for(int i = 0; i < showCount + 2; i++){ int startY = itemHeight * (i - 1); int stringIndex = select - showCount / 2 - 1 + i; if(stringIndex < 0){ stringIndex = lists.size() + stringIndex; } wheelItems.add(new WheelItem(startY, width, itemHeight, fontColor, fontSize, lists.get(stringIndex))); } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: mTouchY = event.getY(); return true; case MotionEvent.ACTION_MOVE: float dy = event.getY() - mTouchY; mTouchY = event.getY(); handleMove(dy); break; case MotionEvent.ACTION_UP: handleUp(); break; } return super.onTouchEvent(event); } /** * 处理移动操作 * @param dy */ private void handleMove(float dy) { //调整坐标 for(WheelItem item : wheelItems){ item.adjust(dy); } invalidate(); //调整 adjust(); } /** * 处理抬起操作 */ private void handleUp(){ int index = -1; //得到应该选择的那一项 for(int i = 0; i < wheelItems.size(); i++){ WheelItem item = wheelItems.get(i); //如果startY在selectItem的中点上面,则将该项作为选择项 if(item.getStartY() > wheelSelect.getStartY() && item.getStartY() < (wheelSelect.getStartY() + itemHeight / 2)){ index = i; break; } //如果startY在selectItem的中点下面,则将上一项作为选择项 if(item.getStartY() >= (wheelSelect.getStartY() + itemHeight / 2) && item.getStartY() < (wheelSelect.getStartY() + itemHeight)){ index = i - 1; break; } } //如果没找到或者其他因素,直接返回