横向滑动选择器,点击自动切换选项。

先来个效果:   (可以左右滑动,可以选中上一个,下一个,点击具体项选中那项)

(中间字体大,两边字体小,稍微有点拱桥形状,实现在onDraw后面几行代码,可自行优化)

 

 

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="HSelectView">
        <!--可见数目-->
        <attr name="ViewSeesize" format="integer"></attr>
        <!--被选择文字的大小和颜色-->
        <attr name="selectedTextSize" format="float"></attr>
        <attr name="selectedTextColor" format="color|reference"></attr>

        <!--未被被选择文字的大小和颜色-->
        <attr name="textSize" format="float"></attr>
        <attr name="textColor" format="color|reference"></attr>
    </declare-styleable>

</resources>

 

      初始化(不多赘述)

/**
     * 初始化画笔
     */
    private void initPaint() {
        textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setTextSize(textSize);
        textPaint.setColor(textColor);
        selectedPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        selectedPaint.setColor(selectedColor);
        selectedPaint.setTextSize(selectedTextSize);
    }


    /**
     * 初始化属性
     *
     * @param attrs
     */
    private void initAttrs(AttributeSet attrs) {
        TypedArray tta = context.obtainStyledAttributes(attrs,
                R.styleable.HSelectView);
        //两种字体颜色和字体大小
        seeSize = tta.getInteger(R.styleable.HSelectView_ViewSeesize, 5);
        selectedTextSize = tta.getFloat(R.styleable.HSelectView_selectedTextSize, 50);
        selectedColor = tta.getColor(R.styleable.HSelectView_selectedTextColor, context.getResources().getColor(android.R.color.black));
        textSize = tta.getFloat(R.styleable.HSelectView_textSize, 40);
        textColor = tta.getColor(R.styleable.HSelectView_textColor, context.getResources().getColor(android.R.color.darker_gray));

        tta.recycle();
    }

 

onDraw方法(确定每个item的坐标,然后加上偏移量,这一串东西就可以滑动了,左右任滑)

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);


        if (firstVisible) {//第一次绘制的时候得到控件 宽高;
            width = getWidth();
            height = getHeight();
            anInt = width / seeSize;
            firstVisible = false;
        }
        if (selectItem >= 0 && selectItem <= stringList.size() - 1) {//加个保护;防止越界

            String selectStr = stringList.get(selectItem);//得到被选中的文字
            /**
             * 得到被选中文字 绘制时所需要的宽高
             */
            selectedPaint.getTextBounds(selectStr, 0, selectStr.length(), rect);
            //3从矩形区域中读出文本内容的宽高
            int centerTextWidth = rect.width();
            centerTextHeight = rect.height();

            float selectWidth = getWidth() / 2 - centerTextWidth / 2 + anOffset;
            selectedPaint.setTextSize(calcTextSize(selectWidth + textWidth / 2));
            canvas.drawText(stringList.get(selectItem),
                    selectWidth,
                    getHeight() / 2 + centerTextHeight / 2,
                    selectedPaint);//绘制被选中文字,注意点是y坐标

            for (int i = 0; i < stringList.size(); i++) {//遍历strings,把每个地方都绘制出来,

                if (selectItem > 0 && selectItem < stringList.size() - 1) {
                    //这里主要是因为strings数据源的文字长度不一样,为了让被选中两边文字距离中心宽度一样,我们取得左右两个文字长度的平均值
                    textPaint.getTextBounds(stringList.get(selectItem - 1), 0, stringList.get(selectItem - 1).length(), rect);
                    int width1 = rect.width();
                    textPaint.getTextBounds(stringList.get(selectItem + 1), 0, stringList.get(selectItem + 1).length(), rect);
                    int width2 = rect.width();
                    textWidth = (width1 + width2) / 2;
                }
                if (i == 0) {//得到高,高度是一样的,所以无所谓
                    textPaint.getTextBounds(stringList.get(0), 0, stringList.get(0).length(), rect);
                    textHeight = rect.height();
                }


                if (i != selectItem) {
                    float weith = (i - selectItem) * anInt + getWidth() / 2 - textWidth / 2 + anOffset;
                    locX[i] = weith;//保存位置

                    textPaint.setTextSize(calcTextSize(weith + textWidth / 2));

                    canvas.drawText(stringList.get(i),
                            weith,
                            getHeight() / 2 + centerTextHeight / 2 + Math.abs(i - selectItem) * 3,
                            textPaint);//画出每组文字
                } else {
                    locX[i] = selectWidth;//保存位置
                }
            }


        }

    }
