Android知乎选项卡动态隐藏

作者: 达峰a | 来源:发表于2018-02-03 13:24 被阅读0次
    选项卡动态隐藏.gif

    效果呢,和知乎首页一样,可以去知乎看看;点击back键可以返回顶部。

    想法:

    • 列表上拉,选项卡隐藏,下滑出现;recycleView滚动监听(OnScrollListener)中onScrolled方法的dy参数,dy>0表示上拉,dy<0表示下滑,刚好合适。
    • 选项卡怎么隐藏呢,属性动画,移动选项卡的相对位置View.TRANSLATION_Y(Y轴方向移动肯定是_Y),View.TRANSLATION系列都是相对运动,参考系是view原本的位置。
    • 还有个问题,对于选项卡来说,它需要的显隐时机是列表滑动方向改变,而不是只监听它的滑动;上拉改下滑,下滑改上拉这2个时机才能执行动画,不能在列表同一方向持续滚动时重复调用动画。

    步骤:

    要写多少代码呢? fragmeng中一个recycleView的监听要写,一个接口要写;activity中接口实现。没了,代码不多。

    Fragment:

    public interface RvScrollListener {
        //滑动方向监听
        void scrollType(boolean direction);
        //是否滑动到顶部监听
        void inTop(boolean top,RecyclerView recyclerView);
    }
    
    private RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            if (fragmentposition != 0) {
                //如果不是第一个fragment则返回
                return;
            }
            LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
            //得到当前列表第一个完全显示的item的position
            int position = layoutManager.findFirstCompletelyVisibleItemPosition();
            if (position == 0) {
                //如果position为0表示列表正处于顶部
                mRvScrollListener.inTop(true, recyclerView);
            } else {
                mRvScrollListener.inTop(false, recyclerView);
            }
        }
    
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            //判断滑动方向,recycleView item 上拉 下滑不同动画
            if (dy > 0) {
                isUp = true;
            } else {
                isUp = false;
            }
    
            if (fragmentposition != 0) {
                return;
                //如果不是第一个fragment则返回
            }
            //过滤掉一些缓慢的滑动
            if (Math.abs(dy) > 10) {
                //滑动方向
                mRvScrollListener.scrollType(dy > 0);
            }
        }
    };
    
    • recycleView第一个监听方法:

    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {}

    这个里面就做一件事情,判断当前recycleView是否滑动到顶部,然后通过接口传递到activity中,当点击back键时,如果不在顶部,则调用方法滚动到顶部。

    • recycleView第二个监听方法:

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {}

    做2件事,一是recyleView的item做动画时,因为上拉和下滑动画不一样,代码中 isUp 就是用来区分上拉下滑的((给recycleView的item做加载动画使用))
    二是判断滑动方向,接口传递到activity中。

    Activity:

    //上拉状态
    private boolean hasup = true;
    //下滑状态
    private boolean hasdown = true;
    //是否在顶部
    private boolean inTop = true;
    //从fragment传递过来的recycleView
    private RecyclerView topRecyclerView;
    
    BlankFragment.RvScrollListener mRvScrollListener = new BlankFragment.RvScrollListener() {
    
        @Override
        public void scrollType(boolean direction) {
            //上拉
            if (direction) {
                hasdown = true;
                //连续上拉,第一次有效
                if (hasup) {
                    ObjectAnimator.ofFloat(mTablayout, View.TRANSLATION_Y, mTablayout.getTranslationY(), PixelChange.dp2px(XjwTablayoutActivity.this, 50)).setDuration(400).start();
                    hasup = false;
                }
            } else {//下滑
                hasup = true;
                //连续下滑,第一次有效
                if (hasdown) {
                    ObjectAnimator.ofFloat(mTablayout, View.TRANSLATION_Y, mTablayout.getTranslationY(), 0).setDuration(400).start();
                    hasdown = false;
                }
            }
        }
    
        @Override
        public void inTop(boolean top, RecyclerView recyclerView) {
            inTop = top;
            topRecyclerView = recyclerView;
        }
    };
    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        //点击返回键
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            //如果当前不是第一个fragmeng则显示第一个
            if (mViewPager.getCurrentItem() != 0) {
                mViewPager.setCurrentItem(0);
                return true;
            }
            //如果当前recycleView没有在顶部则返回顶部
            if (!inTop) {
                topRecyclerView.smoothScrollToPosition(0);
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }
    
    • 实现接口第一个方法:

    @Override
    public void scrollType(boolean direction) {}

    方法里用到三个boolean值 direction ,hasup, hasdown ,direction判断执行上拉动画或者下滑动画;hasup和hasdown作用是:滑动有上拉,下滑2个状态,处于一种状态时动画只执行一次;比如列表正在持续上拉,监听也会触发多次,上拉的多次触动中只执行一次动画。

    • 实现接口第二个方法:

    @Override
    public void inTop(boolean top, RecyclerView recyclerView) {}

    就一个赋值作用,用在back键的点击事件中onKeyDown。

    • back键点击

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {}

    一点需要注意:recycleView滚动到顶部,调用的是smoothScrollToPosition()方法,这个最简单,调用别的方法譬如smoothScrollBy(),还需要算距离,不过这个方法可以给插值器。

    • 属性动画
    //隐藏
    ObjectAnimator.ofFloat(mTablayout, View.TRANSLATION_Y, mTablayout.getTranslationY(), PixelChange.dp2px(XjwTablayoutActivity.this, 50)).setDuration(400).start();
    //显示
     ObjectAnimator.ofFloat(mTablayout, View.TRANSLATION_Y, mTablayout.getTranslationY(), 0).setDuration(400).start();
    

    注意2个点,一个是 View.TRANSLATION_Y 这个参数要写对,
    另外一个是动画的起始值:
    如果隐藏动画是从0dp移动到50dp,快速切换上拉下滑状态时(手指快速上下滑动)控件就会闪。所以隐藏动画中从 mTablayout.getTranslationY()的位置移动到 50 dp的位置,动态获取当前选项卡位置就好了,显示动画同理。(写50dp是因为我选项卡高度就是50dp)

    另外把recycleView的item加载动画代码给出来:(这个写在Adapter里面的,因为要在onBindViewHolder时调用)

    protected Animator[] getAnimators(View view) {  //上滑动画
        return new Animator[]{
                ObjectAnimator.ofFloat(view, View.ROTATION, 120, 0).setDuration(400)
        };
    }
    
    protected Animator[] getAnimatorsDown(View view) {  //下拉动画
        return new Animator[]{
                ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, -100, 0).setDuration(400),
                ObjectAnimator.ofFloat(view, View.SCALE_X, 0.7f, 1f).setDuration(400)
        };
    }
    
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (isUp) { //上拉
            Animator[] animators = getAnimators(holder.itemView);
            if (animators.length > 1) {
                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.playTogether(animators);
                animatorSet.start();
            } else {
                for (Animator annimator : animators) {
                    annimator.start();
                }
            }
        } else {//下拉
            Animator[] animatorsDown = getAnimatorsDown(holder.itemView);
            if (animatorsDown.length > 1) {
                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.playTogether(animatorsDown);
                animatorSet.start();
            } else {
                for (Animator annimator : animatorsDown) {
                    annimator.start();
                }
            }
        }
    }
    

    item加载动画和选项卡显隐动画差不多,你可以把 View.SCALE_X 这种参数改一改,多试试效果,注意起始值。





    对于生活理想,应该像宗教徒对待宗教一样充满虔诚与热情!

    相关文章

      网友评论

        本文标题:Android知乎选项卡动态隐藏

        本文链接:https://www.haomeiwen.com/subject/mvuszxtx.html