Android开发——自定义炫酷PickerView快速滚动魔改

最近由于课内压力的增加和安卓课设项目,故没有怎么刷acm题,基本上学校要训练也就去水一波,程序设计相关内容也鸽了。
由于从来没有做过开发,研究的时候还是有点辛苦的,但是好在把想要的效果做出来了,故贴出来和其他初学者共享
这个魔改是基于 别人的博客。魔改的原因是,他的动画做的不错,但是在滚动的时候没有惯性滚动的效果,即手指快速滑动之后,滑轮能继续按照惯性滑动一段距离。在强迫症的压迫之下,我实在无法接受这一不太让人舒适的操作,就开始自己研究他的代码,并自己魔改 UI 绘制操作,并进行了一部分的封装,使得这个代码的使用更加简便,也能有惯性滚动的效果。 下面先上代码,注意把com.greyka.*换成自己的包。
具体有问题可以咨询本人qq 948897521

package com.greyka.imgr.classes;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import com.greyka.imgr.R;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;public class PickerView extends View {private final VelocityTracker myVelocityTracker = VelocityTracker.obtain();private float mSpeed = 0;private float mSlowDownRate = 0.95f;private float fastSlideSpeed = 200;private int textColor = 0XFFFFFFFF;public static final String TAG = "PickerView";/*** text之间间距和minTextSize之比*/public static final float MARGIN_ALPHA = 2.3f;/*** 自动回滚到中间的速度*/public static final float SPEED = 6;private List<String> mDataList;/*** 选中的位置,这个位置是mDataList的中心位置,一直不变*/private int mCurrentSelected;private Paint mPaint;private float mMaxTextSize = 30;private float mMinTextSize = 15;private float mMaxTextSizeRate = 4;private float mMinTextSizeRate =2;private float mMaxTextAlpha = 255;private float mMinTextAlpha = 100;private int mViewHeight;private int mViewWidth;private float mLastDownY;/*** 滑动的距离*/private float mMoveLen = 0;private boolean isInit = false;private onSelectListener mSelectListener;private Timer timer;private MyTimerTask mTask;@SuppressLint("HandlerLeak")Handler updateHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {//如果滚轮惯性滑动速度小于10就直接结束滑动if(Math.abs(mSpeed) <= 10){mSpeed = 0;}else{//每0.1秒进行减速操作mSpeed *= mSlowDownRate;Log.d("speed",mSpeed+"a");doMove(mSpeed * (float)0.1);return;}if (Math.abs(mMoveLen) < SPEED) {mMoveLen = 0;if (mTask != null) {mTask.cancel();mTask = null;performSelect();}} else// 这里mMoveLen / Math.abs(mMoveLen)是为了保有mMoveLen的正负号,以实现上滚或下滚mMoveLen = mMoveLen - mMoveLen / Math.abs(mMoveLen) * SPEED;invalidate();}};public PickerView(Context context) {super(context);init();}public PickerView(Context context, AttributeSet attrs) {super(context, attrs);initAttrs(context,attrs);init();}public void setOnSelectListener(onSelectListener listener) {mSelectListener = listener;}private void performSelect() {if (mSelectListener != null)mSelectListener.onSelect(mDataList.get(mCurrentSelected));}public void setData(List<String> datas) {mDataList = datas;mCurrentSelected = datas.size() / 2;invalidate();}/*** 选择选中的item的index** @param selected*/public void setSelected(int selected) {mCurrentSelected = selected;int distance = mDataList.size() / 2 - mCurrentSelected;if (distance < 0)for (int i = 0; i < -distance; i++) {moveHeadToTail();mCurrentSelected--;}else if (distance > 0)for (int i = 0; i < distance; i++) {moveTailToHead();mCurrentSelected++;}invalidate();}/*** 选择选中的内容** @param mSelectItem*/public void setSelected(String mSelectItem) {for (int i = 0; i < mDataList.size(); i++)if (mDataList.get(i).equals(mSelectItem)) {setSelected(i);break;}}private void moveHeadToTail() {String head = mDataList.get(0);mDataList.remove(0);mDataList.add(head);}private void moveTailToHead() {String tail = mDataList.get(mDataList.size() - 1);mDataList.remove(mDataList.size() - 1);mDataList.add(0, tail);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);mViewHeight = getMeasuredHeight();mViewWidth = getMeasuredWidth();// 按照View的高度计算字体大小mMaxTextSize = mViewHeight / mMaxTextSizeRate;mMinTextSize = mMaxTextSize / mMinTextSizeRate;isInit = true;invalidate();}private void init() {timer = new Timer();mDataList = new ArrayList<String>();mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setStyle(Paint.Style.FILL);mPaint.setTextAlign(Paint.Align.CENTER);mPaint.setColor(Color.WHITE);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 根据index绘制viewif (isInit)drawData(canvas);}private void drawData(Canvas canvas) {// 先绘制选中的text再往上往下绘制其余的textfloat scale = parabola(mViewHeight / 4.0f, mMoveLen);float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize;mPaint.setTextSize(size);mPaint.setColor(textColor);mPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha));// text居中绘制,注意baseline的计算才能达到居中,y值是text中心坐标float x = (float) (mViewWidth / 2.0);float y = (float) (mViewHeight / 2.0 + mMoveLen);Paint.FontMetricsInt fmi = mPaint.getFontMetricsInt();float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0));if(mDataList.size() > 0)canvas.drawText(mDataList.get(mCurrentSelected), x, baseline, mPaint);// 绘制上方datafor (int i = 1; (mCurrentSelected - i) >= 0; i++) {drawOtherText(canvas, i, -1);}// 绘制下方datafor (int i = 1; (mCurrentSelected + i) < mDataList.size(); i++) {drawOtherText(canvas, i, 1);}}/*** @param canvas* @param position 距离mCurrentSelected的差值* @param type     1表示向下绘制,-1表示向上绘制*/private void drawOtherText(Canvas canvas, int position, int type) {float d = (float) (MARGIN_ALPHA * mMinTextSize * position + type* mMoveLen);float scale = parabola(mViewHeight / 4.0f, d);float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize;mPaint.setTextSize(size);mPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha));float y = (float) (mViewHeight / 2.0 + type * d);Paint.FontMetricsInt fmi = mPaint.getFontMetricsInt();float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0));canvas.drawText(mDataList.get(mCurrentSelected + type * position),(float) (mViewWidth / 2.0), baseline, mPaint);}/*** 抛物线** @param zero 零点坐标* @param x    偏移量* @return scale*/private float parabola(float zero, float x) {float f = (float) (1 - Math.pow(x / zero, 2));return f < 0 ? 0 : f;}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getActionMasked()) {case MotionEvent.ACTION_DOWN:doDown(event);break;case MotionEvent.ACTION_MOVE:doMove(event);break;case MotionEvent.ACTION_UP:doUp(event);break;}return true;}private void doDown(MotionEvent event) {if (mTask != null) {mTask.cancel();mTask = null;}mLastDownY = event.getY();}private void doMove(MotionEvent event) {myVelocityTracker.addMovement(event);myVelocityTracker.computeCurrentVelocity(100);mMoveLen += (event.getY() - mLastDownY);if (mMoveLen > MARGIN_ALPHA * mMinTextSize / 2) {// 往下滑超过离开距离moveTailToHead();mMoveLen = mMoveLen - MARGIN_ALPHA * mMinTextSize;} else if (mMoveLen < -MARGIN_ALPHA * mMinTextSize / 2) {// 往上滑超过离开距离moveHeadToTail();mMoveLen = mMoveLen + MARGIN_ALPHA * mMinTextSize;}mLastDownY = event.getY();invalidate();}/*用来处理惯性滑动操作*/private void doMove(float moveY) {mMoveLen += moveY;Log.d("move",moveY+"");if (mMoveLen > MARGIN_ALPHA * mMinTextSize / 2) {// 往下滑超过离开距离moveTailToHead();mMoveLen = mMoveLen - MARGIN_ALPHA * mMinTextSize;} else if (mMoveLen < -MARGIN_ALPHA * mMinTextSize / 2) {// 往上滑超过离开距离moveHeadToTail();mMoveLen = mMoveLen + MARGIN_ALPHA * mMinTextSize;}invalidate();}private void doUp(MotionEvent event) {mSpeed = myVelocityTracker.getYVelocity();Log.d("mspeed",mSpeed+"");if(Math.abs(mSpeed) < fastSlideSpeed){mSpeed = 0;}// 抬起手后mCurrentSelected的位置由当前位置move到中间选中位置if (Math.abs(mMoveLen) < 0.0001) {mMoveLen = 0;return;}if (mTask != null) {mTask.cancel();mTask = null;}mTask = new MyTimerTask(updateHandler);timer.schedule(mTask, 0, 10);}class MyTimerTask extends TimerTask {Handler handler;public MyTimerTask(Handler handler) {this.handler = handler;}@Overridepublic void run() {Log.d("a","aaaaa");handler.sendMessage(handler.obtainMessage());}}public interface onSelectListener {void onSelect(String text);}private void initAttrs(Context context, AttributeSet attrs) {TypedArray typeArray = context.getTheme().obtainStyledAttributes(attrs,R.styleable.PickerView, 0, 0);fastSlideSpeed = typeArray.getFloat(R.styleable.PickerView_fastSlideSpeed,200);mSlowDownRate = typeArray.getFloat(R.styleable.PickerView_slowDownRate,0.96f);mMaxTextAlpha = typeArray.getFloat(R.styleable.PickerView_maxTextAlpha, 255);mMinTextAlpha = typeArray.getFloat(R.styleable.PickerView_minTextAlpha,100);mMaxTextSizeRate = typeArray.getFloat(R.styleable.PickerView_maxTextSizeRate,30);mMinTextSizeRate = typeArray.getFloat(R.styleable.PickerView_minTextSizeRate,15);textColor = typeArray.getColor(R.styleable.PickerView_textColor,0xFF000000);}
}

attrs.xml文件中添加如下属性:

    <declare-styleable name="PickerView">//滚轮字体颜色设置<attr name="textColor" format="color"/>//滑动惯性设置,范围0~1,表示松开手指后每隔0.01s当前滚轮速度*=slowDownRate,推荐值0.96<attr name="slowDownRate" format="float"/>//滑动惯性阈值,如果手指速度小于这个值不会进行惯性滚动,以方便精准操作,范围0~*,推荐值500<attr name="fastSlideSpeed" format="float"/>//被选中的文字大小比例,即选中字体大小为整个view的 高度/maxTextSizeRate,推荐值4<attr name="maxTextSizeRate" format="float"/>//待选文字大小比例,即待选字体大小为 选中字体大小/minTextSizeRate,推荐值2<attr name="minTextSizeRate" format="float"/>//选中字体透明度设置,范围0~255,推荐值255,即不透明<attr name="maxTextAlpha" format="float"/>//待选字体透明度设置,范围0~255,推荐值100,即半透明<attr name="minTextAlpha" format="float"/></declare-styleable>

设置示例:

<com.greyka.imgr.classes.PickerViewandroid:id="@+id/pickerview"android:layout_width="200dp"android:layout_height="200dp"app:textColor="@color/white"app:fastSlideSpeed="500"app:maxTextAlpha="255"app:minTextAlpha="100"app:maxTextSizeRate="4"app:minTextSizeRate="2"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" />

实现onSelectListerner和onSelectedChangeListener:

//设置滚轮滑动的时候选择项目变更的时候的触发事件,可以播放提示音或者短振动等
pickerView.setOnSelectedChangeListener(new PickerView.onSelectedChangeListener() {@Overridepublic void onSelectedChange() {myUtils.myToastHelper.showText(getApplicationContext(),"roll",Toast.LENGTH_SHORT);}});
//设置滚轮停止时的触发事件,text为被选中的项
pickerView.setOnSelectListener(new PickerView.onSelectListener() {@Overridepublic void onSelect(String text) {myUtils.myToastHelper.showText(getApplicationContext(),"select",Toast.LENGTH_SHORT);}});

其他使用方法和上文给出的博客所述一致。

Android开发——自定义炫酷PickerView惯性滚动魔改相关推荐

  1. qt android漂亮控件,qt android 开发之炫酷控件的制做

    手机应用开发,就是吸引用户,要吸引用户,确定离不开用户体验!此次给你们分享一个自做的炫酷qml控件:fanControler 这个控件目前有点bug,不过整体效果仍是很赞的,经过滑动划片来控制风扇的转 ...

  2. 超酷的计步器APP(一)——炫酷功能实现,自定义水波纹特效、自定义炫酷开始按钮、属性动画的综合体验

    超酷的计步器APP(一)--炫酷功能实现,自定义水波纹特效.自定义炫酷开始按钮.属性动画的综合体验 好久没写博客了,没给大家分享技术了,真是有些惭愧.这段时间我在找工作,今年Android的行情也不怎 ...

  3. iOS动画开发之五——炫酷的粒子效果

    iOS动画开发之五--炫酷的粒子效果 在上几篇博客中,我们对UIView层的动画以及iOS的核心动画做了介绍,基本已经可以满足iOS应用项目中所有的动画需求,如果你觉得那些都还不够炫酷,亦或是你灵光一 ...

  4. android自定义省略号,Android开发自定义TextView省略号样式的方法

    本文实例讲述了Android开发自定义TextView省略号样式的方法.分享给大家供大家参考,具体如下: 在布局xml中设置textView的字段 android:maxLines="2&q ...

  5. Android开发自定义UI组件

    Android开发自定义UI组件实现红色小球跟随手指移动 要写实现自定义UI组件,要创建一个BallView类,继承View类,在BallView类中创建画笔,然后重写OnDraw()方法和OnTou ...

  6. 自定义炫酷powershell

    自定义炫酷powershell(美化) linux上的bash和zsh之类的命令行终端炫酷无比. window上的cmd和powershell丑的不忍直视. 很久之前不知参考谁的一篇文章自定义了一下, ...

  7. android开发自定义View(三)仿芝麻信用积分

    此文参考了https://github.com/HotBitmapGG/CreditSesameRingView 感谢作者的分享!! 首先看一下支付宝上显示的样子 然后看一下模仿的效果 代码 基础部分 ...

  8. 如何利用 Android 自定义控件实现炫酷的动画?|CSDN 博文精选

    作者 | u012551350 本文精选自 CSDN 博客,已获作者授权 「知足常乐」,很多人不满足现状,各种折腾,往往舍本逐末,常乐才能少一分浮躁,多一分宁静.近期在笔者身上发生了许多事情,心态也发 ...

  9. Android探索之旅(第十四篇)Android中实现炫酷效果的Demo(持续收录中......)

    RangeSeekBar Android简单实现订单模块类APP的物流详情页 Android开发中阴影效果的实现 Android 炫酷多重水波纹 MultiWaveHeader 利用Spannable ...

最新文章

  1. 精通Spring Boot—— 第二十一篇:Spring Social OAuth 登录简介
  2. 单例设计模式-容器单例
  3. 1443B. Saving the City
  4. 解密ControlRotation与ActorRotation
  5. 错误:Type javax.xml.bind.JAXBContext not present
  6. Android UI 备忘:DrawerLayout
  7. Linux平台升级chrome浏览器后,再打开会提示:“您的个人资料来自新版 Google Chrome 浏览器,因此无法使用。某些功能可能无法使用。请指定其他个人资料目录,或使用新版本”
  8. 泰迪杯数据挖掘挑战赛—数据预处理(一)
  9. 基于收发一体超声波探头的超声波测距方案(附源代码和原理图)
  10. 搜索计算机文件夹的记录怎么删除,Win7如何删除“我的电脑”搜索栏里面的搜索记录...
  11. 中国古代木制机器发展简史
  12. 悠漓带你玩转C语言(函数)
  13. comsol结构力学-应力应变仿真
  14. 智能小车系列文章之小车简介
  15. Neuron segmentation using 3D wavelet integratedencoder–decoder network
  16. 高程、方位角、图幅编号
  17. java 去除警告_Java——警告消除
  18. RPA—pyautogui+PIL+pandas识别全版本(2.0-3.0)滑动验证码,获取表格数据
  19. 一大波好看的国产漫画来袭,在线动漫迷们做好准备了吗?
  20. 计算机组成翟学明,东北大学秦皇岛分校组成原理课程设计.doc

热门文章

  1. 深入理解协方差(图文详解)
  2. 饥荒联机版在线服务器登陆,《饥荒:联机版》不搜索房间直连服务器教程
  3. Additive Angular Margin Loss for Deep Face Recognition翻译笔记
  4. AI智慧安监:打电话/玩手机智能检测,构筑安全生产新防线
  5. Kotlin 3. Kotlin 特殊符号的用法:双感叹号!!,问号?,双冒号::
  6. ARM发布的A76性能有多强 未来抢占PC市场
  7. 135编辑器第二季校园新媒体文案排版大赛(西部站)开始啦!
  8. KaliLinux钓鱼Wifi搭建
  9. 神奇的点9工具与.9图片效果
  10. 应用宝ysdk接入心得