Android自定义控件实现按钮滚动选择效果
本文实例为大家分享了Android实现按钮滚动选择效果的具体代码,供大家参考,具体内容如下
公司主营业务:成都网站设计、网站制作、外贸营销网站建设、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。成都创新互联是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。成都创新互联推出玛多免费做网站回馈大家。
效果图
代码实现
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 ListnewList; private ImageView img; private ImageView img_left; private ImageView img_right; private int mPosition; private Handler handler = new Handler(); /** * 延迟线程,看是否还有下一个字符输入 */ private Runnable delayRun = new Runnable() { @Override public 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() { @Override public void onSelected(ScrollPickerView scrollPickerView, final int position) { mPosition = position; handler.postDelayed(delayRun, 1500); } }); stringScrollPicker.setOnSelectedListener(new StringScrollPicker.OnDataSelectedListener() { @Override public void DataSelected(CharSequence data) { //tv.setText("电池剩余"+data+"%提醒你"); } }); stringScrollPicker.setSelectedPosition(0,false);//中间Item位置 stringScrollPicker.setVisibility(INVISIBLE); stringScrollPicker.setOnTouchListener(new OnTouchListener() { @Override public 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() { @Override public 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{ 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 (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; } @Override protected 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(); } } @Override public void drawItem(Canvas canvas, List 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; // 其他默认为mEndColor if (relative == -1 || relative == 1) { // 上一个或下一个 // 处理上一个item且向上滑动 或者 处理下一个item且向下滑动 ,颜色为mEndColor if ((relative == -1 && moveLength < 0) || (relative == 1 && moveLength > 0)) { color = mEndColor; } else { // 计算渐变的颜色 float rate = (itemSize - Math.abs(moveLength)) / itemSize; color = ColorUtil.computeGradientColor(mStartColor, mEndColor, rate); } } else if (relative == 0) { // 中间item float rate = Math.abs(moveLength) / itemSize; color = ColorUtil.computeGradientColor(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 ScrollPickerViewextends 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 mData; private int mItemHeight = 0; // 每个条目的高度,当垂直滚动时,高度=mMeasureHeight/mVisibleItemCount private int mItemWidth = 0; // 每个条目的宽度,当水平滚动时,宽度=mMeasureWidth/mVisibleItemCount private int mItemSize; // 当垂直滚动时,mItemSize = mItemHeight;水平滚动时,mItemSize = mItemWidth private int mCenterPosition = -1; // 中间item的位置,0<=mCenterPosition<mVisibleItemCount,默认为 mVisibleItemCount / 2 private int mCenterY; // 中间item的起始坐标y(不考虑偏移),当垂直滚动时,y= mCenterPosition*mItemHeight private int mCenterX; // 中间item的起始坐标x(不考虑偏移),当垂直滚动时,x = mCenterPosition*mItemWidth private int mCenterPoint; // 当垂直滚动时,mCenterPoint = mCenterY;水平滚动时,mCenterPoint = mCenterX private float mLastMoveY; // 触摸的坐标y private float mLastMoveX; // 触摸的坐标X private 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的坐标y private int mLastScrollX = 0; // Scroller的坐标x private 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(); } } @Override protected void onDraw(Canvas canvas) { if (mData == null || mData.size() <= 0) { return; } // 选中item的背景色 if (mCenterItemBackground != null) { mCenterItemBackground.draw(canvas); } // 只绘制可见的item int 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,相对位置为 -i position = mSelected - i < 0 ? mData.size() + mSelected - i : mSelected - i; // 传入位置信息,绘制item if (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,相对位置为 i position = mSelected + i >= mData.size() ? mSelected + i - mData.size() : mSelected + i; // 传入位置信息,绘制item if (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); } } } // 选中的item drawItem(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 data, int position, int relative, float moveLength, float top); @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected 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) @Override public 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(); } } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { // 正在滚动 if (mIsHorizontal) { // 可以把scroller看做模拟的触屏滑动操作,mLastScrollX为上次滑动的坐标 mMoveLength = mMoveLength + mScroller.getCurrX() - mLastScrollX; } else { // 可以把scroller看做模拟的触屏滑动操作,mLastScrollY为上次滑动的坐标 mMoveLength = mMoveLength + mScroller.getCurrY() - mLastScrollY; } mLastScrollY = mScroller.getCurrY(); mLastScrollX = mScroller.getCurrX(); checkCirculation(); // 检测当前选中的item invalidate(); } 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个item mScroller.fling((int) from, 0, (int) vel, 0, -10 * mItemWidth, 10 * mItemWidth, 0, 0); } else { mLastScrollY = (int) from; mIsFling = true; // 最多可以惯性滑动10个item mScroller.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() { @Override public 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; // 多一圈 // 让其尽量接近length final 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=0 mAutoScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public 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() { @Override public 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() { @Override public 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() { @Override public 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 { @Override public 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; } @Override public 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) @Override public 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 getData() { return mData; } public void setData(List data) { if (data == null) { mData = new ArrayList (); } 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); } @Override public 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() { @Override public void setAirTemper(int temperature) { LogUtils.e("空调温度===",temperature+""); setTemperature(temperature);//网络请求方法 } });
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持创新互联。
新闻标题:Android自定义控件实现按钮滚动选择效果
文章起源:http://azwzsj.com/article/iieije.html