仿网易新闻的顶部导航指示器,字节Android高工面试

View nextTab = tabsContainer.getChildAt(currentPosition + 1);

final float nextTabLeft = nextTab.getLeft();

final float nextTabRight = nextTab.getRight();

lineLeft = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset) *

lineLeft);

lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) *

lineRight);

}

canvas.drawRect(lineLeft, height - indicatorHeight, lineRight, height, rectPaint);

// draw underline

rectPaint.setColor(underlineColor);

canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint);

// draw divider

dividerPaint.setColor(dividerColor);

for (int i = 0; i < tabCount - 1; i++) {

View tab = tabsContainer.getChildAt(i);

if (!isSame) {

canvas.drawLine(tab.getRight(), dividerPadding, tab.getRight(),

height - dividerPadding, dividerPaint);

} else {

canvas.drawLine(tab.getRight() + horizontalPadding, dividerPadding,

tab.getRight() + horizontalPadding, height - dividerPadding, dividerPaint);

}

}

}

private class PageListener implements OnPageChangeListener {

@Override

public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

currentPosition = position;

currentPositionOffset = positionOffset;

View child = tabsContainer.getChildAt(position);

int width = child.getWidth();

if (isSame) {

width += horizontalPadding * 2;

}

Log.i(TAG, “onPageScrolled:width=” + width);

// 调用这个方法是HorizontalScrollView滑动到相应的位置

scrollToChild(position, (int) (positionOffset * width));

//调用这个方法重新绘制

invalidate();

if (delegatePageListener != null) {

delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels);

}

}

@Override

public void onPageScrollStateChanged(int state) {

if (delegatePageListener != null) {

delegatePageListener.onPageScrollStateChanged(state);

}

}

@Override

public void onPageSelected(int position) {

if (delegatePageListener != null) {

delegatePageListener.onPageSelected(position);

}

}

}

public void setIndicatorColor(int indicatorColor) {

this.indicatorColor = indicatorColor;

invalidate();

}

public void setIndicatorColorResource(int resId) {

this.indicatorColor = getResources().getColor(resId);

invalidate();

}

public int getIndicatorColor() {

return this.indicatorColor;

}

public void setIndicatorHeight(int indicatorLineHeightPx) {

this.indicatorHeight = indicatorLineHeightPx;

invalidate();

}

public int getIndicatorHeight() {

return indicatorHeight;

}

public void setUnderlineColor(int underlineColor) {

this.underlineColor = underlineColor;

invalidate();

}

public void setUnderlineColorResource(int resId) {

this.underlineColor = getResources().getColor(resId);

invalidate();

}

public int getUnderlineColor() {

return underlineColor;

}

public void setDividerColor(int dividerColor) {

this.dividerColor = dividerColor;

invalidate();

}

public void setDividerColorResource(int resId) {

this.dividerColor = getResources().getColor(resId);

invalidate();

}

public int getDividerColor() {

return dividerColor;

}

public void setUnderlineHeight(int underlineHeightPx) {

this.underlineHeight = underlineHeightPx;

invalidate();

}

public int getUnderlineHeight() {

return underlineHeight;

}

public void setDividerPadding(int dividerPaddingPx) {

this.dividerPadding = dividerPaddingPx;

invalidate();

}

public int getDividerPadding() {

return dividerPadding;

}

public void setScrollOffset(int scrollOffsetPx) {

this.scrollOffset = scrollOffsetPx;

invalidate();

}

public int getScrollOffset() {

return scrollOffset;

}

public void setExpand(boolean expand) {

this.isExpand = expand;

requestLayout();

}

public boolean getExpand() {

return isExpand;

}

public boolean isTextAllCaps() {

return textAllCaps;

}

public void setAllCaps(boolean textAllCaps) {

this.textAllCaps = textAllCaps;

}

public void setTextSize(int textSizePx) {

this.tabTextSize = textSizePx;

updateTabStyles();

}

public int getTextSize() {

return tabTextSize;

}

public void setTextColor(int textColor) {

this.tabTextColor = textColor;

updateTabStyles();

}

public void setTextColorResource(int resId) {

this.tabTextColor = getResources().getColor(resId);

updateTabStyles();

}

public int getTextColor() {

return tabTextColor;

}

public void setTypeface(Typeface typeface, int style) {

this.tabTypeface = typeface;

this.tabTypefaceStyle = style;

updateTabStyles();

}

public void setTabBackground(int resId) {

this.tabBackgroundResId = resId;

}

public int getTabBackground() {

return tabBackgroundResId;

}

public void setTabPaddingLeftRight(int paddingPx) {

this.horizontalPadding = paddingPx;

updateTabStyles();

}

public int getTabPaddingLeftRight() {

return horizontalPadding;

}

@Override

public void onRestoreInstanceState(Parcelable state) {

SavedState savedState = (SavedState) state;

super.onRestoreInstanceState(savedState.getSuperState());

currentPosition = savedState.currentPosition;

requestLayout();

}

@Override

public Parcelable onSaveInstanceState() {

Parcelable superState = super.onSaveInstanceState();

SavedState savedState = new SavedState(superState);

savedState.currentPosition = currentPosition;

return savedState;

}

//用来保存状态

static class SavedState extends BaseSavedState {

int currentPosition;

public SavedState(Parcelable superState) {

super(superState);

}

private SavedState(Parcel in) {

super(in);

currentPosition = in.readInt();

}

@Override

public void writeToParcel(Parcel dest, int flags) {

super.writeToParcel(dest, flags);

dest.writeInt(currentPosition);

}

public static final Creator CREATOR = new Creator() {

@Override

public SavedState createFromParcel(Parcel in) {

return new SavedState(in);

}

@Override

public SavedState[] newArray(int size) {

return new SavedState[size];

}

};

}

/**

  • 定义4种模式

*/

public enum IndicatorMode {

// 给枚举传入自定义的int值

MODE_WRAP_EXPAND_SAME(1),// 可扩展,导航线跟标题相等

MODE_WRAP_EXPAND_NOSAME(2),// 可扩展,导标不相等

MODE_WEIGHT_EXPAND_SAME(3),// 可扩展,导航线跟标题相等

MODE_WEIGHT_EXPAND_NOSAME(4);// 可扩展,导标不相等

private int value;

IndicatorMode(int value) {

this.value = value;

}

public int getValue() {

return value;

}

}

}

思路主要 可以分为以下几个步骤

1)在构造方法里面初始化各种工作,包括一些自定义属性,画笔等等

public TabPagerIndicator(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

//初始化各种工作

//根据IndicatorMode初始化各个变量

setIndicatorMode(curMode);

//初始化自定义属性

obtainAttrs(context, attrs);

rectPaint = new Paint();

rectPaint.setAntiAlias(true);

rectPaint.setStyle(Style.FILL);

dividerPaint = new Paint();

dividerPaint.setAntiAlias(true);

dividerPaint.setStrokeWidth(dividerWidth);

if (locale == null) {

locale = getResources().getConfiguration().locale;

}

}

2)通过setViewPager()这个方法将控件与ViewPager联系起来

public void setViewPager(ViewPager pager) {

this.pager = pager;

if (pager.getAdapter() == null) {

throw new IllegalStateException(“ViewPager does not have adapter instance.”);

}

pager.addOnPageChangeListener(pageListener);

notifyDataSetChanged();

}

public void notifyDataSetChanged() {

//先移除掉所有的View ,防止重复添加

tabsContainer.removeAllViews();

tabCount = pager.getAdapter().getCount();

for (int i = 0; i < tabCount; i++) {

//区分是文字还是Icon的导航

if (pager.getAdapter() instanceof IconTabProvider) {

addIconTab(i, ((IconTabProvider) pager.getAdapter()).getPageIconResId(i));

} else {

addTextTab(i, pager.getAdapter().getPageTitle(i).toString());

}

}

updateTabStyles();

//监听视图树,在绘制完毕后调用相关的方法完成初始化工作

getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

@SuppressWarnings(“deprecation”)

@SuppressLint(“NewApi”)

@Override

public void onGlobalLayout() {

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {

getViewTreeObserver().removeGlobalOnLayoutListener(this);

} else {

getViewTreeObserver().removeOnGlobalLayoutListener(this);

}

currentPosition = pager.getCurrentItem();

scrollToChild(currentPosition, 0);

}

});

}

