使用ListView实现滚筒选择器效果

转载请注明出处http://blog.csdn.net/u011453163/article/details/52960329

Android和ios应用开发虽然大同小异,但是对于原生sdk提供的组件来说,ios要相对美观点,但是Android 的组件也是可以高度自定义的,基本上ios有的Android都能自定义达到相同的效果。
今天通过对listview做一点点小修改实现一个wheelview的效果(滚筒选择器) 虽然网上已经有很多这样的效果,但有时候我只想实现一个比较简单的效果并不想去引入太多的类。。
先看效果图
使用ListView实现滚筒选择器效果_第1张图片

这个选择器效果我只是在原来的listview上做了一些小修改,也踩了一些坑。
有两个点

1.数据循环   
2.滑动后的回弹居中

第一点我用的是取余的方法,这个在循环的viewpager里应该很多人都用过了。

第二点回弹居中的效果 我用的是属性动画(ValueAnimator)而没用Scroller

关于第一点就不多说了,处理适配器的两个方法就可以了

  @Override
        public int getCount() {
            return datas!=null?2000:0;
        }

        @Override
        public String getItem(int position) {
            return datas!=null?datas.get(position%datas.size()):null;
        }
关于第二点回弹居中的效果
回弹居中效果,虽然listview有提供好几个平滑滚动的方法 
smoothScrollToPosition(int position)
smoothScrollByOffset(int offset) 

基本上带smooth的方法都是带有平滑效果的,但是都不是很理想,都有不能被接受的准确性,就是偏移了。
但是有一个方法是非常准确的
@Override
    public void setSelection(int position) {
        setSelectionFromTop(position, 0);
    }

这个方法一点也不陌生,就是滚到指定位置的。虽然不知道为什么这个不会偏差,然后找到里面的

 public void setSelectionFromTop(int position, int y)

很好,这个方法是可以直接用的。这个方法的效果是 滚动到指定的位置 偏移头的量,setSelectionFromTop(position, 0),就是setSelection(int position);
所以想到利用这个偏移量来达到回弹居中的效果 想法大概是这样的
使用ListView实现滚筒选择器效果_第2张图片

因为listview的复用 ,所以保证listview的高是item的奇数倍数就好了,所以只要监听listview的滚动就可以了


    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if(scrollState==SCROLL_STATE_IDLE){
            if(view instanceof ListView){
                View v = view.getChildAt(0);
                int position= view.getFirstVisiblePosition();
                if(-v.getTop()>v.getHeight()/2){
                    ScrollAnim((ListView) view,position, v.getTop(),-v.getHeight());
                    selectPosition=position+1;
                }else {
                    ScrollAnim((ListView) view,position, v.getTop(),0);
                    selectPosition=position;
               }

            }
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if(view instanceof ListView){
            for (int i = 0; i < view.getChildCount(); i++) {
                View v=view.getChildAt(i);
                if(v instanceof TextView){
                if(v.getY()+v.getHeight()/2>view.getHeight()/3&&v.getY()+v.getHeight()/23*2){
                    ((TextView) v).setTextColor(selectColor);
                    ((TextView) v).setTextSize(TypedValue.COMPLEX_UNIT_SP,selectTextSize);
                }else {
                    ((TextView) v).setTextColor(defColor);
                    ((TextView) v).setTextSize(TypedValue.COMPLEX_UNIT_SP,defTextSize);
                }
                }
            }
        }
    }

onScrollStateChanged里做的事是在滑动停止以后做回弹居中的效果
onScroll里是做了滑动过程中的一些选中变化,这里是做了字体颜色和大小的变化。

if(scrollState==SCROLL_STATE_IDLE){
            if(view instanceof ListView){
                View v = view.getChildAt(0);
                int position= view.getFirstVisiblePosition();
                if(-v.getTop()>v.getHeight()/2){
                    ScrollAnim((ListView) view,position, v.getTop(),-v.getHeight());
                    selectPosition=position+1;
                }else {
                    ScrollAnim((ListView) view,position, v.getTop(),0);
                    selectPosition=position;
               }

            }
        }

停止以后 计算第一个显示的item的偏移量 以决定是向上滚动或者向下滚动,平滑滚动是用的属性动画api ValueAnimator

 private void ScrollAnim(final ListView v, final int position, int y1, int y2){
        ValueAnimator valueAnimator= ValueAnimator.ofInt(y1,y2);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                v.setSelectionFromTop(position,((Integer) animation.getAnimatedValue()).intValue());
            }
        });
        valueAnimator.setDuration(150);
        valueAnimator.start();
    }

这里就是用到了setSelectionFromTop(position,y)这个方法;

