android 底部tab效果,Android 仿微信底部渐变Tab效果

先来看一下效果图

d67fd5727b0b19c38d2459186e7777f7.gif

除了第三个的发现Tab有所差别外,其他的基本还原了微信的底部Tab渐变效果

每个Tab都是一个自定义View,根据ImageView的tint属性来实现颜色渐变效果,tint属性的使用可以看我的上一篇文章

我将自定义View命名为ShadeView,包含四个自定义属性

意思分别为图标、背景色、底部文本、底部文本大小

ShadeView的定义如下,主要是进行绘图操作,并向外提供改变透明度和图标的方法

public class ShadeView extends View {

/**

* 图标

*/

private Bitmap iconBitmap;

/**

* 图标背景色

*/

private int iconBackgroundColor;

/**

* 图标默认背景色

*/

private final int DEFAULT_ICON_BACKGROUND_COLOR = 0x3CAF36;

/**

* 图标底部文本

*/

private String text;

/**

* 图标底部文字默认大小(sp)

*/

private final int DEFAULT_TEXT_SIZE = 12;

/**

* 图标底部文字默认颜色

*/

private final int DEFAULT_TEXT_COLOR = 0x2B2B2B;

/**

* 图标绘制范围

*/

private Rect iconRect;

/**

* 文字笔画

*/

private Paint textPaint;

/**

* 文字范围

*/

private Rect textBound;

/**

* 透明度(0.0-1.0)

*/

private float mAlpha;

private Bitmap mBitmap;

public ShadeView(Context context, AttributeSet attrs) {

super(context, attrs);

//获取自定义属性值

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ShadeView);

BitmapDrawable drawable = (BitmapDrawable) typedArray.getDrawable(R.styleable.ShadeView_icon);

if (drawable != null) {

iconBitmap = drawable.getBitmap();

}

iconBackgroundColor = typedArray.getColor(R.styleable.ShadeView_color, DEFAULT_ICON_BACKGROUND_COLOR);

text = typedArray.getString(R.styleable.ShadeView_text);

int textSize = (int) typedArray.getDimension(R.styleable.ShadeView_text_size,

TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE, getResources().getDisplayMetrics()));

//资源回收

typedArray.recycle();

//初始化

textBound = new Rect();

textPaint = new Paint();

textPaint.setTextSize(textSize);

textPaint.setColor(DEFAULT_TEXT_COLOR);

textPaint.setAntiAlias(true);

textPaint.setDither(true);

textPaint.getTextBounds(text, 0, text.length(), textBound);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

//因为图标是正方形且需要居中显示的,所以View的大小去掉padding和文字所占空间后,

//剩余的空间的宽和高的最小值才是图标的边长

int bitmapSide = Math.min(getMeasuredWidth() - getPaddingLeft()

- getPaddingRight(), getMeasuredHeight() - getPaddingTop()

- getPaddingBottom() - textBound.height());

int left = getMeasuredWidth() / 2 - bitmapSide / 2;

int top = (getMeasuredHeight() - textBound.height()) / 2 - bitmapSide / 2;

//获取图标的绘制范围

iconRect = new Rect(left, top, left + bitmapSide, top + bitmapSide);

}

@Override

protected void onDraw(Canvas canvas) {

//进一取整

int alpha = (int) Math.ceil((255 * mAlpha));

//绘制原图标

canvas.drawBitmap(iconBitmap, null, iconRect, null);

setupTargetBitmap(alpha);

drawSourceText(canvas, alpha);

drawTargetText(canvas, alpha);

canvas.drawBitmap(mBitmap, 0, 0, null);

}

/**

* 在mBitmap上绘制以iconBackgroundColor颜色为Dst,DST_IN模式下的图标

*

* @param alpha Src颜色的透明度

*/

private void setupTargetBitmap(int alpha) {

mBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Config.ARGB_8888);

Canvas canvas = new Canvas(mBitmap);

Paint paint = new Paint();

paint.setColor(iconBackgroundColor);

paint.setAntiAlias(true);

paint.setDither(true);

paint.setAlpha(alpha);

//在图标背后先绘制一层iconBackgroundColor颜色的背景

canvas.drawRect(iconRect, paint);

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

paint.setAlpha(255);

