当前位置: 移动技术网 > IT编程>移动开发>Android > Android 微信6.1 tab栏图标和字体颜色渐变的实现

Android 微信6.1 tab栏图标和字体颜色渐变的实现

2019年07月24日  | 移动技术网IT编程  | 我要评论

高冠变色龙,叫声爸爸不容易,2010汤姆斯杯

相信大家都见到了微信图标颜色渐变的过程,是不是感觉很牛逼?不得不说微信团队确实是很厉害的团队,不管是从设计还是开发人员。

今天我带大家来看看,微信 tab 栏图标和字体颜色渐变的过程。先上图吧!今天学了一招制作 gif 动态图的快捷方法。刚好用的上,以前一直想写点什么东西,

苦于一直不知道怎么生成动态图,现在终于学会了,哈哈,让我偷偷的乐一会。额,还是上图吧。。。

好了,效果图也看到了,那么我也就不多啰嗦了,直接进入主题,看代码

[java] view plain copy
package moon.wechat.view; 
import android.content.context; 
import android.graphics.bitmap; 
import android.graphics.bitmapfactory; 
import android.graphics.canvas; 
import android.graphics.paint; 
import android.graphics.rect; 
import android.util.attributeset; 
import android.util.typedvalue; 
import android.view.view; 
/** 
 * created by moon.zhong on 2015/2/4. 
 */ 
public class tabitem extends view { 
  /*字体大小*/ 
  private int mtextsize ; 
  /*字体选中的颜色*/ 
  private int mtextcolorselect ; 
  /*字体未选择的时候的颜色*/ 
  private int mtextcolornormal; 
  /*绘制未选中时字体的画笔*/ 
  private paint mtextpaintnormal; 
  /*绘制已选中时字体的画笔*/ 
  private paint mtextpaintselect; 
  /*每个 item 的宽和高,包括字体和图标一起*/ 
  private int mviewheight, mviewwidth; 
  /*字体的内容*/ 
  private string mtextvalue ; 
  /*已选中时的图标*/ 
  private bitmap miconnormal; 
  /*未选中时的图标*/ 
  private bitmap miconselect; 
  /*用于记录字体大小*/ 
  private rect mboundtext; 
  /*已选中是图标的画笔*/ 
  private paint miconpaintselect; 
  /*为选中时图标的画笔*/ 
  private paint miconpaintnormal; 
  public tabitem(context context) { 
    this(context, null); 
  } 
  public tabitem(context context, attributeset attrs) { 
    this(context, attrs, 0); 
  } 
  public tabitem(context context, attributeset attrs, int defstyleattr) { 
    super(context, attrs, defstyleattr); 
    initview(); 
    inittext(); 
  } 
  /*初始化一些东西*/ 
  private void initview() { 
    mboundtext = new rect(); 
  } 
  /*初始化画笔,并设置出是内容*/ 
  private void inittext() { 
    mtextpaintnormal = new paint(); 
    mtextpaintnormal.settextsize(typedvalue.applydimension(typedvalue.complex_unit_sp, mtextsize, getresources().getdisplaymetrics())); 
    mtextpaintnormal.setcolor(mtextcolornormal); 
    mtextpaintnormal.setantialias(true); 
    mtextpaintnormal.setalpha(0xff); 
    mtextpaintselect = new paint(); 
    mtextpaintselect.settextsize(typedvalue.applydimension(typedvalue.complex_unit_sp, mtextsize, getresources().getdisplaymetrics())); 
    mtextpaintselect.setcolor(mtextcolorselect); 
    mtextpaintselect.setantialias(true); 
    mtextpaintselect.setalpha(0); 
    miconpaintselect = new paint(paint.anti_alias_flag) ; 
    miconpaintselect.setalpha(0); 
    miconpaintnormal = new paint(paint.anti_alias_flag) ; 
    miconpaintnormal.setalpha(0xff); 
  } 
  /*测量字体的大小*/ 
  private void measuretext() { 
    mtextpaintnormal.gettextbounds(mtextvalue, 0, mtextvalue.length(), mboundtext); 
  } 
  /*测量字体和图标的大小,并设置自身的宽和高*/ 
  @override 
  protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { 
    int widthmode = measurespec.getmode(widthmeasurespec); 
    int heightmode = measurespec.getmode(heightmeasurespec); 
    int widthsize = measurespec.getsize(widthmeasurespec); 
    int heightsize = measurespec.getsize(heightmeasurespec); 
    int width = 0, height = 0; 
    measuretext(); 
    int contentwidth = math.max(mboundtext.width(), miconnormal.getwidth()); 
    int desiredwidth = getpaddingleft() + getpaddingright() + contentwidth; 
    switch (widthmode) { 
      case measurespec.at_most: 
        width = math.min(widthsize, desiredwidth); 
        break; 
      case measurespec.exactly: 
        width = widthsize; 
        break; 
      case measurespec.unspecified: 
        width = desiredwidth; 
        break; 
    } 
    int contentheight = mboundtext.height() + miconnormal.getheight(); 
    int desiredheight = getpaddingtop() + getpaddingbottom() + contentheight; 
    switch (heightmode) { 
      case measurespec.at_most: 
        height = math.min(heightsize, desiredheight); 
        break; 
      case measurespec.exactly: 
        height = heightsize; 
        break; 
      case measurespec.unspecified: 
        height = contentheight; 
        break; 
    } 
    setmeasureddimension(width, height); 
    mviewwidth = getmeasuredwidth() ; 
    mviewheight = getmeasuredheight() ; 
  } 
  @override 
  protected void ondraw(canvas canvas) { 
    drawbitmap(canvas) ; 
    drawtext(canvas) ; 
  } 
  /*话图标,先画为选中的图标,在画已选中的图标*/ 
  private void drawbitmap(canvas canvas) { 
    int left = (mviewwidth - miconnormal.getwidth())/2 ; 
    int top = (mviewheight - miconnormal.getheight() - mboundtext.height()) /2 ; 
    canvas.drawbitmap(miconnormal, left, top ,miconpaintnormal); 
    canvas.drawbitmap(miconselect, left, top , miconpaintselect); 
  } 
  /*画字体*/ 
  private void drawtext(canvas canvas) { 
    float x = (mviewwidth - mboundtext.width())/2.0f ; 
    float y = (mviewheight + miconnormal.getheight() + mboundtext.height()) /2.0f ; 
    canvas.drawtext(mtextvalue,x,y, mtextpaintnormal); 
    canvas.drawtext(mtextvalue,x,y, mtextpaintselect); 
  } 
  public void settextsize(int textsize) { 
    this.mtextsize = textsize; 
    mtextpaintnormal.settextsize(textsize); 
    mtextpaintselect.settextsize(textsize); 
  } 
  public void settextcolorselect(int mtextcolorselect) { 
    this.mtextcolorselect = mtextcolorselect; 
    mtextpaintselect.setcolor(mtextcolorselect); 
    mtextpaintselect.setalpha(0); 
  } 
  public void settextcolornormal(int mtextcolornormal) { 
    this.mtextcolornormal = mtextcolornormal; 
    mtextpaintnormal.setcolor(mtextcolornormal); 
    mtextpaintnormal.setalpha(0xff); 
  } 
  public void settextvalue(string textvalue) { 
    this.mtextvalue = textvalue; 
  } 
  public void seticontext(int[] iconselid,string textvalue) { 
    this.miconselect = bitmapfactory.decoderesource(getresources(), iconselid[0]); 
    this.miconnormal = bitmapfactory.decoderesource(getresources(), iconselid[1]); 
    this.mtextvalue = textvalue; 
  } 
  /*通过 alpha 来设置 每个画笔的透明度,从而实现现实的效果*/ 
  public void settabalpha(float alpha){ 
    int paintalpha = (int)(alpha*255) ; 
    miconpaintselect.setalpha(paintalpha); 
    miconpaintnormal.setalpha(255-paintalpha); 
    mtextpaintselect.setalpha(paintalpha); 
    mtextpaintnormal.setalpha(255-paintalpha); 
    invalidate(); 
  } 
} 