最后是划线的,上下蒙层效果

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawLine(20,getHeight()/3,getWidth()-20,getHeight()/3,paintLine);
        canvas.drawLine(20,getHeight()/3*2,getWidth()-20,getHeight()/3*2,paintLine);
    }
    @Override
    public void draw(Canvas canvas) {
        initFirst();
        canvas.saveLayer(0,0,this.getWidth(),this.getHeight(),null,Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
        super.draw(canvas);
        paintShader.setShader(shaderTop);
        canvas.drawRect(0, 0, this.getWidth(), 100,paintShader);
        paintShader.setShader(shaderButtom);
        canvas.drawRect(0, this.getHeight()-100,this.getWidth(), this.getHeight(),paintShader);
    }

详细的看代码。

package com.weizhenbin.show.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.TextView;

/**
 * Created by weizhenbin on 2016/10/26.
 */
public class WheelListView extends ListView implements AbsListView.OnScrollListener {

    private Paint paintLine,paintShader;
    private int selectColor= Color.BLUE;//选择字体颜色
    private int defColor= Color.BLACK;//默认字体颜色
    private int selectTextSize=20;//选择字体大小  单位sp
    private int defTextSize=15;//默认字体大小  单位sp

    private LinearGradient shaderTop;
    private LinearGradient shaderButtom;
    private boolean hasInit=false;

    public int selectPosition;
    public WheelListView(Context context) {
        this(context,null);
    }

    public WheelListView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public WheelListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOnScrollListener(this);
        paintLine=new Paint();
        paintLine.setStrokeWidth(2);
        paintLine.setColor(selectColor);
    }


    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if(scrollState==SCROLL_STATE_IDLE){
            if(view instanceof ListView){
                View v = view.getChildAt(0);
                int position= view.getFirstVisiblePosition();
                if(-v.getTop()>v.getHeight()/2){
                    ScrollAnim((ListView) view,position, v.getTop(),-v.getHeight());
                    selectPosition=position+1;
                }else {
                    ScrollAnim((ListView) view,position, v.getTop(),0);
                    selectPosition=position;
               }

            }
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if(view instanceof ListView){
            for (int i = 0; i < view.getChildCount(); i++) {
                View v=view.getChildAt(i);
                if(v instanceof TextView){
                if(v.getY()+v.getHeight()/2>view.getHeight()/3&&v.getY()+v.getHeight()/23*2){
                    ((TextView) v).setTextColor(selectColor);
                    ((TextView) v).setTextSize(TypedValue.COMPLEX_UNIT_SP,selectTextSize);
                }else {
                    ((TextView) v).setTextColor(defColor);
                    ((TextView) v).setTextSize(TypedValue.COMPLEX_UNIT_SP,defTextSize);
                }
                }
            }
        }
    }
    private void initFirst() {
        if(!hasInit){
            paintShader =new Paint();
            paintShader.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
            shaderTop=new LinearGradient(0, 0, 0, 100, 0xFF000000, 0,
                    Shader.TileMode.CLAMP);
            shaderButtom=new LinearGradient(0, this.getHeight()-100, 0, this.getHeight(), 0, 0xFF000000,
                    Shader.TileMode.CLAMP);
            hasInit=true;
        }
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawLine(20,getHeight()/3,getWidth()-20,getHeight()/3,paintLine);
        canvas.drawLine(20,getHeight()/3*2,getWidth()-20,getHeight()/3*2,paintLine);
    }
    @Override
    public void draw(Canvas canvas) {
        initFirst();
        canvas.saveLayer(0,0,this.getWidth(),this.getHeight(),null,Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
        super.draw(canvas);
        paintShader.setShader(shaderTop);
        canvas.drawRect(0, 0, this.getWidth(), 100,paintShader);
        paintShader.setShader(shaderButtom);
        canvas.drawRect(0, this.getHeight()-100,this.getWidth(), this.getHeight(),paintShader);
    }
    /**
     * 平滑滚动
     * */
    private void ScrollAnim(final ListView v, final int position, int y1, int y2){
        ValueAnimator valueAnimator= ValueAnimator.ofInt(y1,y2);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                v.setSelectionFromTop(position,((Integer) animation.getAnimatedValue()).intValue());
            }
        });
        valueAnimator.setDuration(150);
        valueAnimator.start();
    }

    public void setSelectColor(int selectColor) {
        this.selectColor = selectColor;
    }

    public void setDefColor(int defColor) {
        this.defColor = defColor;
    }

    public void setSelectTextSize(int selectTextSize) {
        this.selectTextSize = selectTextSize;
    }

    public void setDefTextSize(int defTextSize) {
        this.defTextSize = defTextSize;
    }
}

好到此就分享完了,这里只是给一个思路而已,如果有幸被使用,注意的是 WheelListView的高度是item的三倍最好,还没很完善的做好适配。

你可能感兴趣的:(知识分享)