当前位置:   article > 正文

android 动态时间控件,Android滚轮选择时间控件使用详解

android 时间选择实现滚动震动

滚轮选择控件

Android自带的选择时间控件有点丑,往往产品和设计都比较嫌弃,希望做成ios一样的滚轮选择,下面是我在NumberPicker的基础上自定义的选择控件,效果如下:

e50d59bc5f702bf1be9eda65d776d2f1.gif

原理

基于NumberPicker实现

动态填充数值

联动

接口监听回调

实现滚轮效果有github上mark比较多的WheelView,但是阅读源码发现数据是一次性填入的,选择时间的话,填入10年就是10*365=3650条数据,也就是new出三千多个TextView,想想都觉得恐怖,肯定是不行的,于是便想到用NumberPicker,动态填充数据,一次只设置5个数据,当选中变化时,重新设置数据填充,所以关键在于填充的数据的计算。

设置数据部分逻辑代码:

/**

* 更新左侧控件

* 日期:选择年控件

* 时间:选择月份和日期控件

*

* @param timeMillis

*/

private void updateLeftValue(long timeMillis) {

SimpleDateFormat sdf;

String str[] = new String[DATA_SIZE];

if (mCurrentType == TYPE_PICK_DATE) {

sdf = new SimpleDateFormat("yyyy");

for (int i = 0; i < DATA_SIZE; i++) {

Calendar cal = Calendar.getInstance();

cal.setTimeInMillis(timeMillis);

cal.add(Calendar.YEAR, (i - DATA_SIZE / 2));

str[i] = sdf.format(cal.getTimeInMillis());

}

} else {

sdf = new SimpleDateFormat("MM-dd EEE");

for (int i = 0; i < DATA_SIZE; i++) {

Calendar cal = Calendar.getInstance();

cal.setTimeInMillis(timeMillis);

cal.add(Calendar.DAY_OF_MONTH, (i - DATA_SIZE / 2));

str[i] = sdf.format(cal.getTimeInMillis());

}

}

mNpLeft.setDisplayedValues(str);

mNpLeft.setValue(DATA_SIZE / 2);

mNpLeft.postInvalidate();

}

对滚轮的监听,并重新设置填充数据:

@Override

public void onValueChange(NumberPicker picker, int oldVal, int newVal) {

Calendar calendar = Calendar.getInstance();

calendar.setTimeInMillis(mTimeMillis);

int year = calendar.get(Calendar.YEAR);

int month = calendar.get(Calendar.MONTH);

int day = calendar.get(Calendar.DAY_OF_MONTH);

int hour = calendar.get(Calendar.HOUR_OF_DAY);

int offset = newVal - oldVal;

if (picker == mNpLeft) {

if (mCurrentType == TYPE_PICK_DATE) {

calendar.add(Calendar.YEAR, offset);

} else {

calendar.add(Calendar.DAY_OF_MONTH, offset);

}

updateLeftValue(calendar.getTimeInMillis());

mTimeMillis = calendar.getTimeInMillis();

} else if (picker == mNpMiddle) {

if (mCurrentType == TYPE_PICK_DATE) {

calendar.add(Calendar.MONTH, offset);

if (calendar.get(Calendar.YEAR) != year) {

calendar.set(Calendar.YEAR, year);

}

} else {

calendar.add(Calendar.HOUR_OF_DAY, offset);

if (calendar.get(Calendar.DAY_OF_MONTH) != day) {

calendar.set(Calendar.DAY_OF_MONTH, day);

}

if (calendar.get(Calendar.MONTH) != month) {

calendar.set(Calendar.MONTH, month);

}

if (calendar.get(Calendar.YEAR) != year) {

calendar.set(Calendar.YEAR, year);

}

}

updateMiddleValue(calendar.getTimeInMillis());

updateRightValue(calendar.getTimeInMillis());

mTimeMillis = calendar.getTimeInMillis();

} else if (picker == mNpRight) {

if (mCurrentType == TYPE_PICK_DATE) {

int days = getMaxDayOfMonth(year, month + 1);

if(day == 1 && offset < 0){

calendar.set(Calendar.DAY_OF_MONTH,days);

}else if(day == days && offset > 0){

calendar.set(Calendar.DAY_OF_MONTH,1);

}else{

calendar.add(Calendar.DAY_OF_MONTH, offset);

}

if (calendar.get(Calendar.MONTH) != month) {

calendar.set(Calendar.MONTH, month);

}

if (calendar.get(Calendar.YEAR) != year) {

calendar.set(Calendar.YEAR, year);

}

Log.e(TAG,"time:::"+test.format(calendar.getTimeInMillis()));

} else {

calendar.add(Calendar.MINUTE, offset);

if (calendar.get(Calendar.HOUR_OF_DAY) != hour) {

calendar.set(Calendar.HOUR_OF_DAY, hour);

}

if (calendar.get(Calendar.DAY_OF_MONTH) != day) {

calendar.set(Calendar.DAY_OF_MONTH, day);

}

if (calendar.get(Calendar.MONTH) != month) {

calendar.set(Calendar.MONTH, month);

}

if (calendar.get(Calendar.YEAR) != year) {

calendar.set(Calendar.YEAR, year);

}

}

updateRightValue(calendar.getTimeInMillis());

mTimeMillis = calendar.getTimeInMillis();

}

/**

* 向外部发送当前选中时间

*/

if (mOnSelectedChangeListener != null) {

mOnSelectedChangeListener.onSelected(this,mTimeMillis);

}

Log.e(TAG, "selected time:" + test.format(mTimeMillis));

}

