android-Viewflow开源项目学习
2015-11-18 12:43
351 查看
一直以来,没有写过技术博客。最近想整理一下自己学过的开源项目,通过动手加深对技术的理解
将ListView的移动方向改变城左右滑动。
只显示一项数据。
因此Viewflow很自然的是继承AdapterView。
Viewflow中只显示一项ChildView,高度为ChildView的高度与上下padding之和,宽度为屏幕宽度;onLayout为每个childView申请布局。
android在绘制View前,都会调用computeScroll,因此在该方法中处理ChildView切换:
android在Touch过程中,设置Scroller参数,获取滑动速率,用于ChildView切换计算
Viewflow中加载的ChildView保存到mLoadViews链表中,不再需要加载的ChildView放在mRecycledViews中。在需要生成新的ChildView加载数据时,可以重新利用mRecycleViews中的没在加载的ChildView。
在加载时,
mRecycleViews中恢复的ChildView,调用attachViewToParent
新创建的ChildView,调用addViewInLayout
在切换显示View时,处理缓存view:
将切换不需要加载的View加入mRecycleViews中:
Viewflow项目介绍
Viewflow是github上很好用的横向滑动View的开源项目。他有跟android原生ListView一样的接口,在项目中能够快速运用。ListView的启发
Viewflow的设计很自然联想到能够上下滑动的ListView,因此大部分人独立去实现这样的View时,比较容易出现的想法是改造ListView。我们通常想到:将ListView的移动方向改变城左右滑动。
只显示一项数据。
因此Viewflow很自然的是继承AdapterView。
View宽高计算
android View在计算宽高布局时,有onMeasure和onLayout两个方法。Viewflow中只显示一项ChildView,高度为ChildView的高度与上下padding之和,宽度为屏幕宽度;onLayout为每个childView申请布局。
滑动事件处理
android中不是单一view的滑动处理通常是onInterceptTouchEvent和onTouchEvent两个方法处理触摸事件的响应,Scroller处理视图滑动,并通过VelocityTracker处理滑动速度。Viewflow在滑动中显示什么位置的ChildView可以计算:private void snapToDestination(){ final int screenWidth = getChildWdith(); final int whichScreen = (getScrollX() + (screenWidth / 2)) / screenWidth; snapToScreen(whichScreen); }
android在绘制View前,都会调用computeScroll,因此在该方法中处理ChildView切换:
@Override public void computeScroll() { if(mScroller.computeScrollOffset()){ scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); }else if(mNextScreen != INVALID_SCREEN){ mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1)); mNextScr a0db een = INVALID_SCREEN; post(new Runnable() { @Override public void run() { postViewSwitched(mLastScrollDirection); } }); } }
android在Touch过程中,设置Scroller参数,获取滑动速率,用于ChildView切换计算
@Override public boolean onTouchEvent(MotionEvent event) { if(getChildCount() == 0) return false; if(mVelocityTracker == null){ mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); final int action = event.getAction(); final float x = event.getX(); switch(action){ case MotionEvent.ACTION_DOWN: /** * If being flinged and user touches, stop the fling. isFinished * will be false if being flinged. */ if(mScroller.isFinished()){ mScroller.abortAnimation(); } // Remember where the motion event started mLastMotionX = x; mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; break; case MotionEvent.ACTION_MOVE: final int deltaX = (int)(mLastMotionX - x); boolean xMoved = Math.abs(deltaX) > mTouchSlop; if(xMoved){ mTouchState = TOUCH_STATE_SCROLLING; if(mViewInitializeListener != null) initializeView(deltaX); } if(mTouchState == TOUCH_STATE_SCROLLING){ // Scroll to follow the motion event mLastMotionX = x; int scrollX = getScrollX(); if(deltaX < 0){ if(scrollX > 0){ scrollBy(Math.max(-scrollX, deltaX), 0); } }else if(deltaX > 0){ final int availableToScroll = getChildAt( getChildCount() - 1).getRight() - getPaddingRight() - getHorizontalFadingEdgeLength() - scrollX - getChildWdith(); scrollBy(Math.min(availableToScroll, deltaX), 0); } return true; } break; case MotionEvent.ACTION_UP: if(mTouchState == TOUCH_STATE_SCROLLING){ final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int veloctiyX = (int) velocityTracker.getXVelocity(); if(veloctiyX > SNAP_VELOCITY && mCurrentScreen > 0){ snapToScreen(mCurrentScreen - 1); }else if(veloctiyX < - SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1){ snapToScreen(mCurrentScreen + 1); }else{ snapToDestination(); } if(mVelocityTracker != null){ mVelocityTracker.recycle(); mVelocityTracker = null; } } mTouchState = TOUCH_STATE_REST; break; case MotionEvent.ACTION_CANCEL: snapToDestination(); mTouchState = TOUCH_STATE_REST; break; } return true; }
ChildView缓存
Viewflow中使用链表处理ChildView的缓存。private LinkedList<View> mLoadedViews; private LinkedList<View> mRecycledViews;
Viewflow中加载的ChildView保存到mLoadViews链表中,不再需要加载的ChildView放在mRecycledViews中。在需要生成新的ChildView加载数据时,可以重新利用mRecycleViews中的没在加载的ChildView。
private View obtainView(int position){ View convertView = getRecycledView(); View view = mAdapter.getView(position, convertView, this); if(view != convertView && convertView != null){ mRecycledViews.add(convertView); } mLastObtainedViewWasRecycled = (view == convertView); LayoutParams p = view.getLayoutParams(); if(p == null){ p = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); view.setLayoutParams(p); } return view; }
在加载时,
mRecycleViews中恢复的ChildView,调用attachViewToParent
新创建的ChildView,调用addViewInLayout
在切换显示View时,处理缓存view:
private void postViewSwitched(int direction){ if(direction == 0) return; if(direction > 0){ // to the right mCurrentAdapterIndex ++; mCurrentBufferIndex ++; mLazyInit.remove(LazyInit.LEFT); mLazyInit.add(LazyInit.RIGHT); // Recycle view outside buffer range if(mCurrentAdapterIndex > mSideBuffer){ recycleView(mLoadedViews.removeFirst()); mCurrentBufferIndex --; } int newBufferIndex = mCurrentAdapterIndex + mSideBuffer; if(newBufferIndex < mAdapter.getCount()) mLoadedViews.addLast(makeAndAddView(newBufferIndex, true)); }else { //to the left mCurrentAdapterIndex --; mCurrentBufferIndex --; mLazyInit.add(LazyInit.LEFT); mLazyInit.remove(LazyInit.RIGHT); if((mAdapter.getCount() - 1 - mCurrentAdapterIndex) > mSideBuffer){ recycleView(mLoadedViews.removeLast()); } int newBufferIndex = mCurrentAdapterIndex - mSideBuffer; if(newBufferIndex > -1){ mLoadedViews.addFirst(makeAndAddView(newBufferIndex, false)); mCurrentBufferIndex ++; } } requestLayout(); setVisibleView(mCurrentBufferIndex, true); if(mIndicator != null){ mIndicator.onSwitched(mLoadedViews.get(mCurrentBufferIndex), mCurrentAdapterIndex); } if(mViewSwitchListener != null){ mViewSwitchListener.onSwitched(mLoadedViews.get(mCurrentBufferIndex), mCurrentAdapterIndex); } }
将切换不需要加载的View加入mRecycleViews中:
protected void recycleView(View v){ if(v == null) return; mRecycledViews.addFirst(v); detachViewFromParent(v); }
结束语
Viewflow的github链接,本人认识有限,希望大家指正。相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories