仿美团滑动后头部固定源码分析

原创 2016年08月29日 14:03:43
public class StickyScrollView extends ScrollView {
    private static final String STICKY = "sticky";
    private View mCurrentStickyView;
    private Drawable mShadowDrawable;
    /**
     * 存放tagstickyView集合
     */
    private List<View> mStickyViews;
    private int mStickyViewTopOffset;
    private int defaultShadowHeight = 10;
    private float density;
    private boolean redirectTouchToStickyView;

    /**
     * 当点击Sticky的时候,实现某些背景的渐变
     */
    private Runnable mInvalidateRunnable = new Runnable() {

        @Override
        public void run() {
            if (mCurrentStickyView != null) {
                int left = mCurrentStickyView.getLeft();
                int top = mCurrentStickyView.getTop();
                int right = mCurrentStickyView.getRight();
                int bottom = getScrollY() + (mCurrentStickyView.getHeight() + mStickyViewTopOffset);
                invalidate(left, top, right, bottom);
            }
            postDelayed(this, 50);
        }
    };

    public StickyScrollView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public StickyScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mShadowDrawable = context.getResources().getDrawable(R.drawable.sticky_shadow_default);
        mStickyViews = new LinkedList();
        density = context.getResources().getDisplayMetrics().density;
    }

    /**
     * 找到设置tagView
     *
     * @param viewGroup
     */
    private void findViewByStickyTag(ViewGroup viewGroup) {
        int childCount = viewGroup.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = viewGroup.getChildAt(i);

            if (getStringTagForView(child).contains(STICKY)) {
                mStickyViews.add(child);
            }

            if (child instanceof ViewGroup) {
                findViewByStickyTag((ViewGroup) child);
            }
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed) {
            findViewByStickyTag((ViewGroup) getChildAt(0));
        }
        showStickyView();
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        showStickyView();
    }

    /**
     * 总结:
     * 只有一个StickyView的情况下
     * topOffset > 0(getScrollY() < stickyView.getTop()) curStickyView = null;
     * topOffset <= 0(getScrollY() >= stickyView.getTop()) curStickyView = view;
     */
    private void showStickyView() {
        View curStickyView = null;
        for (View v : mStickyViews) {
            int topOffset = v.getTop() - getScrollY();
            // 滑动距离开始大于v.getTop(),StickyView即将被滑出屏幕,CurrentStickyView即将被绘制出来
            if (topOffset <= 0) {
                if (curStickyView == null) {
                    curStickyView = v;
                }
            }
        }
        if (curStickyView != null) {
            // 悬浮框出现之前curStickyView一直为null, 出现之后不为null
            mStickyViewTopOffset = 0;
            mCurrentStickyView = curStickyView;
            post(mInvalidateRunnable);
        } else {
            mCurrentStickyView = null;
            removeCallbacks(mInvalidateRunnable);
        }
    }

    private String getStringTagForView(View v) {
        Object tag = v.getTag();
        return String.valueOf(tag);
    }

    /**
     * sticky画出来
     */
    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (mCurrentStickyView != null) {
            //先保存起来
            canvas.save();
            //将坐标原点移动到(0, getScrollY() + mStickyViewTopOffset)
            canvas.translate(0, getScrollY() + mStickyViewTopOffset);
            // 绘制StickyView
            canvas.clipRect(0, mStickyViewTopOffset, mCurrentStickyView.getWidth(), mCurrentStickyView.getHeight());
            mCurrentStickyView.draw(canvas);
            // 绘制阴影
            if (mShadowDrawable != null) {
                int left = 0;
                int top = mCurrentStickyView.getHeight() + mStickyViewTopOffset;
                int right = mCurrentStickyView.getWidth();
                int bottom = top + (int) (density * defaultShadowHeight + 0.5f);
                mShadowDrawable.setBounds(left, top, right, bottom);
                mShadowDrawable.draw(canvas);
            }
            //重置坐标原点参数
            canvas.restore();
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            redirectTouchToStickyView = true;
        }
        // 按下事件开始处理
        if (redirectTouchToStickyView) {
            // 头部布局是否为空
            redirectTouchToStickyView = mCurrentStickyView != null;
            // 判断是否点击的头部布局的范围
            if (redirectTouchToStickyView) {
                redirectTouchToStickyView = ev.getY() <= (mCurrentStickyView
                        .getHeight() + mStickyViewTopOffset)
                        && ev.getX() >= mCurrentStickyView.getLeft()
                        && ev.getX() <= mCurrentStickyView.getRight();
            }
        }
        if (redirectTouchToStickyView) {
            // 事件触摸位置设置到原StickyView的对应位置上
            ev.offsetLocation(0, -1 * ((getScrollY() + mStickyViewTopOffset) - mCurrentStickyView.getTop()));
        }
        return super.dispatchTouchEvent(ev);
    }

}

