Bladeren bron

订阅管理功能部分UI

RaoMeng 9 jaren geleden
bovenliggende
commit
7bc59e53fa
61 gewijzigde bestanden met toevoegingen van 4005 en 19 verwijderingen
  1. 3 2
      WeiChat/build.gradle
  2. 2 1
      WeiChat/src/main/AndroidManifest.xml
  3. 38 0
      WeiChat/src/main/java/com/xzjmyk/pm/activity/MySwipeMenuCreator.java
  4. 60 0
      WeiChat/src/main/java/com/xzjmyk/pm/activity/adapter/MySubscriptionAdapter.java
  5. 58 0
      WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/erp/activity/SubcribeManageActivity.java
  6. 39 0
      WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/erp/adapter/SubscribeManegeVpAdapter.java
  7. 52 0
      WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/erp/fragment/BaseFragment.java
  8. 30 0
      WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/erp/fragment/SubscriptionAllFragment.java
  9. 103 0
      WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/erp/fragment/SubscriptionMyFragment.java
  10. 10 7
      WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/message/SubscriptionActivity.java
  11. 41 0
      WeiChat/src/main/java/com/xzjmyk/pm/activity/view/NoSlideViewpager.java
  12. 15 0
      WeiChat/src/main/res/anim/popup_fade_in.xml
  13. 16 0
      WeiChat/src/main/res/anim/popup_fade_out.xml
  14. BIN
      WeiChat/src/main/res/drawable-hdpi/dividing_line.png
  15. 19 0
      WeiChat/src/main/res/drawable/selector_subscribe_manage.xml
  16. 21 0
      WeiChat/src/main/res/layout/activity_subscribe_manage.xml
  17. 11 0
      WeiChat/src/main/res/layout/fragment_all_subscription.xml
  18. 13 0
      WeiChat/src/main/res/layout/fragment_my_subscription.xml
  19. 36 0
      WeiChat/src/main/res/layout/layout_my_subscribe_list.xml
  20. 44 0
      WeiChat/src/main/res/layout/pop_cancle_my_subscribe.xml
  21. 3 3
      WeiChat/src/main/res/layout/subscription.xml
  22. 11 0
      WeiChat/src/main/res/menu/menu_manage_subscribe.xml
  23. 1 1
      WeiChat/src/main/res/values/colors.xml
  24. 43 4
      WeiChat/src/main/res/values/styles.xml
  25. 2 0
      build.gradle
  26. 9 0
      library-viewpager-indicator/src/main/AndroidManifest.xml
  27. 555 0
      library-viewpager-indicator/src/main/java/com/viewpagerindicator/CirclePageIndicator.java
  28. 172 0
      library-viewpager-indicator/src/main/java/com/viewpagerindicator/IconPageIndicator.java
  29. 11 0
      library-viewpager-indicator/src/main/java/com/viewpagerindicator/IconPagerAdapter.java
  30. 182 0
      library-viewpager-indicator/src/main/java/com/viewpagerindicator/IcsLinearLayout.java
  31. 447 0
      library-viewpager-indicator/src/main/java/com/viewpagerindicator/LinePageIndicator.java
  32. 63 0
      library-viewpager-indicator/src/main/java/com/viewpagerindicator/PageIndicator.java
  33. 283 0
      library-viewpager-indicator/src/main/java/com/viewpagerindicator/TabPageIndicator.java
  34. 870 0
      library-viewpager-indicator/src/main/java/com/viewpagerindicator/TitlePageIndicator.java
  35. 402 0
      library-viewpager-indicator/src/main/java/com/viewpagerindicator/UnderlinePageIndicator.java
  36. 24 0
      library-viewpager-indicator/src/main/res/color/vpi__dark_theme.xml
  37. 26 0
      library-viewpager-indicator/src/main/res/color/vpi__light_theme.xml
  38. BIN
      library-viewpager-indicator/src/main/res/drawable-hdpi/vpi__tab_selected_focused_holo.9.png
  39. BIN
      library-viewpager-indicator/src/main/res/drawable-hdpi/vpi__tab_selected_holo.9.png
  40. BIN
      library-viewpager-indicator/src/main/res/drawable-hdpi/vpi__tab_selected_pressed_holo.9.png
  41. BIN
      library-viewpager-indicator/src/main/res/drawable-hdpi/vpi__tab_unselected_focused_holo.9.png
  42. BIN
      library-viewpager-indicator/src/main/res/drawable-hdpi/vpi__tab_unselected_holo.9.png
  43. BIN
      library-viewpager-indicator/src/main/res/drawable-hdpi/vpi__tab_unselected_pressed_holo.9.png
  44. BIN
      library-viewpager-indicator/src/main/res/drawable-mdpi/vpi__tab_selected_focused_holo.9.png
  45. BIN
      library-viewpager-indicator/src/main/res/drawable-mdpi/vpi__tab_selected_holo.9.png
  46. BIN
      library-viewpager-indicator/src/main/res/drawable-mdpi/vpi__tab_selected_pressed_holo.9.png
  47. BIN
      library-viewpager-indicator/src/main/res/drawable-mdpi/vpi__tab_unselected_focused_holo.9.png
  48. BIN
      library-viewpager-indicator/src/main/res/drawable-mdpi/vpi__tab_unselected_holo.9.png
  49. BIN
      library-viewpager-indicator/src/main/res/drawable-mdpi/vpi__tab_unselected_pressed_holo.9.png
  50. BIN
      library-viewpager-indicator/src/main/res/drawable-xhdpi/vpi__tab_selected_focused_holo.9.png
  51. BIN
      library-viewpager-indicator/src/main/res/drawable-xhdpi/vpi__tab_selected_holo.9.png
  52. BIN
      library-viewpager-indicator/src/main/res/drawable-xhdpi/vpi__tab_selected_pressed_holo.9.png
  53. BIN
      library-viewpager-indicator/src/main/res/drawable-xhdpi/vpi__tab_unselected_focused_holo.9.png
  54. BIN
      library-viewpager-indicator/src/main/res/drawable-xhdpi/vpi__tab_unselected_holo.9.png
  55. BIN
      library-viewpager-indicator/src/main/res/drawable-xhdpi/vpi__tab_unselected_pressed_holo.9.png
  56. 34 0
      library-viewpager-indicator/src/main/res/drawable/vpi__tab_indicator.xml
  57. 129 0
      library-viewpager-indicator/src/main/res/values/attrs.xml
  58. 26 0
      library-viewpager-indicator/src/main/res/values/colors.xml
  59. 53 0
      library-viewpager-indicator/src/main/res/values/defaults.xml
  60. 47 0
      library-viewpager-indicator/src/main/res/values/styles.xml
  61. 1 1
      settings.gradle

+ 3 - 2
WeiChat/build.gradle

@@ -4,7 +4,7 @@ apply plugin: 'com.getkeepsafe.dexcount'
 android {
     signingConfigs {
         config {
-            storeFile file('C:/Users/Arison/Desktop/applicationsignname[20150409]')
+            storeFile file('C:/CompanyProject/applicationsignname[20150409]')
             storePassword '13237658359'
             keyAlias 'jie-20150409'
             keyPassword '13237658359'
@@ -94,5 +94,6 @@ dependencies {
     compile 'me.gujun.android.taggroup:library:1.4@aar'
     compile 'com.umeng.analytics:analytics:latest.integration'
     compile 'com.android.support:multidex:1.0.1'
-  
+    compile project(':library-viewpager-indicator')
+    compile project(':library-swipemenu_lv')
 }

+ 2 - 1
WeiChat/src/main/AndroidManifest.xml

@@ -68,7 +68,7 @@
         android:allowBackup="true"
         android:icon="@drawable/uuu"
         android:label="@string/app_name"
-        android:theme="@style/AppTheme">
+        android:theme="@style/StyledIndicators">
         <meta-data
             android:name="UMENG_APPKEY"
             android:value="574504a167e58eec520017df" />
@@ -150,6 +150,7 @@
             android:name=".ui.message.AddContactsActivity"
             android:configChanges="orientation|keyboardHidden"
             android:windowSoftInputMode="adjustUnspecified|stateHidden" />
+        <activity android:name=".ui.erp.activity.SubcribeManageActivity"/>
         <!-- 群聊 -->
         <activity android:name=".ui.groupchat.SelectContactsActivity" />
 

+ 38 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/MySwipeMenuCreator.java

@@ -0,0 +1,38 @@
+package com.xzjmyk.pm.activity;
+
+import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
+import android.util.TypedValue;
+
+import com.baoyz.swipemenulistview.SwipeMenu;
+import com.baoyz.swipemenulistview.SwipeMenuCreator;
+import com.baoyz.swipemenulistview.SwipeMenuItem;
+
+/**
+ * Created by PROD on 2016/9/1.
+ */
+public class MySwipeMenuCreator implements SwipeMenuCreator {
+    private Context mContext;
+
+    public MySwipeMenuCreator(Context mContext) {
+        this.mContext = mContext;
+    }
+
+    @Override
+    public void create(SwipeMenu menu) {
+        SwipeMenuItem openItem = new SwipeMenuItem(mContext);
+        SwipeMenuItem deleteItem = new SwipeMenuItem(mContext);
+        deleteItem.setBackground(new ColorDrawable(mContext.getResources().getColor(R.color.red)));
+        deleteItem.setTitleColor(mContext.getResources().getColor(R.color.white));
+        deleteItem.setTitleSize(15);
+        deleteItem.setTitle("取消订阅");
+        deleteItem.setWidth(dp2px(100));
+
+        menu.addMenuItem(deleteItem);
+    }
+
+    private int dp2px(int dp) {
+        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
+                mContext.getResources().getDisplayMetrics());
+    }
+}

+ 60 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/adapter/MySubscriptionAdapter.java

@@ -0,0 +1,60 @@
+package com.xzjmyk.pm.activity.adapter;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import com.xzjmyk.pm.activity.R;
+
+import java.util.List;
+
+/**
+ * Created by PROD on 2016/9/1.
+ */
+public class MySubscriptionAdapter extends BaseAdapter {
+    private Context mContext;
+    private List<String> mStrings;
+
+    public MySubscriptionAdapter(Context mContext, List<String> mStrings) {
+        this.mContext = mContext;
+        this.mStrings = mStrings;
+    }
+
+    @Override
+    public int getCount() {
+        return mStrings.size();
+    }
+
+    @Override
+    public Object getItem(int i) {
+        return mStrings.get(i);
+    }
+
+    @Override
+    public long getItemId(int i) {
+        return i;
+    }
+
+    @Override
+    public View getView(int i, View view, ViewGroup viewGroup) {
+        ViewHolder viewHolder = null;
+        if (view == null){
+            view = View.inflate(mContext, R.layout.layout_my_subscribe_list, null);
+            viewHolder = new ViewHolder();
+//            viewHolder.textView = (TextView) view.findViewById(android.R.id.text1);
+            view.setTag(viewHolder);
+
+        }else {
+            viewHolder = (ViewHolder) view.getTag();
+        }
+
+//        viewHolder.textView.setText(mStrings.get(i));
+        return view;
+    }
+
+    class ViewHolder{
+        TextView textView;
+    }
+}

+ 58 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/erp/activity/SubcribeManageActivity.java

@@ -0,0 +1,58 @@
+package com.xzjmyk.pm.activity.ui.erp.activity;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+
+import com.viewpagerindicator.TabPageIndicator;
+import com.viewpagerindicator.UnderlinePageIndicator;
+import com.xzjmyk.pm.activity.R;
+import com.xzjmyk.pm.activity.ui.base.BaseActivity;
+import com.xzjmyk.pm.activity.ui.erp.adapter.SubscribeManegeVpAdapter;
+import com.xzjmyk.pm.activity.ui.erp.fragment.BaseFragment;
+import com.xzjmyk.pm.activity.ui.erp.fragment.SubscriptionAllFragment;
+import com.xzjmyk.pm.activity.ui.erp.fragment.SubscriptionMyFragment;
+import com.xzjmyk.pm.activity.view.NoSlideViewpager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by PROD on 2016/9/5.
+ */
+public class SubcribeManageActivity extends BaseActivity{
+    private TabPageIndicator mTabPageIndicator;
+    private UnderlinePageIndicator mUnderlinePageIndicator;
+    private SubscriptionAllFragment mSubscriptionAllFragment;
+    private SubscriptionMyFragment mSubscriptionMyFragment;
+    private List<Fragment> mSubscribeFragments;
+    private SubscribeManegeVpAdapter mSubscribeManegeVpAdapter;
+    private NoSlideViewpager mViewPager;
+    private List<String> mTitleStrings;
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_subscribe_manage);
+        getSupportActionBar().setTitle("订阅号");
+
+        mTabPageIndicator = (TabPageIndicator) findViewById(R.id.subscribe_manage_tab);
+        mUnderlinePageIndicator = (UnderlinePageIndicator) findViewById(R.id.subscribe_manage_undertab);
+        mViewPager = (NoSlideViewpager) findViewById(R.id.subscribe_manage_vp);
+        mSubscriptionAllFragment = BaseFragment.newInstance(SubscriptionAllFragment.class);
+        mSubscriptionMyFragment = BaseFragment.newInstance(SubscriptionMyFragment.class);
+
+        mSubscribeFragments = new ArrayList<>();
+        mSubscribeFragments.add(mSubscriptionMyFragment);
+        mSubscribeFragments.add(mSubscriptionAllFragment);
+
+        mTitleStrings = new ArrayList<>();
+        mTitleStrings.add("我的订阅");
+        mTitleStrings.add("全部订阅");
+        mSubscribeManegeVpAdapter = new SubscribeManegeVpAdapter(this,mSubscribeFragments,mTitleStrings,getSupportFragmentManager());
+
+        mViewPager.setAdapter(mSubscribeManegeVpAdapter);
+        mTabPageIndicator.setViewPager(mViewPager);
+        mUnderlinePageIndicator.setFades(false);
+        mUnderlinePageIndicator.setViewPager(mViewPager);
+        mTabPageIndicator.setOnPageChangeListener(mUnderlinePageIndicator);
+    }
+}

+ 39 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/erp/adapter/SubscribeManegeVpAdapter.java

@@ -0,0 +1,39 @@
+package com.xzjmyk.pm.activity.ui.erp.adapter;
+
+import android.content.Context;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+
+import java.util.List;
+
+/**
+ * Created by PROD on 2016/9/5.
+ */
+public class SubscribeManegeVpAdapter extends FragmentPagerAdapter{
+    private Context mContext;
+    private List<Fragment> mFragments;
+    private List<String> mStrings;
+
+    public SubscribeManegeVpAdapter(Context context,List<Fragment> fragments,List<String> strings,FragmentManager fm) {
+        super(fm);
+        mContext = context;
+        mFragments = fragments;
+        mStrings = strings;
+    }
+
+    @Override
+    public Fragment getItem(int position) {
+        return mFragments.get(position);
+    }
+
+    @Override
+    public int getCount() {
+        return mFragments.size();
+    }
+
+    @Override
+    public CharSequence getPageTitle(int position) {
+        return mStrings.get(position);
+    }
+}

+ 52 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/erp/fragment/BaseFragment.java

