1. 程式人生 > >Android 仿今日頭條首頁標題欄效果

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

}