自定义view二--可以横向滚动且只显示两张半的imageview

来源:互联网 发布:linux下echo命令 编辑:程序博客网 时间:2024/06/09 20:54

进阶之路:自定义View一可以横向滚动且只显示两张半的imageview

需求分析

需求:

一个类似于轮播图的控件:当有三张图片时显示两张半图片并且可以左右滑动;当只有两张图片时,显示完整的两张图片,不可以左右滑动;当只有1张图片时,显示完整的1张图片,不可以左右滑动.(如下图:)
这里写图片描述

分析:

首先图片的宽度会出现三种方式,要根据手机屏幕的宽度来显示。也就是说我们直接在xml中写layout_width的方式不行了,我们只有重写imagview的测量方法,使其根据屏幕的宽度来设置imageview的宽度,如当有三张图片时既每张图片宽度为(x+x+x/2=1)屏幕宽的0.4;如当有2张图片时既每张图片宽度为(x+x=1)屏幕宽的0.5;如当有1张图片时既每张图片宽度为(x=1)屏幕宽的1;

其次:有了三张imagview图片,如何让图片滚动起来,我们知道viewgroup中有scrollBy和scrollTo方法可以滚动view中内容,因此我们就有必要继承一个viewgroup实现滚动。

重写imageview

根据刚才的分析我们就重写imageview的onMeasure如下:

    @Override//主要将宽设置为上面分析的宽度    protected void onMeasure(int widthMeasureSpec, int  heightMeasureSpec) {        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);        if(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode ==           MeasureSpec.AT_MOST){            setMeasuredDimension((int     (widthSpecSize*weight),defaultHeight);        }else if(widthSpecMode == MeasureSpec.AT_MOST){            setMeasuredDimension((int)(widthSpecSize*weight),heightSpecSize);        }else if(heightSpecMode == MeasureSpec.AT_MOST){            setMeasuredDimension((int)(widthSpecSize*weight), defaultHeight);        }else{            setMeasuredDimension((int)(widthSpecSize*weight), heightSpecSize);        }    }

weight是自定义的属性的屏幕宽度占比,比如上面分析的0.4,0.5,1等。如下:

  public MyImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        TypedArray typedArray = context.obtainStyledAttributes(attrs,  R.styleable.MyImageView);        if (typedArray != null) {            weight = typedArray.getFloat(R.styleable.MyImageView_width_weight, 0.4f);            typedArray.recycle();        }    }

重写ViewGroup

重写onMeasure

首先得重写onMeasure,计算出只控件的高度,以便ViewGroup的高度设置为wrap_content时,能准确显示高度:

 @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int childCount = getChildCount();        int childViewHeight=250;        for (int i = 0; i < childCount; i++) {            View childView = getChildAt(i);            // 为ScrollerLayout中的每一个子控件测量大小            measureChild(childView, widthMeasureSpec, heightMeasureSpec);            childViewHeight=childView.getMeasuredHeight();        }        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);        if(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){            setMeasuredDimension(widthSpecSize,childViewHeight);        }else if(widthSpecMode == MeasureSpec.AT_MOST){            setMeasuredDimension(widthSpecSize,heightSpecSize);        }else if(heightSpecMode == MeasureSpec.AT_MOST){            setMeasuredDimension(widthSpecSize, childViewHeight);        }else{            setMeasuredDimension(widthSpecSize, heightSpecSize);        }    }

重写onLayout

我们要横向滚动就得实现横向排列,并且在此方法中计算出所有子view的左右边界,以便下一步运算,因此需要重写此方法:

   @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {//        if (changed) {            int childCount = getChildCount();            for (int i = 0; i < childCount; i++) {                View childView = getChildAt(i);                // 为ScrollerLayout中的每一个子控件在水平方向上进行布局                int childViewWidth=(int)(childView.getMeasuredWidth());//*0.4                childView.layout(i * childViewWidth, 0, (i + 1) * (childViewWidth), childView.getMeasuredHeight());            }            // 初始化左右边界值            leftBorder = getChildAt(0).getLeft();            rightBorder = getChildAt(getChildCount() - 1).getRight();//        }    }

重写onTouchEvent方法

我们知道要实现滚动就得监听手指的滑动事件,而滑动事件MotionEvent.ACTION_MOVE的监听在onTouchEvent和onTouch方法中都可以,但是为了简单我们,我们就在onTouchEvent方法中去监听:

@Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_MOVE:                mXMove = event.getRawX();                int scrolledX = (int) (mXLastMove - mXMove);                if (getScrollX() + scrolledX < leftBorder) {//滑动到左边界,就不让滑动                    scrollTo(leftBorder, 0);                    return true;                } else if (getScrollX() + getWidth() + scrolledX > rightBorder) {                    scrollTo(rightBorder - getWidth(), 0);                    return true;                }                scrollBy(scrolledX, 0);                mXLastMove = mXMove;                break;            case MotionEvent.ACTION_UP://                // 当手指抬起时,根据当前的滚动值来判定应该滚动到哪个子控件的界面//                int targetIndex = (getScrollX() + getWidth() / 2) / getWidth();//                int dx = targetIndex * getWidth() - getScrollX();//                // 第二步,调用startScroll()方法来初始化滚动数据并刷新界面//                mScroller.startScroll(getScrollX(), 0, dx, 0);//                invalidate();                break;        }        return super.onTouchEvent(event);//    }

重写onInterceptTouchEvent

这样就行了吗?但是怎么划不动呢?其实我们知道view要滑动我们的view得接收到他的事件,如果是view默认是能接收到事件的(父viewGroup不拦截),但是我们的viewgroup默认是不拦截事件的以便事件能够分发到子view中去,因此我们如果要监听到滑动事件,需要在此处拦截(有对事件分发和拦截不清楚的,我后面会单独写一篇博客):

  @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                mXDown = ev.getRawX();                mXLastMove = mXDown;                break;            case MotionEvent.ACTION_MOVE:                mXMove = ev.getRawX();                float diff = Math.abs(mXMove - mXDown);                mXLastMove = mXMove;                // 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件                if (diff > mTouchSlop) {                    return true;                }                break;        }        return super.onInterceptTouchEvent(ev);    }

总结

看起来很难的view,其实一步一步的分析下来并不难,所以大家可以尝试开始一步一步的写view,不要让他的表象吓住。

源码下载

http://download.csdn.net/detail/hzmming2008/9884761

阅读全文
0 0
原创粉丝点击