Android仿iphone自定义滚动选择器

本文实例为大家分享了Android仿iphone自定义滚动选择器的具体代码,供大家参考,具体内容如下

一、多的不说,效果图,先走起

二、实例源码

(1)自定义控件

package com.pickerscrollview.views; 

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask; 

import android.annotation.SuppressLint;
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.view.MotionEvent;
import android.view.View; 

import com.pickerscrollview.bean.Pickers; 

/**
 * 自定义滚动选择器
 *
 * @author zengtao 2015年5月20日 下午7:36:03
 *
 */
@SuppressLint({ "HandlerLeak", "ClickableViewAccessibility" })
public class PickerScrollView 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<Pickers> mDataList;
  /**
   * 选中的位置,这个位置是mDataList的中心位置,一直不变
   */
  private int mCurrentSelected;
  private Paint mPaint; 

  private float mMaxTextSize = 20;
  private float mMinTextSize = 10; 

  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 PickerScrollView(Context context) {
    super(context);
    init();
  } 

  public PickerScrollView(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));
  } 

  public void setData(List<Pickers> 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() {
    Pickers head = mDataList.get(0);
    mDataList.remove(0);
    mDataList.add(head);
  } 

  private void moveTailToHead() {
    Pickers tail = mDataList.get(mDataList.size() - 1);
    mDataList.remove(mDataList.size() - 1);
    mDataList.add(0, tail);
  } 

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    mViewHeight = getMeasuredHeight();
    mViewWidth = getMeasuredWidth();
    // 按照View的高度计算字体大小
    mMaxTextSize = mViewHeight / 8.0f;
    mMinTextSize = mMaxTextSize / 2f;
    isInit = true;
    invalidate();
  } 

  private void init() {
    timer = new Timer();
    mDataList = new ArrayList<Pickers>();
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setStyle(Style.FILL);
    mPaint.setTextAlign(Align.CENTER);
    mPaint.setColor(mColorText);
  } 

  @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)); 

    int indexs = mCurrentSelected;
    String textData = mDataList.get(indexs).getShowConetnt();
    canvas.drawText(textData, x, baseline, mPaint); 

    // 绘制上方data
    for (int i = 1; (mCurrentSelected - i) >= 0; i++) {
      drawOtherText(canvas, i, -1);
    }
    // 绘制下方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);
    FontMetricsInt fmi = mPaint.getFontMetricsInt();
    float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0)); 

    int indexs = mCurrentSelected + type * position;
    String textData = mDataList.get(indexs).getShowConetnt();
    canvas.drawText(textData, (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;
  } 

  @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.getY();
  } 

  private void doMove(MotionEvent event) { 

    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 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, 10);
  } 

  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(Pickers pickers);
  }
} 

(2)实体类

package com.pickerscrollview.bean; 

import java.io.Serializable; 

/**
 *
 * @author zengtao 2015年5月20日下午7:18:14
 *
 */
public class Pickers implements Serializable { 

  private static final long serialVersionUID = 1L; 

  private String showConetnt;
  private String showId; 

  public String getShowConetnt() {
    return showConetnt;
  } 

  public String getShowId() {
    return showId;
  } 

  public Pickers(String showConetnt, String showId) {
    super();
    this.showConetnt = showConetnt;
    this.showId = showId;
  } 

  public Pickers() {
    super();
  } 

} 

(3)主界面

package com.pickerscrollview.ui; 

import java.util.ArrayList;
import java.util.List; 

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.RelativeLayout; 

import com.pickerscrollview.bean.Pickers;
import com.pickerscrollview.views.PickerScrollView;
import com.pickerscrollview.views.PickerScrollView.onSelectListener; 

/**
 * 主界面
 *
 * @author zengtao 2015年5月20日 下午7:36:03
 *
 */
public class MainActivity extends Activity { 

  private Button bt_scrollchoose; // 滚动选择器按钮
  private PickerScrollView pickerscrlllview; // 滚动选择器
  private List<Pickers> list; // 滚动选择器数据
  private String[] id;
  private String[] name; 