@@ -0,0 +1,52 @@
+package com.xzjmyk.pm.activity.ui.erp.fragment;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Created by PROD on 2016/8/19.
+ */
+public abstract class BaseFragment extends Fragment {
+    protected View root;
+
+    public static <T extends BaseFragment> T newInstance(Class<T> tClass){
+        if (tClass != null){
+            try {
+                T t = tClass.newInstance();
+                return t;
+            } catch (java.lang.InstantiationException e) {
+                e.printStackTrace();
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        root = inflater.inflate(getLayout(),container,false);
+        return root;
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        initViews();
+        initEvents();
+        initDatas();
+    }
+
+    protected abstract int getLayout();
+
+    protected abstract void initViews();
+
+    protected abstract void initEvents();
+
+    protected abstract void initDatas();
+}

+ 30 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/erp/fragment/SubscriptionAllFragment.java

@@ -0,0 +1,30 @@
+package com.xzjmyk.pm.activity.ui.erp.fragment;
+
+import com.handmark.pulltorefresh.library.PullToRefreshListView;
+import com.xzjmyk.pm.activity.R;
+
+/**
+ * Created by PROD on 2016/9/5.
+ */
+public class SubscriptionAllFragment extends BaseFragment {
+    private PullToRefreshListView mPullToRefreshListView;
+    @Override
+    protected int getLayout() {
+        return R.layout.fragment_all_subscription;
+    }
+
+    @Override
+    protected void initViews() {
+        mPullToRefreshListView = (PullToRefreshListView) root.findViewById(R.id.subscription_all_ptlv);
+    }
+
+    @Override
+    protected void initEvents() {
+
+    }
+
+    @Override
+    protected void initDatas() {
+
+    }
+}

+ 103 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/erp/fragment/SubscriptionMyFragment.java

@@ -0,0 +1,103 @@
+package com.xzjmyk.pm.activity.ui.erp.fragment;
+
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import com.baoyz.swipemenulistview.SwipeMenu;
+import com.baoyz.swipemenulistview.SwipeMenuListView;
+import com.xzjmyk.pm.activity.MySwipeMenuCreator;
+import com.xzjmyk.pm.activity.R;
+import com.xzjmyk.pm.activity.adapter.MySubscriptionAdapter;
+import com.xzjmyk.pm.activity.util.DisplayUtil;
+import com.xzjmyk.pm.activity.view.crouton.Crouton;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by PROD on 2016/9/5.
+ */
+public class SubscriptionMyFragment extends BaseFragment {
+    private SwipeMenuListView mSwipeMenuListView;
+    private List<String> mStrings = new ArrayList<>();
+    private MySwipeMenuCreator mMySwipeMenuCreator;
+    private MySubscriptionAdapter mMySubscriptionAdapter;
+    private PopupWindow mCancleWindow;
+    @Override
+    protected int getLayout() {
+        return R.layout.fragment_my_subscription;
+    }
+
+    @Override
+    protected void initViews() {
+        for (int i = 0; i < 20; i++) {
+            mStrings.add("this is item "+i);
+        }
+
+        mSwipeMenuListView = (SwipeMenuListView) root.findViewById(R.id.my_subscripton_smlv);
+        mMySubscriptionAdapter = new MySubscriptionAdapter(getActivity(),mStrings);
+        mMySwipeMenuCreator = new MySwipeMenuCreator(getActivity());
+
+    }
+
+    @Override
+    protected void initEvents() {
+        mSwipeMenuListView.setAdapter(mMySubscriptionAdapter);
+        mSwipeMenuListView.setMenuCreator(mMySwipeMenuCreator);
+        mSwipeMenuListView.setSwipeDirection(SwipeMenuListView.DIRECTION_LEFT);
+        mSwipeMenuListView.setOnMenuItemClickListener(new SwipeMenuListView.OnMenuItemClickListener() {
+            @Override
+            public boolean onMenuItemClick(int position, SwipeMenu menu, int index) {
+                View cancleView = View.inflate(getActivity(),R.layout.pop_cancle_my_subscribe,null);
+                TextView cancleTextView = (TextView) cancleView.findViewById(R.id.cancel_subscribe_cancle_tv);
+                TextView contineTextView = (TextView) cancleView.findViewById(R.id.cancel_subscribe_contine_tv);
+
+                cancleTextView.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        Crouton.makeText(getActivity(),"取消成功", 0xff99cc00, 1500).show();
+                        closeWarningPopupWindow();
+                    }
+                });
+
+                contineTextView.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        closeWarningPopupWindow();
+                    }
+                });
+
+                mCancleWindow = new PopupWindow(cancleView, LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT,true);
+                mCancleWindow.setAnimationStyle(R.style.MenuAnimationFade);
+                mCancleWindow.showAtLocation(mSwipeMenuListView, Gravity.BOTTOM,0,0);
+                DisplayUtil.backgroundAlpha(getActivity(), 0.5f);
+
+                mCancleWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
+                    @Override
+                    public void onDismiss() {
+                        closeWarningPopupWindow();
+                    }
+                });
+                return false;
+            }
+        });
+    }
+
+    @Override
+    protected void initDatas() {
+
+    }
+
+    private void closeWarningPopupWindow() {
+        if (mCancleWindow != null) {
+            mCancleWindow.dismiss();
+            mCancleWindow = null;
+            DisplayUtil.backgroundAlpha(getActivity(), 1f);
+
+        }
+
+    }
+}

+ 10 - 7
WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/message/SubscriptionActivity.java

@@ -1,8 +1,8 @@
 package com.xzjmyk.pm.activity.ui.message;
 
 import android.app.AlertDialog;
-import android.content.Context;
 import android.content.DialogInterface;
+import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -11,16 +11,12 @@ import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.WindowManager;
 import android.widget.BaseAdapter;
-import android.widget.Button;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.PopupWindow;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.alibaba.fastjson.JSON;
 import com.handmark.pulltorefresh.library.PullToRefreshBase;
@@ -30,6 +26,7 @@ import com.xzjmyk.pm.activity.bean.message.SortSubsrciprion;
 import com.xzjmyk.pm.activity.bean.message.SubscriptionMessage;
 import com.xzjmyk.pm.activity.db.dao.SubscriptionDao;
 import com.xzjmyk.pm.activity.ui.base.BaseActivity;
+import com.xzjmyk.pm.activity.ui.erp.activity.SubcribeManageActivity;
 import com.xzjmyk.pm.activity.ui.erp.net.ViewUtil;
 import com.xzjmyk.pm.activity.ui.erp.util.CommonUtil;
 import com.xzjmyk.pm.activity.ui.erp.util.Constants;