3)在 onDraw里面根据不同的 Mode绘制不同的下划线样式

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if (isInEditMode() || tabCount == 0) {

return;

}

final int height = getHeight();

// draw indicator line

rectPaint.setColor(indicatorColor);

// default: line below current tab

View currentTab = tabsContainer.getChildAt(currentPosition);

float lineLeft = currentTab.getLeft();

float lineRight = currentTab.getRight();

// if there is an offset, start interpolating left and right coordinates between current

// and next tab

if (currentPositionOffset > 0f && currentPosition < tabCount - 1) {

View nextTab = tabsContainer.getChildAt(currentPosition + 1);

final float nextTabLeft = nextTab.getLeft();

final float nextTabRight = nextTab.getRight();

lineLeft = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset) *

lineLeft);

lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) *

lineRight);

}

canvas.drawRect(lineLeft, height - indicatorHeight, lineRight, height, rectPaint);

// draw underline

rectPaint.setColor(underlineColor);

canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint);

// draw divider

dividerPaint.setColor(dividerColor);

for (int i = 0; i < tabCount - 1; i++) {

View tab = tabsContainer.getChildAt(i);

if (!isSame) {

canvas.drawLine(tab.getRight(), dividerPadding, tab.getRight(),

height - dividerPadding, dividerPaint);

} else {

canvas.drawLine(tab.getRight() + horizontalPadding, dividerPadding,

tab.getRight() + horizontalPadding, height - dividerPadding, dividerPaint);

}

}

}

4)在ViewPager滑动的时候,会调用相应的方法来刷新界面,因为前面我们在setViewPager的时候为其添加pageListener监听器

public void setViewPager(ViewPager pager) {

//省略了若干方法

pager.addOnPageChangeListener(pageListener);

}

private class PageListener implements OnPageChangeListener {

@Override

public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

currentPosition = position;

currentPositionOffset = positionOffset;

View child = tabsContainer.getChildAt(position);

int width = child.getWidth();

if (isSame) {

width += horizontalPadding * 2;

}

Log.i(TAG, “onPageScrolled:width=” + width);

// 调用这个方法是HorizontalScrollView滑动到相应的位置

scrollToChild(position, (int) (positionOffset * width));

//调用这个方法重新绘制

invalidate();

if (delegatePageListener != null) {

delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels);

}

}

@Override

public void onPageScrollStateChanged(int state) {

if (delegatePageListener != null) {

delegatePageListener.onPageScrollStateChanged(state);

}

}

@Override

public void onPageSelected(int position) {

if (delegatePageListener != null) {

delegatePageListener.onPageSelected(position);

}

}

}

源码到此分析为止

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

同时我经过多年的收藏目前也算收集到了一套完整的学习资料以及高清详细的Android架构进阶学习导图及笔记免费分享给大家,希望对想成为架构师的朋友有一定的参考和帮助。

下面是部分资料截图,诚意满满:特别适合有开发经验的Android程序员们学习。

资料免费领取方式:点击我的GitHub~

不论遇到什么困难,都不应该成为我们放弃的理由!

}

}

源码到此分析为止

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-N0JZZCRt-1710894774960)]
[外链图片转存中…(img-jfdhiq0y-1710894774960)]
[外链图片转存中…(img-8fQeiJ65-1710894774961)]
[外链图片转存中…(img-Hu8cCBeN-1710894774961)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-0Tw7BXpH-1710894774962)]

最后

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

同时我经过多年的收藏目前也算收集到了一套完整的学习资料以及高清详细的Android架构进阶学习导图及笔记免费分享给大家,希望对想成为架构师的朋友有一定的参考和帮助。

下面是部分资料截图,诚意满满:特别适合有开发经验的Android程序员们学习。

[外链图片转存中…(img-iUnE2cp2-1710894774963)]

资料免费领取方式:点击我的GitHub~

不论遇到什么困难,都不应该成为我们放弃的理由!

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值