热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android仿微信底部渐变Tab效果

这篇文章主要介绍了Android仿微信底部渐变Tab效果,需要的朋友可以参考下

先来看一下效果图

这里写图片描述

除了第三个的发现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);
    }
  }
}

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


  
  
    
    
    
    
  

因为主界面是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  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效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!


推荐阅读
  • 本文介绍了GitHub上的一些Python开源项目,特别是IM(即时通讯)技术的应用。通过Sealtalk项目,探讨了如何利用开源SDK提升开发效率。 ... [详细]
  • pypy 真的能让 Python 比 C 还快么?
    作者:肖恩顿来源:游戏不存在最近“pypy为什么能让python比c还快”刷屏了,原文讲的内容偏理论,干货比较少。我们可以再深入一点点,了解pypy的真相。正式开始之前,多唠叨两句 ... [详细]
  • Vulnhub DC3 实战记录与分析
    本文记录了在 Vulnhub DC3 靶机上的渗透测试过程,包括漏洞利用、内核提权等关键步骤,并总结了实战经验和教训。 ... [详细]
  • 本文介绍了如何在Android应用中通过Intent调用其他应用的Activity,并提供了详细的代码示例和注意事项。 ... [详细]
  • 2023年最新Linux环境下Android开发环境搭建指南
    2023年最新Linux环境下Android开发环境搭建指南,帮助Android开发者在Linux系统上快速搭建开发环境,解决常见的配置问题。 ... [详细]
  • 本文介绍了一种通过设置主题(Theme)来实现快速启动的Android引导页,并详细说明了如何避免因不同屏幕分辨率导致的图片拉伸问题。 ... [详细]
  • 本文介绍了如何使用 Gesture Detector 和 overridePendingTransition 方法来实现滑动界面和过渡动画。 ... [详细]
  • 本文介绍了如何使用开源工具ChkBugReport来解析和分析Android设备的Bugreport。ChkBugReport能够将复杂的Bugreport转换为易于阅读的HTML报告,并提供详细的图表和分析结论。 ... [详细]
  • 本文讲述了一位80后的普通男性程序员,尽管没有高学历,但通过不断的努力和学习,在IT行业中逐渐找到了自己的位置。从最初的仓库管理员到现在的多技能开发者,他的职业生涯充满了挑战与机遇。 ... [详细]
  • Java EE 平台集成了多种服务、API 和协议,旨在支持基于 Web 的多层应用程序开发。本文将详细介绍 Java EE 中的 13 种关键技术规范,帮助开发者更好地理解和应用这些技术。 ... [详细]
  • Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面Android异步处理二:使用AsyncTask异步更新UI界面Android异步处理三:Handler+Loope ... [详细]
  • AngularJS 控制器详解
    本文通过一个示例详细介绍了 AngularJS 控制器的使用方法,并探讨了控制器之间数据共享的问题。 ... [详细]
  • 本文详细介绍了如何将Spring框架与Hibernate ORM框架进行集成,包括配置文件的设置和数据持久化操作的实现。 ... [详细]
  • java解析json转Map前段时间在做json报文处理的时候,写了一个针对不同格式json转map的处理工具方法,总结记录如下:1、单节点单层级、单节点多层级json转mapim ... [详细]
  • 可参照github代码:https:github.comrabbitmqrabbitmq-tutorialsblobmasterjavaEmitLogTopic.ja ... [详细]
author-avatar
蟹子萝卜控
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有