自定义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
- 自定义view二--可以横向滚动且只显示两张半的imageview
- 简单实现自定义横向滚动选择View
- 自定义view (二<ImageView>)
- 自定义view-----滚动的刻度尺(二)
- Android可以动态控制图片显示区域的自定义ImageView
- 可以播放GIF和显示圆形图片的自定义ImageView
- Android滚动条广告,可以设置自定义view的ViewSwitcher
- ScrollingImageView 横向滚动的image view
- 基于view的复用的自定义横向滚动的Viewgroup
- JS(JQ)实现table表格固定表头且表头可以随横向滚动而滚动
- JS(JQ)实现table表格固定表头且表头可以随横向滚动而滚动
- Android中View的事件体系(3)——自定义横向滚动viewGroup
- android自定义view-打造圆形ImageView(二)
- 一个可以一直滚动的ImageView
- 自定义View 之ImageView(二) 圆角ImageView
- Android自定义可以在文字上方显示备注的View
- 一个简单的自定义View,可以显示上图下字
- android 自定义View(二) 自定义属性和带滚动的View
- 编程基础刷题(四)
- Thinkphp3.2 行为扩展和插件(Hook)实例详解
- ssm整合中的拦截器的使用
- java的反射
- php获取下月1月1号的时间
- 自定义view二--可以横向滚动且只显示两张半的imageview
- NSSet
- 程序员量子力学-海森堡式BUG
- LeetCode OJ 15 3Sum [Medium]
- swipe笔记 swipe.js 轻松实现手机端滑动效果
- Java的JAR包, EAR包 ,WAR包 都是干什么的,有什么区别
- 微信小程序 使用LeanCloud
- IDL波段分解与合成源代码
- HDU2444 The Accomodation of Students