选择数值和字符串

同样的,使用NumberPicker进行封装,动态填充数值从而实现滚动变换的效果。

考虑到通用性,传入的是Object类型的数组,在控件里进行判断。

可以选择一列数值、两列数值、三列数值,字符串同理。每一列数值可以设置它的单位、标题等,默认是隐藏,需要自己设置。

可以设置步长step

完整代码如下:

package com.example.moore.picktimeview.widget;

import android.content.Context;

import android.graphics.Color;

import android.util.AttributeSet;

import android.util.Log;

import android.view.Gravity;

import android.view.ViewGroup;

import android.widget.LinearLayout;

import android.widget.NumberPicker;

import android.widget.TextView;

/**

* Created by Moore on 2016/10/21.

*/

public class PickValueView extends LinearLayout implements NumberPicker.OnValueChangeListener {

private Context mContext;

/**

* 组件 标题、单位、滚轮

*/

private TextView mTitleLeft, mTitleMiddle, mTitleRight;

private TextView mUnitLeft, mUnitMiddle, mUnitRight;

private MyNumberPicker mNpLeft, mNpMiddle, mNpRight;

/**

* 数据个数 1列 or 2列 or 3列

*/

private int mViewCount = 1;

/**

* 一组数据长度

*/

private final int DATA_SIZE = 3;

/**

* 需要设置的值与默认值

*/

private Object[] mLeftValues;

private Object[] mMiddleValues;

private Object[] mRightValues;

private Object mDefaultLeftValue;

private Object mDefaultMiddleValue;

private Object mDefaultRightValue;

/**

* 当前正在显示的值

*/

private Object[] mShowingLeft = new Object[DATA_SIZE];

private Object[] mShowingMiddle = new Object[DATA_SIZE];

private Object[] mShowingRight = new Object[DATA_SIZE];

/**

* 步长

*/

private int mLeftStep = 5;

private int mMiddleStep = 1;

private int mRightStep = 1;

/**

* 回调接口对象

*/

private onSelectedChangeListener mSelectedChangeListener;

public PickValueView(Context context) {

super(context);

this.mContext = context;

generateView();

}

public PickValueView(Context context, AttributeSet attrs) {

super(context, attrs);

this.mContext = context;

generateView();

}

public PickValueView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

this.mContext = context;

generateView();

}

/**

* 生成视图

*/

private void generateView() {

//标题

LinearLayout titleLayout = new LinearLayout(mContext);

LayoutParams titleParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

titleParams.setMargins(0, 0, 0, dip2px(12));

titleLayout.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

titleLayout.setOrientation(HORIZONTAL);

mTitleLeft = new TextView(mContext);

mTitleMiddle = new TextView(mContext);

mTitleRight = new TextView(mContext);

LayoutParams params = new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1);

TextView[] titles = new TextView[]{mTitleLeft, mTitleMiddle, mTitleRight};

for (int i = 0; i < titles.length; i++) {

titles[i].setLayoutParams(params);

titles[i].setGravity(Gravity.CENTER);

titles[i].setTextColor(Color.parseColor("#3434EE"));

}

titleLayout.addView(mTitleLeft);

titleLayout.addView(mTitleMiddle);

titleLayout.addView(mTitleRight);

//内容

LinearLayout contentLayout = new LinearLayout(mContext);

contentLayout.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

contentLayout.setOrientation(HORIZONTAL);

contentLayout.setGravity(Gravity.CENTER);

mNpLeft = new MyNumberPicker(mContext);

