自定义view package com.android.joyfultest.rollingselect; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Paint.FontMetricsInt; import android.graphics.Paint.Style; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import com.android.joyfultest.R; import com.android.joyfultest.utils.SizeConvertUtil; import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; /** * 滚轮选择器 * <p/> */ public class WheelView extends View { public static final String TAG = "WheelView"; /** * 自动回滚到中间的速度 */ public static final float SPEED = 2; /** * 除选中item外,上下各需要显示的备选项数目 */ public static final int SHOW_SIZE = 1; private Context context; private List<String> itemList; private int itemCount; /** * item高度 */ private int itemHeight = 50; /** * 选中的位置,这个位置是mDataList的中心位置,一直不变 */ private int currentItem; private Paint selectPaint; private Paint mPaint; // 画背景图中单独的画笔 private Paint centerLinePaint; private float centerY; private float centerX; private float mLastDownY; /** * 滑动的距离 */ private float mMoveLen = 0; private boolean isInit = false; private SelectListener mSelectListener; private Timer timer; private MyTimerTask mTask; Handler updateHandler = new Handler() { @Override public void handleMessage(Message msg) { if (Math.abs(mMoveLen) < SPEED) { // 如果偏移量少于最少偏移量 mMoveLen = 0; if (null != timer) { timer.cancel(); timer.purge(); timer = null; } if (mTask != null) { mTask.cancel(); mTask = null; performSelect(); } } else { // 这里mMoveLen / Math.abs(mMoveLen)是为了保有mMoveLen的正负号,以实现上滚或下滚 mMoveLen = mMoveLen - mMoveLen / Math.abs(mMoveLen) * SPEED; } invalidate(); } }; public WheelView(Context context) { super(context); init(context); } public WheelView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public void setOnSelectListener(SelectListener listener) { mSelectListener = listener; } public void setWheelStyle(int style) { itemList = WheelStyle.getItemList(context, style); if (itemList != null) { itemCount = itemList.size(); resetCurrentSelect(); invalidate(); } else { Log.i(TAG, "item is null"); } } public void setWheelItemList(List<String> itemList) { this.itemList = itemList; if (itemList != null) { itemCount = itemList.size(); resetCurrentSelect(); } else { Log.i(TAG, "item is null"); } } private void resetCurrentSelect() { if (currentItem < 0) { currentItem = 0; } while (currentItem >= itemCount) { currentItem--; } if (currentItem >= 0 && currentItem < itemCount) { invalidate(); } else { Log.i(TAG, "current item is invalid"); } } public int getItemCount() { return itemCount; } /** * 选择选中的item的index */ public void setCurrentItem(int selected) { currentItem = selected; resetCurrentSelect(); } public int getCurrentItem() { return currentItem; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int mViewHeight = getMeasuredHeight(); int mViewWidth = getMeasuredWidth(); centerX = (float) (mViewWidth / 2.0); centerY = (float) (mViewHeight / 2.0); isInit = true; invalidate(); } private void init(Context context) { this.context = context; timer = new Timer(); itemList = new ArrayList<>(); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Style.FILL); mPaint.setTextAlign(Align.CENTER); mPaint.setColor(getResources().getColor(R.color.wheel_unselect_text)); mPaint.setTextSize(SizeConvertUtil.spTopx(context, 18)); selectPaint = new Paint(Paint.ANTI_ALIAS_FLAG); selectPaint.setStyle(Style.FILL); selectPaint.setTextAlign(Align.CENTER); selectPaint.setColor(getResources().getColor(R.color.orange)); selectPaint.setTextSize(SizeConvertUtil.spTopx(context, 26)); centerLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); centerLinePaint.setStrokeWidth(1); centerLinePaint.setStyle(Style.FILL); centerLinePaint.setTextAlign(Align.CENTER); centerLinePaint.setColor(getResources().getColor(R.color.line)); // 绘制背景 setBackground(null); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (isInit) { drawData(canvas); } } private void drawData(Canvas canvas) { // 先绘制选中的text再往上往下绘制其余的text if (!itemList.isEmpty()) { // 绘制中间data drawCenterText(canvas); // 绘制上方data for (int i = 1; i < SHOW_SIZE + 1; i++) { drawOtherText(canvas, i, -1); } // 绘制下方data for (int i = 1; i < SHOW_SIZE + 1; i++) { drawOtherText(canvas, i, 1); } } } private void drawCenterText(Canvas canvas) { // text居中绘制,注意baseline的计算才能达到居中,y值是text中心坐标 float y = centerY + mMoveLen; FontMetricsInt fmi = selectPaint.getFontMetricsInt(); float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0)); canvas.drawText(itemList.get(currentItem), centerX, baseline, selectPaint); } /** * @param canvas 画布 * @param position 距离mCurrentSelected的差值 * @param type 1表示向下绘制,-1表示向上绘制 */ private void drawOtherText(Canvas canvas, int position, int type) { int index = currentItem + type * position; if (index >= itemCount) { index = index - itemCount; } if (index < 0) { index = index + itemCount; } String text = itemList.get(index); int itemHeight = getHeight() / (SHOW_SIZE * 2 + 1); float d = itemHeight * position + type * mMoveLen; float y = centerY + type * d; FontMetricsInt fmi = mPaint.getFontMetricsInt(); float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0)); canvas.drawText(text, centerX, baseline, mPaint); } @Override public void setBackground(Drawable background) { background = new Drawable() { @Override public void draw(Canvas canvas) { itemHeight = getHeight() / (SHOW_SIZE * 2 + 1); int width = getWidth(); canvas.drawLine(2, itemHeight, width * 5-2, itemHeight, centerLinePaint); canvas.drawLine(2, itemHeight * 2, width * 5-2, itemHeight * 2, centerLinePaint); } @Override public void setAlpha(int alpha) { } @Override public void setColorFilter(ColorFilter cf) { } @SuppressLint("WrongConstant") @Override public int getOpacity() { return 0; } }; super.setBackground(background); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: doDown(event); break; case MotionEvent.ACTION_MOVE: doMove(event); break; case MotionEvent.ACTION_UP: doUp(); break; default: break; } return true; } private void doDown(MotionEvent event) { if (mTask != null) { mTask.cancel(); mTask = null; } mLastDownY = event.getY(); } private void doMove(MotionEvent event) { mMoveLen += (event.getY() - mLastDownY); if (mMoveLen > itemHeight / 2) { // 往下滑超过离开距离 mMoveLen = mMoveLen - itemHeight; currentItem--; if (currentItem < 0) { currentItem = itemCount - 1; } } else if (mMoveLen < -itemHeight / 2) { // 往上滑超过离开距离 mMoveLen = mMoveLen + itemHeight; currentItem++; if (currentItem >= itemCount) { currentItem = 0; } } mLastDownY = event.getY(); invalidate(); } private void doUp() { // 抬起手后mCurrentSelected的位置由当前位置move到中间选中位置 if (Math.abs(mMoveLen) < 0.0001) { mMoveLen = 0; return; } if (mTask != null) { mTask.cancel(); mTask = null; } if (null == timer) { timer = new Timer(); } mTask = new MyTimerTask(updateHandler); timer.schedule(mTask, 0, 10); } class MyTimerTask extends TimerTask { Handler handler; public MyTimerTask(Handler handler) { this.handler = handler; } @Override public void run() { handler.sendMessage(handler.obtainMessage()); } } private void performSelect() { if (mSelectListener != null) { mSelectListener.onSelect(currentItem, itemList.get(currentItem)); } else { //滑动一个条目时的监听 } } public interface SelectListener { void onSelect(int index, String text); } } 为选择器设置数据
package com.android.joyfultest.rollingselect; import android.content.Context; import java.util.ArrayList; import java.util.List; /** * 生成wheel的各种选项. */ public class WheelStyle { public static final int minYear = 1980; public static final int maxYear = 2020; /** * Wheel Style Hour */ public static final int STYLE_HOUR_DAY = 1; public static final int STYLE_HOUR = 9; /** * Wheel Style Minute */ public static final int STYLE_MINUTE = 2; /** * Wheel Style Year */ public static final int STYLE_YEAR = 3; /** * Wheel Style Month */ public static final int STYLE_MONTH = 4; /** * Wheel Style Day */ public static final int STYLE_DAY = 5; /** * Wheel Style Light Time */ public static final int STYLE_LIGHT_TIME = 7; private WheelStyle() {} public static List<String> getItemList(Context context, int Style) { if (Style == STYLE_HOUR_DAY) { return createHourString(); } else if(Style==STYLE_HOUR){ return createHourDayString(); }else if (Style == STYLE_MINUTE) { return createMinuteString(); } else if (Style == STYLE_YEAR) { return createYearString(); } else if (Style == STYLE_MONTH) { return createMonthString(); } else if (Style == STYLE_DAY) { return createDayString(); } else { throw new IllegalArgumentException("style is illegal"); } } private static List<String> createHourString() { List<String> wheelString = new ArrayList<>(); for (int i = 0; i < 24; i++) { wheelString.add(String.format("%02d", i)); } return wheelString; } private static List<String> createHourDayString() { List<String> wheelString = new ArrayList<>(); for (int i = 1; i < 13; i++) { wheelString.add(String.format("%02d", i)); } return wheelString; } private static List<String> createMinuteString() { List<String> wheelString = new ArrayList<>(); for (int i = 0; i < 60; i++) { wheelString.add(String.format("%02d", i)); } return wheelString; } private static List<String> createYearString() { List<String> wheelString = new ArrayList<>(); for (int i = minYear; i <= maxYear; i++) { wheelString.add(Integer.toString(i)); } return wheelString; } private static List<String> createMonthString() { List<String> wheelString = new ArrayList<>(); for (int i = 1; i <= 12; i++) { wheelString.add(String.format("%02d", i)); } return wheelString; } private static List<String> createDayString() { List<String> wheelString = new ArrayList<>(); for (int i = 1; i <= 31; i++) { wheelString.add(String.format("%02d", i)); } return wheelString; } public static List<String> createDayString(int year, int month) { List<String> wheelString = new ArrayList<>(); int size; if (isLeapMonth(month)) { size = 31; } else if (month == 2) { if (isLeapYear(year)) { size = 29; } else { size = 28; } } else { size = 30; } for (int i = 1; i <= size; i++) { wheelString.add(String.format("%02d", i)); } return wheelString; } /** * 计算闰月 * * @param month * @return */ private static boolean isLeapMonth(int month) { return month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12; } /** * 计算闰年 * * @param year * @return */ private static boolean isLeapYear(int year) { return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; } public static List<String> createHeightString() { List<String> wheelString = new ArrayList<>(); for (int i = 110; i < 171; i++) { wheelString.add(String.valueOf(i)); } return wheelString; } } activity 中使用
package com.android.joyfultest.rollingselect; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.RelativeLayout; import com.android.joyfultest.R; import java.util.ArrayList; import java.util.List; /** * Created by jiangzhaole on 18-3-23. */ public class RollingselectionActivity extends Activity{ private Button bt_scrollchoose; // 滚动选择器按钮 private WheelView pickerscrlllview; // 滚动选择器 private List<String> list; // 滚动选择器数据 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_rollingselection); list= WheelStyle.createHeightString(); initView(); pickerscrlllview.setWheelItemList(list); bt_scrollchoose.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Log.e("TAG",list.get(pickerscrlllview.getCurrentItem())+"---"+pickerscrlllview.getCurrentItem()); } }); } /** * 初始化 */ private void initView() { bt_scrollchoose = (Button) findViewById(R.id.bt_scrollchoose); pickerscrlllview = (WheelView) findViewById(R.id.pickerscrlllview); } }
Xml 文件
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/bt_scrollchoose" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="开始" android:textSize="20dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="10px" /> <RelativeLayout android:id="@+id/picker_rel" android:layout_above="@id/bt_scrollchoose" android:layout_width="match_parent" android:layout_height="match_parent"> <com.android.joyfultest.rollingselect.WheelView android:id="@+id/pickerscrlllview" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout> </RelativeLayout>