? ? ? ? ? android:layout_height="50dp"
? ? ? ? ? ? android:background="#ffffffff"
? ? ? ? ? ? android:gravity="center" >
? ? ? ?
?//省略了无数控件
? ?
没撒说的 ,let's go 。?
public class StickyNavLayout extends LinearLayout
{
?private View mTop;
?private View mNav;
?private ViewPager mViewPager;
?
?private int mTopViewHeight;
?private ScrollView mInnerScrollView;
?private boolean isTopHidden = false;
?private OverScroller mScroller;
?private VelocityTracker mVelocityTracker;
?private int mTouchSlop;
?private int mMaximumVelocity, mMinimumVelocity;
?
?private float mLastY;
?private boolean mDragging;
?
?
?public StickyNavLayout(Context context, AttributeSet attrs)
?{
? super(context, attrs);
? setOrientation(LinearLayout.VERTICAL);
? mScroller = new OverScroller(context);
? mVelocityTracker = VelocityTracker.obtain();
? mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
? mMaximumVelocity = ViewConfiguration.get(context)
? ? .getScaledMaximumFlingVelocity();
? mMinimumVelocity = ViewConfiguration.get(context)
? ? .getScaledMinimumFlingVelocity();
?}
?@Override
?protected void onFinishInflate()
?{
? super.onFinishInflate();
? mTop = findViewById(R.id.id_stickynavlayout_topview);
? mNav = findViewById(R.id.id_stickynavlayout_indicator);
? View view = findViewById(R.id.id_stickynavlayout_viewpager);
? if (!(view instanceof ViewPager))
? {
? ?throw new RuntimeException(
? ? ?"id_stickynavlayout_viewpager show used by ViewPager !");
? }
? mViewPager = (ViewPager) view;
?}
ok,首先看下成员变量,和我们的变量的初始化,mTop、mNav、mViewPager代表我们布局的三大块了,初始化在onFinishInflate中完成,可以看到直接通过我们的id资源读取就ok。接下来还有些mScroller、mVelocityTracker、mTouchSlop、mMaximumVelocity、mMinimumVelocity、mLastY、mDragging,不用说大家都能想到这是和移动相关的,OverScroller是个辅助类,用于自定移动时帮我们处理掉数学的计算部分。mVelocityTracker相关几个变量,当然是计算什么时候需要自动移动;mTouchSlop帮我区别用户是点击还是拖拽。android中封装了很多常量在ViewConfiguration中,大家有兴趣可以了解他,之所以使用这些常量不仅仅是说,省的我们自己去定义,而是为了和系统的行为保持一致。
看完了构造以后,由于我们使用的是LinearLayout,直接setOrientation(LinearLayout.VERTICAL);也就不必去layout了,控件都自定垂直排列了。那么我们在onMeasure中还需要做些处理??
@Override
?protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
?{
? super.onMeasure(widthMeasureSpec, heightMeasureSpec);
? ViewGroup.LayoutParams params = mViewPager.getLayoutParams();
? params.height = getMeasuredHeight() - mNav.getMeasuredHeight();
?}
?@Override
?protected void onSizeChanged(int w, int h, int oldw, int oldh)
?{
? super.onSizeChanged(w, h, oldw, oldh);
? mTopViewHeight = mTop.getMeasuredHeight();
?}
主要是去设置ViewPager的高度,给它一个固定值,ViewPager自己在测量自己的时候,你要是不给它固定值,可能测量结果与你的预期会差距很大。比如你给它设置个WRAP_CONTENT,你希望他去计算孩子的高度去设置自己的,那么你就想多了。大家可以看下ViewPager测量的源码:
可以看到,对于AT_MOST和EXACTLY两种模式,都是直接读取父类的传入的测量值,也就是说,他不会去测量自己孩子的高度。然后如果模式是:UNSPECIFIED,那么高度直接为0呢。这里大家如果做过这个例子,应该能遇到这种情况,ScrollView中放ViewPager时,测量模式就是UNSPECIFIED,那么Vp直接不显示,原因就在这里。
扯远了,回来,我们继续。
我们设置为Vp的值以后,理论上来说,我们的显示已经正常了,控件都按照我们的预期显示了,但是,但是什么呢?我们现在在自定义的LinearLayout,那么移动是不是应该我们自己去写。
移动的代码很简单,想必大家都知道,直接拿到dx,然后scrollBy就行了。
?@Override
?public boolean onTouchEvent(MotionEvent event)
?{
? mVelocityTracker.addMovement(event);
? int action = event.getAction();
? float y = event.getY();
? switch (action)
? {
? case MotionEvent.ACTION_DOWN:
? ?if (!mScroller.isFinished())
? ? mScroller.abortAnimation();
? ?mVelocityTracker.clear();
? ?mVelocityTracker.addMovement(