android 简单的滚动选择器

本文介绍了一个自定义的Android `WheelView`组件,该组件可以作为滚动选择器使用。`WheelView`实现了自动回滚到中间的功能,并且可以设置不同的数据源,如小时、分钟、年份等。它通过触摸事件来处理滑动操作,同时提供了选择监听接口,方便在选中某项时进行回调。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


自定义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;
    }

    /**
     * 选择选中的itemindex
     */
    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>


 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值