mNpMiddle = new MyNumberPicker(mContext);

mNpRight = new MyNumberPicker(mContext);

mUnitLeft = new TextView(mContext);

mUnitMiddle = new TextView(mContext);

mUnitRight = new TextView(mContext);

MyNumberPicker[] nps = new MyNumberPicker[]{mNpLeft, mNpMiddle, mNpRight};

for (int i = 0; i < nps.length; i++) {

nps[i].setLayoutParams(params);

nps[i].setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);

nps[i].setOnValueChangedListener(this);

}

contentLayout.addView(mNpLeft);

contentLayout.addView(mUnitLeft);

contentLayout.addView(mNpMiddle);

contentLayout.addView(mUnitMiddle);

contentLayout.addView(mNpRight);

contentLayout.addView(mUnitRight);

this.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

this.setOrientation(VERTICAL);

this.addView(titleLayout);

this.addView(contentLayout);

}

/**

* 初始化数据和值

*/

private void initViewAndPicker() {

if (mViewCount == 1) {

this.mNpMiddle.setVisibility(GONE);

this.mNpRight.setVisibility(GONE);

this.mUnitMiddle.setVisibility(GONE);

this.mUnitRight.setVisibility(GONE);

} else if (mViewCount == 2) {

this.mNpRight.setVisibility(GONE);

this.mUnitRight.setVisibility(GONE);

}

//初始化数组值

if (mLeftValues != null && mLeftValues.length != 0) {

if (mLeftValues.length < DATA_SIZE) {

for (int i = 0; i < mLeftValues.length; i++) {

mShowingLeft[i] = mLeftValues[i];

}

for (int i = mLeftValues.length; i < DATA_SIZE; i++) {

mShowingLeft[i] = -9999;

}

} else {

for (int i = 0; i < DATA_SIZE; i++) {

mShowingLeft[i] = mLeftValues[i];

}

}

mNpLeft.setMinValue(0);

mNpLeft.setMaxValue(DATA_SIZE - 1);

if (mDefaultLeftValue != null)

updateLeftView(mDefaultLeftValue);

else

updateLeftView(mShowingLeft[0]);

}

/**

* 中间控件

*/

if (mViewCount == 2 || mViewCount == 3) {

if (mMiddleValues != null && mMiddleValues.length != 0) {

if (mMiddleValues.length < DATA_SIZE) {

for (int i = 0; i < mMiddleValues.length; i++) {

mShowingMiddle[i] = mMiddleValues[i];

}

for (int i = mMiddleValues.length; i < DATA_SIZE; i++) {

mShowingMiddle[i] = -9999;

}

} else {

for (int i = 0; i < DATA_SIZE; i++) {

mShowingMiddle[i] = mMiddleValues[i];

}

}

mNpMiddle.setMinValue(0);

mNpMiddle.setMaxValue(DATA_SIZE - 1);

if (mDefaultMiddleValue != null)

updateMiddleView(mDefaultMiddleValue);

else

updateMiddleView(mShowingMiddle[0]);

}

}

/**

* 右侧控件

*/

if (mViewCount == 3) {

if (mRightValues != null && mRightValues.length != 0) {

if (mRightValues.length < DATA_SIZE) {

for (int i = 0; i < mRightValues.length; i++) {

mShowingRight[i] = mRightValues[i];

}

for (int i = mRightValues.length; i < DATA_SIZE; i++) {

mShowingRight[i] = -9999;

}

} else {

for (int i = 0; i < DATA_SIZE; i++) {

mShowingRight[i] = mRightValues[i];

}

}

mNpRight.setMinValue(0);

mNpRight.setMaxValue(DATA_SIZE - 1);

if (mDefaultRightValue != null)

updateRightView(mDefaultRightValue);

else

updateRightView(mShowingRight[0]);

}

}

}

private void updateLeftView(Object value) {

updateValue(value, 0);

}

private void updateMiddleView(Object value) {

updateValue(value, 1);

}

private void updateRightView(Object value) {

updateValue(value, 2);

}

/**

* 更新滚轮视图

*

* @param value

* @param index

*/

