效果图
代码实现
package com.demo.ui.view;import android.annotation.TargetApi;import android.content.Context;import android.os.Build;import android.os.Handler;import android.support.v4.content.ContextCompat;import android.view.Gravity;import android.view.MotionEvent;import android.view.View;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.RelativeLayout;import android.widget.TextView;import com.demo.R;import com.demo.factory.Axis;import com.demo.utils.LogUtils;import java.util.ArrayList;import java.util.List;public class AirTemperatureView extends RelativeLayout{private Context context;private TextView tv;private TextView tv_middle;// private TextView tv_middle_small;private RelativeLayout Auto_layout;private StringScrollPicker stringScrollPicker;private List<CharSequence> newList;private ImageView img;private ImageView img_left;private ImageView img_right;private int mPosition;private Handler handler = new Handler();/*** 延迟线程,看是否还有下一个字符输入*/private Runnable delayRun = new Runnable() {@Overridepublic void run() {stringScrollPicker.setVisibility(INVISIBLE);img.setBackgroundResource(R.drawable.air_temp_bg);tv_middle.setText(newList.get(mPosition));//tv.setText("电池剩余"+newList.get(mPosition)+"%提醒你");tv_middle.setVisibility(VISIBLE);img_right.setVisibility(VISIBLE);img_left.setVisibility(VISIBLE);if(mListener != null) {mListener.setAirTemper(mPosition+18);}LogUtils.e("空调刷新===","温度");}};public AirTemperatureView(Context context){super(context);this.context = context;init();}@TargetApi(Build.VERSION_CODES.M)public void init(){super.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, Axis.scaleX(230)));LinearLayout parent = new LinearLayout(context);parent.setOrientation(LinearLayout.VERTICAL);LayoutParams parent_Params = new LayoutParams(LayoutParams.MATCH_PARENT, Axis.scaleX(230));super.addView(parent,parent_Params);RelativeLayout tv_layout = new RelativeLayout(context);LinearLayout.LayoutParams tv_layout_Params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, Axis.scaleX(50));parent.addView(tv_layout,tv_layout_Params);tv = new TextView(context);tv.setText("温度");tv.setTextSize(Axis.scaleTextSize(36));tv.setTextColor(ContextCompat.getColor(context, R.color.white_70_color));//LayoutParams tv_Params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);tv_Params.addRule(RelativeLayout.CENTER_IN_PARENT);tv_layout.addView(tv,tv_Params);/*** 滑动选择器*/Auto_layout = new RelativeLayout(context);LinearLayout.LayoutParams Auto_layout_Params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, Axis.scaleX(150));Auto_layout_Params.setMargins(0,Axis.scaleX(30),0,0);parent.addView(Auto_layout,Auto_layout_Params);img = new ImageView(context);img.setId(R.id.EleRemindImage);img.setBackgroundResource(R.drawable.air_temp_bg);LayoutParams img_Params = new LayoutParams(Axis.scaleX(150), Axis.scaleX(150));img_Params.addRule(RelativeLayout.CENTER_IN_PARENT);Auto_layout.addView(img,img_Params);img_left = new ImageView(context);img_left.setBackgroundResource(R.drawable.battery_point);LayoutParams img_left_Params = new LayoutParams(Axis.scaleX(14), Axis.scaleX(14));img_left_Params.addRule(RelativeLayout.CENTER_VERTICAL);img_left_Params.addRule(RelativeLayout.LEFT_OF,R.id.EleRemindImage);img_left_Params.setMargins(0,0,Axis.scaleX(134),0);Auto_layout.addView(img_left,img_left_Params);img_right = new ImageView(context);img_right.setBackgroundResource(R.drawable.battery_point);LayoutParams img_right_Params = new LayoutParams(Axis.scaleX(14), Axis.scaleX(14));img_right_Params.addRule(RelativeLayout.CENTER_VERTICAL);img_right_Params.addRule(RelativeLayout.RIGHT_OF,R.id.EleRemindImage);img_right_Params.setMargins(Axis.scaleX(134),0,0,0);Auto_layout.addView(img_right,img_right_Params);tv_middle = new TextView(context);tv_middle.setTextSize(Axis.scaleTextSize(64));tv_middle.setGravity(Gravity.CENTER);tv_middle.setTextColor(0xFFFFFFFF);LayoutParams tv_middle_Params = new LayoutParams(Axis.scaleX(150), Axis.scaleX(150));tv_middle_Params.addRule(RelativeLayout.CENTER_IN_PARENT);Auto_layout.addView(tv_middle,tv_middle_Params);newList = new ArrayList<>();newList.add("18");newList.add("19");newList.add("20");newList.add("21");newList.add("22");newList.add("23");newList.add("24");newList.add("25");newList.add("26");newList.add("27");newList.add("28");newList.add("29");newList.add("30");newList.add("31");newList.add("32");tv_middle.setText(newList.get(0));stringScrollPicker = new StringScrollPicker(context);stringScrollPicker.setHorizontal(true);stringScrollPicker.setVisibleItemCount(5);//可见5个 第3个 (3-1)个位中间stringScrollPicker.setCenterPosition(2);stringScrollPicker.setIsCirculation(true);stringScrollPicker.setCanTap(true);stringScrollPicker.setDisallowInterceptTouch(true);LayoutParams stringScrollPicker_Params = new LayoutParams(LayoutParams.MATCH_PARENT,Axis.scaleX(150));Auto_layout.addView(stringScrollPicker,stringScrollPicker_Params);stringScrollPicker.setData(newList);stringScrollPicker.setOnSelectedListener(new ScrollPickerView.OnSelectedListener() {@Overridepublic void onSelected(ScrollPickerView scrollPickerView, final int position) {mPosition = position;handler.postDelayed(delayRun, 1500);}});stringScrollPicker.setOnSelectedListener(new StringScrollPicker.OnDataSelectedListener() {@Overridepublic void DataSelected(CharSequence data) {//tv.setText("电池剩余"+data+"%提醒你");}});stringScrollPicker.setSelectedPosition(0,false);//中间Item位置stringScrollPicker.setVisibility(INVISIBLE);stringScrollPicker.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:img.setBackgroundResource(R.drawable.battery_btn_p);if(delayRun!=null){handler.removeCallbacks(delayRun);}break;}return false;}});img.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {stringScrollPicker.setVisibility(VISIBLE);tv_middle.setVisibility(INVISIBLE);img_right.setVisibility(INVISIBLE);img_left.setVisibility(INVISIBLE);img.setBackgroundResource(R.drawable.battery_btn_p);}});}/*** 风速设置* @param mPosition*/public void setAirTemperature(int mPosition){/*** 在设置的时候不刷新*/if(stringScrollPicker.getVisibility() == INVISIBLE){if(mPosition > 32){mPosition = 32;}stringScrollPicker.setSelectedPosition(mPosition-18,false);tv_middle.setText(newList.get(mPosition-18));}}public TemperatureListener mListener;public void setOnTemperatureListener (TemperatureListener listener) {mListener = listener;}public interface TemperatureListener {void setAirTemper(int temperature);}}
StringScrollPicker 类
package com.demo.ui.view;import android.annotation.TargetApi;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.os.Build;import android.support.annotation.RequiresApi;import android.text.Layout;import android.text.StaticLayout;import android.text.TextPaint;import android.util.AttributeSet;import android.util.Log;import com.demo.factory.Axis;import com.demo.utils.ColorUtil;import java.util.ArrayList;import java.util.Arrays;import java.util.List;public class StringScrollPicker extends ScrollPickerView<CharSequence> {private int mMeasureWidth;private int mMeasureHeight;private TextPaint mPaint; //private int mMinTextSize = Axis.scaleX(64); // 最小的字体private int mMaxTextSize = Axis.scaleX(64); // 最大的字体// 字体渐变颜色private int mStartColor = Color.WHITE; // 中间选中item的颜色private int mEndColor = Color.GRAY; // 上下两边的颜色private int mMaxLineWidth = -1; // 最大的行宽,默认为itemWidth.超过后文字自动换行private Layout.Alignment mAlignment = Layout.Alignment.ALIGN_CENTER; // 对齐方式,默认居中@TargetApi(Build.VERSION_CODES.CUPCAKE)public StringScrollPicker(Context context) {this(context, null);}@RequiresApi(api = Build.VERSION_CODES.CUPCAKE)public StringScrollPicker(Context context, AttributeSet attrs) {this(context, attrs, 0);}@RequiresApi(api = Build.VERSION_CODES.CUPCAKE)public StringScrollPicker(Context context, AttributeSet attrs,int defStyleAttr) {super(context, attrs, defStyleAttr);mPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);mPaint.setStyle(Paint.Style.FILL);mPaint.setColor(Color.BLACK);setData(new ArrayList<CharSequence>(Arrays.asList(new String[]{"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"})));}/*** @param startColor 正中间的颜色* @param endColor 上下两边的颜色*/public void setColor(int startColor, int endColor) {mStartColor = startColor;mEndColor = endColor;invalidate();}/*** item文字大小** @param minText 沒有被选中时的最小文字* @param maxText 被选中时的最大文字*/public void setTextSize(int minText, int maxText) {mMinTextSize = minText;mMaxTextSize = maxText;invalidate();}public int getStartColor() {return mStartColor;}public int getEndColor() {return mEndColor;}public int getMinTextSize() {return mMinTextSize;}public int getMaxTextSize() {return mMaxTextSize;}public int getMaxLineWidth() {return mMaxLineWidth;}/*** 最大的行宽,默认为itemWidth.超过后文字自动换行* @param maxLineWidth*/public void setMaxLineWidth(int maxLineWidth) {mMaxLineWidth = maxLineWidth;}/*** 最大的行宽,默认为itemWidth.超过后文字自动换行* @return*/public Layout.Alignment getAlignment() {return mAlignment;}public void setAlignment(Layout.Alignment alignment) {mAlignment = alignment;}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mMeasureWidth = getMeasuredWidth();mMeasureHeight = getMeasuredHeight();if (mMaxLineWidth < 0) {mMaxLineWidth = getItemWidth();}}@Overridepublic void drawItem(Canvas canvas, List<CharSequence> data, int position, int relative, float moveLength, float top) {CharSequence text = data.get(position);int itemSize = getItemSize();// 设置文字大小if (relative == -1) { // 上一个if (moveLength < 0) { // 向上滑动mPaint.setTextSize(mMinTextSize);} else { // 向下滑动mPaint.setTextSize(mMinTextSize + (mMaxTextSize - mMinTextSize)* moveLength / itemSize);}} else if (relative == 0) { // 中间item,当前选中mPaint.setTextSize(mMinTextSize + (mMaxTextSize - mMinTextSize)* (itemSize - Math.abs(moveLength)) / itemSize);} else if (relative == 1) { // 下一个if (moveLength > 0) { // 向下滑动mPaint.setTextSize(mMinTextSize);} else { // 向上滑动mPaint.setTextSize(mMinTextSize + (mMaxTextSize - mMinTextSize)* -moveLength / itemSize);}} else { // 其他mPaint.setTextSize(mMinTextSize);}StaticLayout layout = new StaticLayout(text, 0, text.length(), mPaint, mMaxLineWidth, mAlignment, 1.0F, 0.0F, true, null, 0);float x = 0;float y = 0;float lineWidth = layout.getWidth();if (isHorizontal()) { // 水平滚动x = top + (getItemWidth() - lineWidth) / 2;y = (getItemHeight() - layout.getHeight()) / 2;} else { // 垂直滚动x = (getItemWidth() - lineWidth) / 2;y = top + (getItemHeight() - layout.getHeight()) / 2;}// 计算渐变颜色computeColor(relative, itemSize, moveLength,text);// canvas.drawText(text, x, y, mPaint);canvas.save();canvas.translate(x, y);layout.draw(canvas);canvas.restore();}/*** 计算字体颜色,渐变** @param relative 相对中间item的位置*/private String lastString;private String nowSring;private void computeColor(int relative, int itemSize, float moveLength, CharSequence text) {int color = mEndColor; // 其他默认为mEndColorif (relative == -1 || relative == 1) { // 上一个或下一个// 处理上一个item且向上滑动或者处理下一个item且向下滑动,颜色为mEndColorif ((relative == -1 && moveLength < 0)|| (relative == 1 && moveLength > 0)) {color = mEndColor;} else { // 计算渐变的颜色float rate = (itemSize - Math.abs(moveLength))/ itemSize;color = puteGradientColor(mStartColor, mEndColor, rate);}} else if (relative == 0) { // 中间itemfloat rate = Math.abs(moveLength) / itemSize;color = puteGradientColor(mStartColor, mEndColor, rate);nowSring = text.toString();if(nowSring != lastString){Log.e("text=====",text+"");if(mListener != null){mListener.DataSelected(text);}}lastString = nowSring;}mPaint.setColor(color);}public interface OnDataSelectedListener {void DataSelected(CharSequence data);}public void setOnSelectedListener(OnDataSelectedListener listener) {mListener = listener;}public OnDataSelectedListener mListener;}
ScrollPickerView 类
package com.demo.ui.view;import android.animation.Animator;import android.animation.AnimatorListenerAdapter;import android.animation.ValueAnimator;import android.annotation.TargetApi;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.drawable.ColorDrawable;import android.graphics.drawable.Drawable;import android.os.Build;import android.support.annotation.RequiresApi;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;import android.view.ViewParent;import android.view.animation.Interpolator;import android.widget.Scroller;import com.demo.R;import com.demo.utils.LogUtils;import java.util.ArrayList;import java.util.List;/*** 滚动选择器,带惯性滑动*/public abstract class ScrollPickerView<T> extends View {private int mVisibleItemCount = 3; // 可见的item数量private boolean mIsInertiaScroll = true; // 快速滑动时是否惯性滚动一段距离,默认开启private boolean mIsCirculation = true; // 是否循环滚动,默认开启/*不允许父组件拦截触摸事件,设置为true为不允许拦截,此时该设置才生效当嵌入到ScrollView等滚动组件中,为了使该自定义滚动选择器可以正常工作,请设置为true*/private boolean mDisallowInterceptTouch = false;private int mSelected; // 当前选中的item下标private int mLastSelected; // 当前选中的item下标private List<T> mData;private int mItemHeight = 0; // 每个条目的高度,当垂直滚动时,高度=mMeasureHeight/mVisibleItemCountprivate int mItemWidth = 0; // 每个条目的宽度,当水平滚动时,宽度=mMeasureWidth/mVisibleItemCountprivate int mItemSize; // 当垂直滚动时,mItemSize = mItemHeight;水平滚动时,mItemSize = mItemWidthprivate int mCenterPosition = -1; // 中间item的位置,0<=mCenterPosition<mVisibleItemCount,默认为 mVisibleItemCount / 2private int mCenterY; // 中间item的起始坐标y(不考虑偏移),当垂直滚动时,y= mCenterPosition*mItemHeightprivate int mCenterX; // 中间item的起始坐标x(不考虑偏移),当垂直滚动时,x = mCenterPosition*mItemWidthprivate int mCenterPoint; // 当垂直滚动时,mCenterPoint = mCenterY;水平滚动时,mCenterPoint = mCenterXprivate float mLastMoveY; // 触摸的坐标yprivate float mLastMoveX; // 触摸的坐标Xprivate float mMoveLength = 0; // item移动长度,负数表示向上移动,正数表示向下移动private GestureDetector mGestureDetector;private OnSelectedListener mListener;private Scroller mScroller;private boolean mIsFling; // 是否正在惯性滑动private boolean mIsMovingCenter; // 是否正在滑向中间// 可以把scroller看做模拟的触屏滑动操作,mLastScrollY为上次触屏滑动的坐标private int mLastScrollY = 0; // Scroller的坐标yprivate int mLastScrollX = 0; // Scroller的坐标xprivate boolean mDisallowTouch = false; // 不允许触摸private Paint mPaint; //private Drawable mCenterItemBackground = null; // 中间选中item的背景色private boolean mCanTap = true; // 单击切换选项或触发点击监听器private boolean mIsHorizontal = false; // 是否水平滚动private boolean mDrawAllItem = false; // 是否绘制每个item(包括在边界外的item)@RequiresApi(api = Build.VERSION_CODES.CUPCAKE)public ScrollPickerView(Context context, AttributeSet attrs) {this(context, attrs, 0);}@TargetApi(Build.VERSION_CODES.HONEYCOMB)@RequiresApi(api = Build.VERSION_CODES.CUPCAKE)public ScrollPickerView(Context context, AttributeSet attrs,int defStyleAttr) {super(context, attrs, defStyleAttr);mGestureDetector = new GestureDetector(getContext(),new FlingOnGestureListener());mScroller = new Scroller(getContext());mAutoScrollAnimator = ValueAnimator.ofInt(0, 0);mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setStyle(Paint.Style.FILL);init(attrs);}private void init(AttributeSet attrs) {if (attrs != null) {TypedArray typedArray = getContext().obtainStyledAttributes(attrs,R.styleable.ScrollPickerView);if (typedArray.hasValue(R.styleable.ScrollPickerView_spv_center_item_background)) {setCenterItemBackground(typedArray.getDrawable(R.styleable.ScrollPickerView_spv_center_item_background));}setVisibleItemCount(typedArray.getInt(R.styleable.ScrollPickerView_spv_visible_item_count,getVisibleItemCount()));setCenterPosition(typedArray.getInt(R.styleable.ScrollPickerView_spv_center_item_position,getCenterPosition()));setIsCirculation(typedArray.getBoolean(R.styleable.ScrollPickerView_spv_is_circulation, isIsCirculation()));setDisallowInterceptTouch(typedArray.getBoolean(R.styleable.ScrollPickerView_spv_disallow_intercept_touch, isDisallowInterceptTouch()));setHorizontal(typedArray.getInt(R.styleable.ScrollPickerView_spv_orientation, mIsHorizontal ? 1 : 2) == 1);typedArray.recycle();}}@Overrideprotected void onDraw(Canvas canvas) {if (mData == null || mData.size() <= 0) {return;}// 选中item的背景色if (mCenterItemBackground != null) {mCenterItemBackground.draw(canvas);}// 只绘制可见的itemint length = Math.max(mCenterPosition + 1, mVisibleItemCount - mCenterPosition);int position;int start = Math.min(length, mData.size());if (mDrawAllItem) {start = mData.size();}// 上下两边for (int i = start; i >= 1; i--) { // 先从远离中间位置的item绘制,当item内容偏大时,较近的item覆盖在较远的上面if (mDrawAllItem || i <= mCenterPosition + 1) { // 上面的items,相对位置为 -iposition = mSelected - i < 0 ? mData.size() + mSelected - i: mSelected - i;// 传入位置信息,绘制itemif (mIsCirculation) {drawItem(canvas, mData, position, -i, mMoveLength, mCenterPoint + mMoveLength - i * mItemSize);} else if (mSelected - i >= 0) { // 非循环滚动drawItem(canvas, mData, position, -i, mMoveLength, mCenterPoint + mMoveLength - i * mItemSize);}}if (mDrawAllItem || i <= mVisibleItemCount - mCenterPosition) { // 下面的items,相对位置为 iposition = mSelected + i >= mData.size() ? mSelected + i- mData.size() : mSelected + i;// 传入位置信息,绘制itemif (mIsCirculation) {drawItem(canvas, mData, position, i, mMoveLength, mCenterPoint + mMoveLength + i * mItemSize);} else if (mSelected + i < mData.size()) { // 非循环滚动drawItem(canvas, mData, position, i, mMoveLength, mCenterPoint + mMoveLength + i * mItemSize);}}}// 选中的itemdrawItem(canvas, mData, mSelected, 0, mMoveLength, mCenterPoint + mMoveLength);}/*** 绘制item** @param canvas* @param data 数据集* @param position 在data数据集中的位置* @param relative 相对中间item的位置,relative==0表示中间item,relative<0表示上(左)边的item,relative>0表示下(右)边的item* @param moveLength 中间item滚动的距离,moveLength<0则表示向上(右)滚动的距离,moveLength>0则表示向下(左)滚动的距离* @param top 当前绘制item的坐标,当垂直滚动时为顶部y的坐标;当水平滚动时为item最左边x的坐标*/public abstract void drawItem(Canvas canvas, List<T> data, int position, int relative, float moveLength, float top);@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);reset();}private void reset() {if (mCenterPosition < 0) {mCenterPosition = mVisibleItemCount / 2;}if (mIsHorizontal) {mItemHeight = getMeasuredHeight();mItemWidth = getMeasuredWidth() / mVisibleItemCount;mCenterY = 0;mCenterX = mCenterPosition * mItemWidth;mItemSize = mItemWidth;mCenterPoint = mCenterX;} else {mItemHeight = getMeasuredHeight() / mVisibleItemCount;mItemWidth = getMeasuredWidth();mCenterY = mCenterPosition * mItemHeight;mCenterX = 0;mItemSize = mItemHeight;mCenterPoint = mCenterY;}if (mCenterItemBackground != null) {mCenterItemBackground.setBounds(mCenterX, mCenterY, mCenterX + mItemWidth, mCenterY + mItemHeight);}}@RequiresApi(api = Build.VERSION_CODES.FROYO)@Overridepublic boolean onTouchEvent(MotionEvent event) {if (mDisallowTouch) { // 不允许触摸return true;}if (mGestureDetector.onTouchEvent(event)) {return true;}switch (event.getActionMasked()) {case MotionEvent.ACTION_MOVE:if (mIsHorizontal) {if (Math.abs(event.getX() - mLastMoveX) < 0.1f) {return true;}mMoveLength += event.getX() - mLastMoveX;} else {if (Math.abs(event.getY() - mLastMoveY) < 0.1f) {return true;}mMoveLength += event.getY() - mLastMoveY;}mLastMoveY = event.getY();mLastMoveX = event.getX();checkCirculation();invalidate();break;case MotionEvent.ACTION_UP:mLastMoveY = event.getY();mLastMoveX = event.getX();moveToCenter();break;}return true;}/*** @param curr* @param end*/private void computeScroll(int curr, int end, float rate) {if (rate < 1) { // 正在滚动if (mIsHorizontal) {// 可以把scroller看做模拟的触屏滑动操作,mLastScrollX为上次滑动的坐标mMoveLength = mMoveLength + curr - mLastScrollX;mLastScrollX = curr;} else {// 可以把scroller看做模拟的触屏滑动操作,mLastScrollY为上次滑动的坐标mMoveLength = mMoveLength + curr - mLastScrollY;mLastScrollY = curr;}checkCirculation();invalidate();} else { // 滚动完毕mIsMovingCenter = false;mLastScrollY = 0;mLastScrollX = 0;// 直接居中,不通过动画if (mMoveLength > 0) { //// 向下滑动if (mMoveLength < mItemSize / 2) {mMoveLength = 0;} else {mMoveLength = mItemSize;}} else {if (-mMoveLength < mItemSize / 2) {mMoveLength = 0;} else {mMoveLength = -mItemSize;}}checkCirculation();mMoveLength = 0;mLastScrollY = 0;mLastScrollX = 0;notifySelected(true);invalidate();}}@Overridepublic void computeScroll() {if (puteScrollOffset()) { // 正在滚动if (mIsHorizontal) {// 可以把scroller看做模拟的触屏滑动操作,mLastScrollX为上次滑动的坐标mMoveLength = mMoveLength + mScroller.getCurrX() - mLastScrollX;} else {// 可以把scroller看做模拟的触屏滑动操作,mLastScrollY为上次滑动的坐标mMoveLength = mMoveLength + mScroller.getCurrY() - mLastScrollY;}mLastScrollY = mScroller.getCurrY();mLastScrollX = mScroller.getCurrX();checkCirculation(); //检测当前选中的iteminvalidate();} else { // 滚动完毕if (mIsFling) {mIsFling = false;moveToCenter(); // 滚动到中间位置} else if (mIsMovingCenter) { // 选择完成,回调给监听器mMoveLength = 0;mIsMovingCenter = false;mLastScrollY = 0;mLastScrollX = 0;notifySelected(true);}}}public void cancelScroll() {mLastScrollY = 0;mLastScrollX = 0;mIsFling = mIsMovingCenter = false;mScroller.abortAnimation();stopAutoScroll();}// 检测当前选择的item位置private void checkCirculation() {if (mMoveLength >= mItemSize) { // 向下滑动// 该次滚动距离中越过的item数量int span = (int) (mMoveLength / mItemSize);mSelected -= span;if (mSelected < 0) { // 滚动顶部,判断是否循环滚动if (mIsCirculation) {do {mSelected = mData.size() + mSelected;} while (mSelected < 0); // 当越过的item数量超过一圈时mMoveLength = (mMoveLength - mItemSize) % mItemSize;} else { // 非循环滚动mSelected = 0;mMoveLength = mItemSize;if (mIsFling) { // 停止惯性滑动,根据computeScroll()中的逻辑,下一步将调用moveToCenter()mScroller.forceFinished(true);}if (mIsMovingCenter) { // 移回中间位置scroll(mMoveLength, 0);}}} else {mMoveLength = (mMoveLength - mItemSize) % mItemSize;}} else if (mMoveLength <= -mItemSize) { // 向上滑动// 该次滚动距离中越过的item数量int span = (int) (-mMoveLength / mItemSize);mSelected += span;if (mSelected >= mData.size()) { // 滚动末尾,判断是否循环滚动if (mIsCirculation) {do {mSelected = mSelected - mData.size();} while (mSelected >= mData.size()); // 当越过的item数量超过一圈时mMoveLength = (mMoveLength + mItemSize) % mItemSize;} else { // 非循环滚动mSelected = mData.size() - 1;mMoveLength = -mItemSize;if (mIsFling) { // 停止惯性滑动,根据computeScroll()中的逻辑,下一步将调用moveToCenter()mScroller.forceFinished(true);}if (mIsMovingCenter) { // 移回中间位置scroll(mMoveLength, 0);}}} else {mMoveLength = (mMoveLength + mItemSize) % mItemSize;}}}// 移动到中间位置private void moveToCenter() {if (!mScroller.isFinished() || mIsFling || mMoveLength == 0) {return;}cancelScroll();// 向下滑动if (mMoveLength > 0) {if (mIsHorizontal) {if (mMoveLength < mItemWidth / 2) {scroll(mMoveLength, 0);} else {scroll(mMoveLength, mItemWidth);}} else {if (mMoveLength < mItemHeight / 2) {scroll(mMoveLength, 0);} else {scroll(mMoveLength, mItemHeight);}}} else {if (mIsHorizontal) {if (-mMoveLength < mItemWidth / 2) {scroll(mMoveLength, 0);} else {scroll(mMoveLength, -mItemWidth);}} else {if (-mMoveLength < mItemHeight / 2) {scroll(mMoveLength, 0);} else {scroll(mMoveLength, -mItemHeight);}}}}// 平滑滚动private void scroll(float from, int to) {if (mIsHorizontal) {mLastScrollX = (int) from;mIsMovingCenter = true;mScroller.startScroll((int) from, 0, 0, 0);mScroller.setFinalX(to);} else {mLastScrollY = (int) from;mIsMovingCenter = true;mScroller.startScroll(0, (int) from, 0, 0);mScroller.setFinalY(to);}invalidate();}// 惯性滑动,private void fling(float from, float vel) {if (mIsHorizontal) {mLastScrollX = (int) from;mIsFling = true;// 最多可以惯性滑动10个itemmScroller.fling((int) from, 0, (int) vel, 0, -10 * mItemWidth,10 * mItemWidth, 0, 0);} else {mLastScrollY = (int) from;mIsFling = true;// 最多可以惯性滑动10个itemmScroller.fling(0, (int) from, 0, (int) vel, 0, 0, -10 * mItemHeight,10 * mItemHeight);}invalidate();}private void notifySelected(final boolean trigger) {if (mListener != null) {// 告诉监听器选择完毕post(new Runnable() {@Overridepublic void run() {if(mLastSelected != mSelected){if(trigger){mListener.onSelected(ScrollPickerView.this, mSelected);}}mLastSelected = mSelected;}});}}private boolean mIsAutoScrolling = false;private ValueAnimator mAutoScrollAnimator;private final static SlotInterpolator sAutoScrollInterpolator = new SlotInterpolator();/*** 自动滚动(必须设置为可循环滚动)** @param position* @param duration* @param speed 每毫秒移动的像素点*/@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)public void autoScrollFast(final int position, long duration, float speed, final Interpolator interpolator) {if (mIsAutoScrolling || !mIsCirculation) {return;}cancelScroll();mIsAutoScrolling = true;int length = (int) (speed * duration);int circle = (int) (length * 1f / (mData.size() * mItemSize) + 0.5f); // 圈数circle = circle <= 0 ? 1 : circle;int aPlan = circle * (mData.size()) * mItemSize + (mSelected - position) * mItemSize;int bPlan = aPlan + (mData.size()) * mItemSize; // 多一圈// 让其尽量接近lengthfinal int end = Math.abs(length - aPlan) < Math.abs(length - bPlan) ? aPlan : bPlan;mAutoScrollAnimator.cancel();mAutoScrollAnimator.setIntValues(0, end);mAutoScrollAnimator.setInterpolator(interpolator);mAutoScrollAnimator.setDuration(duration);mAutoScrollAnimator.removeAllUpdateListeners();if (end != 0) { // itemHeight为0导致endy=0mAutoScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float rate = 0;rate = animation.getCurrentPlayTime() * 1f / animation.getDuration();computeScroll((int) animation.getAnimatedValue(), end, rate);}});mAutoScrollAnimator.removeAllListeners();mAutoScrollAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);mIsAutoScrolling = false;}});mAutoScrollAnimator.start();} else {computeScroll(end, end, 1);mIsAutoScrolling = false;}}/*** 自动滚动,默认速度为 0.6dp/ms** @see ScrollPickerView#autoScrollFast(int, long, float, Interpolator)*/@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)public void autoScrollFast(final int position, long duration) {float speed = dip2px(0.6f);autoScrollFast(position, duration, speed, sAutoScrollInterpolator);}/*** 自动滚动** @see ScrollPickerView#autoScrollFast(int, long, float, Interpolator)*/@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)public void autoScrollFast(final int position, long duration, float speed) {autoScrollFast(position, duration, speed, sAutoScrollInterpolator);}/*** 滚动到指定位置** @param toPosition 需要滚动到的位置* @param duration滚动时间* @param interpolator*/@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)public void autoScrollToPosition(int toPosition, long duration, final Interpolator interpolator) {toPosition = toPosition % mData.size();final int endY = (mSelected - toPosition) * mItemHeight;autoScrollTo(endY, duration, interpolator, false);}/*** @param endY 需要滚动到的位置* @param duration滚动时间* @param interpolator* @param canIntercept 能否终止滚动,比如触摸屏幕终止滚动*/@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)public void autoScrollTo(final int endY, long duration, final Interpolator interpolator, boolean canIntercept) {if (mIsAutoScrolling) {return;}final boolean temp = mDisallowTouch;mDisallowTouch = !canIntercept;mIsAutoScrolling = true;mAutoScrollAnimator.cancel();mAutoScrollAnimator.setIntValues(0, endY);mAutoScrollAnimator.setInterpolator(interpolator);mAutoScrollAnimator.setDuration(duration);mAutoScrollAnimator.removeAllUpdateListeners();mAutoScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float rate = 0;rate = animation.getCurrentPlayTime() * 1f / animation.getDuration();computeScroll((int) animation.getAnimatedValue(), endY, rate);}});mAutoScrollAnimator.removeAllListeners();mAutoScrollAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);mIsAutoScrolling = false;mDisallowTouch = temp;}});mAutoScrollAnimator.start();}/*** 停止自动滚动*/@TargetApi(Build.VERSION_CODES.HONEYCOMB)public void stopAutoScroll() {mIsAutoScrolling = false;mAutoScrollAnimator.cancel();}private static class SlotInterpolator implements Interpolator {@Overridepublic float getInterpolation(float input) {return (float) (Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;}}/*** 快速滑动时,惯性滑动一段距离** @author huangziwei*/private class FlingOnGestureListener extends GestureDetector.SimpleOnGestureListener {private boolean mIsScrollingLastTime = false;public boolean onDown(MotionEvent e) {if (mDisallowInterceptTouch) { // 不允许父组件拦截事件ViewParent parent = getParent();if (parent != null) {parent.requestDisallowInterceptTouchEvent(true);}}mIsScrollingLastTime = isScrolling(); // 记录是否从滚动状态终止// 点击时取消所有滚动效果cancelScroll();mLastMoveY = e.getY();mLastMoveX = e.getX();return true;}@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,final float velocityY) {// 惯性滑动if (mIsInertiaScroll) {cancelScroll();if (mIsHorizontal) {fling(mMoveLength, velocityX);} else {fling(mMoveLength, velocityY);}}return true;}@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)@Overridepublic boolean onSingleTapUp(MotionEvent e) {mLastMoveY = e.getY();mLastMoveX = e.getX();float lastMove = 0;if (isHorizontal()) {mCenterPoint = mCenterX;lastMove = mLastMoveX;LogUtils.e("lastMove===",lastMove+"");LogUtils.e("mCenterPoint===",mCenterPoint+"");} else {mCenterPoint = mCenterY;lastMove = mLastMoveY;}if (mCanTap && !isScrolling() && !mIsScrollingLastTime) {if (lastMove >= mCenterPoint && lastMove <= mCenterPoint + mItemSize) {//performClick();} else if (lastMove < mCenterPoint) {int bs = (int)((mCenterPoint+mItemSize) - lastMove)/mItemSize;int move = bs*mItemSize;autoScrollTo(move, 150, sAutoScrollInterpolator, false);} else if (lastMove > mCenterPoint + mItemSize) {int bs = (int)((lastMove -mCenterPoint)/mItemWidth);int move = -bs*mItemSize;autoScrollTo(move, 150, sAutoScrollInterpolator, false);} else {moveToCenter();}} else {moveToCenter();}return true;}}public List<T> getData() {return mData;}public void setData(List<T> data) {if (data == null) {mData = new ArrayList<T>();} else {this.mData = data;}mSelected = mData.size() / 2;invalidate();}public T getSelectedItem() {return mData.get(mSelected);}public int getSelectedPosition() {return mSelected;}public void setSelectedPosition(int position,boolean trigger) {if (position < 0 || position > mData.size() - 1|| position == mSelected) {return;}mSelected = position;invalidate();if (mListener != null) {notifySelected(trigger);}}public void setOnSelectedListener(OnSelectedListener listener) {mListener = listener;}public OnSelectedListener getListener() {return mListener;}public boolean isInertiaScroll() {return mIsInertiaScroll;}public void setInertiaScroll(boolean inertiaScroll) {this.mIsInertiaScroll = inertiaScroll;}public boolean isIsCirculation() {return mIsCirculation;}public void setIsCirculation(boolean isCirculation) {this.mIsCirculation = false;}public boolean isDisallowInterceptTouch() {return mDisallowInterceptTouch;}public int getVisibleItemCount() {return mVisibleItemCount;}public void setVisibleItemCount(int visibleItemCount) {mVisibleItemCount = visibleItemCount;reset();invalidate();}/*** 是否允许父元素拦截事件,设置true后可以保证在ScrollView下正常滚动*/public void setDisallowInterceptTouch(boolean disallowInterceptTouch) {mDisallowInterceptTouch = disallowInterceptTouch;}public int getItemHeight() {return mItemHeight;}public int getItemWidth() {return mItemWidth;}/*** @return 当垂直滚动时,mItemSize = mItemHeight;水平滚动时,mItemSize = mItemWidth*/public int getItemSize() {return mItemSize;}/*** @return 中间item的起始坐标x(不考虑偏移), 当垂直滚动时,x = mCenterPosition*mItemWidth*/public int getCenterX() {return mCenterX;}/*** @return 中间item的起始坐标y(不考虑偏移), 当垂直滚动时,y= mCenterPosition*mItemHeight*/public int getCenterY() {return mCenterY;}/*** @return 当垂直滚动时,mCenterPoint = mCenterY;水平滚动时,mCenterPoint = mCenterX*/public int getCenterPoint() {return mCenterPoint;}public boolean isDisallowTouch() {return mDisallowTouch;}/*** 设置是否允许手动触摸滚动** @param disallowTouch*/public void setDisallowTouch(boolean disallowTouch) {mDisallowTouch = disallowTouch;}/*** 中间item的位置,0 <= centerPosition <= mVisibleItemCount** @param centerPosition*/public void setCenterPosition(int centerPosition) {if (centerPosition < 0) {mCenterPosition = 0;} else if (centerPosition >= mVisibleItemCount) {mCenterPosition = mVisibleItemCount - 1;} else {mCenterPosition = centerPosition;}mCenterY = mCenterPosition * mItemHeight;invalidate();}/*** 中间item的位置,默认为 mVisibleItemCount / 2** @return*/public int getCenterPosition() {return mCenterPosition;}public void setCenterItemBackground(Drawable centerItemBackground) {mCenterItemBackground = centerItemBackground;mCenterItemBackground.setBounds(mCenterX, mCenterY, mCenterX + mItemWidth, mCenterY + mItemHeight);invalidate();}public void setCenterItemBackground(int centerItemBackgroundColor) {mCenterItemBackground = new ColorDrawable(centerItemBackgroundColor);mCenterItemBackground.setBounds(mCenterX, mCenterY, mCenterX + mItemWidth, mCenterY + mItemHeight);invalidate();}public Drawable getCenterItemBackground() {return mCenterItemBackground;}public boolean isScrolling() {return mIsFling || mIsMovingCenter || mIsAutoScrolling;}public boolean isFling() {return mIsFling;}public boolean isMovingCenter() {return mIsMovingCenter;}public boolean isAutoScrolling() {return mIsAutoScrolling;}public boolean isCanTap() {return mCanTap;}/*** 设置 单击切换选项或触发点击监听器** @param canTap*/public void setCanTap(boolean canTap) {mCanTap = canTap;}public boolean isHorizontal() {return mIsHorizontal;}public boolean isVertical() {return !mIsHorizontal;}public void setHorizontal(boolean horizontal) {if (mIsHorizontal == horizontal) {return;}mIsHorizontal = horizontal;reset();if (mIsHorizontal) {mItemSize = mItemWidth;} else {mItemSize = mItemHeight;}invalidate();}public void setVertical(boolean vertical) {if (mIsHorizontal == !vertical) {return;}mIsHorizontal = !vertical;reset();if (mIsHorizontal) {mItemSize = mItemWidth;} else {mItemSize = mItemHeight;}invalidate();}public boolean isDrawAllItem() {return mDrawAllItem;}public void setDrawAllItem(boolean drawAllItem) {mDrawAllItem = drawAllItem;}/*** @author huangziwei*/public interface OnSelectedListener {void onSelected(ScrollPickerView scrollPickerView, int position);}public int dip2px(float dipVlue) {DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();float sDensity = metrics.density;return (int) (dipVlue * sDensity + 0.5F);}@Overridepublic void setVisibility(int visibility) {super.setVisibility(visibility);if (visibility == VISIBLE) {moveToCenter();}}}
ColorUtil 类
package com.demo.utils;import android.graphics.Color;/*** 颜色工具栏*/public class ColorUtil {/*** 计算渐变后的颜色** @param startColor 开始颜色* @param endColor 结束颜色* @param rate 渐变率(0,1)* @return 渐变后的颜色,当rate=0时,返回startColor,当rate=1时返回endColor*/public static int computeGradientColor(int startColor, int endColor, float rate) {if (rate < 0) {rate = 0;}if (rate > 1) {rate = 1;}int alpha = Color.alpha(endColor) - Color.alpha(startColor);int red = Color.red(endColor) - Color.red(startColor);int green = Color.green(endColor) - Color.green(startColor);int blue = Color.blue(endColor) - Color.blue(startColor);return Color.argb(Math.round(Color.alpha(startColor) + alpha * rate),Math.round(Color.red(startColor) + red * rate),Math.round(Color.green(startColor) + green * rate),Math.round(Color.blue(startColor) + blue * rate));}}
调用
/*** 温度*/airTemperatureView = new AirTemperatureView(context);LinearLayout.LayoutParams airTemperatureView_Params = new LinearLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,Axis.scaleX(230));airTemperatureView_Params.setMargins(0,Axis.scaleX(100),0,0);bodyLayout.addView(airTemperatureView,airTemperatureView_Params);//父布局 addView()airTemperatureView.setOnTemperatureListener(new AirTemperatureView.TemperatureListener() {@Overridepublic void setAirTemper(int temperature) {LogUtils.e("空调温度===",temperature+"");setTemperature(temperature);//网络请求方法}});