//在mBitmap上绘制以iconBackgroundColor颜色为Dst,DST_IN模式下的图标

canvas.drawBitmap(iconBitmap, null, iconRect, paint);

}

/**

* 绘制默认状态下的字体

*

* @param canvas Canvas

* @param alpha 字体颜色透明度

*/

private void drawSourceText(Canvas canvas, int alpha) {

textPaint.setColor(DEFAULT_TEXT_COLOR);

textPaint.setAlpha(255 - alpha);

canvas.drawText(text, iconRect.left + iconRect.width() / 2 - textBound.width() / 2,

iconRect.bottom + textBound.height(), textPaint);

}

/**

* 绘制滑动到该标签时的字体

*

* @param canvas Canvas

* @param alpha 字体颜色透明度

*/

private void drawTargetText(Canvas canvas, int alpha) {

textPaint.setColor(iconBackgroundColor);

textPaint.setAlpha(alpha);

canvas.drawText(text, iconRect.left + iconRect.width() / 2 - textBound.width() / 2,

iconRect.bottom + textBound.height(), textPaint);

}

/**

* 设置图标透明度并重绘

*

* @param alpha 透明度

*/

public void setIconAlpha(float alpha) {

if (mAlpha != alpha) {

this.mAlpha = alpha;

invalidateView();

}

}

public void setIconBitmap(Context context, int resourceID) {

BitmapDrawable bitmapDrawable = (BitmapDrawable) ContextCompat.getDrawable(context, resourceID);

if (!bitmapDrawable.getBitmap().equals(iconBitmap)) {

iconBitmap = bitmapDrawable.getBitmap();

invalidateView();

}

}

/**

* 判断当前是否为UI线程,是则直接重绘,否则调用postInvalidate()利用Handler来重绘

*/

private void invalidateView() {

if (Looper.getMainLooper() == Looper.myLooper()) {

invalidate();

} else {

postInvalidate();

}

}

private static final String STATE_INSTANCE = "STATE_INSTANCE";

private static final String STATE_ALPHA = "STATE_ALPHA";

/**

* 保存状态

*

* @return Parcelable

*/

@Override

protected Parcelable onSaveInstanceState() {

Bundle bundle = new Bundle();

bundle.putParcelable(STATE_INSTANCE, super.onSaveInstanceState());

bundle.putFloat(STATE_ALPHA, mAlpha);

return bundle;

}

/**

* 恢复状态

*

* @param parcelable Parcelable

*/

@Override

protected void onRestoreInstanceState(Parcelable parcelable) {

if (parcelable instanceof Bundle) {

Bundle bundle = (Bundle) parcelable;

mAlpha = bundle.getFloat(STATE_ALPHA);

super.onRestoreInstanceState(bundle.getParcelable(STATE_INSTANCE));

} else {

super.onRestoreInstanceState(parcelable);

}

}

}

然后在布局文件中声明使用,这里不需要每个自定义属性都使用到,因为我也提供了默认值

xmlns:app="http://schemas.android.com/apk/res-auto"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:id="@+id/id_viewpager"

android:layout_width="match_parent"

android:layout_height="0dp"

android:layout_weight="1" />

android:layout_width="match_parent"

android:layout_height="60dp"

android:background="@drawable/tab_background"

android:orientation="horizontal"

android:paddingBottom="3dp"

android:paddingTop="1dp">

android:id="@+id/id_indicator_one"

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1"

android:padding="5dp"

app:icon="@drawable/weixin"

app:text="微信" />

android:id="@+id/id_indicator_two"

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1"

android:padding="5dp"

app:icon="@drawable/address_book"

app:text="通讯录" />

android:id="@+id/id_indicator_three"

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1"

android:padding="5dp"

app:icon="@drawable/discover"

app:text="发现" />

android:id="@+id/id_indicator_four"

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1"

android:padding="5dp"

app:icon="@drawable/me"

app:text="我" />

因为主界面是ViewPager,这里就需要一个Fragment子类

public class TabFragment extends Fragment {

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

String mTitle = "微信";

if (getArguments() != null) {

mTitle = getArguments().getString("Title", "微信");

}

TextView textView = new TextView(getActivity());

textView.setTextSize(25);

textView.setGravity(Gravity.CENTER);

textView.setText(mTitle);

return textView;

}

}