@@ -115,14 +112,14 @@ public class SubscriptionActivity extends BaseActivity {
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
-        getMenuInflater().inflate(R.menu.menu_subscribe, menu);
+        getMenuInflater().inflate(R.menu.menu_manage_subscribe, menu);
         return super.onCreateOptionsMenu(menu);
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
-            case R.id.subscribe_add:
+            /*case R.id.subscribe_add:
                 WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
 
                 View view = View.inflate(SubscriptionActivity.this, R.layout.popupwindow_subscribe_menu, null);
@@ -157,6 +154,12 @@ public class SubscriptionActivity extends BaseActivity {
                     }
                 });
 
+                break;*/
+
+            case R.id.subscribe_manage:
+                Intent intent = new Intent();
+                intent.setClass(this,SubcribeManageActivity.class);
+                startActivity(intent);
                 break;
             case android.R.id.home:
                 onBackPressed();

+ 41 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/view/NoSlideViewpager.java

@@ -0,0 +1,41 @@
+package com.xzjmyk.pm.activity.view;
+
+import android.content.Context;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+/**
+ * Created by PROD on 2016/9/5.
+ */
+public class NoSlideViewpager extends ViewPager {
+    private boolean noSlide = true;
+
+    public NoSlideViewpager(Context context) {
+        super(context);
+    }
+
+    public NoSlideViewpager(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (noSlide){
+            return false;
+        }else {
+            return super.onTouchEvent(ev);
+
+        }
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (noSlide){
+            return false;
+        }else {
+            return super.onInterceptTouchEvent(ev);
+
+        }
+    }
+}

+ 15 - 0
WeiChat/src/main/res/anim/popup_fade_in.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <alpha
+        android:duration="500"
+        android:fromAlpha="0.0"
+        android:toAlpha="1.0" />
+    <translate
+        android:duration="500"
+        android:fromXDelta="0"
+        android:fromYDelta="100%"
+        android:toXDelta="0"
+        android:toYDelta="0" />
+
+</set>

+ 16 - 0
WeiChat/src/main/res/anim/popup_fade_out.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <alpha
+        android:duration="500"
+        android:fromAlpha="1.0"
+        android:toAlpha="0.0" />
+
+    <translate
+        android:duration="500"
+        android:fromXDelta="0"
+        android:fromYDelta="0"
+        android:toXDelta="0"
+        android:toYDelta="100%" />
+
+</set>

BIN
WeiChat/src/main/res/drawable-hdpi/dividing_line.png


+ 19 - 0
WeiChat/src/main/res/drawable/selector_subscribe_manage.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Non focused states -->
+    <item android:drawable="@drawable/vpi__tab_unselected_holo" android:state_focused="false" android:state_pressed="false" android:state_selected="false" />
+    <item android:drawable="@drawable/vpi__tab_selected_holo" android:state_focused="false" android:state_pressed="false" android:state_selected="true" />
+
+    <!-- Focused states -->
+    <item android:drawable="@drawable/vpi__tab_unselected_focused_holo" android:state_focused="true" android:state_pressed="false" android:state_selected="false" />
+    <item android:drawable="@drawable/vpi__tab_selected_focused_holo" android:state_focused="true" android:state_pressed="false" android:state_selected="true" />
+
+    <!-- Pressed -->
+    <!--    Non focused states -->
+    <item android:drawable="@drawable/vpi__tab_unselected_pressed_holo" android:state_focused="false" android:state_pressed="true" android:state_selected="false" />
+    <item android:drawable="@drawable/vpi__tab_selected_pressed_holo" android:state_focused="false" android:state_pressed="true" android:state_selected="true" />
+
+    <!--    Focused states -->
+    <item android:drawable="@drawable/vpi__tab_unselected_pressed_holo" android:state_focused="true" android:state_pressed="true" android:state_selected="false" />
+    <item android:drawable="@drawable/vpi__tab_selected_pressed_holo" android:state_focused="true" android:state_pressed="true" android:state_selected="true" />
+</selector>

+ 21 - 0
WeiChat/src/main/res/layout/activity_subscribe_manage.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<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">
+
+    <com.viewpagerindicator.TabPageIndicator
+        android:id="@+id/subscribe_manage_tab"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+    <com.viewpagerindicator.UnderlinePageIndicator
+        android:id="@+id/subscribe_manage_undertab"
+        android:layout_width="match_parent"
+        android:layout_height="3dp"/>
+
+    <com.xzjmyk.pm.activity.view.NoSlideViewpager
+        android:id="@+id/subscribe_manage_vp"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</LinearLayout>

+ 11 - 0
WeiChat/src/main/res/layout/fragment_all_subscription.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <com.handmark.pulltorefresh.library.PullToRefreshListView
+        android:id="@+id/subscription_all_ptlv"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</LinearLayout>

+ 13 - 0
WeiChat/src/main/res/layout/fragment_my_subscription.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <com.baoyz.swipemenulistview.SwipeMenuListView
+        android:id="@+id/my_subscripton_smlv"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:divider="@color/gray_light"
+        android:dividerHeight="1dp"/>
+</LinearLayout>

+ 36 - 0
WeiChat/src/main/res/layout/layout_my_subscribe_list.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="80dp"
+    android:padding="10dp"
+    android:gravity="center_vertical">
+
+    <ImageView
+        android:id="@+id/my_subscribe_iv"
+        android:layout_width="60dp"
+        android:layout_height="60dp"
+        android:src="@drawable/f_static_000"
+        android:scaleType="fitXY"
+        />
+
+    <TextView
+        android:id="@+id/my_subscribe_title_tv"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_toRightOf="@id/my_subscribe_iv"
+        android:layout_marginLeft="10dp"
+        android:textSize="22sp"
+        android:text="xxxxx"/>
+
+    <TextView
+        android:id="@+id/my_subscribe_content_tv"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_toRightOf="@id/my_subscribe_iv"
+        android:layout_alignParentBottom="true"
+        android:layout_marginLeft="10dp"
+        android:layout_marginBottom="5dp"
+        android:textSize="16sp"
+        android:text="YYYYYYYYYYY"/>
+
+</RelativeLayout>

+ 44 - 0
WeiChat/src/main/res/layout/pop_cancle_my_subscribe.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="10dp"
+    android:orientation="vertical"
+    android:background="@color/white">
+
+    <LinearLayout
+        android:id="@+id/cancle_subscribe_warning_ll"
+        android:layout_width="match_parent"
+        android:layout_height="60dp"
+        android:orientation="vertical">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="60dp"
+            android:layout_gravity="center_horizontal"
+            android:gravity="center"
+            android:text="取消订阅后将不再收到其推送消息"
+            android:textColor="@color/gray"
+            android:drawableLeft="@drawable/ic_warning"
+            android:drawablePadding="10dp"
+            />
+    </LinearLayout>
+
+    <View
+        style="@style/app_line_gray_1dp"/>
+    <TextView
+        android:id="@+id/cancel_subscribe_cancle_tv"
+        android:layout_width="match_parent"
+        android:layout_height="40dp"
+        android:text="不再关注"
+        android:gravity="center"
+        android:textColor="@color/red"/>
+    <View
+        style="@style/app_line_gray_1dp"/>
+    <TextView
+        android:id="@+id/cancel_subscribe_contine_tv"
+        android:layout_width="match_parent"
+        android:layout_height="40dp"
+        android:text="继续关注"
+        android:gravity="center"
+        />
+</LinearLayout>

+ 3 - 3
WeiChat/src/main/res/layout/subscription.xml

@@ -10,9 +10,9 @@
         android:layout_height="wrap_content"
         android:layout_marginLeft="10dp"
         android:layout_marginRight="10dp"
-        android:background="@color/white"
+        android:background="@color/transparent"
         android:gravity="center_vertical"
-        android:paddingLeft="2dp" />
+        android:paddingLeft="5dp" />
 
     <com.xzjmyk.pm.activity.view.MyListView
         android:id="@+id/listview"
@@ -23,7 +23,7 @@
         android:layout_marginRight="10dp"
         android:background="@color/white"
         android:divider="@color/item_line"
-        android:paddingTop="5dp"
+        android:layout_marginTop="4dp"
         android:scrollbarSize="0dp"
         android:scrollbars="none|vertical" />
 </LinearLayout>

+ 11 - 0
WeiChat/src/main/res/menu/menu_manage_subscribe.xml

@@ -0,0 +1,11 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:context="com.xzjmyk.pm.activity.MainActivity">
+
+    <item
+        android:id="@+id/subscribe_manage"
+        android:title="管理"
+        app:showAsAction="ifRoom" />
+
+</menu>

+ 1 - 1
WeiChat/src/main/res/values/colors.xml

@@ -2,7 +2,7 @@
 <resources>
    <color name="crm_title_task">#91B43D</color>
     <!--by gongpengming-->
-    <color name="subscrip_text">#60a0a0a0</color>
+    <color name="subscrip_text">#809b9797</color>
     <color name="item_color1">#30a0a0a0</color>
     <color name="pop_bg">#aeaeae</color>
     <color name="item_color2">@color/white</color>

+ 43 - 4
WeiChat/src/main/res/values/styles.xml

@@ -25,10 +25,6 @@
     </style>
 
 
-    
-    
-    
-
     <!-- Application theme. -->
     <style name="AppTheme" parent="AppBaseTheme">
         <!-- All customizations that are NOT specific to a particular API-level can go here. -->
@@ -807,4 +803,47 @@
         <item name="android:layout_marginLeft">10dp</item>
         <item name="android:src">@drawable/ic_flag</item>
     </style>
+
+    <!--TabPageIndicatorStyle-->
+    <style name="StyledIndicators" parent="@style/AppTheme">
+        <item name="vpiTabPageIndicatorStyle">@style/CustomTabPageIndicator</item>
+        <item name="vpiUnderlinePageIndicatorStyle">@style/CustomUnderlinePageIndicator</item>
+    </style>
+
+    <style name="CustomTabPageIndicator" parent="Widget.TabPageIndicator">
+        <!-- 点击时在不同状态下显示的背景色 -->
+        <!--<item name="android:background">@drawable/vpi__tab_indicator</item>-->
+        <item name="android:background">#00000000</item>
+        <!-- 分割线图片设置 -->
+        <item name="android:divider">@drawable/dividing_line</item>
+        <!-- 设置分割线的显示位置,或是不显示 -->
+        <item name="android:showDividers">none</item>
+
+        <item name="android:textAppearance">@style/CustomTabPageIndicator.Text</item>
+        <!--<item name="android:textColor">@drawable/selector_subscribe_manage</item>-->
+        <item name="android:textSize">16sp</item>
+        <item name="android:dividerPadding">10dp</item>
+        <item name="android:paddingLeft">8dp</item>
+        <item name="android:paddingRight">8dp</item>
+        <item name="android:fadingEdgeLength">10dp</item>
+        <item name="android:fadingEdge">horizontal</item>
+    </style>
+
+    <style name="CustomTabPageIndicator.Text" parent="android:TextAppearance.Medium">
+        <item name="android:typeface">monospace</item>
+    </style>
+
+    <style name="CustomUnderlinePageIndicator">
+        <item name="selectedColor">@color/orange</item>
+        <item name="android:background">#FFCCCCCC</item>
+        <!--<item name="fadeLength">1000</item>
+        <item name="fadeDelay">1000</item>-->
+    </style>
+
+    <!-- PopupWindow窗口动画 -->
+    <style name="MenuAnimationFade">
+        <item name="android:windowEnterAnimation">@anim/popup_fade_in</item>
+        <item name="android:windowExitAnimation">@anim/popup_fade_out</item>
+
+    </style>
 </resources>

+ 2 - 0
build.gradle

@@ -3,10 +3,12 @@
 buildscript {
     repositories {
         jcenter()
+        maven { url "https://jcenter.bintray.com" }
     }
     dependencies {
         classpath 'com.android.tools.build:gradle:2.1.0'
         classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
+        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
     }
 }
 

+ 9 - 0
library-viewpager-indicator/src/main/AndroidManifest.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.viewpagerindicator"
+    android:versionCode="65"
+    android:versionName="2.4.1">
+
+    <uses-sdk android:minSdkVersion="4" />
+</manifest>

+ 555 - 0
library-viewpager-indicator/src/main/java/com/viewpagerindicator/CirclePageIndicator.java

@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2011 Patrik Akerfeldt
+ * Copyright (C) 2011 Jake Wharton
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.viewpagerindicator;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ViewConfigurationCompat;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+import static android.graphics.Paint.ANTI_ALIAS_FLAG;
+import static android.widget.LinearLayout.HORIZONTAL;
+import static android.widget.LinearLayout.VERTICAL;
+
+/**
+ * Draws circles (one for each view). The current view position is filled and
+ * others are only stroked.
+ */
+public class CirclePageIndicator extends View implements PageIndicator {
+    private static final int INVALID_POINTER = -1;
+
+    private float mRadius;
+    private final Paint mPaintPageFill = new Paint(ANTI_ALIAS_FLAG);
+    private final Paint mPaintStroke = new Paint(ANTI_ALIAS_FLAG);
+    private final Paint mPaintFill = new Paint(ANTI_ALIAS_FLAG);
+    private ViewPager mViewPager;
+    private ViewPager.OnPageChangeListener mListener;
+    private int mCurrentPage;
+    private int mSnapPage;
+    private float mPageOffset;
+    private int mScrollState;
+    private int mOrientation;
+    private boolean mCentered;
+    private boolean mSnap;
+
+    private int mTouchSlop;
+    private float mLastMotionX = -1;
+    private int mActivePointerId = INVALID_POINTER;
+    private boolean mIsDragging;
+
+
+    public CirclePageIndicator(Context context) {
+        this(context, null);
+    }
+
+    public CirclePageIndicator(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.vpiCirclePageIndicatorStyle);
+    }
+
+    public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        if (isInEditMode()) return;
+
+        //Load defaults from resources
+        final Resources res = getResources();
+        final int defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color);
+        final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color);
+        final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation);
+        final int defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color);
+        final float defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width);
+        final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius);
+        final boolean defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered);
+        final boolean defaultSnap = res.getBoolean(R.bool.default_circle_indicator_snap);
+
+        //Retrieve styles attributes
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, 0);
+
+        mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered);
+        mOrientation = a.getInt(R.styleable.CirclePageIndicator_android_orientation, defaultOrientation);
+        mPaintPageFill.setStyle(Style.FILL);
+        mPaintPageFill.setColor(a.getColor(R.styleable.CirclePageIndicator_pageColor, defaultPageColor));
+        mPaintStroke.setStyle(Style.STROKE);
+        mPaintStroke.setColor(a.getColor(R.styleable.CirclePageIndicator_strokeColor, defaultStrokeColor));
+        mPaintStroke.setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth));
+        mPaintFill.setStyle(Style.FILL);
+        mPaintFill.setColor(a.getColor(R.styleable.CirclePageIndicator_fillColor, defaultFillColor));
+        mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius);
+        mSnap = a.getBoolean(R.styleable.CirclePageIndicator_snap, defaultSnap);
+
+        Drawable background = a.getDrawable(R.styleable.CirclePageIndicator_android_background);
+        if (background != null) {
+          setBackgroundDrawable(background);
+        }
+
+        a.recycle();
+
+        final ViewConfiguration configuration = ViewConfiguration.get(context);
+        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
+    }
+
+
+    public void setCentered(boolean centered) {
+        mCentered = centered;
+        invalidate();
+    }
+
+    public boolean isCentered() {
+        return mCentered;
+    }
+
+    public void setPageColor(int pageColor) {
+        mPaintPageFill.setColor(pageColor);
+        invalidate();
+    }
+
+    public int getPageColor() {
+        return mPaintPageFill.getColor();
+    }
+
+    public void setFillColor(int fillColor) {
+        mPaintFill.setColor(fillColor);
+        invalidate();
+    }
+
+    public int getFillColor() {
+        return mPaintFill.getColor();
+    }
+
+    public void setOrientation(int orientation) {
+        switch (orientation) {
+            case HORIZONTAL:
+            case VERTICAL:
+                mOrientation = orientation;
+                requestLayout();
+                break;
+
+            default:
+                throw new IllegalArgumentException("Orientation must be either HORIZONTAL or VERTICAL.");
+        }
+    }
+
+    public int getOrientation() {
+        return mOrientation;
+    }
+
+    public void setStrokeColor(int strokeColor) {
+        mPaintStroke.setColor(strokeColor);
+        invalidate();
+    }
+
+    public int getStrokeColor() {
+        return mPaintStroke.getColor();
+    }
+
+    public void setStrokeWidth(float strokeWidth) {
+        mPaintStroke.setStrokeWidth(strokeWidth);
+        invalidate();
+    }
+
+    public float getStrokeWidth() {
+        return mPaintStroke.getStrokeWidth();
+    }
+
+    public void setRadius(float radius) {
+        mRadius = radius;
+        invalidate();
+    }
+
+    public float getRadius() {
+        return mRadius;
+    }
+
+    public void setSnap(boolean snap) {
+        mSnap = snap;
+        invalidate();
+    }
+
+    public boolean isSnap() {
+        return mSnap;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (mViewPager == null) {
+            return;
+        }
+        final int count = mViewPager.getAdapter().getCount();
+        if (count == 0) {
+            return;
+        }
+
+        if (mCurrentPage >= count) {
+            setCurrentItem(count - 1);
+            return;
+        }
+
+        int longSize;
+        int longPaddingBefore;
+        int longPaddingAfter;
+        int shortPaddingBefore;
+        if (mOrientation == HORIZONTAL) {
+            longSize = getWidth();
+            longPaddingBefore = getPaddingLeft();
+            longPaddingAfter = getPaddingRight();
+            shortPaddingBefore = getPaddingTop();
+        } else {
+            longSize = getHeight();
+            longPaddingBefore = getPaddingTop();
+            longPaddingAfter = getPaddingBottom();
+            shortPaddingBefore = getPaddingLeft();
+        }
+
+        final float threeRadius = mRadius * 3;
+        final float shortOffset = shortPaddingBefore + mRadius;
+        float longOffset = longPaddingBefore + mRadius;
+        if (mCentered) {
+            longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f) - ((count * threeRadius) / 2.0f);
+        }
+
+        float dX;
+        float dY;
+
+        float pageFillRadius = mRadius;
+        if (mPaintStroke.getStrokeWidth() > 0) {
+            pageFillRadius -= mPaintStroke.getStrokeWidth() / 2.0f;
+        }
+
+        //Draw stroked circles
+        for (int iLoop = 0; iLoop < count; iLoop++) {
+            float drawLong = longOffset + (iLoop * threeRadius);
+            if (mOrientation == HORIZONTAL) {
+                dX = drawLong;
+                dY = shortOffset;
+            } else {
+                dX = shortOffset;
+                dY = drawLong;
+            }
+            // Only paint fill if not completely transparent
+            if (mPaintPageFill.getAlpha() > 0) {
+                canvas.drawCircle(dX, dY, pageFillRadius, mPaintPageFill);
+            }
+
+            // Only paint stroke if a stroke width was non-zero
+            if (pageFillRadius != mRadius) {
+                canvas.drawCircle(dX, dY, mRadius, mPaintStroke);
+            }
+        }
+
+        //Draw the filled circle according to the current scroll
+        float cx = (mSnap ? mSnapPage : mCurrentPage) * threeRadius;
+        if (!mSnap) {
+            cx += mPageOffset * threeRadius;
+        }
+        if (mOrientation == HORIZONTAL) {
+            dX = longOffset + cx;
+            dY = shortOffset;
+        } else {
+            dX = shortOffset;
+            dY = longOffset + cx;
+        }
+        canvas.drawCircle(dX, dY, mRadius, mPaintFill);
+    }
+
+    public boolean onTouchEvent(android.view.MotionEvent ev) {
+        if (super.onTouchEvent(ev)) {
+            return true;
+        }
+        if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) {
+            return false;
+        }
+
+        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
+                mLastMotionX = ev.getX();
+                break;
+
+            case MotionEvent.ACTION_MOVE: {
+                final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
+                final float x = MotionEventCompat.getX(ev, activePointerIndex);
+                final float deltaX = x - mLastMotionX;
+
+                if (!mIsDragging) {
+                    if (Math.abs(deltaX) > mTouchSlop) {
+                        mIsDragging = true;
+                    }
+                }
+
+                if (mIsDragging) {
+                    mLastMotionX = x;
+                    if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {
+                        mViewPager.fakeDragBy(deltaX);
+                    }
+                }
+
+                break;
+            }
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                if (!mIsDragging) {
+                    final int count = mViewPager.getAdapter().getCount();
+                    final int width = getWidth();
+                    final float halfWidth = width / 2f;
+                    final float sixthWidth = width / 6f;
+
+                    if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) {
+                        if (action != MotionEvent.ACTION_CANCEL) {
+                            mViewPager.setCurrentItem(mCurrentPage - 1);
+                        }
+                        return true;
+                    } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) {
+                        if (action != MotionEvent.ACTION_CANCEL) {
+                            mViewPager.setCurrentItem(mCurrentPage + 1);
+                        }
+                        return true;
+                    }
+                }
+
+                mIsDragging = false;
+                mActivePointerId = INVALID_POINTER;
+                if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();
+                break;
+
+            case MotionEventCompat.ACTION_POINTER_DOWN: {
+                final int index = MotionEventCompat.getActionIndex(ev);
+                mLastMotionX = MotionEventCompat.getX(ev, index);
+                mActivePointerId = MotionEventCompat.getPointerId(ev, index);
+                break;
+            }
+
+            case MotionEventCompat.ACTION_POINTER_UP:
+                final int pointerIndex = MotionEventCompat.getActionIndex(ev);
+                final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
+                if (pointerId == mActivePointerId) {
+                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+                    mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
+                }
+                mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId));
+                break;
+        }
+
+        return true;
+    }
+
+    @Override
+    public void setViewPager(ViewPager view) {
+        if (mViewPager == view) {
+            return;
+        }
+        if (mViewPager != null) {
+            mViewPager.setOnPageChangeListener(null);
+        }
+        if (view.getAdapter() == null) {
+            throw new IllegalStateException("ViewPager does not have adapter instance.");
+        }
+        mViewPager = view;
+        mViewPager.setOnPageChangeListener(this);
+        invalidate();
+    }
+
+    @Override
+    public void setViewPager(ViewPager view, int initialPosition) {
+        setViewPager(view);
+        setCurrentItem(initialPosition);
+    }
+
+    @Override
+    public void setCurrentItem(int item) {
+        if (mViewPager == null) {
+            throw new IllegalStateException("ViewPager has not been bound.");
+        }
+        mViewPager.setCurrentItem(item);
+        mCurrentPage = item;
+        invalidate();
+    }
+
+    @Override
+    public void notifyDataSetChanged() {
+        invalidate();
+    }
+
+    @Override
+    public void onPageScrollStateChanged(int state) {
+        mScrollState = state;
+
+        if (mListener != null) {
+            mListener.onPageScrollStateChanged(state);
+        }
+    }
+
+    @Override
+    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+        mCurrentPage = position;
+        mPageOffset = positionOffset;
+        invalidate();
+
+        if (mListener != null) {
+            mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
+        }
+    }
+
+    @Override
+    public void onPageSelected(int position) {
+        if (mSnap || mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+            mCurrentPage = position;
+            mSnapPage = position;
+            invalidate();
+        }
+
+        if (mListener != null) {
+            mListener.onPageSelected(position);
+        }
+    }
+
+    @Override
+    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+        mListener = listener;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see android.view.View#onMeasure(int, int)
+     */
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mOrientation == HORIZONTAL) {
+            setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec));
+        } else {
+            setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec));
+        }
+    }
+
+    /**
+     * Determines the width of this view
+     *
+     * @param measureSpec
+     *            A measureSpec packed into an int
+     * @return The width of the view, honoring constraints from measureSpec
+     */
+    private int measureLong(int measureSpec) {
+        int result;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize = MeasureSpec.getSize(measureSpec);
+
+        if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) {
+            //We were told how big to be
+            result = specSize;
+        } else {
+            //Calculate the width according the views count
+            final int count = mViewPager.getAdapter().getCount();
+            result = (int)(getPaddingLeft() + getPaddingRight()
+                    + (count * 2 * mRadius) + (count - 1) * mRadius + 1);
+            //Respect AT_MOST value if that was what is called for by measureSpec
+            if (specMode == MeasureSpec.AT_MOST) {
+                result = Math.min(result, specSize);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Determines the height of this view
+     *
+     * @param measureSpec
+     *            A measureSpec packed into an int
+     * @return The height of the view, honoring constraints from measureSpec
+     */
+    private int measureShort(int measureSpec) {
+        int result;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize = MeasureSpec.getSize(measureSpec);
+
+        if (specMode == MeasureSpec.EXACTLY) {
+            //We were told how big to be
+            result = specSize;
+        } else {
+            //Measure the height
+            result = (int)(2 * mRadius + getPaddingTop() + getPaddingBottom() + 1);
+            //Respect AT_MOST value if that was what is called for by measureSpec
+            if (specMode == MeasureSpec.AT_MOST) {
+                result = Math.min(result, specSize);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        SavedState savedState = (SavedState)state;
+        super.onRestoreInstanceState(savedState.getSuperState());
+        mCurrentPage = savedState.currentPage;
+        mSnapPage = savedState.currentPage;
+        requestLayout();
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+        SavedState savedState = new SavedState(superState);
+        savedState.currentPage = mCurrentPage;
+        return savedState;
+    }
+
+    static class SavedState extends BaseSavedState {
+        int currentPage;
+
+        public SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        private SavedState(Parcel in) {
+            super(in);
+            currentPage = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(currentPage);
+        }
+
+        @SuppressWarnings("UnusedDeclaration")
+        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+}

+ 172 - 0
library-viewpager-indicator/src/main/java/com/viewpagerindicator/IconPageIndicator.java

@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2012 Jake Wharton
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.viewpagerindicator;
+
+import android.content.Context;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.HorizontalScrollView;
+import android.widget.ImageView;
+
+import static android.view.ViewGroup.LayoutParams.FILL_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+/**
+ * This widget implements the dynamic action bar tab behavior that can change
+ * across different configurations or circumstances.
+ */
+public class IconPageIndicator extends HorizontalScrollView implements PageIndicator {
+    private final IcsLinearLayout mIconsLayout;
+
+    private ViewPager mViewPager;
+    private OnPageChangeListener mListener;
+    private Runnable mIconSelector;
+    private int mSelectedIndex;
+
+    public IconPageIndicator(Context context) {
+        this(context, null);
+    }
+
+    public IconPageIndicator(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setHorizontalScrollBarEnabled(false);
+
+        mIconsLayout = new IcsLinearLayout(context, R.attr.vpiIconPageIndicatorStyle);
+        addView(mIconsLayout, new LayoutParams(WRAP_CONTENT, FILL_PARENT, Gravity.CENTER));
+    }
+
+    private void animateToIcon(final int position) {
+        final View iconView = mIconsLayout.getChildAt(position);
+        if (mIconSelector != null) {
+            removeCallbacks(mIconSelector);
+        }
+        mIconSelector = new Runnable() {
+            public void run() {
+                final int scrollPos = iconView.getLeft() - (getWidth() - iconView.getWidth()) / 2;
+                smoothScrollTo(scrollPos, 0);
+                mIconSelector = null;
+            }
+        };
+        post(mIconSelector);
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mIconSelector != null) {
+            // Re-post the selector we saved
+            post(mIconSelector);
+        }
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mIconSelector != null) {
+            removeCallbacks(mIconSelector);
+        }
+    }
+
+    @Override
+    public void onPageScrollStateChanged(int arg0) {
+        if (mListener != null) {
+            mListener.onPageScrollStateChanged(arg0);
+        }
+    }
+
+    @Override
+    public void onPageScrolled(int arg0, float arg1, int arg2) {
+        if (mListener != null) {
+            mListener.onPageScrolled(arg0, arg1, arg2);
+        }
+    }
+
+    @Override
+    public void onPageSelected(int arg0) {
+        setCurrentItem(arg0);
+        if (mListener != null) {
+            mListener.onPageSelected(arg0);
+        }
+    }
+
+    @Override
+    public void setViewPager(ViewPager view) {
+        if (mViewPager == view) {
+            return;
+        }
+        if (mViewPager != null) {
+            mViewPager.setOnPageChangeListener(null);
+        }
+        PagerAdapter adapter = view.getAdapter();
+        if (adapter == null) {
+            throw new IllegalStateException("ViewPager does not have adapter instance.");
+        }
+        mViewPager = view;
+        view.setOnPageChangeListener(this);
+        notifyDataSetChanged();
+    }
+
+    public void notifyDataSetChanged() {
+        mIconsLayout.removeAllViews();
+        IconPagerAdapter iconAdapter = (IconPagerAdapter) mViewPager.getAdapter();
+        int count = iconAdapter.getCount();
+        for (int i = 0; i < count; i++) {
+            ImageView view = new ImageView(getContext(), null, R.attr.vpiIconPageIndicatorStyle);
+            view.setImageResource(iconAdapter.getIconResId(i));
+            mIconsLayout.addView(view);
+        }
+        if (mSelectedIndex > count) {
+            mSelectedIndex = count - 1;
+        }
+        setCurrentItem(mSelectedIndex);
+        requestLayout();
+    }
+
+    @Override
+    public void setViewPager(ViewPager view, int initialPosition) {
+        setViewPager(view);
+        setCurrentItem(initialPosition);
+    }
+
+    @Override
+    public void setCurrentItem(int item) {
+        if (mViewPager == null) {
+            throw new IllegalStateException("ViewPager has not been bound.");
+        }
+        mSelectedIndex = item;
+        mViewPager.setCurrentItem(item);
+
+        int tabCount = mIconsLayout.getChildCount();
+        for (int i = 0; i < tabCount; i++) {
+            View child = mIconsLayout.getChildAt(i);
+            boolean isSelected = (i == item);
+            child.setSelected(isSelected);
+            if (isSelected) {
+                animateToIcon(item);
+            }
+        }
+    }
+
+    @Override
+    public void setOnPageChangeListener(OnPageChangeListener listener) {
+        mListener = listener;
+    }
+}

+ 11 - 0
library-viewpager-indicator/src/main/java/com/viewpagerindicator/IconPagerAdapter.java

@@ -0,0 +1,11 @@
+package com.viewpagerindicator;
+
+public interface IconPagerAdapter {
+    /**
+     * Get icon representing the page at {@code index} in the adapter.
+     */
+    int getIconResId(int index);
+
+    // From PagerAdapter
+    int getCount();
+}

+ 182 - 0
library-viewpager-indicator/src/main/java/com/viewpagerindicator/IcsLinearLayout.java

@@ -0,0 +1,182 @@
+package com.viewpagerindicator;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * A simple extension of a regular linear layout that supports the divider API
+ * of Android 4.0+. The dividers are added adjacent to the children by changing
+ * their layout params. If you need to rely on the margins which fall in the
+ * same orientation as the layout you should wrap the child in a simple
+ * {@link android.widget.FrameLayout} so it can receive the margin.
+ */
+class IcsLinearLayout extends LinearLayout {
+    private static final int[] LL = new int[] {
+        /* 0 */ android.R.attr.divider,
+        /* 1 */ android.R.attr.showDividers,
+        /* 2 */ android.R.attr.dividerPadding,
+    };
+    private static final int LL_DIVIDER = 0;
+    private static final int LL_SHOW_DIVIDER = 1;
+    private static final int LL_DIVIDER_PADDING = 2;
+
+    private Drawable mDivider;
+    private int mDividerWidth;
+    private int mDividerHeight;
+    private int mShowDividers;
+    private int mDividerPadding;
+
+
+    public IcsLinearLayout(Context context, int themeAttr) {
+        super(context);
+
+        TypedArray a = context.obtainStyledAttributes(null, LL, themeAttr, 0);
+        setDividerDrawable(a.getDrawable(IcsLinearLayout.LL_DIVIDER));
+        mDividerPadding = a.getDimensionPixelSize(LL_DIVIDER_PADDING, 0);
+        mShowDividers = a.getInteger(LL_SHOW_DIVIDER, SHOW_DIVIDER_NONE);
+        a.recycle();
+    }
+
+    public void setDividerDrawable(Drawable divider) {
+        if (divider == mDivider) {
+            return;
+        }
+        mDivider = divider;
+        if (divider != null) {
+            mDividerWidth = divider.getIntrinsicWidth();
+            mDividerHeight = divider.getIntrinsicHeight();
+        } else {
+            mDividerWidth = 0;
+            mDividerHeight = 0;
+        }
+        setWillNotDraw(divider == null);
+        requestLayout();
+    }
+
+    @Override
+    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
+        final int index = indexOfChild(child);
+        final int orientation = getOrientation();
+        final LayoutParams params = (LayoutParams) child.getLayoutParams();
+        if (hasDividerBeforeChildAt(index)) {
+            if (orientation == VERTICAL) {
+                //Account for the divider by pushing everything up
+                params.topMargin = mDividerHeight;
+            } else {
+                //Account for the divider by pushing everything left
+                params.leftMargin = mDividerWidth;
+            }
+        }
+
+        final int count = getChildCount();
+        if (index == count - 1) {
+            if (hasDividerBeforeChildAt(count)) {
+                if (orientation == VERTICAL) {
+                    params.bottomMargin = mDividerHeight;
+                } else {
+                    params.rightMargin = mDividerWidth;
+                }
+            }
+        }
+        super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mDivider != null) {
+            if (getOrientation() == VERTICAL) {
+                drawDividersVertical(canvas);
+            } else {
+                drawDividersHorizontal(canvas);
+            }
+        }
+        super.onDraw(canvas);
+    }
+
+    private void drawDividersVertical(Canvas canvas) {
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+
+            if (child != null && child.getVisibility() != GONE) {
+                if (hasDividerBeforeChildAt(i)) {
+                    final android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) child.getLayoutParams();
+                    final int top = child.getTop() - lp.topMargin/* - mDividerHeight*/;
+                    drawHorizontalDivider(canvas, top);
+                }
+            }
+        }
+
+        if (hasDividerBeforeChildAt(count)) {
+            final View child = getChildAt(count - 1);
+            int bottom = 0;
+            if (child == null) {
+                bottom = getHeight() - getPaddingBottom() - mDividerHeight;
+            } else {
+                //final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                bottom = child.getBottom()/* + lp.bottomMargin*/;
+            }
+            drawHorizontalDivider(canvas, bottom);
+        }
+    }
+
+    private void drawDividersHorizontal(Canvas canvas) {
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+
+            if (child != null && child.getVisibility() != GONE) {
+                if (hasDividerBeforeChildAt(i)) {
+                    final android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) child.getLayoutParams();
+                    final int left = child.getLeft() - lp.leftMargin/* - mDividerWidth*/;
+                    drawVerticalDivider(canvas, left);
+                }
+            }
+        }
+
+        if (hasDividerBeforeChildAt(count)) {
+            final View child = getChildAt(count - 1);
+            int right = 0;
+            if (child == null) {
+                right = getWidth() - getPaddingRight() - mDividerWidth;
+            } else {
+                //final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                right = child.getRight()/* + lp.rightMargin*/;
+            }
+            drawVerticalDivider(canvas, right);
+        }
+    }
+
+    private void drawHorizontalDivider(Canvas canvas, int top) {
+        mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
+                getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
+        mDivider.draw(canvas);
+    }
+
+    private void drawVerticalDivider(Canvas canvas, int left) {
+        mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
+                left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
+        mDivider.draw(canvas);
+    }
+
+    private boolean hasDividerBeforeChildAt(int childIndex) {
+        if (childIndex == 0 || childIndex == getChildCount()) {
+          return false;
+        }
+        if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
+            boolean hasVisibleViewBefore = false;
+            for (int i = childIndex - 1; i >= 0; i--) {
+                if (getChildAt(i).getVisibility() != GONE) {
+                    hasVisibleViewBefore = true;
+                    break;
+                }
+            }
+            return hasVisibleViewBefore;
+        }
+        return false;
+    }
+}

