android-Viewflow开源项目学习

原创 2015年11月18日 12:43:05

一直以来,没有写过技术博客。最近想整理一下自己学过的开源项目,通过动手加深对技术的理解

Viewflow项目介绍

Viewflow是github上很好用的横向滑动View的开源项目。他有跟android原生ListView一样的接口,在项目中能够快速运用。

ListView的启发

Viewflow的设计很自然联想到能够上下滑动的ListView,因此大部分人独立去实现这样的View时,比较容易出现的想法是改造ListView。我们通常想到:
  1. 将ListView的移动方向改变城左右滑动。
  2. 只显示一项数据。
因此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)); 
mNextScreen = 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; 
} 

在加载时,
  1. mRecycleViews中恢复的ChildView,调用attachViewToParent
  2. 新创建的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链接,本人认识有限,希望大家指正。

版权声明:本文为博主原创文章,未经博主允许不得转载。

使用ViewFLow制作循环滑动广告牌

广告牌是android客户端中常见得一种效果,那么我们今天就用viewflow这个开源控件来制作一个可以循环滑动的广告牌。、       首先可以从git上下载插件,然后集成到我们的项目中。地址:ht...
  • forrey
  • forrey
  • 2015年11月05日 16:46
  • 3615

Android ViewFlow的使用

注意ViewFlow不是google官方的api,它是gethub上的一个开源项目,利用ViewFlow可以产生视图切换的效果。ViewFlow 相当于 Android UI 部件提供水平滚动的 ...
  • wangjinyu501
  • wangjinyu501
  • 2012年11月16日 19:13
  • 8024

使用ViewFlow实现无限循环轮播图和滑动冲突解决

ViewFlow
  • zqrdy10
  • zqrdy10
  • 2017年01月04日 13:57
  • 779

Android ViewFlow的一个例子

完成这个例子的步骤: 1.下载ViewFlow的源码,然后将类ViewFlow放在自己的工程的src的某个包下。 2.下载的源码里有2个工程view flow,viewflow-examp...
  • liuxiIT
  • liuxiIT
  • 2012年02月14日 10:29
  • 14586

android ViewPager,ViewFlipper,ViewFlow实现左右滑动

开篇         首页只是作为ViewPager,ViewFlipper,ViewFlow的入口,提供三个Button进行跳转。                1. ViewPager...
  • zhouyuanjing
  • zhouyuanjing
  • 2012年12月13日 15:44
  • 27348

Android中FlowTagLayout流式布局的使用

何为FlowTagLayout 如果对Java的Swing比较熟悉的话一定不会陌生,就是控件根据ViewGroup的宽,自动的往右添加,如果当前行剩余空间不足,则自动添加到下一行。有点所有的控件都往...
  • shihuiyun
  • shihuiyun
  • 2016年08月09日 10:55
  • 2713

Android-教你自作一个简单而又实用的流式Tag标签布局

在这一章节,我们继续学习Android自定义控件。这里要自定义的是Android里面的一个常用控件-Android流式Tag布局,这里我们命名为:FlowTagLayout,我们要实现的流式布局,有如...
  • hanhailong726188
  • hanhailong726188
  • 2015年10月20日 22:02
  • 8163

ViewFlow源码

  • 2015年04月07日 15:09
  • 2.49MB
  • 下载

ViewFlow例子

  • 2011年11月19日 00:07
  • 581KB
  • 下载

读《借助开源项目,学习软件开发》总结

读《借助开源项目,学习软件开发》总结 GitBook文章源地址:文章源地址 一.前言 准备条件:(1)一台能上网的电脑(2)首选系统Ubuntu,其次Windows 二.开始 (1)软件开发...
  • baidu_22522021
  • baidu_22522021
  • 2016年03月03日 18:11
  • 696
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:android-Viewflow开源项目学习
举报原因:
原因补充:

(最多只允许输入30个字)