分析: 以上代码,功能实现 tab 的每个 item 的内容,在微信的项目中也就是,一个图标加一个字体,

关键代码就在public void settabalpha(float alpha) 这个方法,此方法是 viewpager 切换 item 时,不断改变偏移量来调用,从而不断改变

每个画笔的透明度,实现图标和颜色的渐变;是不是很简单?到这里其实我们颜色渐变的代码就已经实现了。接下来的内容可以忽略

这样我们只需要在 mainactivity 的 xml 中定义一个线性布局,然后放如四个我们自定义的 view 进去,就可以了。但是这样你就满足了吗?

我先来给你们看看我的mainactivity的代码;

[java] view plain copy
package moon.wechat; 
import android.support.v4.app.fragment; 
import android.support.v4.app.fragmentmanager; 
import android.support.v4.app.fragmentpageradapter; 
import android.support.v4.view.viewpager; 
import android.support.v7.app.actionbaractivity; 
import android.os.bundle; 
import java.util.hashmap; 
import java.util.map; 
import moon.wechat.view.tabview; 
public class mainactivity extends actionbaractivity { 
  private string[] mtitle = {"微信", "通讯录", "发现", "我"}; 
  private int[] miconselect = {r.drawable.al_, r.drawable.al8, r.drawable.alb, r.drawable.ald}; 
  private int[] miconnormal = {r.drawable.ala, r.drawable.al9, r.drawable.alc, r.drawable.ale}; 
  private viewpager mviewpager ; 
  private tabview mtabview ; 
  private map<integer,fragment> mfragmentmap ; 
  @override 
  protected void oncreate(bundle savedinstancestate) { 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.activity_main); 
    mfragmentmap = new hashmap<>() ; 
    mviewpager = (viewpager)findviewbyid(r.id.id_view_pager) ; 
    mviewpager.setoffscreenpagelimit(4); 
    mviewpager.setadapter(new pageadapter(getsupportfragmentmanager())); 
    mtabview = (tabview)findviewbyid(r.id.id_tab) ; 
    mtabview.setviewpager(mviewpager); 
  } 
  private fragment getfragment(int position){ 
    fragment fragment = mfragmentmap.get(position) ; 
    if(fragment == null){ 
      switch (position){ 
        case 0: 
          fragment = new wechatfragment() ; 
          break ; 
        case 1: 
          fragment = new wecontactfragment(); 
          break ; 
        case 2: 
          fragment = new wediscoverfragment(); 
          break; 
        case 3: 
          fragment = new gamefragment() ; 
//          fragment = new weminefragment(); 
          break; 
      } 
      mfragmentmap.put(position,fragment) ; 
    } 
    return fragment ; 
  } 
  class pageadapter extends fragmentpageradapter implements tabview.onitemicontextselectlistener{ 
    public pageadapter(fragmentmanager fm) { 
      super(fm); 
    } 
    @override 
    public fragment getitem(int position) { 
      return getfragment(position); 
    } 
    @override 
    public int[] oniconselect(int position) { 
      int icon[] = new int[2] ; 
      icon[0] = miconselect[position] ; 
      icon[1] = miconnormal[position] ; 
      return icon; 
    } 
    @override 
    public string ontextselect(int position) { 
      return mtitle[position]; 
    } 
    @override 
    public int getcount() { 
      return mtitle.length; 
    } 
  } 
} 

