Android仿iPhone的时间轮的工具怎么用
发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,本篇文章给大家分享的是有关Android仿iPhone的时间轮的工具怎么用,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。相信大家一定都见
千家信息网最后更新 2026年11月01日Android仿iPhone的时间轮的工具怎么用
本篇文章给大家分享的是有关Android仿iPhone的时间轮的工具怎么用,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
相信大家一定都见过iPhone上面的时间滚动轮的效果,类似轮盘一样的滚动来选择数据,非常有意思,动画效果也很生动,相比较安卓自带的spinner,TimePicker等控件,用户体验要好很多。在android上面去实现这样的效果,需要自定义view来实现,下面一个demo,进行了详细的注释,希望对您有所帮助,下面先放上改动之后的效果图片:
由于代码比较多,只贴上了自定义view的相关代码:
package com.cogent.iPhonewheel.widget; import java.util.LinkedList; import java.util.List; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.GradientDrawable.Orientation; import android.os.Handler; import android.os.Message; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.util.AttributeSet; import android.util.FloatMath; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.animation.Interpolator; import android.widget.Scroller; import com.cogent.iPhonewheel.Interface.OnWheelChangedListener; import com.cogent.iPhonewheel.Interface.OnWheelScrollListener; import com.cogent.iPhonewheel.Interface.WheelAdapter; import com.cogent.iPhonewheel.UI.R; /** * 自定义的滚轮view * * @author Administrator * */ public class WheelView extends View { /** 滚动持续的时间 */ private static final int SCROLLING_DURATION = 400; /** 最少滚动的位置 */ private static final int MIN_DELTA_FOR_SCROLLING = 1; /** 当前值和标签的颜色 */ private static final int VALUE_TEXT_COLOR = 0xF0FF6347; /** item文字的颜色 */ private static final int ITEMS_TEXT_COLOR = 0xFF000000; /** 顶部和底部阴影的颜色 */ private static final int[] SHADOWS_COLORS = new int[] { 0xFF111111, 0x00AAAAAA, 0x00AAAAAA }; /** 附加的item的高度 */ private static final int ADDITIONAL_ITEM_HEIGHT = 15; /** 字体大小 */ private static final int TEXT_SIZE = 30; /** 顶部和底部item的偏移值 */ private static final int ITEM_OFFSET = TEXT_SIZE / 5; /** item布局的附加宽度 */ private static final int ADDITIONAL_ITEMS_SPACE = 10; /** 标签偏移值 */ private static final int LABEL_OFFSET = 8; /** 左右padding值 */ private static final int PADDING = 10; /** 默认可见的item数目 */ private static final int DEF_VISIABLE_ITEMS = 5; /** 初始化wheeladpater */ private WheelAdapter adapter = null; /** 当前item位置 */ private int currentItem = 0; /** item宽度 */ private int itemsWidth = 0; /** 标签宽度 */ private int labelWidth = 0; /** 可见item数目 */ private int visibleItems = DEF_VISIABLE_ITEMS; /** item高度 */ private int itemHeight = 0; /** item的字符串属性对象 */ private TextPaint itemsPaint; /** value的字符串属性对象 */ private TextPaint valuePaint; // Layouts private StaticLayout itemsLayout, labelLayout, valueLayout; private String label; private Drawable centerDrawable; /** 顶部渐变drawable对象 */ private GradientDrawable topShadow; /** 顶部渐变drawable对象 */ private GradientDrawable bottomShadow; /** 滚动动作是否执行 */ private boolean isScrollingPerformed; /** 滚动偏移量 */ private int scrollingOffset; /** 手势侦测对象 */ private GestureDetector gestureDetector; private Scroller scroller; private int lastScrollY; /** 是否可循环 */ private boolean isCyclic = false; /** 实例化OnWheelChangedListener */ private List changingListeners = new LinkedList(); /** 实例化OnWheelScrollListener */ private List scrollingListeners = new LinkedList(); /** * 3个参数构造函数 */ public WheelView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initData(context); } /** * 2个参数构造函数 */ public WheelView(Context context, AttributeSet attrs) { super(context, attrs); initData(context); } /** * 1个参数构造函数 */ public WheelView(Context context) { super(context); initData(context); } private void initData(Context context) { gestureDetector = new GestureDetector(context, gestureListener); gestureDetector.setIsLongpressEnabled(false);// 设置手势长按不起作用 scroller = new Scroller(context); } /** * 获取滚轮适配器 * * @return */ public WheelAdapter getAdapter() { return adapter; } /** * 设置滚轮适配器 * * @param adapter */ public void setAdapter(WheelAdapter adapter) { this.adapter = adapter; invalidateLayouts(); invalidate();// 是视图无效 } /** * 设置指定的滚轮动画变化率 * * @param interpolator */ public void setInterpolator(Interpolator interpolator) { scroller.forceFinished(true); scroller = new Scroller(getContext(), interpolator); } /** * 得到可见item的数目 * * @return the count of visible items */ public int getVisibleItems() { return visibleItems; } /** * 设置可见item的数目 * * @param count * the new count */ public void setVisibleItems(int count) { visibleItems = count; invalidate(); } /** * 得到标签 * * @return */ public String getLabel() { return label; } /** * 设置标签 * * @param newLabel */ public void setLabel(String newLabel) { if (label == null || !label.equals(newLabel)) { label = newLabel; labelLayout = null; invalidate(); } } /** * 增加滚轮变化监听器 * * @param listener */ public void addChangingListener(OnWheelChangedListener listener) { changingListeners.add(listener); } /** * 移除滚轮变化监听器 * * @param listener */ public void removeChangingListener(OnWheelChangedListener listener) { changingListeners.remove(listener); } /** * 通知改变的监听器 * * @param oldValue * @param newValue */ protected void notifyChangingListeners(int oldValue, int newValue) { for (OnWheelChangedListener listener : changingListeners) { listener.onChanged(this, oldValue, newValue); } } /** * 增加滚轮监听器 * * @param listener * the listener */ public void addScrollingListener(OnWheelScrollListener listener) { scrollingListeners.add(listener); } /** * 移除滚轮监听器 * * @param listener * the listener */ public void removeScrollingListener(OnWheelScrollListener listener) { scrollingListeners.remove(listener); } /** * 通知监听器开始滚动 */ protected void notifyScrollingListenersAboutStart() { for (OnWheelScrollListener listener : scrollingListeners) { listener.onScrollingStarted(this); } } /** * 通知监听器结束滚动 */ protected void notifyScrollingListenersAboutEnd() { for (OnWheelScrollListener listener : scrollingListeners) { listener.onScrollingFinished(this); } } /** * 取得当前item * * @return */ public int getCurrentItem() { return currentItem; } /** * 设置当前item * @param index * @param animated */ public void setCurrentItem(int index, boolean animated) { if (adapter == null || adapter.getItemsCount() == 0) { return; } if (index < 0 || index >= adapter.getItemsCount()) { if (isCyclic) { while (index < 0) { index += adapter.getItemsCount(); } index %= adapter.getItemsCount(); } else { return; } } if (index != currentItem) { if (animated) { scroll(index - currentItem, SCROLLING_DURATION); } else { invalidateLayouts(); int old = currentItem; currentItem = index; notifyChangingListeners(old, currentItem); invalidate(); } } } /** * 设置当前item w/o 动画. 当index有误是不做任何响应. * * @param index * the item index */ public void setCurrentItem(int index) { setCurrentItem(index, false); } /** * 测试滚轮是否可循环. * * @return true if wheel is cyclic */ public boolean isCyclic() { return isCyclic; } /** * 设置滚轮循环标志 * * @param isCyclic * the flag to set */ public void setCyclic(boolean isCyclic) { this.isCyclic = isCyclic; invalidate(); invalidateLayouts(); } /** * 使布局无效 */ private void invalidateLayouts() { itemsLayout = null; valueLayout = null; scrollingOffset = 0; } /** * 初始化资源信息 */ private void initResourceIfNecessary() { if (itemsPaint == null) { itemsPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.FAKE_BOLD_TEXT_FLAG); itemsPaint.setTextSize(TEXT_SIZE); } if (valuePaint == null) { valuePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.FAKE_BOLD_TEXT_FLAG | Paint.DITHER_FLAG); valuePaint.setTextSize(TEXT_SIZE); valuePaint.setShadowLayer(0.1f, 0, 0.1f, 0xFFC0C0C0); } if (centerDrawable == null) { centerDrawable = getContext().getResources().getDrawable( R.drawable.wheel_val); } if (topShadow == null) { topShadow = new GradientDrawable(Orientation.TOP_BOTTOM, SHADOWS_COLORS); } if (bottomShadow == null) { bottomShadow = new GradientDrawable(Orientation.BOTTOM_TOP, SHADOWS_COLORS); } setBackgroundResource(R.drawable.wheel_bg); } /** * 计算layout所需的高度 * @param layout * @return */ private int getDesiredHeight(Layout layout) { if (layout == null) { return 0; } int desired = getItemHeight() * visibleItems - ITEM_OFFSET * 2 - ADDITIONAL_ITEM_HEIGHT; desired = Math.max(desired,getSuggestedMinimumHeight()); return desired; } /** * 通过index得到text * @param index * @return */ private String getTextItem(int index){ if(adapter == null || adapter.getItemsCount() == 0){ return null; } int count = adapter.getItemsCount(); if((index < 0 || index >= count) && !isCyclic){ return null; }else{ while(index < 0){ index += count; } } index %= count; return adapter.getItem(index); } /** * 根据当前值构建text * * @param useCurrentValue * @return the text */ private String buildText(boolean useCurrentValue) { StringBuilder itemsText = new StringBuilder(); int addItems = visibleItems / 2 + 1; for (int i = currentItem - addItems; i <= currentItem + addItems; i++) { if (useCurrentValue || i != currentItem) { String text = getTextItem(i); if (text != null) { itemsText.append(text); } } if (i < currentItem + addItems) { itemsText.append("\n"); } } return itemsText.toString(); } /** * 返回可以表示的item的***长度 * @return the max length */ private int getMaxTextLength() { WheelAdapter adapter = getAdapter(); if (adapter == null) { return 0; } int adapterLength = adapter.getMaximumLength(); if (adapterLength > 0) { return adapterLength; } String maxText = null; int addItems = visibleItems / 2; for (int i = Math.max(currentItem - addItems, 0); i < Math.min(currentItem + visibleItems, adapter.getItemsCount()); i++) { String text = adapter.getItem(i); if (text != null && (maxText == null || maxText.length() < text.length())) { maxText = text; } } return maxText != null ? maxText.length() : 0; } /** * 返回滚轮item的高度 * @return the item height */ private int getItemHeight() { if (itemHeight != 0) { return itemHeight; } else if (itemsLayout != null && itemsLayout.getLineCount() > 2) { itemHeight = itemsLayout.getLineTop(2) - itemsLayout.getLineTop(1); return itemHeight; } return getHeight() / visibleItems; } /** * 计算控制宽度和创建text布局 * @param widthSize the input layout width * @param mode the layout mode * @return the calculated control width */ private int calculateLayoutWidth(int widthSize, int mode) { initResourceIfNecessary(); int width = widthSize; int maxLength = getMaxTextLength(); if (maxLength > 0) { float textWidth = FloatMath.ceil(Layout.getDesiredWidth("0", itemsPaint)); itemsWidth = (int) (maxLength * textWidth); } else { itemsWidth = 0; } itemsWidth += ADDITIONAL_ITEMS_SPACE; // make it some more labelWidth = 0; if (label != null && label.length() > 0) { labelWidth = (int) FloatMath.ceil(Layout.getDesiredWidth(label, valuePaint)); } boolean recalculate = false; if (mode == MeasureSpec.EXACTLY) { width = widthSize; recalculate = true; } else { width = itemsWidth + labelWidth + 2 * PADDING; if (labelWidth > 0) { width += LABEL_OFFSET; } // Check against our minimum width width = Math.max(width, getSuggestedMinimumWidth()); if (mode == MeasureSpec.AT_MOST && widthSize < width) { width = widthSize; recalculate = true; } } if (recalculate) { // recalculate width int pureWidth = width - LABEL_OFFSET - 2 * PADDING; if (pureWidth <= 0) { itemsWidth = labelWidth = 0; } if (labelWidth > 0) { double newWidthItems = (double) itemsWidth * pureWidth / (itemsWidth + labelWidth); itemsWidth = (int) newWidthItems; labelWidth = pureWidth - itemsWidth; } else { itemsWidth = pureWidth + LABEL_OFFSET; // no label } } if (itemsWidth > 0) { createLayouts(itemsWidth, labelWidth); } return width; } /** * 创建布局 * @param widthItems width of items layout * @param widthLabel width of label layout */ private void createLayouts(int widthItems, int widthLabel) { if (itemsLayout == null || itemsLayout.getWidth() > widthItems) { itemsLayout = new StaticLayout(buildText(isScrollingPerformed), itemsPaint, widthItems, widthLabel > 0 ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_CENTER, 1, ADDITIONAL_ITEM_HEIGHT, false); } else { itemsLayout.increaseWidthTo(widthItems); } if (!isScrollingPerformed && (valueLayout == null || valueLayout.getWidth() > widthItems)) { String text = getAdapter() != null ? getAdapter().getItem(currentItem) : null; valueLayout = new StaticLayout(text != null ? text : "", valuePaint, widthItems, widthLabel > 0 ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_CENTER, 1, ADDITIONAL_ITEM_HEIGHT, false); } else if (isScrollingPerformed) { valueLayout = null; } else { valueLayout.increaseWidthTo(widthItems); } if (widthLabel > 0) { if (labelLayout == null || labelLayout.getWidth() > widthLabel) { labelLayout = new StaticLayout(label, valuePaint, widthLabel, Layout.Alignment.ALIGN_NORMAL, 1, ADDITIONAL_ITEM_HEIGHT, false); } else { labelLayout.increaseWidthTo(widthLabel); } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width = calculateLayoutWidth(widthSize, widthMode); int height; if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { height = getDesiredHeight(itemsLayout); if (heightMode == MeasureSpec.AT_MOST) { height = Math.min(height, heightSize); } } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (itemsLayout == null) { if (itemsWidth == 0) { calculateLayoutWidth(getWidth(), MeasureSpec.EXACTLY); } else { createLayouts(itemsWidth, labelWidth); } } if (itemsWidth > 0) { canvas.save(); // Skip padding space and hide a part of top and bottom items canvas.translate(PADDING, -ITEM_OFFSET); drawItems(canvas); drawValue(canvas); canvas.restore(); } drawCenterRect(canvas); drawShadows(canvas); } /** * 在顶部和底部画阴影的控制 * @param canvas the canvas for drawing */ private void drawShadows(Canvas canvas) { topShadow.setBounds(0, 0, getWidth(), getHeight() / visibleItems); topShadow.draw(canvas); bottomShadow.setBounds(0, getHeight() - getHeight() / visibleItems, getWidth(), getHeight()); bottomShadow.draw(canvas); } /** * 画value和标签的布局 * @param canvas the canvas for drawing */ private void drawValue(Canvas canvas) { valuePaint.setColor(VALUE_TEXT_COLOR); valuePaint.drawableState = getDrawableState(); Rect bounds = new Rect(); itemsLayout.getLineBounds(visibleItems / 2, bounds); // draw label if (labelLayout != null) { canvas.save(); canvas.translate(itemsLayout.getWidth() + LABEL_OFFSET, bounds.top); labelLayout.draw(canvas); canvas.restore(); } // draw current value if (valueLayout != null) { canvas.save(); canvas.translate(0, bounds.top + scrollingOffset); valueLayout.draw(canvas); canvas.restore(); } } /** * 画items * @param canvas the canvas for drawing */ private void drawItems(Canvas canvas) { canvas.save(); int top = itemsLayout.getLineTop(1); canvas.translate(0, - top + scrollingOffset); itemsPaint.setColor(ITEMS_TEXT_COLOR); itemsPaint.drawableState = getDrawableState(); itemsLayout.draw(canvas); canvas.restore(); } /** * 画当前值的矩形 * @param canvas the canvas for drawing */ private void drawCenterRect(Canvas canvas) { int center = getHeight() / 2; int offset = getItemHeight() / 2; centerDrawable.setBounds(0, center - offset, getWidth(), center + offset); centerDrawable.draw(canvas); } @Override public boolean onTouchEvent(MotionEvent event) { WheelAdapter adapter = getAdapter(); if (adapter == null) { return true; } if (!gestureDetector.onTouchEvent(event) && event.getAction() == MotionEvent.ACTION_UP) { justify(); } return true; } /** * 滚动滚轮 * @param delta the scrolling value */ private void doScroll(int delta) { scrollingOffset += delta; int count = scrollingOffset / getItemHeight(); int pos = currentItem - count; if (isCyclic && adapter.getItemsCount() > 0) { // fix position by rotating while (pos < 0) { pos += adapter.getItemsCount(); } pos %= adapter.getItemsCount(); } else if (isScrollingPerformed) { // if (pos < 0) { count = currentItem; pos = 0; } else if (pos >= adapter.getItemsCount()) { count = currentItem - adapter.getItemsCount() + 1; pos = adapter.getItemsCount() - 1; } } else { // fix position pos = Math.max(pos, 0); pos = Math.min(pos, adapter.getItemsCount() - 1); } int offset = scrollingOffset; if (pos != currentItem) { setCurrentItem(pos, false); } else { invalidate(); } // update offset scrollingOffset = offset - count * getItemHeight(); if (scrollingOffset > getHeight()) { scrollingOffset = scrollingOffset % getHeight() + getHeight(); } } // gesture listener private SimpleOnGestureListener gestureListener = new SimpleOnGestureListener() { public boolean onDown(MotionEvent e) { if (isScrollingPerformed) { scroller.forceFinished(true); clearMessages(); return true; } return false; } public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { startScrolling(); doScroll((int)-distanceY); return true; } public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { lastScrollY = currentItem * getItemHeight() + scrollingOffset; int maxY = isCyclic ? 0x7FFFFFFF : adapter.getItemsCount() * getItemHeight(); int minY = isCyclic ? -maxY : 0; scroller.fling(0, lastScrollY, 0, (int) -velocityY / 2, 0, 0, minY, maxY); setNextMessage(MESSAGE_SCROLL); return true; } }; // Messages private final int MESSAGE_SCROLL = 0; private final int MESSAGE_JUSTIFY = 1; /** * Set next message to queue. Clears queue before. * * @param message the message to set */ private void setNextMessage(int message) { clearMessages(); animationHandler.sendEmptyMessage(message); } /** * Clears messages from queue */ private void clearMessages() { animationHandler.removeMessages(MESSAGE_SCROLL); animationHandler.removeMessages(MESSAGE_JUSTIFY); } // animation handler private Handler animationHandler = new Handler() { public void handleMessage(Message msg) { scroller.computeScrollOffset(); int currY = scroller.getCurrY(); int delta = lastScrollY - currY; lastScrollY = currY; if (delta != 0) { doScroll(delta); } // scrolling is not finished when it comes to final Y // so, finish it manually if (Math.abs(currY - scroller.getFinalY()) < MIN_DELTA_FOR_SCROLLING) { currY = scroller.getFinalY(); scroller.forceFinished(true); } if (!scroller.isFinished()) { animationHandler.sendEmptyMessage(msg.what); } else if (msg.what == MESSAGE_SCROLL) { justify(); } else { finishScrolling(); } } }; /** * Justifies wheel */ private void justify() { if (adapter == null) { return; } lastScrollY = 0; int offset = scrollingOffset; int itemHeight = getItemHeight(); boolean needToIncrease = offset > 0 ? currentItem < adapter.getItemsCount() : currentItem > 0; if ((isCyclic || needToIncrease) && Math.abs((float) offset) > (float) itemHeight / 2) { if (offset < 0) offset += itemHeight + MIN_DELTA_FOR_SCROLLING; else offset -= itemHeight + MIN_DELTA_FOR_SCROLLING; } if (Math.abs(offset) > MIN_DELTA_FOR_SCROLLING) { scroller.startScroll(0, 0, 0, offset, SCROLLING_DURATION); setNextMessage(MESSAGE_JUSTIFY); } else { finishScrolling(); } } /** * 开始滚动 */ private void startScrolling() { if (!isScrollingPerformed) { isScrollingPerformed = true; notifyScrollingListenersAboutStart(); } } /** * 停止滚动 */ void finishScrolling() { if (isScrollingPerformed) { notifyScrollingListenersAboutEnd(); isScrollingPerformed = false; } invalidateLayouts(); invalidate(); } public void scroll(int itemsToScroll, int time) { scroller.forceFinished(true); lastScrollY = scrollingOffset; int offset = itemsToScroll * getItemHeight(); scroller.startScroll(0, lastScrollY, 0, offset - lastScrollY, time); setNextMessage(MESSAGE_SCROLL); startScrolling(); } }以上就是Android仿iPhone的时间轮的工具怎么用,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注行业资讯频道。
滚轮
监听器
监听
标签
对象
布局
顶部
时间
宽度
效果
数目
函数
动画
参数
底部
颜色
高度
偏移
变化
工具
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
互联网科技会
河南真二互联网科技有限公司
腾讯阿里互联网科技
易互联网科技有限公司
有木互联网科技
互联网科技有限公司 翻译
四个字互联网科技公司取名
太原新华互联网科技学校怎么样
科技互联网的估值
互联网科技热点资讯
上海宁丞科技互联网
民营互联网公司国防科技
翼展科技 首家互联网医院
威龙国际互联网科技公司
杭州披云加互联网科技有限公司
兰石爱特工业互联网科技分公司
西安左小互联网科技有限公司
互联网的黑科技实验室
武汉亿视互联网科技有限公司
互联网 科技节活动 微视频大赛
咖啡豆
互联网科技公司起名
科技平台互联网
互联网金融公司科技
美国互联网黑科技
安控科技rtu产品工业互联网
北京大学互联网科技怎么样
山东星卡互联网科技有限公司官网
广西科技产业互联网是什么
科技互联网经济论坛