一、项目运行效果图
二、项目结构
三、自定义ViewPager指示器,继承LinearLayout
package com.yuanlei.viewpagerindicator.view;ViewPagerIndicator
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.yuanlei.viewpagerindicator.R;
import java.util.List;
/**
* Created by 袁磊 on 2017/2/22.
*/
public class ViewPagerIndicator extends LinearLayout {
private Paint mPaint;//画笔
private Path mPath;//画笔路径
private int mTriangleWidth;//三角形宽度
private int mTriangleHeight;//三角形高度
private static final float RADIO_TRIANGLE_WIDTH = 1 / 6F;//三角形宽度与所指TextView的宽度比例
//三角形底边的最大宽度
private final int DIMENSION_TRIANGLE_WIDTH_MAX = (int) (getScreenWidth() / 3 * RADIO_TRIANGLE_WIDTH);
private int mInitTranslationX;//三角形所在初始位置
private int mTranslationX;//移动时候的位置坐标
private int mTabVisibleCount;//显示的tab数量
private static final int COUNT_DEFAULT_TAB = 4;//默认显示四个
private static final int COLOR_TEXT_NORMAL = 0x77FFFFFF;//字体颜色
private static final int COLOR_TEXT_HIGHLIGHT = 0xFFFFFFFF;//字体颜色高亮显示
private List<String> mTitles;
public ViewPagerIndicator(Context context) {
this(context, null);
}
public ViewPagerIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
//获取自定义属性(可见Tab数量)
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewPagerIndicator);
mTabVisibleCount = a.getInt(R.styleable.ViewPagerIndicator_visible_tab_count, COUNT_DEFAULT_TAB);
if (mTabVisibleCount < 0) {
mTabVisibleCount = COUNT_DEFAULT_TAB;//获取到显示的Tab后在onFinishInflate()方法中使用
}
a.recycle();//释放
//初始化画笔
mPaint = new Paint();
mPaint.setAntiAlias(true);//抗锯齿
mPaint.setColor(Color.parseColor("#ffffffff"));
mPaint.setStyle(Paint.Style.FILL);
mPaint.setPathEffect(new CornerPathEffect(3));//圆角
}
@Override
protected void dispatchDraw(Canvas canvas) {
//绘制三角形
canvas.save();
canvas.translate(mInitTranslationX + mTranslationX, getHeight() + 2);
canvas.drawPath(mPath, mPaint);
canvas.restore();
super.dispatchDraw(canvas);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mTriangleWidth = (int) (w / mTabVisibleCount * RADIO_TRIANGLE_WIDTH);//三角形宽度
mTriangleWidth = Math.min(mTriangleWidth, DIMENSION_TRIANGLE_WIDTH_MAX);//获取最大值和当前值中最小的
mInitTranslationX = w / mTabVisibleCount / 2 - mTriangleWidth / 2;//三角形初始坐标
initTriangle();
}
/**
* 通过布局添加Tab的时候
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
int cCount = getChildCount();
if (cCount == 0) return;
for (int i = 0; i < cCount; i++) {
View view = getChildAt(i);//得到每一个子View
//注意:layoutParams为LinearLayout.LayoutParams
LayoutParams lp = (LayoutParams) view.getLayoutParams();
lp.weight = 0;
lp.width = getScreenWidth() / mTabVisibleCount;//每一个子View宽度=屏幕宽度/显示的Tab数量
view.setLayoutParams(lp);
}
setItemClickEvent();//设置Tab点击事件
}
/**
* 初始化三角形
*/
private void initTriangle() {
mTriangleHeight = mTriangleWidth / 2;
mPath = new Path();
mPath.moveTo(0, 0);//画笔从三角形左下角(0,0)开始
mPath.lineTo(mTriangleWidth, 0);
mPath.lineTo(mTriangleWidth / 2, -mTriangleHeight);//y轴向上是负
mPath.close();
}
/**
* 指示器跟随手指进行移动
*
* @param position
* @param offset
*/
public void scroll(int position, float offset) {
int tabWidth = getWidth() / mTabVisibleCount;
mTranslationX = (int) (tabWidth * (offset + position));
/**
* 容器(整个指示器)移动,当Tab处于移动至最后一个时
*/
if (position >= (mTabVisibleCount - 2) && //三角形所在位置数>(当前显示Tab的个数-2)(position从0开始)
offset > 0 //偏移量>0
&& getChildCount() > mTabVisibleCount //所有Tab个数>当前显示Tab个数
&& position != (mTitles.size() - 2)) { //位置!=tab总个数-2(position从0开始)
if (mTabVisibleCount != 1) { //防止mTabVisibleCount - 2=-1
this.scrollTo((position - (mTabVisibleCount - 2)) * tabWidth + (int) (tabWidth * offset), 0);
} else {
this.scrollTo(position * tabWidth + (int) (tabWidth * offset), 0);
}
}
//重绘方法
invalidate();
}
/**
* 获得屏幕宽度
*
* @return
*/
private int getScreenWidth() {
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.widthPixels;
}
/**
* 动态添加子View
*
* @param titles
*/
public void setTabItemTitles(List<String> titles) {
if (titles != null && titles.size() > 0) {
this.removeAllViews();//先清理所有View
mTitles = titles;
for (String title : mTitles) {
addView(generateTextView(title));
}
setItemClickEvent();//设置Tab点击事件
}
}
/**
* 设置可见的Tab数量
* 注意:需要在setTabItemTitles()之前调用
*
* @param count
*/
public void setVisibleTabCount(int count) {
mTabVisibleCount = count;
}
/**
* 根据title创建tab
*
* @param title
* @return
*/
private View generateTextView(String title) {
TextView tv = new TextView(getContext());
LinearLayout.LayoutParams lp = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
lp.width = getScreenWidth() / mTabVisibleCount;
tv.setText(title);
tv.setGravity(Gravity.CENTER);
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
tv.setTextColor(COLOR_TEXT_NORMAL);
tv.setLayoutParams(lp);
return tv;
}
private ViewPager mViewPager;
/**
* 自定义接口
* 复制源代码中的三个抽象方法
*/
public interface PagerOnChangeListener {
void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
void onPageSelected(int position);
void onPageScrollStateChanged(int state);
}
//接口成员
private PagerOnChangeListener mListener;
//公布接口
public void setPagerOnChangeListener(PagerOnChangeListener listener) {
this.mListener = listener;
}
/**
* 设置关联的ViewPager
*
* @param viewPager
* @param position 当前位置
*/
public void setViewPager(ViewPager viewPager, int position) {
mViewPager = viewPager;
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
/**
* 三角形总的偏移量=
* tabWidth(三角形移动距离为一个tabWidth)*positionOffset(ViewPger的偏移量)+
* postion*tabWidth
*/
scroll(position, positionOffset);
//通过自定义接口将回调方法公布出去
if (mListener != null) {
mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
@Override
public void onPageSelected(int position) {
//通过自定义接口将回调方法公布出去
if (mListener != null) {
mListener.onPageSelected(position);
}
highLightTextView(position);
}
@Override
public void onPageScrollStateChanged(int state) {
//通过自定义接口将回调方法公布出去提供外部使用
if (mListener != null) {
mListener.onPageScrollStateChanged(state);
}
}
});
mViewPager.setCurrentItem(position);
highLightTextView(position);
}
/**
* 重置Tab的文本颜色
*/
private void resetTextView() {
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
if (view instanceof TextView) {
((TextView) view).setTextColor(COLOR_TEXT_NORMAL);
}
}
}
/**
* 高亮某个Tab的文本
*
* @param position
*/
private void highLightTextView(int position) {
resetTextView();//高亮之前全部文本颜色全部重置
View view = getChildAt(position);
if (view instanceof TextView) {
((TextView) view).setTextColor(COLOR_TEXT_HIGHLIGHT);
}
}
/**
* 设置Tab点击事件
*/
private void setItemClickEvent() {
int cCount = getChildCount();
for (int i = 0; i < cCount; i++) {
final int j = i;//供内部类调用
View view = getChildAt(i);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mViewPager.setCurrentItem(j);
}
});
}
}
}
四、自定义属性: attrs.xml
<?xml version="1.0" encoding="utf-8"?>attrs
<resources>
<attr name="visible_tab_count" format="integer" />
<declare-styleable name="ViewPagerIndicator">
<attr name="visible_tab_count" />
</declare-styleable>
</resources>
五、一个简单的Fragment,供ViewPager使用
package com.yuanlei.viewpagerindicator.viewpagerindicator;VpSimpleFragment
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
/**
* Created by 袁磊 on 2017/2/22.
*/
public class VpSimpleFragment extends Fragment {
private String mTitle;
public static final String BUNDLE_TITLE = "title";
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Bundle bundle = getArguments();
if (bundle != null) {
mTitle = bundle.getString(BUNDLE_TITLE);
}
TextView tv = new TextView(getActivity());
tv.setText(mTitle);
tv.setGravity(Gravity.CENTER);
return tv;
}
public static VpSimpleFragment newInstanse(String title) {
Bundle bundle = new Bundle();
bundle.putString(BUNDLE_TITLE, title);
VpSimpleFragment fragment = new VpSimpleFragment();
fragment.setArguments(bundle);
return fragment;
}
}
六、设置没有标题
七、使用
布局文件:
<?xml version="1.0" encoding="utf-8"?>activity_main
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.yuanlei.viewpagerindicator.view.ViewPagerIndicator
android:id="@+id/my_vp_indicator"
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@drawable/bg_top"
android:orientation="horizontal" />
<android.support.v4.view.ViewPager
android:id="@+id/vp_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
代码:
package com.yuanlei.viewpagerindicator.viewpagerindicator;MainActivity
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.yuanlei.viewpagerindicator.R;
import com.yuanlei.viewpagerindicator.view.ViewPagerIndicator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ViewPagerIndicator myIndicator;
private ViewPager vpContent;
private List<String> mTitles = Arrays.asList("短信1", "收藏2", "推荐3", "短信4", "收藏5", "推荐6", "短信7", "收藏8", "推荐9");
private List<VpSimpleFragment> mContents = new ArrayList<>();
private FragmentPagerAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
initViews();
initDatas();
myIndicator.setVisibleTabCount(4);
myIndicator.setTabItemTitles(mTitles);
myIndicator.setViewPager(vpContent, 0);
vpContent.setAdapter(mAdapter);
}
private void initViews() {
vpContent = (ViewPager) findViewById(R.id.vp_content);
myIndicator = (ViewPagerIndicator) findViewById(R.id.my_vp_indicator);
}
private void initDatas() {
for (String title : mTitles) {
VpSimpleFragment fragment = VpSimpleFragment.newInstanse(title);
mContents.add(fragment);
}
mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
return mContents.get(position);
}
@Override
public int getCount() {
return mContents.size();
}
};
}
}