private void updateValue(Object value, int index) {

String showStr[] = new String[DATA_SIZE];

MyNumberPicker picker;

Object[] showingValue;

Object[] values;

int step;

if (index == 0) {

picker = mNpLeft;

showingValue = mShowingLeft;

values = mLeftValues;

step = mLeftStep;

} else if (index == 1) {

picker = mNpMiddle;

showingValue = mShowingMiddle;

values = mMiddleValues;

step = mMiddleStep;

} else {

picker = mNpRight;

showingValue = mShowingRight;

values = mRightValues;

step = mRightStep;

}

if (values instanceof Integer[]) {

for (int i = 0; i < DATA_SIZE; i++) {

showingValue[i] = (int) value - step * (DATA_SIZE / 2 - i);

int offset = (int) values[values.length - 1] - (int) values[0] + step;

if ((int) showingValue[i] < (int) values[0]) {

showingValue[i] = (int) showingValue[i] + offset;

}

if ((int) showingValue[i] > (int) values[values.length - 1]) {

showingValue[i] = (int) showingValue[i] - offset;

}

showStr[i] = "" + showingValue[i];

}

} else {

int strIndex = 0;

for (int i = 0; i < values.length; i++) {

if (values[i].equals(value)) {

strIndex = i;

break;

}

}

for (int i = 0; i < DATA_SIZE; i++) {

int temp = strIndex - (DATA_SIZE / 2 - i);

if (temp < 0) {

temp += values.length;

}

if (temp >= values.length) {

temp -= values.length;

}

showingValue[i] = values[temp];

showStr[i] = (String) values[temp];

}

}

picker.setDisplayedValues(showStr);

picker.setValue(DATA_SIZE / 2);

picker.postInvalidate();

}

@Override

public void onValueChange(NumberPicker picker, int oldVal, int newVal) {

if (picker == mNpLeft) {

updateLeftView(mShowingLeft[newVal]);

} else if (picker == mNpMiddle) {

updateMiddleView(mShowingMiddle[newVal]);

} else if (picker == mNpRight) {

updateRightView(mShowingRight[newVal]);

}

if (mSelectedChangeListener != null) {

mSelectedChangeListener.onSelected(this, mShowingLeft[DATA_SIZE / 2], mShowingMiddle[DATA_SIZE / 2], mShowingRight[DATA_SIZE / 2]);

}

}

/**

* 设置数据--单列数据

*

* @param leftValues

* @param mDefaultLeftValue

*/

public void setValueData(Object[] leftValues, Object mDefaultLeftValue) {

this.mViewCount = 1;

this.mLeftValues = leftValues;

this.mDefaultLeftValue = mDefaultLeftValue;

initViewAndPicker();

}

/**

* 设置数据--两列数据

*

* @param leftValues

* @param mDefaultLeftValue

* @param middleValues

* @param defaultMiddleValue

*/

public void setValueData(Object[] leftValues, Object mDefaultLeftValue, Object[] middleValues, Object defaultMiddleValue) {

this.mViewCount = 2;

this.mLeftValues = leftValues;

this.mDefaultLeftValue = mDefaultLeftValue;

this.mMiddleValues = middleValues;

this.mDefaultMiddleValue = defaultMiddleValue;

initViewAndPicker();

}

/**

* 设置数据--三列数据

*

* @param leftValues

* @param mDefaultLeftValue

* @param middleValues

* @param defaultMiddleValue

* @param rightValues

* @param defaultRightValue

*/

public void setValueData(Object[] leftValues, Object mDefaultLeftValue, Object[] middleValues, Object defaultMiddleValue, Object[] rightValues, Object defaultRightValue) {

this.mViewCount = 3;

this.mLeftValues = leftValues;

this.mDefaultLeftValue = mDefaultLeftValue;

this.mMiddleValues = middleValues;

this.mDefaultMiddleValue = defaultMiddleValue;

this.mRightValues = rightValues;

this.mDefaultRightValue = defaultRightValue;

initViewAndPicker();

}

/**

* 设置左边数据步长

*

* @param step

*/

public void setLeftStep(int step) {

this.mLeftStep = step;

initViewAndPicker();

}

/**

* 设置中间数据步长

*

* @param step

*/

public void setMiddleStep(int step) {

this.mMiddleStep = step;

initViewAndPicker();

}

/**

* 设置右边数据步长

*

* @param step

*/

public void setRightStep(int step) {

this.mRightStep = step;

initViewAndPicker();

}

/**

* 设置标题

*

* @param left

* @param middle

* @param right

*/

public void setTitle(String left, String middle, String right) {

if (left != null) {

mTitleLeft.setVisibility(VISIBLE);

mTitleLeft.setText(left);

} else {

mTitleLeft.setVisibility(GONE);

}

if (middle != null) {

mTitleMiddle.setVisibility(VISIBLE);

mTitleMiddle.setText(middle);

} else {

mTitleMiddle.setVisibility(GONE);

}

if (right != null) {

mTitleRight.setVisibility(VISIBLE);

mTitleRight.setText(right);

} else {

mTitleRight.setVisibility(GONE);

}

this.postInvalidate();

}