+ 447 - 0
library-viewpager-indicator/src/main/java/com/viewpagerindicator/LinePageIndicator.java

@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2012 Jake Wharton
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.viewpagerindicator;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ViewConfigurationCompat;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+/**
+ * Draws a line for each page. The current page line is colored differently
+ * than the unselected page lines.
+ */
+public class LinePageIndicator extends View implements PageIndicator {
+    private static final int INVALID_POINTER = -1;
+
+    private final Paint mPaintUnselected = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final Paint mPaintSelected = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private ViewPager mViewPager;
+    private ViewPager.OnPageChangeListener mListener;
+    private int mCurrentPage;
+    private boolean mCentered;
+    private float mLineWidth;
+    private float mGapWidth;
+
+    private int mTouchSlop;
+    private float mLastMotionX = -1;
+    private int mActivePointerId = INVALID_POINTER;
+    private boolean mIsDragging;
+
+
+    public LinePageIndicator(Context context) {
+        this(context, null);
+    }
+
+    public LinePageIndicator(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.vpiLinePageIndicatorStyle);
+    }
+
+    public LinePageIndicator(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        if (isInEditMode()) return;
+
+        final Resources res = getResources();
+
+        //Load defaults from resources
+        final int defaultSelectedColor = res.getColor(R.color.default_line_indicator_selected_color);
+        final int defaultUnselectedColor = res.getColor(R.color.default_line_indicator_unselected_color);
+        final float defaultLineWidth = res.getDimension(R.dimen.default_line_indicator_line_width);
+        final float defaultGapWidth = res.getDimension(R.dimen.default_line_indicator_gap_width);
+        final float defaultStrokeWidth = res.getDimension(R.dimen.default_line_indicator_stroke_width);
+        final boolean defaultCentered = res.getBoolean(R.bool.default_line_indicator_centered);
+
+        //Retrieve styles attributes
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LinePageIndicator, defStyle, 0);
+
+        mCentered = a.getBoolean(R.styleable.LinePageIndicator_centered, defaultCentered);
+        mLineWidth = a.getDimension(R.styleable.LinePageIndicator_lineWidth, defaultLineWidth);
+        mGapWidth = a.getDimension(R.styleable.LinePageIndicator_gapWidth, defaultGapWidth);
+        setStrokeWidth(a.getDimension(R.styleable.LinePageIndicator_strokeWidth, defaultStrokeWidth));
+        mPaintUnselected.setColor(a.getColor(R.styleable.LinePageIndicator_unselectedColor, defaultUnselectedColor));
+        mPaintSelected.setColor(a.getColor(R.styleable.LinePageIndicator_selectedColor, defaultSelectedColor));
+
+        Drawable background = a.getDrawable(R.styleable.LinePageIndicator_android_background);
+        if (background != null) {
+          setBackgroundDrawable(background);
+        }
+
+        a.recycle();
+
+        final ViewConfiguration configuration = ViewConfiguration.get(context);
+        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
+    }
+
+
+    public void setCentered(boolean centered) {
+        mCentered = centered;
+        invalidate();
+    }
+
+    public boolean isCentered() {
+        return mCentered;
+    }
+
+    public void setUnselectedColor(int unselectedColor) {
+        mPaintUnselected.setColor(unselectedColor);
+        invalidate();
+    }
+
+    public int getUnselectedColor() {
+        return mPaintUnselected.getColor();
+    }
+
+    public void setSelectedColor(int selectedColor) {
+        mPaintSelected.setColor(selectedColor);
+        invalidate();
+    }
+
+    public int getSelectedColor() {
+        return mPaintSelected.getColor();
+    }
+
+    public void setLineWidth(float lineWidth) {
+        mLineWidth = lineWidth;
+        invalidate();
+    }
+
+    public float getLineWidth() {
+        return mLineWidth;
+    }
+
+    public void setStrokeWidth(float lineHeight) {
+        mPaintSelected.setStrokeWidth(lineHeight);
+        mPaintUnselected.setStrokeWidth(lineHeight);
+        invalidate();
+    }
+
+    public float getStrokeWidth() {
+        return mPaintSelected.getStrokeWidth();
+    }
+
+    public void setGapWidth(float gapWidth) {
+        mGapWidth = gapWidth;
+        invalidate();
+    }
+
+    public float getGapWidth() {
+        return mGapWidth;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (mViewPager == null) {
+            return;
+        }
+        final int count = mViewPager.getAdapter().getCount();
+        if (count == 0) {
+            return;
+        }
+
+        if (mCurrentPage >= count) {
+            setCurrentItem(count - 1);
+            return;
+        }
+
+        final float lineWidthAndGap = mLineWidth + mGapWidth;
+        final float indicatorWidth = (count * lineWidthAndGap) - mGapWidth;
+        final float paddingTop = getPaddingTop();
+        final float paddingLeft = getPaddingLeft();
+        final float paddingRight = getPaddingRight();
+
+        float verticalOffset = paddingTop + ((getHeight() - paddingTop - getPaddingBottom()) / 2.0f);
+        float horizontalOffset = paddingLeft;
+        if (mCentered) {
+            horizontalOffset += ((getWidth() - paddingLeft - paddingRight) / 2.0f) - (indicatorWidth / 2.0f);
+        }
+
+        //Draw stroked circles
+        for (int i = 0; i < count; i++) {
+            float dx1 = horizontalOffset + (i * lineWidthAndGap);
+            float dx2 = dx1 + mLineWidth;
+            canvas.drawLine(dx1, verticalOffset, dx2, verticalOffset, (i == mCurrentPage) ? mPaintSelected : mPaintUnselected);
+        }
+    }
+
+    public boolean onTouchEvent(android.view.MotionEvent ev) {
+        if (super.onTouchEvent(ev)) {
+            return true;
+        }
+        if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) {
+            return false;
+        }
+
+        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
+                mLastMotionX = ev.getX();
+                break;
+
+            case MotionEvent.ACTION_MOVE: {
+                final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
+                final float x = MotionEventCompat.getX(ev, activePointerIndex);
+                final float deltaX = x - mLastMotionX;
+
+                if (!mIsDragging) {
+                    if (Math.abs(deltaX) > mTouchSlop) {
+                        mIsDragging = true;
+                    }
+                }
+
+                if (mIsDragging) {
+                    mLastMotionX = x;
+                    if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {
+                        mViewPager.fakeDragBy(deltaX);
+                    }
+                }
+
+                break;
+            }
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                if (!mIsDragging) {
+                    final int count = mViewPager.getAdapter().getCount();
+                    final int width = getWidth();
+                    final float halfWidth = width / 2f;
+                    final float sixthWidth = width / 6f;
+
+                    if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) {
+                        if (action != MotionEvent.ACTION_CANCEL) {
+                            mViewPager.setCurrentItem(mCurrentPage - 1);
+                        }
+                        return true;
+                    } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) {
+                        if (action != MotionEvent.ACTION_CANCEL) {
+                            mViewPager.setCurrentItem(mCurrentPage + 1);
+                        }
+                        return true;
+                    }
+                }
+
+                mIsDragging = false;
+                mActivePointerId = INVALID_POINTER;
+                if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();
+                break;
+
+            case MotionEventCompat.ACTION_POINTER_DOWN: {
+                final int index = MotionEventCompat.getActionIndex(ev);
+                mLastMotionX = MotionEventCompat.getX(ev, index);
+                mActivePointerId = MotionEventCompat.getPointerId(ev, index);
+                break;
+            }
+
+            case MotionEventCompat.ACTION_POINTER_UP:
+                final int pointerIndex = MotionEventCompat.getActionIndex(ev);
+                final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
+                if (pointerId == mActivePointerId) {
+                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+                    mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
+                }
+                mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId));
+                break;
+        }
+
+        return true;
+    }
+
+    @Override
+    public void setViewPager(ViewPager viewPager) {
+        if (mViewPager == viewPager) {
+            return;
+        }
+        if (mViewPager != null) {
+            //Clear us from the old pager.
+            mViewPager.setOnPageChangeListener(null);
+        }
+        if (viewPager.getAdapter() == null) {
+            throw new IllegalStateException("ViewPager does not have adapter instance.");
+        }
+        mViewPager = viewPager;
+        mViewPager.setOnPageChangeListener(this);
+        invalidate();
+    }
+
+    @Override
+    public void setViewPager(ViewPager view, int initialPosition) {
+        setViewPager(view);
+        setCurrentItem(initialPosition);
+    }
+
+    @Override
+    public void setCurrentItem(int item) {
+        if (mViewPager == null) {
+            throw new IllegalStateException("ViewPager has not been bound.");
+        }
+        mViewPager.setCurrentItem(item);
+        mCurrentPage = item;
+        invalidate();
+    }
+
+    @Override
+    public void notifyDataSetChanged() {
+        invalidate();
+    }
+
+    @Override
+    public void onPageScrollStateChanged(int state) {
+        if (mListener != null) {
+            mListener.onPageScrollStateChanged(state);
+        }
+    }
+
+    @Override
+    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+        if (mListener != null) {
+            mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
+        }
+    }
+
+    @Override
+    public void onPageSelected(int position) {
+        mCurrentPage = position;
+        invalidate();
+
+        if (mListener != null) {
+            mListener.onPageSelected(position);
+        }
+    }
+
+    @Override
+    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
+    }
+
+    /**
+     * Determines the width of this view
+     *
+     * @param measureSpec
+     *            A measureSpec packed into an int
+     * @return The width of the view, honoring constraints from measureSpec
+     */
+    private int measureWidth(int measureSpec) {
+        float result;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize = MeasureSpec.getSize(measureSpec);
+
+        if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) {
+            //We were told how big to be
+            result = specSize;
+        } else {
+            //Calculate the width according the views count
+            final int count = mViewPager.getAdapter().getCount();
+            result = getPaddingLeft() + getPaddingRight() + (count * mLineWidth) + ((count - 1) * mGapWidth);
+            //Respect AT_MOST value if that was what is called for by measureSpec
+            if (specMode == MeasureSpec.AT_MOST) {
+                result = Math.min(result, specSize);
+            }
+        }
+        return (int)Math.ceil(result);
+    }
+
+    /**
+     * Determines the height of this view
+     *
+     * @param measureSpec
+     *            A measureSpec packed into an int
+     * @return The height of the view, honoring constraints from measureSpec
+     */
+    private int measureHeight(int measureSpec) {
+        float result;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize = MeasureSpec.getSize(measureSpec);
+
+        if (specMode == MeasureSpec.EXACTLY) {
+            //We were told how big to be
+            result = specSize;
+        } else {
+            //Measure the height
+            result = mPaintSelected.getStrokeWidth() + getPaddingTop() + getPaddingBottom();
+            //Respect AT_MOST value if that was what is called for by measureSpec
+            if (specMode == MeasureSpec.AT_MOST) {
+                result = Math.min(result, specSize);
+            }
+        }
+        return (int)Math.ceil(result);
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        SavedState savedState = (SavedState)state;
+        super.onRestoreInstanceState(savedState.getSuperState());
+        mCurrentPage = savedState.currentPage;
+        requestLayout();
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+        SavedState savedState = new SavedState(superState);
+        savedState.currentPage = mCurrentPage;
+        return savedState;
+    }
+
+    static class SavedState extends BaseSavedState {
+        int currentPage;
+
+        public SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        private SavedState(Parcel in) {
+            super(in);
+            currentPage = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(currentPage);
+        }
+
+        @SuppressWarnings("UnusedDeclaration")
+        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+}