  private Button bt_yes; // 确定按钮
  private RelativeLayout picker_rel; // 选择器布局 

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main); 

    initView();
    initLinstener();
    initData();
  } 

  /**
   * 初始化
   */
  private void initView() {
    bt_scrollchoose = (Button) findViewById(R.id.bt_scrollchoose);
    picker_rel = (RelativeLayout) findViewById(R.id.picker_rel);
    pickerscrlllview = (PickerScrollView) findViewById(R.id.pickerscrlllview);
    bt_yes = (Button) findViewById(R.id.picker_yes);
  } 

  /**
   * 设置监听事件
   */
  private void initLinstener() {
    bt_scrollchoose.setOnClickListener(onClickListener);
    pickerscrlllview.setOnSelectListener(pickerListener);
    bt_yes.setOnClickListener(onClickListener);
  } 

  /**
   * 初始化数据
   */
  private void initData() {
    list = new ArrayList<Pickers>();
    id = new String[] { "1", "2", "3", "4", "5", "6" };
    name = new String[] { "中国银行", "农业银行", "招商银行", "工商银行", "建设银行", "民生银行" };
    for (int i = 0; i < name.length; i++) {
      list.add(new Pickers(name[i], id[i]));
    }
    // 设置数据,默认选择第一条
    pickerscrlllview.setData(list);
    pickerscrlllview.setSelected(0);
  } 

  // 滚动选择器选中事件
  onSelectListener pickerListener = new onSelectListener() { 

    @Override
    public void onSelect(Pickers pickers) {
      System.out.println("选择:" + pickers.getShowId() + "--银行:"
          + pickers.getShowConetnt());
    }
  }; 

  // 点击监听事件
  OnClickListener onClickListener = new OnClickListener() { 

    @Override
    public void onClick(View v) {
      if (v == bt_scrollchoose) {
        picker_rel.setVisibility(View.VISIBLE);
      } else if (v == bt_yes) {
        picker_rel.setVisibility(View.GONE);
      }
    }
  };
} 

三、总结

先做一下解析,在滑动手指的时候,有一个突出的点,就是有点像圆弧那个弧度一样的感觉,所以我们要用parabola这个方法去计算出来,画多大的字也是需要计算控件的高度,和宽度来绘制,首先我们先绘制中间的文体,在绘制上下两边的字,当然,我们也要设置一个手势监听,用于监听用户按下,滑动,抬起的时候,的一个操作,我们会根据手指往上移动和向下移动距离的多少来判断显示那个文字,选中了那个。