public void setUnitLeft(String unitLeft) {

setUnit(unitLeft, 0);

}

public void setmUnitMiddle(String unitMiddle) {

setUnit(unitMiddle, 1);

}

public void setUnitRight(String unitRight) {

setUnit(unitRight, 2);

}

private void setUnit(String unit, int index) {

TextView tvUnit;

if (index == 0) {

tvUnit = mUnitLeft;

} else if (index == 1) {

tvUnit = mUnitMiddle;

} else {

tvUnit = mUnitRight;

}

if (unit != null) {

tvUnit.setText(unit);

} else {

tvUnit.setText(" ");

}

initViewAndPicker();

}

/**

* 设置回调

*

* @param listener

*/

public void setOnSelectedChangeListener(onSelectedChangeListener listener) {

this.mSelectedChangeListener = listener;

}

/**

* dp转px

*

* @param dp

* @return

*/

private int dip2px(int dp) {

float scale = mContext.getResources().getDisplayMetrics().density;

return (int) (scale * dp + 0.5f);

}

/**

* 回调接口

*/

public interface onSelectedChangeListener {

void onSelected(PickValueView view, Object leftValue, Object middleValue, Object rightValue);

}

}

关于NumberPicker

默认的NumberPicker往往字体颜色、分割线颜色等都是跟随系统,不能改变,考虑到可能比较丑或者有其他需求,所以自定义的NumberPicker,通过反射的方式更改里面的一些属性,代码如下:

package com.example.moore.picktimeview.widget;

import android.content.Context;

import android.graphics.Color;

import android.graphics.drawable.ColorDrawable;

import android.util.AttributeSet;

import android.util.Log;

import android.view.View;

import android.view.ViewGroup;

import android.widget.EditText;

import android.widget.ImageButton;

import android.widget.NumberPicker;

import java.lang.reflect.Field;

/**

* Created by Moore on 2016/10/20.

*/

public class MyNumberPicker extends NumberPicker {

private static int mTextSize = 16;

private static int mTextColor = 0x000000;

private static int mDividerColor = 0xFFFF00;

public MyNumberPicker(Context context) {

super(context);

setNumberPickerDividerColor();

}

public MyNumberPicker(Context context, AttributeSet attrs) {

super(context, attrs);

setNumberPickerDividerColor();

}

public MyNumberPicker(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

setNumberPickerDividerColor();

}

@Override

public void addView(View child) {

super.addView(child);

updateView(child);

}

@Override

public void addView(View child, int index, ViewGroup.LayoutParams params) {

super.addView(child, index, params);

updateView(child);

}

@Override

public void addView(View child, ViewGroup.LayoutParams params) {

super.addView(child, params);

updateView(child);

}

private void updateView(View view) {

if (view instanceof EditText) {

// ((EditText) view).setTextSize(mTextSize);

((EditText) view).setTextSize(17);

// ((EditText) view).setTextColor(mTextColor);

((EditText) view).setTextColor(Color.parseColor("#6495ED"));

}

}

private void setNumberPickerDividerColor() {

Field[] pickerFields = NumberPicker.class.getDeclaredFields();

/**

* 设置分割线颜色

*/

for (Field pf : pickerFields) {

if (pf.getName().equals("mSelectionDivider")) {

pf.setAccessible(true);

try {

// pf.set(this, new ColorDrawable(mDividerColor));

pf.set(this, new ColorDrawable(Color.parseColor("#C4C4C4")));

} catch (IllegalAccessException e) {

e.printStackTrace();

}

break;

}

}

/**

* 设置分割线高度

*/

for (Field pf : pickerFields) {

if (pf.getName().equals("mSelectionDividerHeight")) {

pf.setAccessible(true);

try {

pf.set(this, 2);

} catch (IllegalAccessException e) {

e.printStackTrace();

}

break;

}

}

for (Field pf : pickerFields) {

if (pf.getName().equals("mSelectorElementHeight")) {

pf.setAccessible(true);

try {

pf.set(this, 2);

} catch (IllegalAccessException e) {

e.printStackTrace();

}

break;

}

}

}

public void setDividerColor(int color) {

this.mDividerColor = color;

// this.postInvalidate();

}

public void setTextColor(int color) {

this.mTextColor = color;

// this.postInvalidate();

}

public void setTextSize(int textSize) {

this.mTextSize = textSize;

// this.postInvalidate();

}

}

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/278821
推荐阅读
相关标签
  

闽ICP备14008679号