Android 仿今日头条首页标题栏效果

今天带来的是仿今日头条首页的联动滑动效果,废话不多说,先上效果图:
这里写图片描述

思路:
做这个我们需要实现的效果有
1、滑动内容区域,标题栏会有变化来显示当前所处的位置。
2、点击标题栏,内容区域也会随着滑动并跳到指定栏,标题栏会移动到屏幕中间。
3、当标题栏过多时,我们可以滑动标题栏找到超出屏幕的栏。

要实现上面三个问题,我采用的是标题栏用HorizontalScrollView来实现,内容区域用ViewPager 来实现,力求达到最简单的实现方式。

首先HorizontalScrollView 能够水平方向拖动,只要在里面包含一个水平方向的LinearLayout,然后LinearLayout里面包含TextView,然后把HorizontalScrollView的HorizontalScrollBarEnabled设置为false,用于隐藏它原本的水平方向的滚动条,这样就可以实现我们所说的第三点了。

再来看下第一点,要想标题栏随着ViewPager变化,也就是我们得知道ViewPager当前所处的位置,这样自然我们就想到了ViewPager的OnPageChangeListener方法:

@Override
        public void onPageSelected(int position) {
        }

其中的参数position为我们提供这个位置信息。为了让标题栏中的TextView和ViewPager中Fragement一一对应,由于TextView本身没有这个属性,所有我们可以通过重写的方式给他添加一个index位置信息。

现在主要的是第二个问题,这里涉及到两个滑动,ViewPager的滑动是自带的我们可以不用管,而HorizontalScrollView本身是可以通过手动来滑动的,但是怎么让它自动滑到我们想要的地方呢。

还好HorizontalScrollView提供了一个smoothScrollTo(int x, int y)方法,使用这个方法,我们可以将HorizontalScrollView滑动到任意位置。

问题使我们怎么确定这个位置,由于每个TextView里面的文字数目可能不同,意味着TextView的宽度各不相同,这样要怎么计算位置呢?

我们可以使用getLeft()方法获得目标TextView距离HorizontalScrollView最左边的长度,这样就不用管之前的TextView的宽度了,因为getLeft()相当于获得了它们的和,我们希望它移动到中间位置,那么getLeft()再减去(getWidth()-TextView.getWidth())/2就是需要滑动的距离,具体逻辑看图:
这里写图片描述

图中黑框是屏幕,红色矩形是Textview,这里要将Textview从A位置移动到B位置,其中的蓝色线条就是移动的距离TextView.getLeft() - (getWidth() - TextView.getWidth()) / 2。getWidth()获取的是屏幕宽度。
最后调用smoothScrollTo(int x, int y)方法就ok了,x传上面得到的值,y为0就行。

最后给TextView加上属性动画效果就好了,对于属性动画ObjectAnimator 不太了解的自己去百度下吧,这里就不介绍了。

既然问题都解决了下面贴上主要代码:

public class MyIndicator extends HorizontalScrollView implements ViewPager.OnPageChangeListener{
    private ViewPager mViewPager;
    private LinearLayout myLinearLayout;

   /** 
    我们实现了ViewPager.OnPageChangeListener接口,因为我们要监听ViewPager的滑页行为,从而去改变导航栏的状态
    所以我们也可以看到,MyIndicator持有ViewPager的引用,如果我们还想要监听ViewPager怎么办呢? 
    这里我为MyIndicator提供了一个接口,与OnPageChangeListener方法一样调用 **/

    MyOnPageChangeListener mListener;

    private interface MyOnPageChangeListener {
        void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
        void onPageSelected(int position);
        void onPageScrollStateChanged(int state);
    }
    private int oldSelected;

    //为了让TextView能点击,我为每个TextView都设置了OnClickListener,
    //就是获得目标标题栏的index,然后调用setCurrentItem()就可以了,这样就实现了点击滑动的效果,点击标题栏,Viewpager也会跟着翻页。
    private final OnClickListener mTabClickListener = new OnClickListener() {
         public void onClick(View view) {
             MyTabView tabView = (MyTabView)view;
              oldSelected = mViewPager.getCurrentItem();
              final int newSelected = tabView.index;
              setCurrentItem(newSelected);
         }
    };

    public MyIndicator(Context context) {
          super(context);
          init(context);
    }

    public MyIndicator(Context context, AttributeSet attrs) {
          super(context, attrs);
          init(context);
    }

    public MyIndicator(Context context, AttributeSet attrs, int defStyle) {
          super(context, attrs, defStyle);
          init(context);
    }