Html5添加实用的仿Instagram头部固定跟随滚动特效插件教程

一、使用方法   二、Html结构  This is the header of my item.  It will remain stuck to top while ...
  • Angel19951012
  • Angel19951012
  • 2016年01月11日 14:05
  • 762

android中仿<饿了么>listview与stickylistheaderslistview联动

listview与stickylistheaderslistview联动
  • qq_31743309
  • qq_31743309
  • 2017年04月05日 00:22
  • 664

双ListView与顶部标题栏滑动事件处理(仿美团外卖商家详情界面滑动效果)

1.首先贴图说明我要仿做的效果 首先讲下我实现的方式:(自定义一个ListView) 1.首页界面分为两部分:一个是顶部紫色部分,另一个是下面的双ListView部分。 所以我是通过自定...
  • kwb1880502
  • kwb1880502
  • 2016年08月06日 15:17
  • 464

仿美团APP的底部滑动菜单实现

仿美团APP的底部滑动菜单
  • u014131921
  • u014131921
  • 2017年03月09日 18:00
  • 722

css前端头尾部固定,中间内容可滑动

直接复制过来了,不好意思啊,测试可用!!转载自: http://www.cnblogs.com/heyiming/p/5686212.html 头尾固定中间高度自适应布局 ...
  • u012866684
  • u012866684
  • 2016年11月23日 16:27
  • 1724

Android自定义控件之仿美团下拉刷新

美团的下拉刷新分为三个状态: 第一个状态为下拉刷新状态(pull to refresh),在这个状态下是一个绿色的椭圆随着下拉的距离动态改变其大小。 第二个部分为放开刷新状态(release to...
  • nugongahou110
  • nugongahou110
  • 2015年11月01日 14:41
  • 12049

android_如何巧妙在Listview滑动时将头部固定

ListView增加头部,并且该头部在随着ListView滑动时需要固定,下拉刷新采用的是android5的SwipeRefresh。 实现方案: 在顶部增加一个跟ListView头部一模一样 在Li...
  • lwj_zeal
  • lwj_zeal
  • 2016年08月17日 10:58
  • 2463

Android实现固定头部信息,挤压动画(类似通讯录)

半年前,那时候我还是个大四的学生,每天都在找工作度过,想去北京体验一下蚁族生活,奋然离开了济南,哎...在济南我们学校还是数得着的好学校,去了北京就什么都不是了,一切的辛酸只有自己知道,那时候的我只找...
  • ElinaVampire
  • ElinaVampire
  • 2014年10月17日 09:08
  • 3674

仿美团下拉刷新动画

经常使用美团,感觉下拉刷新动画挺好玩的,抽空也做了一个,图片资源是网上获取,先看效果: 本文所实现的功能是在网上流传的下拉刷新框架(PullToRefreshView.java),然后修改而...
  • xutao3716
  • xutao3716
  • 2015年10月19日 12:46
  • 4937

android 仿美团、大众点评滑动viewpager菜单栏

android 仿美团、大众点评滑动viewpager菜单栏部分图片没有找到,就随便替代了一下,功能上面和美团类似,各位需要的可以在git上下载。...
  • u012489412
  • u012489412
  • 2016年03月09日 14:17
  • 1922
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:仿美团滑动后头部固定源码分析
举报原因:
原因补充:

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