是不是很简单,而 xml 更简单

[html] view plain copy
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:zgy="http://schemas.android.com/apk/res-auto" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:background="@android:color/white" 
  android:orientation="vertical"> 
  <android.support.v4.view.viewpager 
    android:id="@+id/id_view_pager" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:layout_weight="1" 
    > 
    </android.support.v4.view.viewpager> 
  <moon.wechat.view.tabview 
    android:id="@+id/id_tab" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:orientation="horizontal" 
    android:background="#eeeeee" 
    zgy:text_size="12sp" 
    zgy:text_normal_color="#ff777777" 
    zgy:text_select_color="#ff45c01a" 
    zgy:item_padding="7dp"> 
   </moon.wechat.view.tabview> 
</linearlayout> 

可以看到没有定义我们刚刚自定义的 tabitem,而是使用的 tabview,那 tabview 到底是啥东西?相信大家都想到了,tabview 其实就是我们自定义的线性布局,

[java] view plain copy
package moon.wechat.view; 
import android.content.context; 
import android.content.res.typedarray; 
import android.support.v4.view.pageradapter; 
import android.support.v4.view.viewpager; 
import android.util.attributeset; 
import android.util.log; 
import android.util.typedvalue; 
import android.view.view; 
import android.view.viewgroup; 
import android.widget.linearlayout; 
import java.util.arraylist; 
import java.util.list; 
import moon.wechat.r; 
/** 
 * created by moon.zhong on 2015/2/4. 
 */ 