源码下载:Android自定义滚动选择器

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android PickerView滚动选择器的使用方法

    手机里设置闹钟需要选择时间,那个选择时间的控件就是滚动选择器,前几天用手机刷了MIUI,发现自带的那个时间选择器效果挺好看的,于是就自己仿写了一个,权当练手.先来看效果: 效果还行吧?实现思路就是自定义一个PickerView,单独滚动的是一个PickerView,显然上图中有分和秒的选择所以在布局里用了两个PickerView.由于这里不涉及到text的点击事件,所以只需要继承View就行了,直接把text用canvas画上去.PickerView的实现的主要难点: 难点1: 字体随距离的渐变

  • Android中颜色选择器和改变字体颜色的实例教程

    1.构建一张七彩图: 我们经常看到这种样子的颜色选择器吧.. 然后其实右边的亮度选择是: 这样我们的代码就可以进行啦... // 创建七彩图片 private void init() { int[] oc = { 0xffff0000, 0xffffff00, 0xff00ff00, 0xff00ffff, 0xff0000ff, 0xffff00ff, 0xffff0000 }; float[] op = { 0, 0.16667f, 0.33333f, 0.5f, 0.66667f, 0.8

  • Android CalendarView,DatePicker,TimePicker,以及NumberPicker的使用

    Android  CalendarView,DatePicker,TimePicker,以及NumberPicker的使用 简单复习下基础UI组件,做个简单的总结,Android的这些组件封装的特别好,基本套上就能使用,当然,这个减轻了开发者的负担!不过如果想要深入研究,这里面还是有很大的空间值得深度分析!简单的几个例子!仅供参考: 不多说,先上效果图: CalendarView ChooseView NumberPicker CalendarView代码区 : main.xml代码区:Cale

  • Android文件选择器ExFilePicker的使用方法

    在新版的android中(如Android 7.0+),文件选择由于权限限制,Uri变化,文件资源路径格式改版等等,变得比较复杂起来,比如,得在Androidmanifest配置FileProvider完了还得写xml目录下的文件path这些等等.一些第三方的文件选择器,就算没有上面这些动作,一般也得在Androidmanifest写几个Activity.而ExFilePicker则无需上面的繁琐操作,一不需要在Androidmanifest里面定义FileProvider,二不需要写xml文件

  • Android自定义wheelview实现滚动日期选择器

    本文实例为大家分享了Android实现滚动日期选择器的具体代码,供大家参考,具体内容如下 wheelview滚动效果的View 这段时间需要用到一个时间选择器,但是不能使用日期对话框, 因为它是筛选条件框架下的,只能是View!这个WheelView改造后可以达到要求! 这个wheelview框架使用的类不多,就几个,还有一些资源文件. 我根据这个框架设计了日期的选择器. 主页面: 第一种日期选择器页面: 动态效果: 使用: 具体的实现是一个LoopView的类,这是一个继承View的类! 理解

  • Android评论图片可移动顺序选择器(推荐)

    好久没写了,现在在广州一家公司实习了,来了一个月了,实习生没什么事干,看到公司一个项目.Android和iOS的做的不一样(ios做这个项目的人多,额不解释..原来做这个玩意的也跳槽了),既ios的做的控件更酷炫,我闲着没事,把其中的一个控件和IOS做的差不多了,来看看效果吧 截的GIF图看上去有点快了,因为CSDN上传图片不能超过两M所以帧有点大,实际效果是正常的.好了,先让我们看看不能移动交换顺序之前是怎么实现的吧. package com.test.jiupin.view; import

  • Android自定义滚动选择器实例代码

    Android自定义滚动选择器 实现图片的效果 代码如下 package com.linzihui.widget; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graph

  • Android开发之滑动数值选择器NumberPicker用法示例

    本文实例讲述了Android开发之滑动数值选择器NumberPicker用法.分享给大家供大家参考,具体如下: 简介: NumberPicker: 用户既可以从键盘输值,也可以拖动来选择值 实际效果: 常用方法: 1. setMinValue() 设置组件支持的最小值 2. setMaxValue() 设置组建支持的最大值 3. setValue() 设置该组件的当前值 在布局文件中调用: <?xml version="1.0" encoding="utf-8"

  • Android日期选择器实现年月日三级联动

    最近项目里面用到了一个日期选择器,实现现在主流的WheelView滑动选择,整理了下,做了个Demo.废话不多说,直接上代码. 主布局:activity_main.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools=&quo

  • Android 中NumberPicker,DatePicker与DatePickerDialog中分割颜色的修改实例代码

    Android 中NumberPicker,DatePicker与DatePickerDialog中分割颜色的修改实例代码 前言: 开发中,为了应用整体美观,需要保持控件的色调和主题色保持一致, 例如:NumberPicker.DatePicker和DatePickerDialog的分割线颜色 一.NumberPicker的分割线颜色 我把对NumberPicker分割线颜色的方法写在一个工具类中,便于全局调用,代码如下: public static void setNumberPickerDi

  • Android自定义可循环的滚动选择器CycleWheelView

    最近碰到个项目要使用到滚动选择器,原生的NumberPicker可定制性太差,不大符合UI要求. 网上开源的WheelView是用ScrollView写的,不能循环滚动,而且当数据量很大时要加载的Item太多,性能非常低. 然后,还是自己写一个比较靠谱,用的是ListView实现的.写完自己体验了一下,性能不错,再大的数据也不怕了. 感觉不错,重新封装了一下,提供了一些接口可以直接按照自己的需求定制,调用方法在MainActivity中. 补个图片: 不多说了,直接上代码: CycleWheel

随机推荐