项目要求:一个十字架效果,水平和垂直滑动来选择每一子项。刚开始做的时候,以为很简单, 两个Gallery,一个水平,一个垂直放置就OK了,后来一看Gallery不能垂直滑动(网上貌似已经有了)。然后想为什么自己不能写一个了,花了两天功夫,终于做出效果来了,效果图如下:

1.代码目录结构

HVScrollbar.java:这个类负责承载每一个子项,其中有一个变量isVertical来判断水平或者垂直。

HVFrameLayout.java:这个类承载水平和垂直方向的滑动条,此类最关键的功能是控制滑动条的滑动,这点也是此功能的难点。

IconStyle.java:一个activity,不做介绍。

2.HVScrollbar.java

此类控制每一个子项的显示位置,并且显示水平或者垂直,在onMeasure和onLayout方法中控制。

@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {// TODO Auto-generated method stubFrameLayout.LayoutParams flp = (FrameLayout.LayoutParams)getLayoutParams();int count = getChildCount();for(int i = 0; i < count; i++){View child = getChildAt(i);ViewGroup.LayoutParams vlp = child.getLayoutParams();if(vlp instanceof LayoutParams){LayoutParams lp = (LayoutParams)vlp;child.layout(lp.x, lp.y, lp.x + lp.width, lp.y+lp.height);//根据LayoutParams来布局}}}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// TODO Auto-generated method stubint count = getChildCount();int widthSpecSize = 0;int heightSpecSize = 0;for (int i = 0; i < count; i++) {View child = getChildAt(i);ViewGroup.LayoutParams vlp = child.getLayoutParams();if(vlp instanceof LayoutParams){LayoutParams lp = (LayoutParams)vlp;if(isVertical){//垂直方向设置子项的top和leftlp.x = 0;lp.y = lp.height * i;widthSpecSize = lp.width;heightSpecSize +=lp.height;}else{//水平方向设置子项的top和leftlp.y = 0;lp.x = lp.width * i;heightSpecSize = lp.height;widthSpecSize +=lp.width;}int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,MeasureSpec.EXACTLY);child.measure(childWidthMeasureSpec, childheightMeasureSpec);//设置子项的宽度和高度(每一子项都一样)}}setMeasuredDimension(widthSpecSize, heightSpecSize);//滑动条的实际宽度和高度}static class LayoutParams extends ViewGroup.LayoutParams{int x;//子项Leftint y;//子项Toppublic LayoutParams(int width, int height) {super(width, height);}}

3.HVFrameLayout.java

此类控制垂直和水平滑动条的滑动,最关键的是两个方法onInterceptTouchEvent(判断是否滑动)和onTouchEvent(控制滑动以及放手后的滑动动画)。

onInterceptTouchEvent

@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {// TODO Auto-generated method stubmFirstLayout = false;acquireVelocityTrackerAndAddMovement(ev);//手指滑动速度跟踪int action = ev.getAction();final float x = ev.getX();final float y = ev.getY();switch(action){case MotionEvent.ACTION_DOWN://手指放下,初始化mDownX = mLastX = x;mDownY = mLastY = y;mTotalX = mTotalY = 0;mAnchorX = (getWidth() - mCellWidth) / 2;mAnchorY = (getHeight() - mCellHeight) / 2;mMinMarginTop = mAnchorY - mVertical.getHeight() + mCellHeight;mMaxMarginTop = mAnchorY;mMinMarginLeft = mAnchorX - mHorizontal.getWidth() +  mCellWidth;mMaxMarginLeft = mAnchorX;if(isTouchInH(x, y) || isTouchInV(x, y)){mState = STATE_RESET;}else{mState = STATE_OUT;}break;case MotionEvent.ACTION_MOVE:if(mState == STATE_RESET){determineScrollingStart(x, y);//此方法决定是否滑动}break;case MotionEvent.ACTION_UP:default:mState = STATE_RESET;releaseVelocityTracker();break;}return mState == STATE_SCROLLING;}

determineScrollingStart

private void determineScrollingStart(float x, float y){final int xDiff = (int) Math.abs(x - mLastX);final int yDiff = (int) Math.abs(y - mLastY);boolean xMoved = xDiff > mTouchSlop;boolean yMoved = yDiff > mTouchSlop;boolean isAnimationRunning = isAnimationRunning();//开始计算是否开始滑动if((xMoved || yMoved) && !isAnimationRunning){mState = STATE_SCROLLING;mTotalX += x - mLastX;mTotalY += y - mLastY;mLastX = x;mLastY = y;           boolean isTouchInH = isTouchInH(mDownX, mDownY);boolean isTouchInV = isTouchInV(mDownX, mDownY);if(isTouchInH && isTouchInV){//此处为手指滑动在水平和垂直滑动条的公共区域,需要用速度跟踪来判断滑动方向:手指滑动在水平方向和垂直方向//的值的比较mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);mIsVertical = Math.abs(mVelocityTracker.getXVelocity()) < Math.abs(mVelocityTracker.getYVelocity());}else if(isTouchInH){//水平方向mIsVertical = false;}else{//垂直方向mIsVertical = true;}}}

onTouchEvent

@Overridepublic boolean onTouchEvent(MotionEvent event) {// TODO Auto-generated method stubmFirstLayout = false;acquireVelocityTrackerAndAddMovement(event);int action = event.getAction();final float x = event.getX();final float y = event.getY();switch(action){case MotionEvent.ACTION_DOWN:mDownX = mLastX = x;mDownY = mLastY = y;mTotalX = mTotalY = 0;break;case MotionEvent.ACTION_MOVE:if(mState == STATE_SCROLLING){float deltaX = x - mLastX;float deltaY = y - mLastY;//滑动距离超过1时才开始滑动if(mIsVertical && Math.abs(deltaY) > 1.0f){if(mVertical.getLayoutParams() instanceof FrameLayout.LayoutParams){//计算垂直滑动条的topMargin,通过调用requestLayout来控制布局,实现滑动条跟着手指滑动的动画FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams)mVertical.getLayoutParams();int topMargin = (int)(flp.topMargin + deltaY);if(topMargin < mMinMarginTop){topMargin = mMinMarginTop;}else if(topMargin > mMaxMarginTop){topMargin = mMaxMarginTop;}flp.topMargin = topMargin;requestLayout();}mTotalX += deltaX;mTotalY += deltaY;mLastX = x;mLastY = y;}else if(!mIsVertical && Math.abs(deltaX) > 1.0f){//同垂直方向if(mHorizontal.getLayoutParams() instanceof FrameLayout.LayoutParams){FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams)mHorizontal.getLayoutParams();int leftMargin = (int)(flp.leftMargin + deltaX);if(leftMargin < mMinMarginLeft){leftMargin = mMinMarginLeft;}else if(leftMargin > mMaxMarginLeft){leftMargin = mMaxMarginLeft;}flp.leftMargin = leftMargin;requestLayout();}mTotalX += deltaX;mTotalY += deltaY;mLastX = x;mLastY = y;}}break;case MotionEvent.ACTION_UP:if(mState == STATE_SCROLLING){//此处用来处理手指放开后的动画final VelocityTracker velocityTracker = mVelocityTracker;velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);int velocityX = (int)velocityTracker.getXVelocity();int velocityY = (int)velocityTracker.getYVelocity();int duration = 500;if(mHorizontalAnimation != null){mHorizontalAnimation.cancel();mHorizontalAnimation = null;}if(mVerticalAnimation != null){mVerticalAnimation.cancel();mVerticalAnimation = null;}if(mIsVertical){if(mVertical.getLayoutParams() instanceof FrameLayout.LayoutParams){final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams)mVertical.getLayoutParams();//根据速度跟踪(velocityTracker)来计算目的topMargin,此处借鉴AbsListView//滑动后的处理(getSplineFlingDistance)。int dstTopMargin = Math.min(Math.max(mMinMarginTop, getFinalDistance((int)getSplineFlingDistance(velocityY)) + flp.topMargin), mMaxMarginTop);//属性动画mVerticalAnimation = ObjectAnimator.ofInt(mVertical, "topMargin", flp.topMargin, dstTopMargin);mVerticalAnimation.setDuration(mAnimationDuration);mVerticalAnimation.setInterpolator(new DecelerateInterpolator());mVerticalAnimation.addUpdateListener(new AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {// TODO Auto-generated method stub//根据topMargin的变化,通过调用requestLayout,来实现动画flp.topMargin = ((Integer)animation.getAnimatedValue()).intValue();requestLayout();}});mVerticalAnimation.start();}}else{if(mHorizontal.getLayoutParams() instanceof FrameLayout.LayoutParams){final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams)mHorizontal.getLayoutParams();int dstLeftMargin = Math.min(Math.max(mMinMarginLeft, getFinalDistance((int)getSplineFlingDistance(velocityX)) + flp.leftMargin), mMaxMarginLeft);mHorizontalAnimation = ObjectAnimator.ofInt(mHorizontal, "leftMargin", flp.leftMargin, dstLeftMargin);mHorizontalAnimation.setDuration(mAnimationDuration);mHorizontalAnimation.setInterpolator(new DecelerateInterpolator());mHorizontalAnimation.addUpdateListener(new AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {// TODO Auto-generated method stubflp.leftMargin = ((Integer)animation.getAnimatedValue()).intValue();requestLayout();}});mHorizontalAnimation.start();}}}mState = STATE_RESET;releaseVelocityTracker();//release VelocityTrackerbreak;default:break;}return true;}

4.问题和缺陷

(1)速度跟踪还不是太了解。

(2)此功能的滑动借鉴了Launcher的滑动处理,使用时总感觉滑动有点不协调,滑动处理不是很好,这方面肯定有待改进。

(3)此功能不容易拓展,当时只是想完成这个功能,做出效果。

5.总结

这是我第一次写这么长的博客,其实也不长,代码贴的比较多,讲的也不详细。做了一个小demo,欢迎交流。

(demo链接:http://download.csdn.net/detail/whuthm/6417691)

android 十字架效果实现(水平和垂直滑动)相关推荐

  1. Android HorizontalScrollView和ScrollView 水平滚动 垂直滚动

    Android SDK有两个滚动组件,HorizontalScrollView和ScrollView.一个是水平滚动,另一个是垂直滚动.两个嵌套使用,可实现水平和垂直滚动. 本文转自:http://h ...

  2. Python Qt GUI设计:QScrollBar类实现窗口水平或垂直滑动条效果(拓展篇—4)

    使用QScrollBar可以在窗口控件提供了水平的或垂直的滚动条,这样可以扩大当前窗口的有效装载面积,从而装载更多的控件. QScrollBar类中的常用信号如下表所示: 通过示例,了解QScroll ...

  3. Android自定义分割线_水平及垂直分割线的两种实现方法

    很多时候我们在做Android开发的时候.需要设置Item和Item的分割线.尽管不是在ListView里面.我们也会使用到分割线来区分不同选项.达到条目清晰的目的.比如下图中使用了横向的分割线.有长 ...

  4. Android RecyclerViewSwipeDismiss:水平、垂直方向的拖曳删除item

     Android RecyclerViewSwipeDismiss:水平.垂直方向的拖曳删除item RecyclerViewSwipeDismiss是一种支持RecyclerView的水平.垂直 ...

  5. Android 手势拦截的实现(简化水平、垂直手势操作的拦截处理)

    这是手势拦截类的源码.注释,也加的随地时,方便阅读理解.在源码后面,会有使用案例. package com.laka.robotdog.widget;import android.content.Co ...

  6. android中,实现水平方向上三个按钮左对齐、居中对齐、右对齐效果

    解析:使用FrameLayout 可以很容易解决这个问题, 方法一:View 的上.下.左.右.居中对齐是界面中经常接触到的布局效果.单独某种对齐方式有很多种写法.但同一个方向的各种对齐布局,Fram ...

  7. android 垂直自动滚动条,Android实现Activity水平和垂直滚动条的方法

    本文实例讲述了Android实现Activity水平和垂直滚动条的方法.分享给大家供大家参考,具体如下: android:layout_width="match_parent" a ...

  8. android fragment界面滑动切换效果,Android App中使用ViewPager+Fragment实现滑动切换效果...

    在android应用中,多屏滑动是一种很常见的风格,没有采用viewpager的代码实现会很长,如果采用ViewPager,代码就会短很多,但是使用ViewPager也有弊端:需要导入android- ...

  9. android activity滑动切换,Android 向右滑动切换Activity, 随着手势的滑动而滑动的效果...

    [实例简介] Android 向右滑动切换Activity, 随着手势的滑动而滑动的效果,项目详情http://blog.csdn.net/xiaanming/article/details/2093 ...

最新文章

  1. 【Java面试题】54 去掉一个Vector集合中重复的元素
  2. 深入浅出Pytorch:01 课程大纲与PyTorch简介
  3. [转]CNN目标检测(一):Faster RCNN详解
  4. windows api学习笔记-简单的记事本
  5. 聊聊Elasticsearch RestClient的RequestLogger
  6. linux上openmp测试
  7. Unity 工具 之 常用插件分类汇总(UI/VR/AR/建模/Shader/动画/网络/AI/资源/数据/区块链等)
  8. 最新凌风云支付系统网站源码全解无后门V4.1.1版本
  9. Centos 防火墙开启/关闭端口
  10. drozer的安装与模拟器连接
  11. [渝粤教育] 广东-国家-开放大学 21秋期末考试组织行为学10068k2
  12. 余额支付使用短信验证码进行二次确认
  13. Vue + element + Springboot 通过邮箱找回密码
  14. 李占通他道出了我的心声
  15. 机器学习算法——支持向量机SVM4(SMO算法及KTT条件)
  16. 01-利用composer创建php代码包
  17. Java笔记整理七(网络编程,TCP通信程序,函数式接口,方法引用)
  18. 获取壁纸网站第一页至第n页的壁纸
  19. 位图算法BitMap
  20. 算力网络 — 东数西算政策

热门文章

  1. 微信小程序点赞成功,取消点赞、评论。
  2. Git之版本回退与前进
  3. win10计算机属性管理打不开,win10无法打开设置应用的两种解决方法【图文】
  4. Laragon实现快速创建ThinkPHP项目
  5. 绕过限制低价购买和增删低价购买逻辑漏洞
  6. uniapp项目H5端横屏问题-样式错乱+字体大小+video
  7. u盘读不出来怎么修复?
  8. “自然”语言编程(NLC)的到来比你想象的要快
  9. 宏源药业上市破发:跌幅16% 公司市值168亿超募17亿
  10. 面试题:检测杯子的质量