+ 63 - 0
library-viewpager-indicator/src/main/java/com/viewpagerindicator/PageIndicator.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 Patrik Akerfeldt
+ * Copyright (C) 2011 Jake Wharton
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.viewpagerindicator;
+
+import android.support.v4.view.ViewPager;
+
+/**
+ * A PageIndicator is responsible to show an visual indicator on the total views
+ * number and the current visible view.
+ */
+public interface PageIndicator extends ViewPager.OnPageChangeListener {
+    /**
+     * Bind the indicator to a ViewPager.
+     *
+     * @param view
+     */
+    void setViewPager(ViewPager view);
+
+    /**
+     * Bind the indicator to a ViewPager.
+     *
+     * @param view
+     * @param initialPosition
+     */
+    void setViewPager(ViewPager view, int initialPosition);
+
+    /**
+     * <p>Set the current page of both the ViewPager and indicator.</p>
+     *
+     * <p>This <strong>must</strong> be used if you need to set the page before
+     * the views are drawn on screen (e.g., default start page).</p>
+     *
+     * @param item
+     */
+    void setCurrentItem(int item);
+
+    /**
+     * Set a page change listener which will receive forwarded events.
+     *
+     * @param listener
+     */
+    void setOnPageChangeListener(ViewPager.OnPageChangeListener listener);
+
+    /**
+     * Notify the indicator that the fragment list has changed.
+     */
+    void notifyDataSetChanged();
+}

+ 283 - 0
library-viewpager-indicator/src/main/java/com/viewpagerindicator/TabPageIndicator.java

@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2011 Jake Wharton
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.viewpagerindicator;
+
+import android.content.Context;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.HorizontalScrollView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+/**
+ * This widget implements the dynamic action bar tab behavior that can change
+ * across different configurations or circumstances.
+ */
+public class TabPageIndicator extends HorizontalScrollView implements PageIndicator {
+    /** Title text used when no title is provided by the adapter. */
+    private static final CharSequence EMPTY_TITLE = "";
+
+    /**
+     * Interface for a callback when the selected tab has been reselected.
+     */
+    public interface OnTabReselectedListener {
+        /**
+         * Callback when the selected tab has been reselected.
+         *
+         * @param position Position of the current center item.
+         */
+        void onTabReselected(int position);
+    }
+
+    private Runnable mTabSelector;
+
+    private final OnClickListener mTabClickListener = new OnClickListener() {
+        public void onClick(View view) {
+            TabView tabView = (TabView)view;
+            final int oldSelected = mViewPager.getCurrentItem();
+            final int newSelected = tabView.getIndex();
+            mViewPager.setCurrentItem(newSelected);
+            if (oldSelected == newSelected && mTabReselectedListener != null) {
+                mTabReselectedListener.onTabReselected(newSelected);
+            }
+        }
+    };
+
+    private final IcsLinearLayout mTabLayout;
+
+    private ViewPager mViewPager;
+    private ViewPager.OnPageChangeListener mListener;
+
+    private int mMaxTabWidth;
+    private int mSelectedTabIndex;
+
+    private OnTabReselectedListener mTabReselectedListener;
+
+    public TabPageIndicator(Context context) {
+        this(context, null);
+    }
+
+    public TabPageIndicator(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setHorizontalScrollBarEnabled(false);
+
+        mTabLayout = new IcsLinearLayout(context, R.attr.vpiTabPageIndicatorStyle);
+        addView(mTabLayout, new ViewGroup.LayoutParams(WRAP_CONTENT, MATCH_PARENT));
+    }
+
+    public void setOnTabReselectedListener(OnTabReselectedListener listener) {
+        mTabReselectedListener = listener;
+    }
+
+    @Override
+    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
+        setFillViewport(lockedExpanded);
+
+        final int childCount = mTabLayout.getChildCount();
+        if (childCount > 1 && (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
+            if (childCount > 2) {
+                mMaxTabWidth = (int)(MeasureSpec.getSize(widthMeasureSpec) * 0.4f);
+            } else {
+                mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;
+            }
+        } else {
+            mMaxTabWidth = -1;
+        }
+
+        final int oldWidth = getMeasuredWidth();
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        final int newWidth = getMeasuredWidth();
+
+        if (lockedExpanded && oldWidth != newWidth) {
+            // Recenter the tab display if we're at a new (scrollable) size.
+            setCurrentItem(mSelectedTabIndex);
+        }
+    }
+
+    private void animateToTab(final int position) {
+        final View tabView = mTabLayout.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);
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mTabSelector != null) {
+            // Re-post the selector we saved
+            post(mTabSelector);
+        }
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mTabSelector != null) {
+            removeCallbacks(mTabSelector);
+        }
+    }
+
+    private void addTab(int index, CharSequence text, int iconResId) {
+        final TabView tabView = new TabView(getContext());
+        tabView.mIndex = index;
+        tabView.setFocusable(true);
+        tabView.setOnClickListener(mTabClickListener);
+        tabView.setText(text);
+
+        if (iconResId != 0) {
+            tabView.setCompoundDrawablesWithIntrinsicBounds(iconResId, 0, 0, 0);
+        }
+
+        mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, MATCH_PARENT, 1));
+    }
+
+    @Override
+    public void onPageScrollStateChanged(int arg0) {
+        if (mListener != null) {
+            mListener.onPageScrollStateChanged(arg0);
+        }
+    }
+
+    @Override
+    public void onPageScrolled(int arg0, float arg1, int arg2) {
+        if (mListener != null) {
+            mListener.onPageScrolled(arg0, arg1, arg2);
+        }
+    }
+
+    @Override
+    public void onPageSelected(int arg0) {
+        setCurrentItem(arg0);
+        if (mListener != null) {
+            mListener.onPageSelected(arg0);
+        }
+    }
+
+    @Override
+    public void setViewPager(ViewPager view) {
+        if (mViewPager == view) {
+            return;
+        }
+        if (mViewPager != null) {
+            mViewPager.setOnPageChangeListener(null);
+        }
+        final PagerAdapter adapter = view.getAdapter();
+        if (adapter == null) {
+            throw new IllegalStateException("ViewPager does not have adapter instance.");
+        }
+        mViewPager = view;
+        view.setOnPageChangeListener(this);
+        notifyDataSetChanged();
+    }
+
+    public void notifyDataSetChanged() {
+        mTabLayout.removeAllViews();
+        PagerAdapter adapter = mViewPager.getAdapter();
+        IconPagerAdapter iconAdapter = null;
+        if (adapter instanceof IconPagerAdapter) {
+            iconAdapter = (IconPagerAdapter)adapter;
+        }
+        final int count = adapter.getCount();
+        for (int i = 0; i < count; i++) {
+            CharSequence title = adapter.getPageTitle(i);
+            if (title == null) {
+                title = EMPTY_TITLE;
+            }
+            int iconResId = 0;
+            if (iconAdapter != null) {
+                iconResId = iconAdapter.getIconResId(i);
+            }
+            addTab(i, title, iconResId);
+        }
+        if (mSelectedTabIndex > count) {
+            mSelectedTabIndex = count - 1;
+        }
+        setCurrentItem(mSelectedTabIndex);
+        requestLayout();
+    }
+
+    @Override
+    public void setViewPager(ViewPager view, int initialPosition) {
+        setViewPager(view);
+        setCurrentItem(initialPosition);
+    }
+
+    @Override
+    public void setCurrentItem(int item) {
+        if (mViewPager == null) {
+            throw new IllegalStateException("ViewPager has not been bound.");
+        }
+        mSelectedTabIndex = item;
+        mViewPager.setCurrentItem(item);
+
+        final int tabCount = mTabLayout.getChildCount();
+        for (int i = 0; i < tabCount; i++) {
+            final View child = mTabLayout.getChildAt(i);
+            final boolean isSelected = (i == item);
+            child.setSelected(isSelected);
+            if (isSelected) {
+                animateToTab(item);
+            }
+        }
+    }
+
+    @Override
+    public void setOnPageChangeListener(OnPageChangeListener listener) {
+        mListener = listener;
+    }
+
+    private class TabView extends TextView {
+        private int mIndex;
+
+        public TabView(Context context) {
+            super(context, null, R.attr.vpiTabPageIndicatorStyle);
+        }
+
+        @Override
+        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+            // Re-measure if we went beyond our maximum size.
+            if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) {
+                super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY),
+                        heightMeasureSpec);
+            }
+        }
+
+        public int getIndex() {
+            return mIndex;
+        }
+    }
+}

+ 870 - 0
library-viewpager-indicator/src/main/java/com/viewpagerindicator/TitlePageIndicator.java