onTouchEvent方法(主要确定偏移量多少,偏移到一定位置,切换下一个item选中,哈哈哈哈。。。。)
@Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("action", "onTouchEvent: " + event.getAction());
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();//获得点下去的x坐标

                myDownX = event.getX();
                break;
            case MotionEvent.ACTION_MOVE://复杂的是移动时的判断
                float scrollX = event.getX();


                anOffset = scrollX - downX;//滑动时的偏移量,用于计算每个是数据源文字的坐标值

                if (scrollX > downX) {
                    //向右滑动,当滑动距离大于每个单元的长度时,则改变被选中的文字。
                    if (scrollX - downX >= anInt / 2) {
                        if (selectItem > 0) {

                            anOffset = anOffset - anInt;
                            selectItem = selectItem - 1;
                            downX = scrollX - anOffset;

                            setListener(selectItem);
                        }
                    }
                } else {

                    //向左滑动,当滑动距离大于每个单元的长度时,则改变被选中的文字。
                    if (downX - scrollX >= anInt / 2) {

                        if (selectItem < stringList.size() - 1) {

                            anOffset = anInt + anOffset;
                            selectItem = selectItem + 1;
                            downX = scrollX - anOffset;
                            setListener(selectItem);
                        }
                    }
                }

                invalidate();
                break;

            case MotionEvent.ACTION_UP:
                //抬起手指时,偏移量归零,相当于回弹。

                if (Math.abs(myDownX - event.getX()) < 10) {
                    for (int i = 0; i < stringList.size(); i++) {
                        if (event.getX() > locX[i] && (i == stringList.size() - 1 || event.getX() < locX[i + 1])) {
                            selectItem = i;
                            setListener(selectItem);
                            break;
                        }
                    }
                }
                anOffset = 0;
                invalidate();

                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

 

 

全部代码一起贴出:



import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by ruedy on 2017/5/5.
 */

public class HorizontalselectedView extends View {

    private Context context;
    private List<String> stringList = new ArrayList<String>();//数据源字符串数组

    private Float[] locX;//记录每一个item 的x坐标

    private int seeSize = 5;//可见个数

    private int anInt;//每个字母所占的大小; 宽度除于可见个数
    private TextPaint textPaint;
    private boolean firstVisible = true;
    private int width;//控件宽度
    private int height;//控件高度
    private Paint selectedPaint;//被选中文字的画笔
    private int selectItem;
    private float downX;//位置会改变

    private float myDownX;//值不改变,只保存按下x值。
    private float anOffset;//滑动时的偏移量,用于计算每个是数据源文字的坐标值
    private float selectedTextSize;
    private int selectedColor;
    private float textSize;
    private int textColor;
    private Rect rect = new Rect();


    private int textWidth = 0;
    private int textHeight = 0;
    private int centerTextHeight = 0;


    public HorizontalselectedView(Context context) {
        this(context, null);
    }