    private void init(Context mcontext){
          setHorizontalScrollBarEnabled(false);//隐藏自带的滚动条
          //添加linearLayout
          myLinearLayout = new LinearLayout(mcontext);
          myLinearLayout.setOrientation(LinearLayout.HORIZONTAL);

          addView(myLinearLayout, new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
    }

/**
     *  我们必须在调用setViewPager()之前为ViewPager设置Adapter,
        而FragmentPagerAdapter里面的getCount()方法返回了这个数目,
        如果没有设置Adapter,MyIndicator就不知道怎么绘制导航栏了,因为连标题数目都不清楚 */

     public void setViewPager(ViewPager viewPager){
           setViewPager(viewPager,0);
     }

     @SuppressWarnings("deprecation")
    public void setViewPager(ViewPager viewPager,int initPos){
           if (mViewPager == viewPager) {
                 return;
           }
           if (mViewPager != null) {
                 mViewPager.setOnPageChangeListener(null);
           }
           final PagerAdapter adapter = viewPager.getAdapter();
           if (adapter == null) {
                 throw new IllegalStateException("ViewPager does not have adapter instance.");
           }
           mViewPager = viewPager;
           viewPager.setOnPageChangeListener(this);
           notifyDataSetChanged();
           setCurrentItem(initPos);
    }

    private void notifyDataSetChanged(){
           myLinearLayout.removeAllViews();
           PagerAdapter mAdapter = mViewPager.getAdapter();
           int count = mAdapter.getCount();
           for(int i=0;i<count;i++){
                 addTab(i,mAdapter.getPageTitle(i));
           }
           requestLayout();
    }

    /**
     * 代码添加顶部的TextView
     * @param index
     * @param text
     */
    private void addTab(int index,CharSequence text) {
          MyTabView tabView = new MyTabView(getContext());
          tabView.index = index;
          tabView.setFocusable(true);
          tabView.setOnClickListener(mTabClickListener);
          tabView.setText(text);
          tabView.setTextSize(22);
          tabView.setTextColor(getContext().getResources().getColor(R.color.white));
          tabView.setPadding(20,0,20,0);
          myLinearLayout.addView(tabView);
    }

    /**
     * 被选中的动画
     * @param view
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @SuppressLint("NewApi")
    private void animation(View view) {
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1f);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1f);
        ObjectAnimator fade = ObjectAnimator.ofFloat(view, "alpha", 1f);
        AnimatorSet animSet = new AnimatorSet();
        animSet.play(scaleX).with(scaleY).with(fade);
        animSet.setDuration(500);
        animSet.start();
    }

    /**
     * 没选中的动画
     * @param view
     */
    private void animation2(View view) {
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 0.8f);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 0.8f);
        ObjectAnimator fade = ObjectAnimator.ofFloat(view, "alpha", 0.5f);
        AnimatorSet animSet = new AnimatorSet();
        animSet.play(scaleX).with(scaleY).with(fade);
        animSet.setDuration(500);
        animSet.start();
    }

    public void setCurrentItem(int item) {
          if (mViewPager == null) {
                 throw new IllegalStateException("ViewPager has not been bound.");
          }
          int mSelectedTabIndex = item;
          mViewPager.setCurrentItem(item);

          final int tabCount = myLinearLayout.getChildCount();
          for (int i = 0; i < tabCount; i++) {//遍历标题,改变选中的背景
                final View child = myLinearLayout.getChildAt(i);
                final boolean isSelected = (i == item);
                child.setSelected(isSelected);
                if (isSelected) {
                      animation(child);
                      animateToTab(item);//动画效果
                }else{
                      animation2(child);
                      child.setBackgroundColor(Color.TRANSPARENT);
                }
         }
   }

   private Runnable mTabSelector;
        private void animateToTab(final int position) {
              final View tabView = myLinearLayout.getChildAt(position);
              if (mTabSelector != null) {
                    removeCallbacks(mTabSelector);
              }
              mTabSelector = new Runnable() {
                    public void run() {
                         final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;//计算要滑动到的位置
                         smoothScrollTo(scrollPos, 0);
                         mTabSelector = null;
                   }
             };
             post(mTabSelector);    //在主线程执行动画
       }

        public void setMyOnPageChangeListener(MyOnPageChangeListener listener){
              mListener = listener;
        }

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
              if(mListener!=null) mListener.onPageScrolled(position,positionOffset,positionOffsetPixels);
        }

        @Override
        public void onPageSelected(int position) {
              setCurrentItem(position);
              if(mListener!=null) mListener.onPageSelected(position);
        }

        @Override
        public void onPageScrollStateChanged(int state) {
              if(mListener!=null) mListener.onPageScrollStateChanged(state);
        }

 }

因为里面注释都写的差不多了,这里就不再细说了。
下面是布局xml文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.zhu.myindicator.MainActivity" >

    <com.zhu.myindicator.MyIndicator
        android:id="@+id/indicator"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/red" />

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>

最后在Activity里面调用:

public class MainActivity extends FragmentActivity {
    private MyIndicator indicator;
    private ViewPager pager;
    String content[]={"热点","推荐","社会","视频","科技","汽车","趣图", "美图", "美女"};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        FragmentPagerAdapter adapter = new MyFragmentManager(getSupportFragmentManager(), content);

        pager = (ViewPager)findViewById(R.id.pager);
        indicator = (MyIndicator)findViewById(R.id.indicator);

        pager.setAdapter(adapter);
        indicator.setViewPager(pager);
    }

}

最后还有些文件没有贴出来,大家可以下载demo来看:Android 仿今日头条首页标题栏效果

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值