MainActivity代码如下,重点是对viewPager进行滑动监听,根据滑动偏移量来动态改变透明度alpha,从而实现颜色渐变效果

public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener, View.OnClickListener {

private List tabFragments;

private List tabIndicators;

private ViewPager viewPager;

private FragmentPagerAdapter adapter;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

initData();

viewPager = (ViewPager) findViewById(R.id.id_viewpager);

viewPager.setAdapter(adapter);

viewPager.addOnPageChangeListener(this);

}

private void initData() {

tabFragments = new ArrayList<>();

tabIndicators = new ArrayList<>();

String[] titles = new String[]{"微信", "通讯录", "发现", "我"};

for (String title : titles) {

TabFragment tabFragment = new TabFragment();

Bundle bundle = new Bundle();

bundle.putString("Title", title);

tabFragment.setArguments(bundle);

tabFragments.add(tabFragment);

}

adapter = new FragmentPagerAdapter(getSupportFragmentManager()) {

@Override

public int getCount() {

return tabFragments.size();

}

@Override

public Fragment getItem(int arg0) {

return tabFragments.get(arg0);

}

};

initTabIndicator();

}

private void initTabIndicator() {

ShadeView one = (ShadeView) findViewById(R.id.id_indicator_one);

ShadeView two = (ShadeView) findViewById(R.id.id_indicator_two);

ShadeView three = (ShadeView) findViewById(R.id.id_indicator_three);

ShadeView four = (ShadeView) findViewById(R.id.id_indicator_four);

tabIndicators.add(one);

tabIndicators.add(two);

tabIndicators.add(three);

tabIndicators.add(four);

one.setOnClickListener(this);

two.setOnClickListener(this);

three.setOnClickListener(this);

four.setOnClickListener(this);

one.setIconAlpha(1.0f);

}

@Override

public void onClick(View v) {

resetTabsStatus();

switch (v.getId()) {

case R.id.id_indicator_one:

tabIndicators.get(0).setIconAlpha(1.0f);

viewPager.setCurrentItem(0, false);

break;

case R.id.id_indicator_two:

tabIndicators.get(1).setIconAlpha(1.0f);

viewPager.setCurrentItem(1, false);

break;

case R.id.id_indicator_three:

tabIndicators.get(2).setIconAlpha(1.0f);

viewPager.setCurrentItem(2, false);

break;

case R.id.id_indicator_four:

tabIndicators.get(3).setIconAlpha(1.0f);

viewPager.setCurrentItem(3, false);

break;

}

}

/**

* 重置Tab状态

*/

private void resetTabsStatus() {

for (int i = 0; i < tabIndicators.size(); i++) {

tabIndicators.get(i).setIconAlpha(0);

}

}

/**

* 如果是直接点击图标来跳转页面的话,position值为0到3,positionOffset一直为0.0

* 如果是通过滑动来跳转页面的话

* 假如是从第一页滑动到第二页

* 在这个过程中,positionOffset从接近0逐渐增大到接近1.0,滑动完成后又恢复到0.0,而position只有在滑动完成后才从0变为1

* 假如是从第二页滑动到第一页

* 在这个过程中,positionOffset从接近1.0逐渐减小到0.0,而position一直是0

*

* @param position 当前页面索引

* @param positionOffset 偏移量

* @param positionOffsetPixels 偏移量

*/

@Override

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

Log.e("TAG", "position==" + position);

Log.e("TAG", "positionOffset==" + positionOffset);

Log.e("TAG", "positionOffsetPixels==" + positionOffsetPixels);

if (positionOffset > 0) {

ShadeView leftTab = tabIndicators.get(position);

ShadeView rightTab = tabIndicators.get(position + 1);

leftTab.setIconAlpha(1 - positionOffset);

rightTab.setIconAlpha(positionOffset);

}

}

@Override

public void onPageSelected(int position) {

if (position == 2) {

tabIndicators.get(position).setIconBitmap(this, R.drawable.discover_green);

} else {

tabIndicators.get(2).setIconBitmap(this, R.drawable.discover);

}

}

@Override

public void onPageScrollStateChanged(int state) {

}

}

总结

以上所述是小编给大家介绍的Android 仿微信底部渐变Tab效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值