@@ -0,0 +1,870 @@
+/*
+ * Copyright (C) 2011 Jake Wharton
+ * Copyright (C) 2011 Patrik Akerfeldt
+ * Copyright (C) 2011 Francisco Figueiredo Jr.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.viewpagerindicator;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ViewConfigurationCompat;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+import java.util.ArrayList;
+
+/**
+ * A TitlePageIndicator is a PageIndicator which displays the title of left view
+ * (if exist), the title of the current select view (centered) and the title of
+ * the right view (if exist). When the user scrolls the ViewPager then titles are
+ * also scrolled.
+ */
+public class TitlePageIndicator extends View implements PageIndicator {
+    /**
+     * Percentage indicating what percentage of the screen width away from
+     * center should the underline be fully faded. A value of 0.25 means that
+     * halfway between the center of the screen and an edge.
+     */
+    private static final float SELECTION_FADE_PERCENTAGE = 0.25f;
+
+    /**
+     * Percentage indicating what percentage of the screen width away from
+     * center should the selected text bold turn off. A value of 0.05 means
+     * that 10% between the center and an edge.
+     */
+    private static final float BOLD_FADE_PERCENTAGE = 0.05f;
+
+    /**
+     * Title text used when no title is provided by the adapter.
+     */
+    private static final String EMPTY_TITLE = "";
+
+    /**
+     * Interface for a callback when the center item has been clicked.
+     */
+    public interface OnCenterItemClickListener {
+        /**
+         * Callback when the center item has been clicked.
+         *
+         * @param position Position of the current center item.
+         */
+        void onCenterItemClick(int position);
+    }
+
+    public enum IndicatorStyle {
+        None(0), Triangle(1), Underline(2);
+
+        public final int value;
+
+        private IndicatorStyle(int value) {
+            this.value = value;
+        }
+
+        public static IndicatorStyle fromValue(int value) {
+            for (IndicatorStyle style : IndicatorStyle.values()) {
+                if (style.value == value) {
+                    return style;
+                }
+            }
+            return null;
+        }
+    }
+
+    public enum LinePosition {
+        Bottom(0), Top(1);
+
+        public final int value;
+
+        private LinePosition(int value) {
+            this.value = value;
+        }
+
+        public static LinePosition fromValue(int value) {
+            for (LinePosition position : LinePosition.values()) {
+                if (position.value == value) {
+                    return position;
+                }
+            }
+            return null;
+        }
+    }
+
+    private ViewPager mViewPager;
+    private ViewPager.OnPageChangeListener mListener;
+    private int mCurrentPage = -1;
+    private float mPageOffset;
+    private int mScrollState;
+    private final Paint mPaintText = new Paint();
+    private boolean mBoldText;
+    private int mColorText;
+    private int mColorSelected;
+    private Path mPath = new Path();
+    private final Rect mBounds = new Rect();
+    private final Paint mPaintFooterLine = new Paint();
+    private IndicatorStyle mFooterIndicatorStyle;
+    private LinePosition mLinePosition;
+    private final Paint mPaintFooterIndicator = new Paint();
+    private float mFooterIndicatorHeight;
+    private float mFooterIndicatorUnderlinePadding;
+    private float mFooterPadding;
+    private float mTitlePadding;
+    private float mTopPadding;
+    /** Left and right side padding for not active view titles. */
+    private float mClipPadding;
+    private float mFooterLineHeight;
+
+    private static final int INVALID_POINTER = -1;
+
+    private int mTouchSlop;
+    private float mLastMotionX = -1;
+    private int mActivePointerId = INVALID_POINTER;
+    private boolean mIsDragging;
+
+    private OnCenterItemClickListener mCenterItemClickListener;
+
+
+    public TitlePageIndicator(Context context) {
+        this(context, null);
+    }
+
+    public TitlePageIndicator(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.vpiTitlePageIndicatorStyle);
+    }
+
+    public TitlePageIndicator(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        if (isInEditMode()) return;
+
+        //Load defaults from resources
+        final Resources res = getResources();
+        final int defaultFooterColor = res.getColor(R.color.default_title_indicator_footer_color);
+        final float defaultFooterLineHeight = res.getDimension(R.dimen.default_title_indicator_footer_line_height);
+        final int defaultFooterIndicatorStyle = res.getInteger(R.integer.default_title_indicator_footer_indicator_style);
+        final float defaultFooterIndicatorHeight = res.getDimension(R.dimen.default_title_indicator_footer_indicator_height);
+        final float defaultFooterIndicatorUnderlinePadding = res.getDimension(R.dimen.default_title_indicator_footer_indicator_underline_padding);
+        final float defaultFooterPadding = res.getDimension(R.dimen.default_title_indicator_footer_padding);
+        final int defaultLinePosition = res.getInteger(R.integer.default_title_indicator_line_position);
+        final int defaultSelectedColor = res.getColor(R.color.default_title_indicator_selected_color);
+        final boolean defaultSelectedBold = res.getBoolean(R.bool.default_title_indicator_selected_bold);
+        final int defaultTextColor = res.getColor(R.color.default_title_indicator_text_color);
+        final float defaultTextSize = res.getDimension(R.dimen.default_title_indicator_text_size);
+        final float defaultTitlePadding = res.getDimension(R.dimen.default_title_indicator_title_padding);
+        final float defaultClipPadding = res.getDimension(R.dimen.default_title_indicator_clip_padding);
+        final float defaultTopPadding = res.getDimension(R.dimen.default_title_indicator_top_padding);
+
+        //Retrieve styles attributes
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TitlePageIndicator, defStyle, 0);
+
+        //Retrieve the colors to be used for this view and apply them.
+        mFooterLineHeight = a.getDimension(R.styleable.TitlePageIndicator_footerLineHeight, defaultFooterLineHeight);
+        mFooterIndicatorStyle = IndicatorStyle.fromValue(a.getInteger(R.styleable.TitlePageIndicator_footerIndicatorStyle, defaultFooterIndicatorStyle));
+        mFooterIndicatorHeight = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorHeight, defaultFooterIndicatorHeight);
+        mFooterIndicatorUnderlinePadding = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorUnderlinePadding, defaultFooterIndicatorUnderlinePadding);
+        mFooterPadding = a.getDimension(R.styleable.TitlePageIndicator_footerPadding, defaultFooterPadding);
+        mLinePosition = LinePosition.fromValue(a.getInteger(R.styleable.TitlePageIndicator_linePosition, defaultLinePosition));
+        mTopPadding = a.getDimension(R.styleable.TitlePageIndicator_topPadding, defaultTopPadding);
+        mTitlePadding = a.getDimension(R.styleable.TitlePageIndicator_titlePadding, defaultTitlePadding);
+        mClipPadding = a.getDimension(R.styleable.TitlePageIndicator_clipPadding, defaultClipPadding);
+        mColorSelected = a.getColor(R.styleable.TitlePageIndicator_selectedColor, defaultSelectedColor);
+        mColorText = a.getColor(R.styleable.TitlePageIndicator_android_textColor, defaultTextColor);
+        mBoldText = a.getBoolean(R.styleable.TitlePageIndicator_selectedBold, defaultSelectedBold);
+
+        final float textSize = a.getDimension(R.styleable.TitlePageIndicator_android_textSize, defaultTextSize);
+        final int footerColor = a.getColor(R.styleable.TitlePageIndicator_footerColor, defaultFooterColor);
+        mPaintText.setTextSize(textSize);
+        mPaintText.setAntiAlias(true);
+        mPaintFooterLine.setStyle(Paint.Style.FILL_AND_STROKE);
+        mPaintFooterLine.setStrokeWidth(mFooterLineHeight);
+        mPaintFooterLine.setColor(footerColor);
+        mPaintFooterIndicator.setStyle(Paint.Style.FILL_AND_STROKE);
+        mPaintFooterIndicator.setColor(footerColor);
+
+        Drawable background = a.getDrawable(R.styleable.TitlePageIndicator_android_background);
+        if (background != null) {
+          setBackgroundDrawable(background);
+        }
+
+        a.recycle();
+
+        final ViewConfiguration configuration = ViewConfiguration.get(context);
+        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
+    }
+
+
+    public int getFooterColor() {
+        return mPaintFooterLine.getColor();
+    }
+
+    public void setFooterColor(int footerColor) {
+        mPaintFooterLine.setColor(footerColor);
+        mPaintFooterIndicator.setColor(footerColor);
+        invalidate();
+    }
+
+    public float getFooterLineHeight() {
+        return mFooterLineHeight;
+    }
+
+    public void setFooterLineHeight(float footerLineHeight) {
+        mFooterLineHeight = footerLineHeight;
+        mPaintFooterLine.setStrokeWidth(mFooterLineHeight);
+        invalidate();
+    }
+
+    public float getFooterIndicatorHeight() {
+        return mFooterIndicatorHeight;
+    }
+
+    public void setFooterIndicatorHeight(float footerTriangleHeight) {
+        mFooterIndicatorHeight = footerTriangleHeight;
+        invalidate();
+    }
+
+    public float getFooterIndicatorPadding() {
+        return mFooterPadding;
+    }
+
+    public void setFooterIndicatorPadding(float footerIndicatorPadding) {
+        mFooterPadding = footerIndicatorPadding;
+        invalidate();
+    }
+
+    public IndicatorStyle getFooterIndicatorStyle() {
+        return mFooterIndicatorStyle;
+    }
+
+    public void setFooterIndicatorStyle(IndicatorStyle indicatorStyle) {
+        mFooterIndicatorStyle = indicatorStyle;
+        invalidate();
+    }
+
+    public LinePosition getLinePosition() {
+        return mLinePosition;
+    }
+
+    public void setLinePosition(LinePosition linePosition) {
+        mLinePosition = linePosition;
+        invalidate();
+    }
+
+    public int getSelectedColor() {
+        return mColorSelected;
+    }
+
+    public void setSelectedColor(int selectedColor) {
+        mColorSelected = selectedColor;
+        invalidate();
+    }
+
+    public boolean isSelectedBold() {
+        return mBoldText;
+    }
+
+    public void setSelectedBold(boolean selectedBold) {
+        mBoldText = selectedBold;
+        invalidate();
+    }
+
+    public int getTextColor() {
+        return mColorText;
+    }
+
+    public void setTextColor(int textColor) {
+        mPaintText.setColor(textColor);
+        mColorText = textColor;
+        invalidate();
+    }
+
+    public float getTextSize() {
+        return mPaintText.getTextSize();
+    }
+
+    public void setTextSize(float textSize) {
+        mPaintText.setTextSize(textSize);
+        invalidate();
+    }
+
+    public float getTitlePadding() {
+        return this.mTitlePadding;
+    }
+
+    public void setTitlePadding(float titlePadding) {
+        mTitlePadding = titlePadding;
+        invalidate();
+    }
+
+    public float getTopPadding() {
+        return this.mTopPadding;
+    }
+
+    public void setTopPadding(float topPadding) {
+        mTopPadding = topPadding;
+        invalidate();
+    }
+
+    public float getClipPadding() {
+        return this.mClipPadding;
+    }
+
+    public void setClipPadding(float clipPadding) {
+        mClipPadding = clipPadding;
+        invalidate();
+    }
+
+    public void setTypeface(Typeface typeface) {
+        mPaintText.setTypeface(typeface);
+        invalidate();
+    }
+
+    public Typeface getTypeface() {
+        return mPaintText.getTypeface();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see android.view.View#onDraw(android.graphics.Canvas)
+     */
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (mViewPager == null) {
+            return;
+        }
+        final int count = mViewPager.getAdapter().getCount();
+        if (count == 0) {
+            return;
+        }
+
+        // mCurrentPage is -1 on first start and after orientation changed. If so, retrieve the correct index from viewpager.
+        if (mCurrentPage == -1 && mViewPager != null) {
+            mCurrentPage = mViewPager.getCurrentItem();
+        }
+
+        //Calculate views bounds
+        ArrayList<Rect> bounds = calculateAllBounds(mPaintText);
+        final int boundsSize = bounds.size();
+
+        //Make sure we're on a page that still exists
+        if (mCurrentPage >= boundsSize) {
+            setCurrentItem(boundsSize - 1);
+            return;
+        }
+
+        final int countMinusOne = count - 1;
+        final float halfWidth = getWidth() / 2f;
+        final int left = getLeft();
+        final float leftClip = left + mClipPadding;
+        final int width = getWidth();
+        int height = getHeight();
+        final int right = left + width;
+        final float rightClip = right - mClipPadding;
+
+        int page = mCurrentPage;
+        float offsetPercent;
+        if (mPageOffset <= 0.5) {
+            offsetPercent = mPageOffset;
+        } else {
+            page += 1;
+            offsetPercent = 1 - mPageOffset;
+        }
+        final boolean currentSelected = (offsetPercent <= SELECTION_FADE_PERCENTAGE);
+        final boolean currentBold = (offsetPercent <= BOLD_FADE_PERCENTAGE);
+        final float selectedPercent = (SELECTION_FADE_PERCENTAGE - offsetPercent) / SELECTION_FADE_PERCENTAGE;
+
+        //Verify if the current view must be clipped to the screen
+        Rect curPageBound = bounds.get(mCurrentPage);
+        float curPageWidth = curPageBound.right - curPageBound.left;
+        if (curPageBound.left < leftClip) {
+            //Try to clip to the screen (left side)
+            clipViewOnTheLeft(curPageBound, curPageWidth, left);
+        }
+        if (curPageBound.right > rightClip) {
+            //Try to clip to the screen (right side)
+            clipViewOnTheRight(curPageBound, curPageWidth, right);
+        }
+
+        //Left views starting from the current position
+        if (mCurrentPage > 0) {
+            for (int i = mCurrentPage - 1; i >= 0; i--) {
+                Rect bound = bounds.get(i);
+                //Is left side is outside the screen
+                if (bound.left < leftClip) {
+                    int w = bound.right - bound.left;
+                    //Try to clip to the screen (left side)
+                    clipViewOnTheLeft(bound, w, left);
+                    //Except if there's an intersection with the right view
+                    Rect rightBound = bounds.get(i + 1);
+                    //Intersection
+                    if (bound.right + mTitlePadding > rightBound.left) {
+                        bound.left = (int) (rightBound.left - w - mTitlePadding);
+                        bound.right = bound.left + w;
+                    }
+                }
+            }
+        }
+        //Right views starting from the current position
+        if (mCurrentPage < countMinusOne) {
+            for (int i = mCurrentPage + 1 ; i < count; i++) {
+                Rect bound = bounds.get(i);
+                //If right side is outside the screen
+                if (bound.right > rightClip) {
+                    int w = bound.right - bound.left;
+                    //Try to clip to the screen (right side)
+                    clipViewOnTheRight(bound, w, right);
+                    //Except if there's an intersection with the left view
+                    Rect leftBound = bounds.get(i - 1);
+                    //Intersection
+                    if (bound.left - mTitlePadding < leftBound.right) {
+                        bound.left = (int) (leftBound.right + mTitlePadding);
+                        bound.right = bound.left + w;
+                    }
+                }
+            }
+        }
+
+        //Now draw views
+        int colorTextAlpha = mColorText >>> 24;
+        for (int i = 0; i < count; i++) {
+            //Get the title
+            Rect bound = bounds.get(i);
+            //Only if one side is visible
+            if ((bound.left > left && bound.left < right) || (bound.right > left && bound.right < right)) {
+                final boolean currentPage = (i == page);
+                final CharSequence pageTitle = getTitle(i);
+
+                //Only set bold if we are within bounds
+                mPaintText.setFakeBoldText(currentPage && currentBold && mBoldText);
+
+                //Draw text as unselected
+                mPaintText.setColor(mColorText);
+                if(currentPage && currentSelected) {
+                    //Fade out/in unselected text as the selected text fades in/out
+                    mPaintText.setAlpha(colorTextAlpha - (int)(colorTextAlpha * selectedPercent));
+                }
+
+                //Except if there's an intersection with the right view
+                if (i < boundsSize - 1)  {
+                    Rect rightBound = bounds.get(i + 1);
+                    //Intersection
+                    if (bound.right + mTitlePadding > rightBound.left) {
+                        int w = bound.right - bound.left;
+                        bound.left = (int) (rightBound.left - w - mTitlePadding);
+                        bound.right = bound.left + w;
+                    }
+                }
+                canvas.drawText(pageTitle, 0, pageTitle.length(), bound.left, bound.bottom + mTopPadding, mPaintText);
+
+                //If we are within the selected bounds draw the selected text
+                if (currentPage && currentSelected) {
+                    mPaintText.setColor(mColorSelected);
+                    mPaintText.setAlpha((int)((mColorSelected >>> 24) * selectedPercent));
+                    canvas.drawText(pageTitle, 0, pageTitle.length(), bound.left, bound.bottom + mTopPadding, mPaintText);
+                }
+            }
+        }
+
+        //If we want the line on the top change height to zero and invert the line height to trick the drawing code
+        float footerLineHeight = mFooterLineHeight;
+        float footerIndicatorLineHeight = mFooterIndicatorHeight;
+        if (mLinePosition == LinePosition.Top) {
+            height = 0;
+            footerLineHeight = -footerLineHeight;
+            footerIndicatorLineHeight = -footerIndicatorLineHeight;
+        }
+
+        //Draw the footer line
+        mPath.reset();
+        mPath.moveTo(0, height - footerLineHeight / 2f);
+        mPath.lineTo(width, height - footerLineHeight / 2f);
+        mPath.close();
+        canvas.drawPath(mPath, mPaintFooterLine);
+
+        float heightMinusLine = height - footerLineHeight;
+        switch (mFooterIndicatorStyle) {
+            case Triangle:
+                mPath.reset();
+                mPath.moveTo(halfWidth, heightMinusLine - footerIndicatorLineHeight);
+                mPath.lineTo(halfWidth + footerIndicatorLineHeight, heightMinusLine);
+                mPath.lineTo(halfWidth - footerIndicatorLineHeight, heightMinusLine);
+                mPath.close();
+                canvas.drawPath(mPath, mPaintFooterIndicator);
+                break;
+
+            case Underline:
+                if (!currentSelected || page >= boundsSize) {
+                    break;
+                }
+
+                Rect underlineBounds = bounds.get(page);
+                final float rightPlusPadding = underlineBounds.right + mFooterIndicatorUnderlinePadding;
+                final float leftMinusPadding = underlineBounds.left - mFooterIndicatorUnderlinePadding;
+                final float heightMinusLineMinusIndicator = heightMinusLine - footerIndicatorLineHeight;
+
+                mPath.reset();
+                mPath.moveTo(leftMinusPadding, heightMinusLine);
+                mPath.lineTo(rightPlusPadding, heightMinusLine);
+                mPath.lineTo(rightPlusPadding, heightMinusLineMinusIndicator);
+                mPath.lineTo(leftMinusPadding, heightMinusLineMinusIndicator);
+                mPath.close();
+
+                mPaintFooterIndicator.setAlpha((int)(0xFF * selectedPercent));
+                canvas.drawPath(mPath, mPaintFooterIndicator);
+                mPaintFooterIndicator.setAlpha(0xFF);
+                break;
+        }
+    }
+
+    public boolean onTouchEvent(android.view.MotionEvent ev) {
+        if (super.onTouchEvent(ev)) {
+            return true;
+        }
+        if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) {
+            return false;
+        }
+
+        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
+                mLastMotionX = ev.getX();
+                break;
+
+            case MotionEvent.ACTION_MOVE: {
+                final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
+                final float x = MotionEventCompat.getX(ev, activePointerIndex);
+                final float deltaX = x - mLastMotionX;
+
+                if (!mIsDragging) {
+                    if (Math.abs(deltaX) > mTouchSlop) {
+                        mIsDragging = true;
+                    }
+                }
+
+                if (mIsDragging) {
+                    mLastMotionX = x;
+                    if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {
+                        mViewPager.fakeDragBy(deltaX);
+                    }
+                }
+
+                break;
+            }
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                if (!mIsDragging) {
+                    final int count = mViewPager.getAdapter().getCount();
+                    final int width = getWidth();
+                    final float halfWidth = width / 2f;
+                    final float sixthWidth = width / 6f;
+                    final float leftThird = halfWidth - sixthWidth;
+                    final float rightThird = halfWidth + sixthWidth;
+                    final float eventX = ev.getX();
+
+                    if (eventX < leftThird) {
+                        if (mCurrentPage > 0) {
+                            if (action != MotionEvent.ACTION_CANCEL) {
+                                mViewPager.setCurrentItem(mCurrentPage - 1);
+                            }
+                            return true;
+                        }
+                    } else if (eventX > rightThird) {
+                        if (mCurrentPage < count - 1) {
+                            if (action != MotionEvent.ACTION_CANCEL) {
+                                mViewPager.setCurrentItem(mCurrentPage + 1);
+                            }
+                            return true;
+                        }
+                    } else {
+                        //Middle third
+                        if (mCenterItemClickListener != null && action != MotionEvent.ACTION_CANCEL) {
+                            mCenterItemClickListener.onCenterItemClick(mCurrentPage);
+                        }
+                    }
+                }
+
+                mIsDragging = false;
+                mActivePointerId = INVALID_POINTER;
+                if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();
+                break;
+
+            case MotionEventCompat.ACTION_POINTER_DOWN: {
+                final int index = MotionEventCompat.getActionIndex(ev);
+                mLastMotionX = MotionEventCompat.getX(ev, index);
+                mActivePointerId = MotionEventCompat.getPointerId(ev, index);
+                break;
+            }
+
+            case MotionEventCompat.ACTION_POINTER_UP:
+                final int pointerIndex = MotionEventCompat.getActionIndex(ev);
+                final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
+                if (pointerId == mActivePointerId) {
+                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+                    mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
+                }
+                mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId));
+                break;
+        }
+
+        return true;
+    }
+
+    /**
+     * Set bounds for the right textView including clip padding.
+     *
+     * @param curViewBound
+     *            current bounds.
+     * @param curViewWidth
+     *            width of the view.
+     */
+    private void clipViewOnTheRight(Rect curViewBound, float curViewWidth, int right) {
+        curViewBound.right = (int) (right - mClipPadding);
+        curViewBound.left = (int) (curViewBound.right - curViewWidth);
+    }
+
+    /**
+     * Set bounds for the left textView including clip padding.
+     *
+     * @param curViewBound
+     *            current bounds.
+     * @param curViewWidth
+     *            width of the view.
+     */
+    private void clipViewOnTheLeft(Rect curViewBound, float curViewWidth, int left) {
+        curViewBound.left = (int) (left + mClipPadding);
+        curViewBound.right = (int) (mClipPadding + curViewWidth);
+    }
+
+    /**
+     * Calculate views bounds and scroll them according to the current index
+     *
+     * @param paint
+     * @return
+     */
+    private ArrayList<Rect> calculateAllBounds(Paint paint) {
+        ArrayList<Rect> list = new ArrayList<Rect>();
+        //For each views (If no values then add a fake one)
+        final int count = mViewPager.getAdapter().getCount();
+        final int width = getWidth();
+        final int halfWidth = width / 2;
+        for (int i = 0; i < count; i++) {
+            Rect bounds = calcBounds(i, paint);
+            int w = bounds.right - bounds.left;
+            int h = bounds.bottom - bounds.top;
+            bounds.left = (int)(halfWidth - (w / 2f) + ((i - mCurrentPage - mPageOffset) * width));
+            bounds.right = bounds.left + w;
+            bounds.top = 0;
+            bounds.bottom = h;
+            list.add(bounds);
+        }
+
+        return list;
+    }
+
+    /**
+     * Calculate the bounds for a view's title
+     *
+     * @param index
+     * @param paint
+     * @return
+     */
+    private Rect calcBounds(int index, Paint paint) {
+        //Calculate the text bounds
+        Rect bounds = new Rect();
+        CharSequence title = getTitle(index);
+        bounds.right = (int) paint.measureText(title, 0, title.length());
+        bounds.bottom = (int) (paint.descent() - paint.ascent());
+        return bounds;
+    }
+
+    @Override
+    public void setViewPager(ViewPager view) {
+        if (mViewPager == view) {
+            return;
+        }
+        if (mViewPager != null) {
+            mViewPager.setOnPageChangeListener(null);
+        }
+        if (view.getAdapter() == null) {
+            throw new IllegalStateException("ViewPager does not have adapter instance.");
+        }
+        mViewPager = view;
+        mViewPager.setOnPageChangeListener(this);
+        invalidate();
+    }
+
+    @Override
+    public void setViewPager(ViewPager view, int initialPosition) {
+        setViewPager(view);
+        setCurrentItem(initialPosition);
+    }
+
+    @Override
+    public void notifyDataSetChanged() {
+        invalidate();
+    }
+
+    /**
+     * Set a callback listener for the center item click.
+     *
+     * @param listener Callback instance.
+     */
+    public void setOnCenterItemClickListener(OnCenterItemClickListener listener) {
+        mCenterItemClickListener = listener;
+    }
+
+    @Override
+    public void setCurrentItem(int item) {
+        if (mViewPager == null) {
+            throw new IllegalStateException("ViewPager has not been bound.");
+        }
+        mViewPager.setCurrentItem(item);
+        mCurrentPage = item;
+        invalidate();
+    }
+
+    @Override
+    public void onPageScrollStateChanged(int state) {
+        mScrollState = state;
+
+        if (mListener != null) {
+            mListener.onPageScrollStateChanged(state);
+        }
+    }
+
+    @Override
+    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+        mCurrentPage = position;
+        mPageOffset = positionOffset;
+        invalidate();
+
+        if (mListener != null) {
+            mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
+        }
+    }
+
+    @Override
+    public void onPageSelected(int position) {
+        if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+            mCurrentPage = position;
+            invalidate();
+        }
+
+        if (mListener != null) {
+            mListener.onPageSelected(position);
+        }
+    }
+
+    @Override
+    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        //Measure our width in whatever mode specified
+        final int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
+
+        //Determine our height
+        float height;
+        final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+        if (heightSpecMode == MeasureSpec.EXACTLY) {
+            //We were told how big to be
+            height = MeasureSpec.getSize(heightMeasureSpec);
+        } else {
+            //Calculate the text bounds
+            mBounds.setEmpty();
+            mBounds.bottom = (int) (mPaintText.descent() - mPaintText.ascent());
+            height = mBounds.bottom - mBounds.top + mFooterLineHeight + mFooterPadding + mTopPadding;
+            if (mFooterIndicatorStyle != IndicatorStyle.None) {
+                height += mFooterIndicatorHeight;
+            }
+        }
+        final int measuredHeight = (int)height;
+
+        setMeasuredDimension(measuredWidth, measuredHeight);
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        SavedState savedState = (SavedState)state;
+        super.onRestoreInstanceState(savedState.getSuperState());
+        mCurrentPage = savedState.currentPage;
+        requestLayout();
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+        SavedState savedState = new SavedState(superState);
+        savedState.currentPage = mCurrentPage;
+        return savedState;
+    }
+
+    static class SavedState extends BaseSavedState {
+        int currentPage;
+
+        public SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        private SavedState(Parcel in) {
+            super(in);
+            currentPage = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(currentPage);
+        }
+
+        @SuppressWarnings("UnusedDeclaration")
+        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    private CharSequence getTitle(int i) {
+        CharSequence title = mViewPager.getAdapter().getPageTitle(i);
+        if (title == null) {
+            title = EMPTY_TITLE;
+        }
+        return title;
+    }
+}

