使用:
xml布局一个指示器View和viewPager
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.haiming.myapplication.incodor.TrackIndicatorView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:id="@+id/indicator_view"
/>
<androidx.viewpager.widget.ViewPager
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/view_page"
android:layout_weight="1"
/>
</LinearLayout>
在activity中给指示器和viewPager设置adapter:
public class ViewPagerActivity extends AppCompatActivity {
private String[] items ={"直播视频","推荐段子你","精华","直播","推荐","视频","图片","段子你好的","精华","直播","推荐"};
private TrackIndicatorView mTrackIndicatorView;
private ViewPager mViewPager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_page);
init();
}
private void init() {
mTrackIndicatorView = findViewById(R.id.indicator_view);
mViewPager = findViewById(R.id.view_page);
initIndicator();
initViewPager();
}
private void initViewPager() {
//缓存当前页面的左右各两面
mViewPager.setOffscreenPageLimit(2);
mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
@NonNull
@Override
public Fragment getItem(int position) {
return ItemFragment.newInstance(items[position]);
}
@Override
public int getCount() {
return items.length;
}
});
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
private void initIndicator() {
IndicatorAdapter<String> mDapter = new IndicatorAdapter<String>(Arrays.asList(items)) {
@Override
public View getView(int position, ViewGroup parent, List<String> dataList) {
TextView textView = new TextView(ViewPagerActivity.this);
textView.setTextColor(Color.BLACK);
textView.setTextSize(12);
textView.setText(dataList.get(position));
return textView;
}
@Override
public int getCount() {
return items.length;
}
@Override
public void heightLighIndicator(View view) {
TextView textView = (TextView) view;
textView.setTextColor(Color.RED);
}
@Override
public void restoreIndicator(View view) {
TextView textView = (TextView) view;
textView.setTextColor(Color.BLACK);
}
};
mTrackIndicatorView.setAdapter(mDapter,mViewPager);
}
}
TrackIndicatorView源码
适配器,自定义view,和选择和未选择时view的状态
public abstract class IndicatorAdapter<T> {
private List<T> mDataList;
public IndicatorAdapter(List<T> dataList){
this.mDataList=dataList;
}
public abstract View getView(int position, ViewGroup parent,List<T> dataList);
public abstract int getCount();
// 高亮当前位置
public abstract void heightLighIndicator(View view);
// 重置上一位置
public abstract void restoreIndicator(View view);
//底部指示器
public View getBottomTrackView(){
return null;
}
public void setDataList(List<T> dataList) {
mDataList = dataList;
}
public List<T> getDataList() {
return mDataList;
}
}
作用是拿到adapter和viewpager,这样就能拿到view,并根据指示器和viewPager的变化相互转换。还有就是确定每个itemwidth()的大小。
/**
* viewPager 滑动指示器
*/
public class TrackIndicatorView extends HorizontalScrollView implements ViewPager.OnPageChangeListener {
public IndicatorAdapter mAdapter;
private IndicatorGroupView mIndicatorGroup;
//一屏幕显示多少个
private int mTabVisibleNums = 0;
private int mItemWidth;
//当前切换位置
private int mCurrentPosition = 0;
public TrackIndicatorView(Context context) {
this(context,null);
}
public TrackIndicatorView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public TrackIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mIndicatorGroup = new IndicatorGroupView(context);
addView(mIndicatorGroup);
//4.指定item的宽度 自定义属性
initAttrbute(context,attrs);
}
/**
* 初始化
* @param context
* @param attrs
*/
private void initAttrbute(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TrackIndicatorView);
//可设置一屏幕item最多占的数量
mTabVisibleNums = array.getInteger(R.styleable.TrackIndicatorView_tabVisibleNums,0);
array.recycle();
}
/**
* 设置适配器
* @param adapter
* @param viewPager
*/
private ViewPager mViewPager;
public void setAdapter(IndicatorAdapter adapter, ViewPager viewPager){
//与viewPager绑定
this.mViewPager=viewPager;
setAdapter(adapter);
if(viewPager == null){
throw new NullPointerException("viewPager id is null");
}
//设置滑动监听
mViewPager.addOnPageChangeListener(this);
}
public void setAdapter(IndicatorAdapter adapter){
if(adapter == null){
throw new NullPointerException("adapter id is null");
}
mAdapter=adapter;
int itemCount = mAdapter.getCount();
for(int i=0 ; i<itemCount;i++){
//通过适配器拿到itemview,并为每个view设置点击事件
View itemView = mAdapter.getView(i,mIndicatorGroup);
mIndicatorGroup.addItemView(itemView);
// 设置点击事件
if(mViewPager != null){
switchItemClick(itemView,i);
}
}
//默认第一个为红色
mAdapter.heightLighIndicator(mIndicatorGroup.getItemAt(mCurrentPosition));
}
private void switchItemClick(View itemView, final int position) {
itemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//ViewPager划到相应的页数
mViewPager.setCurrentItem(position);
//给itemView移动到中间的位置
SmoothScrollCurrentIndicator(position);
//移动底部的指示器
mIndicatorGroup.scrollBottomIndicator(position);
}
});
}
private void SmoothScrollCurrentIndicator(int position) {
//当前移出屏幕的位置
float totalScroll = (position)*mItemWidth;
//中间左边的位置
int offsetScroll = (getWidth()- mItemWidth)/2;
//减去中间左边的位置
final int finalScroll = (int) (totalScroll-offsetScroll);
smoothScrollTo(finalScroll,0);
}
//不断的滚动指示器
private void scrollCurrentIndicator(int position, float positionOffset) {
//当前移出屏幕的位置
float totalScroll = (position+positionOffset)*mItemWidth;
//中间左边的位置
int offsetScroll = (getWidth()- mItemWidth)/2;
//减去中间左边的位置
final int finalScroll = (int) (totalScroll-offsetScroll);
Log.d("移动位置",""+finalScroll);
scrollTo(finalScroll,0);
}
private boolean isExecuteScroll = false;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(isExecuteScroll){
//滚动的时候,会不断的调用
scrollCurrentIndicator(position,positionOffset);
mIndicatorGroup.scrollBottomTrack(position,positionOffset);
}
}
@Override
public void onPageSelected(int position) {
//将上一个位置重置
mAdapter.restoreIndicator(mIndicatorGroup.getItemAt(mCurrentPosition));
//将当前位置点亮
mCurrentPosition = position;
mAdapter.heightLighIndicator(mIndicatorGroup.getItemAt(mCurrentPosition));
}
@Override
public void onPageScrollStateChanged(int state) {
if(state == 1){
isExecuteScroll =true;
}
if(state == 0){
isExecuteScroll =false;
}
}
//只有在onLayout()时子布局的大小才确定,会在这里重新分配每个ItemWidth
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mItemWidth = getItemWidth();
//循环指定item的宽度
for(int i=0;i<mAdapter.getCount();i++){
mIndicatorGroup.getItemAt(i).getLayoutParams().width=mItemWidth;
}
// 传给底部指示器
mIndicatorGroup.addBottomTrackView(mAdapter.getBottomTrackView(),mItemWidth);
requestLayout();
}
/**
* 获取item的宽度
* @return
*/
private int getItemWidth() {
//拿到全屏的宽度
int parentWidth = getWidth();
//指定全屏的数量
if(mTabVisibleNums != 0){
//取平均值
return parentWidth/mTabVisibleNums;
}
//没有指定的,我们取最大的
int itemWidth = 0;
//获取最宽的
int maxItemWidth = 0;
int allWidh = 0;
for(int i=0 ; i< mAdapter.getCount();i++){
//拿到最大的宽度
int currentItemWidth = mIndicatorGroup.getItemAt(i).getMeasuredWidth();
maxItemWidth = Math.max(currentItemWidth,maxItemWidth);
allWidh+=currentItemWidth;
}
itemWidth = maxItemWidth;
//最后算一次
//如果全部加起来的最大宽度 小于 屏幕宽度
if(itemWidth*mAdapter.getCount() < parentWidth){
itemWidth = parentWidth/mAdapter.getCount();
}
return itemWidth;
}
}
HorizontalScrollView 只能有一个子view,所以我们通过一个ViewGroup来装载我们的指示器的全部布局。
/**
* 底部的指示器
*/
public class IndicatorGroupView extends FrameLayout {
//文字指示器条目的容器
private LinearLayout mIndicatorGroup;
//底部指示器,通过adapter拿到
private View mBottomTrackView;
//底部指示器宽度
private int mItemWidth;
//底部指示器的LayoutParams
private FrameLayout.LayoutParams params;
//底部指示器的Margin初始值
private int mInitMargin;
public IndicatorGroupView(@NonNull Context context) {
this(context,null);
}
public IndicatorGroupView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public IndicatorGroupView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mIndicatorGroup = new LinearLayout(context);
addView(mIndicatorGroup);
}
/**
* 添加文字itemVeiw
* @param itemView
*/
public void addItemView(View itemView) {
mIndicatorGroup.addView(itemView);
}
//获取文字view
public View getItemAt(int i) {
return mIndicatorGroup.getChildAt(i);
}
public void addBottomTrackView(View bottomTrackView, int itemWidth) {
if(bottomTrackView == null){
return;
}
this.mBottomTrackView = bottomTrackView;
this.mItemWidth = itemWidth;
addView(mBottomTrackView);
//让它在底部,一个条目的宽度
params = (LayoutParams) mBottomTrackView.getLayoutParams();
params.gravity= Gravity.BOTTOM;
int trackWidth = params.width;
if(params.width == ViewGroup.LayoutParams.MATCH_PARENT){
trackWidth=mItemWidth;
}
//设置的宽度过大
if(params.width>mItemWidth){
trackWidth = mItemWidth;
}
params.width=trackWidth;
mInitMargin = (mItemWidth-trackWidth)/2;
params.leftMargin = mInitMargin;
mBottomTrackView.setLayoutParams(params);
requestLayout();
}
/**
* 滚动底部的显示器,滑动时
* @param position
* @param positionOffset
*/
public void scrollBottomTrack(int position, float positionOffset) {
if(mBottomTrackView == null){
return;
}
int leftMargin = (int) ((position+positionOffset)*mItemWidth);
params.leftMargin=leftMargin+mInitMargin;
mBottomTrackView.setLayoutParams(params);
}
//点击时
public void scrollBottomIndicator(int position) {
if(mBottomTrackView == null){
return;
}
int leftMargin = (int) ((position)*mItemWidth)+mInitMargin;
int current = params.leftMargin;
int distance = leftMargin-current;
//带动画
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(current,leftMargin).setDuration((long) (Math.abs(distance)*0.5f));
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentLeft = (float) animation.getAnimatedValue();
params.leftMargin= (int) currentLeft;
mBottomTrackView.setLayoutParams(params);
}
});
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.start();
}
}
网友评论