自定义控件pickView
- package com.example.healthembed.util;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Timer;
- import java.util.TimerTask;
- import android.content.Context;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.graphics.Paint.Align;
- import android.graphics.Paint.FontMetricsInt;
- import android.graphics.Paint.Style;
- import android.os.Handler;
- import android.os.Message;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.MotionEvent;
- import android.view.View;
- /**
- * 滚动选择器
- *
- * @author chenjing
- *
- */
- public class PickerView extends View
- {
- public static final String TAG = "PickerView";
- /**
- * text之间间距和minTextSize之比
- */
- public static final float MARGIN_ALPHA = 2.8f;
- /**
- * 自动回滚到中间的速度
- */
- public static final float SPEED = 2;
- private List<String> mDataList;
- /**
- * 选中的位置,这个位置是mDataList的中心位置,一直不变
- */
- private int mCurrentSelected;
- private Paint mPaint;
- private float mMaxTextSize = 80;
- private float mMinTextSize = 40;
- private float mMaxTextAlpha = 255;
- private float mMinTextAlpha = 120;
- private int mColorText = 0x333333;
- 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;
- Handler updateHandler = new Handler()
- {
- @Override
- public void handleMessage(Message msg)
- {
- 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);
- init();
- }
- public void setOnSelectListener(onSelectListener listener)
- {
- mSelectListener = listener;
- }
- private void performSelect()
- {
- if (mSelectListener != null)
- mSelectListener.onSelect(mDataList.get(mCurrentSelected));
- //选中的位置,这个位置是mDataList的中心位置,一直不变
- }
- public void setData(List<String> datas)
- {
- mDataList = datas;
- mCurrentSelected = datas.size() / 2;
- invalidate();
- }
- public void setSelected(int selected)
- {
- mCurrentSelected = selected;
- }
- 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);
- //Inserts the specified element at the specified position in this list (optional operation)
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
- {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- mViewHeight = getMeasuredHeight();
- Log.e(TAG,String.valueOf(mViewHeight));
- mViewWidth = getMeasuredWidth();
- Log.e(TAG,String.valueOf(mViewWidth));
- // 按照View的高度计算字体大小
- mMaxTextSize = mViewWidth / 9.0f;
- mMinTextSize = mMaxTextSize / 2f;
- isInit = true;
- invalidate();//调用invalidate()方法,请求重新draw()
- }
- private void init()
- {
- timer = new Timer();
- mDataList = new ArrayList<String>();
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
- mPaint.setStyle(Style.FILL);
- mPaint.setTextAlign(Align.CENTER);//设置绘制文字的对齐方向
- mPaint.setColor(mColorText);
- }
- public void initDataHigh() {
- for(int j = 0; j<35; j++) {
- moveTailToHead();
- }
- }
- public void initDataLow() {
- for(int j = 0; j<50; j++) {
- moveTailToHead();
- }
- }
- @Override
- protected void onDraw(Canvas canvas)
- {
- super.onDraw(canvas);
- // 根据index绘制view
- if (isInit)
- drawData(canvas);
- }
- private void drawData(Canvas canvas)
- {
- // 先绘制选中的text再往上往下绘制其余的text
- float scale = parabola(mViewHeight / 4.0f, mMoveLen);
- float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize;
- mPaint.setTextSize(size);
- mPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha));
- // text居中绘制,注意baseline的计算才能达到居中,y值是text中心坐标
- /* float x = (float) (mViewWidth / 2.0);
- float y = (float) (mViewHeight / 2.0 + mMoveLen);
- FontMetricsInt fmi = mPaint.getFontMetricsInt();
- float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0));
- */
- float x = (float) (mViewWidth / 2.0+ mMoveLen);
- float y = (float) (mViewHeight / 2.0 );
- FontMetricsInt fmi = mPaint.getFontMetricsInt();
- float baseline = (float) (x - (fmi.bottom+fmi.descent)/2);
- canvas.drawText(mDataList.get(mCurrentSelected), baseline, y, mPaint);
- // 绘制上方data
- for (int i = 1; (mCurrentSelected - i) >= 0; i++)
- {
- drawOtherText(canvas, i, -1);
- Log.e(TAG,String.valueOf(mCurrentSelected));
- }
- // 绘制下方data
- for (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);*/
- float x = (float) (mViewWidth/ 2.0 + type * d);
- FontMetricsInt fmi = mPaint.getFontMetricsInt();
- float baseline = (float) (x - (fmi.bottom+fmi.descent));
- canvas.drawText(mDataList.get(mCurrentSelected + type * position),
- baseline,(float) (mViewHeight / 2.0), mPaint);
- }
- /**
- * 抛物线
- *
- * @param zero
- * 零点坐标
- * @param x
- * 偏移量
- * @return scale
- */
- private float parabola(float zero, float x)
- {
- float f = (float) (1 - Math.pow(x / zero, 2));//x的y次方
- return f < 0 ? 0 : f;
- }
- @Override
- public 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.getX();
- }
- private void doMove(MotionEvent event)
- {
- mMoveLen += (event.getX() - mLastDownY);
- //mMoveLen为正为向下滑
- 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.getX();
- invalidate();
- }
- /*
- task - task to be scheduled.
- firstTime - First time at which task is to be executed.
- period - time in milliseconds between successive task executions.
- */
- private void doUp(MotionEvent event)
- {
- // 抬起手后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, 1000);
- //第三个参数,第一次调用之后,从第二次开始每隔多长的时间调用一次 run() 方法
- }
- class MyTimerTask extends TimerTask
- {
- Handler handler;
- public MyTimerTask(Handler handler)
- {
- this.handler = handler;
- }
- @Override
- public void run()
- {
- handler.sendMessage(handler.obtainMessage());
- }
- }
- public interface onSelectListener
- {
- void onSelect(String text);
- }
- }
- /**
- * 获得屏幕的高度
- */
- WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- DisplayMetrics outMetrics = new DisplayMetrics();
- wm.getDefaultDisplay().getMetrics(outMetrics);
- mScreenHeight = outMetrics.heightPixels;
- Log.i("test",String.valueOf(mScreenHeight)); //2s 720
难点1:
字体随距离的渐变。可以看到,text随离中心位置的距离变化而变化,这里变化的是透明度alpha和字体大小TexSize,这两个值我都设置了Max和Min值,通过其与中心点的距离计算scale。
难点2:
循环滚动。为了解决循环滚动的问题我把存放text的List从中间往上下摊开,通过不断地moveHeadToTail和moveTailToHead使选中的text始终是list的中间position的值。
效果图:
自定义控件pickView的更多相关文章
- android自定义控件一站式入门
自定义控件 Android系统提供了一系列UI相关的类来帮助我们构造app的界面,以及完成交互的处理. 一般的,所有可以在窗口中被展示的UI对象类型,最终都是继承自View的类,这包括展示最终内容的非 ...
- ASP.NET MVC学习之母版页和自定义控件的使用
一.母板页_Layout.cshtml类似于传统WebForm中的.master文件,起到页面整体框架重用的目地1.母板页代码预览 <!DOCTYPE html> <html> ...
- C# 自定义控件VS用户控件
1 自定义控件与用户控件区别 WinForm中, 用户控件(User Control):继承自 UserControl,主要用于开发 Container 控件,Container控件可以添加其他Con ...
- 自定义控件之 圆形 / 圆角 ImageView
一.问题在哪里? 问题来源于app开发中一个很常见的场景——用户头像要展示成圆的: 二.怎么搞? 机智的我,第一想法就是,切一张中间圆形透明.四周与底色相同.尺寸与头像相同的蒙板图片,盖在 ...
- 如何开发FineReport的自定义控件?
FineReport作为插件化开发的报表软件,有些特殊需求的功能需要自己开发,开发的插件包帆软官方有提提供,可以去帆软论坛上找,本文将主要介绍如何开发一个自定义控件,这里讲讲方法论. 第一步:实例化一 ...
- WPF自定义控件第二 - 转盘按钮控件
继之前那个控件,又做了一个原理差不多的控件.这个控件主要模仿百度贴吧WP版帖子浏览界面左下角那个弹出的按钮盘.希望对大家有帮助. 这个控件和之前的也差不多,为了不让大家白看,文章最后发干货. 由于这个 ...
- 【Win 10应用开发】AdaptiveTrigger在自定义控件中是可以触发的
前些天,看到有网友给我留言,说AdaptiveTrigger在自定义控件(模板化控件)中不能触发.因为当时我正在写其他的代码,就没有去做实验来验证,于是我就给这位网友提了使用GotoVisualSta ...
- WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展
一.前言.预览 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要是对文本 ...
- Android自定义控件之自定义ViewGroup实现标签云
前言: 前面几篇讲了自定义控件绘制原理Android自定义控件之基本原理(一),自定义属性Android自定义控件之自定义属性(二),自定义组合控件Android自定义控件之自定义组合控件(三),常言 ...
随机推荐
- poj 3070 Fibonacci (矩阵快速幂乘/模板)
题意:给你一个n,输出Fibonacci (n)%10000的结果 思路:裸矩阵快速幂乘,直接套模板 代码: #include <cstdio> #include <cstring& ...
- vim中文帮助文档安装
vim自带的帮助手册是英文的, 对平时编程的人来说没有多大阅读困难,在何况还有"星级译王"呢, 但是我猜和我一样连英语四级都愁的大有人,可偏偏就有一帮好心人人将其翻译成了中文, 可 ...
- html+css底部自动固定底部
前端在切图过程中,肯定遇见过这种情况. 页面结构由三个部分组成,头部.内容.底部. 当一个页面的内容没撑满屏幕时,底部是跟着内容而并列存在的. 这个时候如果是大屏的话,底部下面会有多余的空白区域,而网 ...
- APP自识别安卓苹果
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- JS - A*寻路
算法核心 A*估值算法 寻路估值算法有非常多:常用的有广度优先算法,深度优先算法,哈夫曼树等等,游戏中用的比较多的如:A*估值 算法描述 对起点与终点进行横纵坐标的运算 代码实现 start: 起点坐 ...
- Python实现二叉树的四种遍历
对于一个没学过数据结构这门课程的编程菜鸟来说,自己能理解数据结构中的相关概念,但是自己动手通过Python,C++来实现它们却总感觉有些吃力.递归,指针,类这些知识点感觉自己应用的不够灵活,这是自己以 ...
- java类集框架(ArrayList,LinkedList,Vector区别)
主要分两个接口:collection和Map 主要分三类:集合(set).列表(List).映射(Map)1.集合:没有重复对象,没有特定排序方式2.列表:对象按索引位置排序,可以有重复对象3.映射: ...
- 探讨.NET Core数据加密和解密问题
前言 一直困扰着我关于数据加密这一块,24号晚上用了接近3个小时去完成一项任务,本以为立马能解决,但是为了保证数据的安全性,我们开始去对数据进行加密,然后接下来3个小时专门去研究加密这一块,然而用着用 ...
- OpenCV 玩九宫格数独(二):knn 数字识别
欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~ 作者:刘潇龙 前言 首先需要说明,这里所说的数字识别不是手写数字识别! 但凡对机器学习有所了解的人,相信看到数 ...
- css3实现checkbox变按钮
css3实现checkbox变按钮 .search_checkbox { margin: 0; padding: 0; margin-left: 15px; display: inline-block ...