NestedScrolling使用

本文介绍了一个自定义的 StickyNavLayout 控件,该控件继承自 LinearLayout 并实现了 NestedScrollingParent 接口。文章详细展示了如何通过覆写一系列滚动相关的方法来实现顶部导航栏的粘性效果,确保在滑动过程中顶部导航栏可以平滑地显示或隐藏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原博客链接:http://blog.youkuaiyun.com/lmj623565791/article/details/52204039

/**
 * http://blog.youkuaiyun.com/yanbober/article/details/49904715 Scroller详解及源码浅析
 */
public class StickyNavLayout extends LinearLayout implements NestedScrollingParent
{
    private static final String TAG = "StickyNavLayout";

    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes)
    {
        Log.e(TAG, "onStartNestedScroll");
        return true;
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes)
    {
        Log.e(TAG, "onNestedScrollAccepted");
    }

    @Override
    public void onStopNestedScroll(View target)
    {
        Log.e(TAG, "onStopNestedScroll");
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)
    {
        Log.e(TAG, "onNestedScroll");
    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed)
    {
        // 手指向上滑动dy > 0      下滑 dy < 0
        // 手指上滑 顶部布局未完全隐藏
        boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight;
        // 手指下滑
        // ViewCompat.canScrollVertically(target, -1) 检测target能否在指定的方向垂直滚动, 负数表示向上滚动  返回true表示能够上下滑 false表示不能下滑
        boolean showTop = dy < 0 && getScrollY() >= 0 && !ViewCompat.canScrollVertically(target, -1);
        if (hiddenTop || showTop)
        {
            // 自己处理事件并且消费事件
            scrollBy(0, dy);
            consumed[1] = dy;
        }
    }

    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed)
    {
        Log.e(TAG, "onNestedFling");
        return false;
    }

    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY)
    {
        Log.e(TAG, "onNestedPreFling");
        //down - //up+  顶部布局完全隐藏
        if (getScrollY() >= mTopViewHeight) {

            return false;
        }
        fling((int) velocityY);
        return true;
    }

    @Override
    public int getNestedScrollAxes()
    {
        Log.e(TAG, "getNestedScrollAxes");
        return 0;
    }

    private View mTop;
    private View mNav;
    private ViewPager mViewPager;

    private int mTopViewHeight;

    private OverScroller mScroller;
    private VelocityTracker mVelocityTracker;


    public StickyNavLayout(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        setOrientation(LinearLayout.VERTICAL);

        mScroller = new OverScroller(context);
    }

    private void initVelocityTrackerIfNotExists()
    {
        if (mVelocityTracker == null)
        {
            mVelocityTracker = VelocityTracker.obtain();
        }
    }

    private void recycleVelocityTracker()
    {
        if (mVelocityTracker != null)
        {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }

    @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;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        //不限制顶部的高度
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        getChildAt(0).measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        ViewGroup.LayoutParams params = mViewPager.getLayoutParams();
        params.height = getMeasuredHeight() - mNav.getMeasuredHeight();
        setMeasuredDimension(getMeasuredWidth(), mTop.getMeasuredHeight() + mNav.getMeasuredHeight() + mViewPager.getMeasuredHeight());

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(w, h, oldw, oldh);
        mTopViewHeight = mTop.getMeasuredHeight();
    }


    public void fling(int velocityY)
    {
        /**
         * 在快速滑动松开的基础上开始惯性滚动,滚动距离取决于fling的初速度
         */
        mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight);
        invalidate();
    }

    @Override
    public void scrollTo(int x, int y)
    {
        if (y < 0)
        {
            y = 0;
        }
        if (y > mTopViewHeight)
        {
            y = mTopViewHeight;
        }
        if (y != getScrollY())
        {
            super.scrollTo(x, y);
        }
    }

    @Override
    public void computeScroll()
    {
        if (mScroller.computeScrollOffset())
        {
            scrollTo(0, mScroller.getCurrY());
            invalidate();
        }
    }


}

public class SimpleViewPagerIndicator extends LinearLayout
{

   private static final int COLOR_TEXT_NORMAL = 0xFF000000;
   private static final int COLOR_INDICATOR_COLOR = Color.GREEN;

   private String[] mTitles;
   private int mTabCount;
   private int mIndicatorColor = COLOR_INDICATOR_COLOR;
   private float mTranslationX;
   private Paint mPaint = new Paint();
   private int mTabWidth;

   public SimpleViewPagerIndicator(Context context)
   {
      this(context, null);
   }

   public SimpleViewPagerIndicator(Context context, AttributeSet attrs)
   {
      super(context, attrs);
      mPaint.setColor(mIndicatorColor);
      mPaint.setStrokeWidth(9.0F);
   }

   @Override
   protected void onSizeChanged(int w, int h, int oldw, int oldh)
   {
      super.onSizeChanged(w, h, oldw, oldh);
      mTabWidth = w / mTabCount;
   }

   public void setTitles(String[] titles)
   {
      mTitles = titles;
      mTabCount = titles.length;
      generateTitleView();

   }

   public void setIndicatorColor(int indicatorColor)
   {
      this.mIndicatorColor = indicatorColor;
   }

   @Override
   protected void dispatchDraw(Canvas canvas)
   {
      super.dispatchDraw(canvas);
      canvas.save();
      // rotate(float)这个方法的旋转中心是坐标的原点
      // 移动我们画图的坐标系的原点
      canvas.translate(mTranslationX, getHeight() - 2);
      canvas.drawLine(0, 0, mTabWidth, 0, mPaint);
      canvas.restore();
   }

   public void scroll(int position, float offset)
   {
      /**
       * <pre>
       *  0-1:position=0 ;1-0:postion=0;
       * </pre>
       */
      mTranslationX = getWidth() / mTabCount * (position + offset);
      invalidate();
   }


   @Override
   public boolean dispatchTouchEvent(MotionEvent ev)
   {
      return super.dispatchTouchEvent(ev);
   }

   private void generateTitleView()
   {
      if (getChildCount() > 0)
         this.removeAllViews();
      int count = mTitles.length;

      setWeightSum(count);
      for (int i = 0; i < count; i++)
      {
         TextView tv = new TextView(getContext());
         LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT);
         lp.weight = 1;
         tv.setGravity(Gravity.CENTER);
         tv.setTextColor(COLOR_TEXT_NORMAL);
         tv.setText(mTitles[i]);
         tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
         tv.setLayoutParams(lp);
         tv.setOnClickListener(new OnClickListener()
         {
            @Override
            public void onClick(View v)
            {

            }
         });
         addView(tv);
      }
   }

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值