    public HorizontalselectedView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public HorizontalselectedView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        setWillNotDraw(false);
        setClickable(true);
        initAttrs(attrs);//初始化属性
        initPaint();//初始化画笔
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setTextSize(textSize);
        textPaint.setColor(textColor);
        selectedPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        selectedPaint.setColor(selectedColor);
        selectedPaint.setTextSize(selectedTextSize);
    }


    /**
     * 初始化属性
     *
     * @param attrs
     */
    private void initAttrs(AttributeSet attrs) {
        TypedArray tta = context.obtainStyledAttributes(attrs,
                R.styleable.HSelectView);
        //两种字体颜色和字体大小
        seeSize = tta.getInteger(R.styleable.HSelectView_ViewSeesize, 5);
        selectedTextSize = tta.getFloat(R.styleable.HSelectView_selectedTextSize, 50);
        selectedColor = tta.getColor(R.styleable.HSelectView_selectedTextColor, context.getResources().getColor(android.R.color.black));
        textSize = tta.getFloat(R.styleable.HSelectView_textSize, 40);
        textColor = tta.getColor(R.styleable.HSelectView_textColor, context.getResources().getColor(android.R.color.darker_gray));

        tta.recycle();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("action", "onTouchEvent: " + event.getAction());
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();//获得点下去的x坐标

                myDownX = event.getX();
                break;
            case MotionEvent.ACTION_MOVE://复杂的是移动时的判断
                float scrollX = event.getX();


                anOffset = scrollX - downX;//滑动时的偏移量,用于计算每个是数据源文字的坐标值

                if (scrollX > downX) {
                    //向右滑动,当滑动距离大于每个单元的长度时,则改变被选中的文字。
                    if (scrollX - downX >= anInt / 2) {
                        if (selectItem > 0) {

                            anOffset = anOffset - anInt;
                            selectItem = selectItem - 1;
                            downX = scrollX - anOffset;

                            setListener(selectItem);
                        }
                    }
                } else {

                    //向左滑动,当滑动距离大于每个单元的长度时,则改变被选中的文字。
                    if (downX - scrollX >= anInt / 2) {

                        if (selectItem < stringList.size() - 1) {

                            anOffset = anInt + anOffset;
                            selectItem = selectItem + 1;
                            downX = scrollX - anOffset;
                            setListener(selectItem);
                        }
                    }
                }

                invalidate();
                break;

            case MotionEvent.ACTION_UP:
                //抬起手指时,偏移量归零,相当于回弹。

                if (Math.abs(myDownX - event.getX()) < 10) {
                    for (int i = 0; i < stringList.size(); i++) {
                        if (event.getX() > locX[i] && (i == stringList.size() - 1 || event.getX() < locX[i + 1])) {
                            selectItem = i;
                            setListener(selectItem);
                            break;
                        }
                    }
                }
                anOffset = 0;
                invalidate();

                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);


        if (firstVisible) {//第一次绘制的时候得到控件 宽高;
            width = getWidth();
            height = getHeight();
            anInt = width / seeSize;
            firstVisible = false;
        }
        if (selectItem >= 0 && selectItem <= stringList.size() - 1) {//加个保护;防止越界

            String selectStr = stringList.get(selectItem);//得到被选中的文字
            /**
             * 得到被选中文字 绘制时所需要的宽高
             */
            selectedPaint.getTextBounds(selectStr, 0, selectStr.length(), rect);
            //3从矩形区域中读出文本内容的宽高
            int centerTextWidth = rect.width();
            centerTextHeight = rect.height();

            float selectWidth = getWidth() / 2 - centerTextWidth / 2 + anOffset;
            selectedPaint.setTextSize(calcTextSize(selectWidth + textWidth / 2));
            canvas.drawText(stringList.get(selectItem),
                    selectWidth,
                    getHeight() / 2 + centerTextHeight / 2,
                    selectedPaint);//绘制被选中文字,注意点是y坐标

            for (int i = 0; i < stringList.size(); i++) {//遍历strings,把每个地方都绘制出来,

                if (selectItem > 0 && selectItem < stringList.size() - 1) {
                    //这里主要是因为strings数据源的文字长度不一样,为了让被选中两边文字距离中心宽度一样,我们取得左右两个文字长度的平均值
                    textPaint.getTextBounds(stringList.get(selectItem - 1), 0, stringList.get(selectItem - 1).length(), rect);
                    int width1 = rect.width();
                    textPaint.getTextBounds(stringList.get(selectItem + 1), 0, stringList.get(selectItem + 1).length(), rect);
                    int width2 = rect.width();
                    textWidth = (width1 + width2) / 2;
                }
                if (i == 0) {//得到高,高度是一样的,所以无所谓
                    textPaint.getTextBounds(stringList.get(0), 0, stringList.get(0).length(), rect);
                    textHeight = rect.height();
                }


                if (i != selectItem) {
                    float weith = (i - selectItem) * anInt + getWidth() / 2 - textWidth / 2 + anOffset;
                    locX[i] = weith;//保存位置

                    textPaint.setTextSize(calcTextSize(weith + textWidth / 2));

                    canvas.drawText(stringList.get(i),
                            weith,
                            getHeight() / 2 + centerTextHeight / 2 + Math.abs(i - selectItem) * 3,
                            textPaint);//画出每组文字
                } else {
                    locX[i] = selectWidth;//保存位置
                }
            }


        }

    }

    /**
     * 根据到中间位置的距离大小,确定字体的大小
     *
     * @param width
     * @return
     */
    private float calcTextSize(float width) {
        float size = selectedTextSize - textSize;//最大字体和最小字体差距

        float bilv = 1 - Math.abs(width - getWidth() / 2) / (getWidth() / 2);

        return textSize + size * bilv;
    }

    /**
     * 改变中间可见文字的数目
     *
     * @param seeSizes 可见数
     */
    public void setSeeSize(int seeSizes) {
        if (seeSize > 0) {
            seeSize = seeSizes;
            invalidate();
        }

    }


    /**
     * 向左移动一个单元
     */
    public void setAnLeftOffset() {
        if (selectItem < stringList.size() - 1) {
            selectItem = selectItem + 1;
            invalidate();
        }

    }

    /**
     * 向右移动一个单元
     */
    public void setAnRightOffset() {
        if (selectItem > 0) {
            selectItem = selectItem - 1;
            invalidate();
        }
    }

    /**
     * 设置个数据源
     *
     * @param strings 数据源String集合
     */
    public void setData(List<String> strings) {
        this.stringList = strings;

        locX = new Float[strings.size()];
        selectItem = strings.size() / 2;
        setListener(selectItem);
        invalidate();
    }

    /**
     * 获得被选中的文本
     *
     * @return 被选中的文本
     */
    public String getSelectedString() {
        if (stringList.size() != 0) {
            return stringList.get(selectItem);
        }
        return null;
    }


    private void setListener(int selectItem) {

        if (listener != null) {
            listener.itemSelect(selectItem);
        }

    }

    private OnItemSelectListener listener;

    public void setOnItemSelectListener(OnItemSelectListener listener) {
        this.listener = listener;
    }

    public interface OnItemSelectListener {

        void itemSelect(int item);
    }
}

 

使用方式:

 <com.iszcc.demo.carview.HorizontalselectedView
            android:id="@+id/hd_main"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_gravity="center_vertical"
            android:layout_weight="30"
            app:ViewSeesize="5"
            app:selectedTextColor="#f00"
            app:selectedTextSize="60"
            app:textColor="#0ff"
            app:textSize="40" />

监听:

hsMain.setOnItemSelectListener(new HorizontalselectedView.OnItemSelectListener() {
            @Override
            public void itemSelect(int item) {
                tvMain.setText(strings.get(item));
            }
        });

上一个、下一个

 hsMain.setAnLeftOffset();
 hsMain.setAnRightOffset();

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值