Android知乎选项卡动态隐藏

选项卡动态隐藏.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 这种参数改一改,多试试效果,注意起始值。





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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容