+ 402 - 0
library-viewpager-indicator/src/main/java/com/viewpagerindicator/UnderlinePageIndicator.java

@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2012 Jake Wharton
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.viewpagerindicator;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ViewConfigurationCompat;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+/**
+ * Draws a line for each page. The current page line is colored differently
+ * than the unselected page lines.
+ */
+public class UnderlinePageIndicator extends View implements PageIndicator {
+    private static final int INVALID_POINTER = -1;
+    private static final int FADE_FRAME_MS = 30;
+
+    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+    private boolean mFades;
+    private int mFadeDelay;
+    private int mFadeLength;
+    private int mFadeBy;
+
+    private ViewPager mViewPager;
+    private ViewPager.OnPageChangeListener mListener;
+    private int mScrollState;
+    private int mCurrentPage;
+    private float mPositionOffset;
+
+    private int mTouchSlop;
+    private float mLastMotionX = -1;
+    private int mActivePointerId = INVALID_POINTER;
+    private boolean mIsDragging;
+
+    private final Runnable mFadeRunnable = new Runnable() {
+      @Override public void run() {
+        if (!mFades) return;
+
+        final int alpha = Math.max(mPaint.getAlpha() - mFadeBy, 0);
+        mPaint.setAlpha(alpha);
+        invalidate();
+        if (alpha > 0) {
+          postDelayed(this, FADE_FRAME_MS);
+        }
+      }
+    };
+
+    public UnderlinePageIndicator(Context context) {
+        this(context, null);
+    }
+
+    public UnderlinePageIndicator(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.vpiUnderlinePageIndicatorStyle);
+    }
+
+    public UnderlinePageIndicator(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        if (isInEditMode()) return;
+
+        final Resources res = getResources();
+
+        //Load defaults from resources
+        final boolean defaultFades = res.getBoolean(R.bool.default_underline_indicator_fades);
+        final int defaultFadeDelay = res.getInteger(R.integer.default_underline_indicator_fade_delay);
+        final int defaultFadeLength = res.getInteger(R.integer.default_underline_indicator_fade_length);
+        final int defaultSelectedColor = res.getColor(R.color.default_underline_indicator_selected_color);
+
+        //Retrieve styles attributes
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UnderlinePageIndicator, defStyle, 0);
+
+        setFades(a.getBoolean(R.styleable.UnderlinePageIndicator_fades, defaultFades));
+        setSelectedColor(a.getColor(R.styleable.UnderlinePageIndicator_selectedColor, defaultSelectedColor));
+        setFadeDelay(a.getInteger(R.styleable.UnderlinePageIndicator_fadeDelay, defaultFadeDelay));
+        setFadeLength(a.getInteger(R.styleable.UnderlinePageIndicator_fadeLength, defaultFadeLength));
+
+        Drawable background = a.getDrawable(R.styleable.UnderlinePageIndicator_android_background);
+        if (background != null) {
+          setBackgroundDrawable(background);
+        }
+
+        a.recycle();
+
+        final ViewConfiguration configuration = ViewConfiguration.get(context);
+        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
+    }
+
+    public boolean getFades() {
+        return mFades;
+    }
+
+    public void setFades(boolean fades) {
+        if (fades != mFades) {
+            mFades = fades;
+            if (fades) {
+                post(mFadeRunnable);
+            } else {
+                removeCallbacks(mFadeRunnable);
+                mPaint.setAlpha(0xFF);
+                invalidate();
+            }
+        }
+    }
+
+    public int getFadeDelay() {
+        return mFadeDelay;
+    }
+
+    public void setFadeDelay(int fadeDelay) {
+        mFadeDelay = fadeDelay;
+    }
+
+    public int getFadeLength() {
+        return mFadeLength;
+    }
+
+    public void setFadeLength(int fadeLength) {
+        mFadeLength = fadeLength;
+        mFadeBy = 0xFF / (mFadeLength / FADE_FRAME_MS);
+    }
+
+    public int getSelectedColor() {
+        return mPaint.getColor();
+    }
+
+    public void setSelectedColor(int selectedColor) {
+        mPaint.setColor(selectedColor);
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (mViewPager == null) {
+            return;
+        }
+        final int count = mViewPager.getAdapter().getCount();
+        if (count == 0) {
+            return;
+        }
+
+        if (mCurrentPage >= count) {
+            setCurrentItem(count - 1);
+            return;
+        }
+
+        final int paddingLeft = getPaddingLeft();
+        final float pageWidth = (getWidth() - paddingLeft - getPaddingRight()) / (1f * count);
+        final float left = paddingLeft + pageWidth * (mCurrentPage + mPositionOffset);
+        final float right = left + pageWidth;
+        final float top = getPaddingTop();
+        final float bottom = getHeight() - getPaddingBottom();
+        canvas.drawRect(left, top, right, bottom, mPaint);
+    }
+
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (super.onTouchEvent(ev)) {
+            return true;
+        }
+        if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) {
+            return false;
+        }
+
+        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
+                mLastMotionX = ev.getX();
+                break;
+
+            case MotionEvent.ACTION_MOVE: {
+                final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
+                final float x = MotionEventCompat.getX(ev, activePointerIndex);
+                final float deltaX = x - mLastMotionX;
+
+                if (!mIsDragging) {
+                    if (Math.abs(deltaX) > mTouchSlop) {
+                        mIsDragging = true;
+                    }
+                }
+
+                if (mIsDragging) {
+                    mLastMotionX = x;
+                    if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {
+                        mViewPager.fakeDragBy(deltaX);
+                    }
+                }
+
+                break;
+            }
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                if (!mIsDragging) {
+                    final int count = mViewPager.getAdapter().getCount();
+                    final int width = getWidth();
+                    final float halfWidth = width / 2f;
+                    final float sixthWidth = width / 6f;
+
+                    if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) {
+                        if (action != MotionEvent.ACTION_CANCEL) {
+                            mViewPager.setCurrentItem(mCurrentPage - 1);
+                        }
+                        return true;
+                    } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) {
+                        if (action != MotionEvent.ACTION_CANCEL) {
+                            mViewPager.setCurrentItem(mCurrentPage + 1);
+                        }
+                        return true;
+                    }
+                }
+
+                mIsDragging = false;
+                mActivePointerId = INVALID_POINTER;
+                if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();
+                break;
+
+            case MotionEventCompat.ACTION_POINTER_DOWN: {
+                final int index = MotionEventCompat.getActionIndex(ev);
+                mLastMotionX = MotionEventCompat.getX(ev, index);
+                mActivePointerId = MotionEventCompat.getPointerId(ev, index);
+                break;
+            }
+
+            case MotionEventCompat.ACTION_POINTER_UP:
+                final int pointerIndex = MotionEventCompat.getActionIndex(ev);
+                final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
+                if (pointerId == mActivePointerId) {
+                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+                    mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
+                }
+                mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId));
+                break;
+        }
+
+        return true;
+    }
+
+    @Override
+    public void setViewPager(ViewPager viewPager) {
+        if (mViewPager == viewPager) {
+            return;
+        }
+        if (mViewPager != null) {
+            //Clear us from the old pager.
+            mViewPager.setOnPageChangeListener(null);
+        }
+        if (viewPager.getAdapter() == null) {
+            throw new IllegalStateException("ViewPager does not have adapter instance.");
+        }
+        mViewPager = viewPager;
+        mViewPager.setOnPageChangeListener(this);
+        invalidate();
+        post(new Runnable() {
+            @Override public void run() {
+                if (mFades) {
+                    post(mFadeRunnable);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setViewPager(ViewPager view, int initialPosition) {
+        setViewPager(view);
+        setCurrentItem(initialPosition);
+    }
+
+    @Override
+    public void setCurrentItem(int item) {
+        if (mViewPager == null) {
+            throw new IllegalStateException("ViewPager has not been bound.");
+        }
+        mViewPager.setCurrentItem(item);
+        mCurrentPage = item;
+        invalidate();
+    }
+
+    @Override
+    public void notifyDataSetChanged() {
+        invalidate();
+    }
+
+    @Override
+    public void onPageScrollStateChanged(int state) {
+        mScrollState = state;
+
+        if (mListener != null) {
+            mListener.onPageScrollStateChanged(state);
+        }
+    }
+
+    @Override
+    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+        mCurrentPage = position;
+        mPositionOffset = positionOffset;
+        if (mFades) {
+            if (positionOffsetPixels > 0) {
+                removeCallbacks(mFadeRunnable);
+                mPaint.setAlpha(0xFF);
+            } else if (mScrollState != ViewPager.SCROLL_STATE_DRAGGING) {
+                postDelayed(mFadeRunnable, mFadeDelay);
+            }
+        }
+        invalidate();
+
+        if (mListener != null) {
+            mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
+        }
+    }
+
+    @Override
+    public void onPageSelected(int position) {
+        if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+            mCurrentPage = position;
+            mPositionOffset = 0;
+            invalidate();
+            mFadeRunnable.run();
+        }
+        if (mListener != null) {
+            mListener.onPageSelected(position);
+        }
+    }
+
+    @Override
+    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        SavedState savedState = (SavedState)state;
+        super.onRestoreInstanceState(savedState.getSuperState());
+        mCurrentPage = savedState.currentPage;
+        requestLayout();
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+        SavedState savedState = new SavedState(superState);
+        savedState.currentPage = mCurrentPage;
+        return savedState;
+    }
+
+    static class SavedState extends BaseSavedState {
+        int currentPage;
+
+        public SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        private SavedState(Parcel in) {
+            super(in);
+            currentPage = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(currentPage);
+        }
+
+        @SuppressWarnings("UnusedDeclaration")
+        public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+}

+ 24 - 0
library-viewpager-indicator/src/main/res/color/vpi__dark_theme.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@color/vpi__bright_foreground_disabled_holo_dark"/>
+    <item android:state_window_focused="false" android:color="@color/vpi__bright_foreground_holo_dark"/>
+    <item android:state_pressed="true" android:color="@color/vpi__bright_foreground_holo_dark"/>
+    <item android:state_selected="true" android:color="@color/vpi__bright_foreground_holo_dark"/>
+    <!--item android:state_activated="true" android:color="@color/vpi__bright_foreground_holo_dark"/-->
+    <item android:color="@color/vpi__bright_foreground_holo_dark"/> <!-- not selected -->
+</selector>

+ 26 - 0
library-viewpager-indicator/src/main/res/color/vpi__light_theme.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@color/vpi__bright_foreground_disabled_holo_light"/>
+    <item android:state_window_focused="false" android:color="@color/vpi__bright_foreground_holo_light"/>
+    <item android:state_pressed="true" android:color="@color/vpi__bright_foreground_holo_light"/>
+    <item android:state_selected="true" android:color="@color/vpi__bright_foreground_holo_light"/>
+    <!--item android:state_activated="true" android:color="@color/vpi__bright_foreground_holo_light"/-->
+    <item android:color="@color/vpi__bright_foreground_holo_light"/> <!-- not selected -->
+    
+</selector>
+

BIN
library-viewpager-indicator/src/main/res/drawable-hdpi/vpi__tab_selected_focused_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-hdpi/vpi__tab_selected_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-hdpi/vpi__tab_selected_pressed_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-hdpi/vpi__tab_unselected_focused_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-hdpi/vpi__tab_unselected_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-hdpi/vpi__tab_unselected_pressed_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-mdpi/vpi__tab_selected_focused_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-mdpi/vpi__tab_selected_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-mdpi/vpi__tab_selected_pressed_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-mdpi/vpi__tab_unselected_focused_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-mdpi/vpi__tab_unselected_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-mdpi/vpi__tab_unselected_pressed_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-xhdpi/vpi__tab_selected_focused_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-xhdpi/vpi__tab_selected_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-xhdpi/vpi__tab_selected_pressed_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-xhdpi/vpi__tab_unselected_focused_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-xhdpi/vpi__tab_unselected_holo.9.png


BIN
library-viewpager-indicator/src/main/res/drawable-xhdpi/vpi__tab_unselected_pressed_holo.9.png


+ 34 - 0
library-viewpager-indicator/src/main/res/drawable/vpi__tab_indicator.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Non focused states -->
+    <item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/vpi__tab_unselected_holo" />
+    <item android:state_focused="false" android:state_selected="true"  android:state_pressed="false" android:drawable="@drawable/vpi__tab_selected_holo" />
+
+    <!-- Focused states -->
+    <item android:state_focused="true" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/vpi__tab_unselected_focused_holo" />
+    <item android:state_focused="true" android:state_selected="true"  android:state_pressed="false" android:drawable="@drawable/vpi__tab_selected_focused_holo" />
+
+    <!-- Pressed -->
+    <!--    Non focused states -->
+    <item android:state_focused="false" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/vpi__tab_unselected_pressed_holo" />
+    <item android:state_focused="false" android:state_selected="true"  android:state_pressed="true" android:drawable="@drawable/vpi__tab_selected_pressed_holo" />
+
+    <!--    Focused states -->
+    <item android:state_focused="true" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/vpi__tab_unselected_pressed_holo" />
+    <item android:state_focused="true" android:state_selected="true"  android:state_pressed="true" android:drawable="@drawable/vpi__tab_selected_pressed_holo" />
+</selector>

+ 129 - 0
library-viewpager-indicator/src/main/res/values/attrs.xml

@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 Jake Wharton
+     Copyright (C) 2011 Patrik Åkerfeldt
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <declare-styleable name="ViewPagerIndicator">
+        <!-- Style of the circle indicator. -->
+        <attr name="vpiCirclePageIndicatorStyle" format="reference"/>
+        <!-- Style of the icon indicator's views. -->
+        <attr name="vpiIconPageIndicatorStyle" format="reference"/>
+        <!-- Style of the line indicator. -->
+        <attr name="vpiLinePageIndicatorStyle" format="reference"/>
+        <!-- Style of the title indicator. -->
+        <attr name="vpiTitlePageIndicatorStyle" format="reference"/>
+        <!-- Style of the tab indicator's tabs. -->
+        <attr name="vpiTabPageIndicatorStyle" format="reference"/>
+        <!-- Style of the underline indicator. -->
+        <attr name="vpiUnderlinePageIndicatorStyle" format="reference"/>
+    </declare-styleable>
+
+    <attr name="centered" format="boolean" />
+    <attr name="selectedColor" format="color" />
+    <attr name="strokeWidth" format="dimension" />
+    <attr name="unselectedColor" format="color" />
+
+    <declare-styleable name="CirclePageIndicator">
+        <!-- Whether or not the indicators should be centered. -->
+        <attr name="centered" />
+        <!-- Color of the filled circle that represents the current page. -->
+        <attr name="fillColor" format="color" />
+        <!-- Color of the filled circles that represents pages. -->
+        <attr name="pageColor" format="color" />
+        <!-- Orientation of the indicator. -->
+        <attr name="android:orientation"/>
+        <!-- Radius of the circles. This is also the spacing between circles. -->
+        <attr name="radius" format="dimension" />
+        <!-- Whether or not the selected indicator snaps to the circles. -->
+        <attr name="snap" format="boolean" />
+        <!-- Color of the open circles. -->
+        <attr name="strokeColor" format="color" />
+        <!-- Width of the stroke used to draw the circles. -->
+        <attr name="strokeWidth" />
+        <!-- View background -->
+        <attr name="android:background"/>
+    </declare-styleable>
+
+    <declare-styleable name="LinePageIndicator">
+        <!-- Whether or not the indicators should be centered. -->
+        <attr name="centered" />
+        <!-- Color of the unselected lines that represent the pages. -->
+        <attr name="unselectedColor" />
+        <!-- Color of the selected line that represents the current page. -->
+        <attr name="selectedColor" />
+        <!-- Width of each indicator line. -->
+        <attr name="lineWidth" format="dimension" />
+        <!-- Width of each indicator line's stroke. -->
+        <attr name="strokeWidth" />
+        <!-- Width of the gap between each indicator line. -->
+        <attr name="gapWidth" format="dimension" />
+        <!-- View background -->
+        <attr name="android:background"/>
+    </declare-styleable>
+
+    <declare-styleable name="TitlePageIndicator">
+        <!-- Screen edge padding. -->
+        <attr name="clipPadding" format="dimension" />
+        <!-- Color of the footer line and indicator. -->
+        <attr name="footerColor" format="color" />
+        <!-- Height of the footer line. -->
+        <attr name="footerLineHeight" format="dimension" />
+        <!-- Style of the indicator. Default is triangle. -->
+        <attr name="footerIndicatorStyle">
+            <enum name="none" value="0" />
+            <enum name="triangle" value="1" />
+            <enum name="underline" value="2" />
+        </attr>
+        <!-- Height of the indicator above the footer line. -->
+        <attr name="footerIndicatorHeight" format="dimension" />
+        <!-- Left and right padding of the underline indicator. -->
+        <attr name="footerIndicatorUnderlinePadding" format="dimension" />
+        <!-- Padding between the bottom of the title and the footer. -->
+        <attr name="footerPadding" format="dimension" />
+        <!-- Position of the line. -->
+        <attr name="linePosition">
+            <enum name="bottom" value="0"/>
+            <enum name="top" value="1"/>
+        </attr>
+        <!-- Color of the selected title. -->
+        <attr name="selectedColor" />
+        <!-- Whether or not the selected item is displayed as bold. -->
+        <attr name="selectedBold" format="boolean" />
+        <!-- Color of regular titles. -->
+        <attr name="android:textColor" />
+        <!-- Size of title text. -->
+        <attr name="android:textSize" />
+        <!-- Padding between titles when bumping into each other. -->
+        <attr name="titlePadding" format="dimension" />
+        <!-- Padding between titles and the top of the View. -->
+        <attr name="topPadding" format="dimension" />
+        <!-- View background -->
+        <attr name="android:background"/>
+    </declare-styleable>
+
+    <declare-styleable name="UnderlinePageIndicator">
+        <!-- Whether or not the selected indicator fades. -->
+        <attr name="fades" format="boolean" />
+        <!-- Length of the delay to fade the indicator. -->
+        <attr name="fadeDelay" format="integer" />
+        <!-- Length of the indicator fade to transparent. -->
+        <attr name="fadeLength" format="integer" />
+        <!-- Color of the selected line that represents the current page. -->
+        <attr name="selectedColor" />
+        <!-- View background -->
+        <attr name="android:background"/>
+    </declare-styleable>
+</resources>

+ 26 - 0
library-viewpager-indicator/src/main/res/values/colors.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 Jake Wharton
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <color name="vpi__background_holo_dark">#ff000000</color>
+    <color name="vpi__background_holo_light">#fff3f3f3</color>
+    <color name="vpi__bright_foreground_holo_dark">@color/vpi__background_holo_light</color>
+    <color name="vpi__bright_foreground_holo_light">@color/vpi__background_holo_dark</color>
+    <color name="vpi__bright_foreground_disabled_holo_dark">#ff4c4c4c</color>
+    <color name="vpi__bright_foreground_disabled_holo_light">#ffb2b2b2</color>
+    <color name="vpi__bright_foreground_inverse_holo_dark">@color/vpi__bright_foreground_holo_light</color>
+    <color name="vpi__bright_foreground_inverse_holo_light">@color/vpi__bright_foreground_holo_dark</color>
+</resources>

+ 53 - 0
library-viewpager-indicator/src/main/res/values/defaults.xml

@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 Jake Wharton
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <bool name="default_circle_indicator_centered">true</bool>
+    <color name="default_circle_indicator_fill_color">#FFFFFFFF</color>
+    <color name="default_circle_indicator_page_color">#00000000</color>
+    <integer name="default_circle_indicator_orientation">0</integer>
+    <dimen name="default_circle_indicator_radius">3dp</dimen>
+    <bool name="default_circle_indicator_snap">false</bool>
+    <color name="default_circle_indicator_stroke_color">#FFDDDDDD</color>
+    <dimen name="default_circle_indicator_stroke_width">1dp</dimen>
+
+    <dimen name="default_line_indicator_line_width">12dp</dimen>
+    <dimen name="default_line_indicator_gap_width">4dp</dimen>
+    <dimen name="default_line_indicator_stroke_width">1dp</dimen>
+    <color name="default_line_indicator_selected_color">#FF33B5E5</color>
+    <color name="default_line_indicator_unselected_color">#FFBBBBBB</color>
+    <bool name="default_line_indicator_centered">true</bool>
+
+    <dimen name="default_title_indicator_clip_padding">4dp</dimen>
+    <color name="default_title_indicator_footer_color">#FF33B5E5</color>
+    <dimen name="default_title_indicator_footer_line_height">2dp</dimen>
+    <integer name="default_title_indicator_footer_indicator_style">2</integer>
+    <dimen name="default_title_indicator_footer_indicator_height">4dp</dimen>
+    <dimen name="default_title_indicator_footer_indicator_underline_padding">20dp</dimen>
+    <dimen name="default_title_indicator_footer_padding">7dp</dimen>
+    <integer name="default_title_indicator_line_position">0</integer>
+    <color name="default_title_indicator_selected_color">#FFFFFFFF</color>
+    <bool name="default_title_indicator_selected_bold">true</bool>
+    <color name="default_title_indicator_text_color">#BBFFFFFF</color>
+    <dimen name="default_title_indicator_text_size">15dp</dimen>
+    <dimen name="default_title_indicator_title_padding">5dp</dimen>
+    <dimen name="default_title_indicator_top_padding">7dp</dimen>
+
+    <bool name="default_underline_indicator_fades">true</bool>
+    <integer name="default_underline_indicator_fade_delay">300</integer>
+    <integer name="default_underline_indicator_fade_length">400</integer>
+    <color name="default_underline_indicator_selected_color">#FF33B5E5</color>
+</resources>

+ 47 - 0
library-viewpager-indicator/src/main/res/values/styles.xml

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 Jake Wharton
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <style name="Theme.PageIndicatorDefaults" parent="android:Theme">
+        <item name="vpiIconPageIndicatorStyle">@style/Widget.IconPageIndicator</item>
+        <item name="vpiTabPageIndicatorStyle">@style/Widget.TabPageIndicator</item>
+    </style>
+
+    <style name="Widget">
+    </style>
+
+    <style name="Widget.TabPageIndicator" parent="Widget">
+        <item name="android:gravity">center</item>
+        <item name="android:background">@drawable/vpi__tab_indicator</item>
+        <item name="android:paddingLeft">22dip</item>
+        <item name="android:paddingRight">22dip</item>
+        <item name="android:paddingTop">12dp</item>
+        <item name="android:paddingBottom">12dp</item>
+        <item name="android:textAppearance">@style/TextAppearance.TabPageIndicator</item>
+        <item name="android:textSize">12sp</item>
+        <item name="android:maxLines">1</item>
+    </style>
+
+    <style name="TextAppearance.TabPageIndicator" parent="Widget">
+        <item name="android:textStyle">bold</item>
+        <item name="android:textColor">@color/vpi__dark_theme</item>
+    </style>
+
+    <style name="Widget.IconPageIndicator" parent="Widget">
+        <item name="android:layout_marginLeft">6dp</item>
+        <item name="android:layout_marginRight">6dp</item>
+    </style>
+</resources>

+ 1 - 1
settings.gradle

@@ -1,2 +1,2 @@
-include ':pullToRefershLibraryMy', ':materialdialogs', ':MPAndroidChart', ':libedittextformlibrary', ':libfloatingactionbutton', ':libbdupdatesdk'
+include ':pullToRefershLibraryMy', ':materialdialogs', ':MPAndroidChart', ':libedittextformlibrary', ':libfloatingactionbutton', ':libbdupdatesdk', ':library-viewpager-indicator', ':library-swipemenu_lv'
 include ':WeiChat'