public class tabview extends linearlayout implements view.onclicklistener { 
  private viewpager mviewpager; 
  private viewpager.onpagechangelistener monpagechangelistener; 
  private pageradapter mpageradapter; 
  private int mchildsize; 
  private list<tabitem> mtabitems; 
  private onitemicontextselectlistener mlistener; 
  private int mtextsize = 12; 
  private int mtextcolorselect = 0xff45c01a; 
  private int mtextcolornormal = 0xff777777; 
  private int mpadding = 10; 
  public tabview(context context) { 
    this(context, null); 
  } 
  public tabview(context context, attributeset attrs) { 
    this(context, attrs, 0); 
  } 
  public tabview(context context, attributeset attrs, int defstyleattr) { 
    super(context, attrs, defstyleattr); 
    typedarray typedarray = getresources().obtainattributes(attrs, r.styleable.tabview); 
    int n = typedarray.getindexcount(); 
    for (int i = 0; i < n; i++) { 
      switch (typedarray.getindex(i)) { 
        case r.styleable.tabview_text_size: 
          mtextsize = (int) typedarray.getdimension(i, typedvalue.applydimension(typedvalue.complex_unit_sp, 
              mtextsize, getresources().getdisplaymetrics())); 
          break; 
        case r.styleable.tabview_text_normal_color: 
          mtextcolornormal = typedarray.getcolor(i, mtextcolornormal); 
          break; 
        case r.styleable.tabview_text_select_color: 
          mtextcolorselect = typedarray.getcolor(i, mtextcolorselect); 
          break; 
        case r.styleable.tabview_item_padding: 
          mpadding = (int) typedarray.getdimension(i, typedvalue.applydimension(typedvalue.complex_unit_dip, 
              mpadding, getresources().getdisplaymetrics())); 
          break; 
      } 
    } 
    typedarray.recycle(); 
    mtabitems = new arraylist<>(); 
  } 
  public void setviewpager(final viewpager mviewpager) { 
    if (mviewpager == null) { 
      return; 
    } 
    this.mviewpager = mviewpager; 
    this.mpageradapter = mviewpager.getadapter(); 
    if (this.mpageradapter == null) { 
      throw new runtimeexception("亲,在您设置tabview的viewpager时,请先设置viewpager的pageradapter"); 
    } 
    this.mchildsize = this.mpageradapter.getcount(); 
    this.mviewpager.setonpagechangelistener(new viewpager.onpagechangelistener() { 
      @override 
      public void onpagescrolled(int position, float positionoffset, int positionoffsetpixels) { 
//        log.v("zgy","=============position="+position+",====positionoffset="+positionoffset) ; 
        view leftview; 
        view rightview; 
        if (positionoffset > 0) { 
          leftview = mviewpager.getchildat(position); 
          rightview = mviewpager.getchildat(position + 1); 
          leftview.setalpha(1 - positionoffset); 
          rightview.setalpha(positionoffset); 
          mtabitems.get(position).settabalpha(1 - positionoffset); 
          mtabitems.get(position + 1).settabalpha(positionoffset); 
        } else { 
          mviewpager.getchildat(position).setalpha(1); 
          mtabitems.get(position).settabalpha(1 - positionoffset); 
        } 
        if (monpagechangelistener != null) { 
monpagechangelistener.onpagescrolled(position, positionoffset, positionoffsetpixels); 
        } 
      } 
      @override 
      public void onpageselected(int position) { 
        if (monpagechangelistener != null) { 
monpagechangelistener.onpageselected(position); 
        } 
      } 
      @override 
      public void onpagescrollstatechanged(int state) { 
        if (monpagechangelistener != null) { 
monpagechangelistener.onpagescrollstatechanged(state); 
        } 
      } 
    }); 
    if (mpageradapter instanceof onitemicontextselectlistener) { 
      mlistener = (onitemicontextselectlistener) mpageradapter; 
    }else { 
      throw new runtimeexception("请让你的pageadapter实现onitemicontextselectlistener接口"); 
    } 
    inititem(); 
  } 
  public void setonpagechangelistener(viewpager.onpagechangelistener monpagechangelistener) { 
    this.monpagechangelistener = monpagechangelistener; 
  } 
  private void inititem() { 
    for (int i = 0; i < mchildsize; i++) { 
      tabitem tabitem = new tabitem(getcontext()); 
      layoutparams params = new layoutparams(0, viewgroup.layoutparams.wrap_content, 1); 
      tabitem.setpadding(mpadding, mpadding, mpadding, mpadding); 
      tabitem.seticontext(mlistener.oniconselect(i), mlistener.ontextselect(i)); 
      tabitem.settextsize(mtextsize); 
      tabitem.settextcolornormal(mtextcolornormal); 
      tabitem.settextcolorselect(mtextcolorselect); 
      tabitem.setlayoutparams(params); 
      tabitem.settag(i); 
      tabitem.setonclicklistener(this); 
      mtabitems.add(tabitem); 
      addview(tabitem); 
    } 
  } 
  @override 
  protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { 
    super.onmeasure(widthmeasurespec, heightmeasurespec); 
  } 
  @override 
  public void onclick(view v) { 
    int position = (integer) v.gettag(); 
    if (mviewpager.getcurrentitem() == position) { 
      return; 
    } 
    for (tabitem tabitem : mtabitems) { 
      tabitem.settabalpha(0); 
    } 
    mtabitems.get(position).settabalpha(1); 
    mviewpager.setcurrentitem(position, false); 
  } 
  public interface onitemicontextselectlistener { 
    int[] oniconselect(int position); 
    string ontextselect(int position); 
  } 
} 

注释有点少,额,不是少,是压根就没有,其实,这个类的代码不需要注释,我相信大家都能看懂,我就讲下他的作用吧,

  1. 添加 item
  2. 监听 viewpager 的滚动事件,从而设置相应 item 之间的颜色渐变,
  3. 设置相应 viewpage 的透明度
  4. 为 tabitem 设置监听事件,

其实上面很多功能本来是在 mainactivity 中实现的,为了减少 activity 内部的代码量,抽取出来,到达低耦合,高内聚的效果。

ok,以上就是 微信6.1 tab 栏颜色渐变效果的实现全过程。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持移动技术网!

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网