Android 微信6.1 tab栏图标和字体颜色渐变的实现
相信大家都见到了微信图标颜色渐变的过程,是不是感觉很牛逼?不得不说微信团队确实是很厉害的团队,不管是从设计还是开发人员。
今天我带大家来看看,微信 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); } }
注释有点少,额,不是少,是压根就没有,其实,这个类的代码不需要注释,我相信大家都能看懂,我就讲下他的作用吧,
- 添加 item
- 监听 viewpager 的滚动事件,从而设置相应 item 之间的颜色渐变,
- 设置相应 viewpage 的透明度
- 为 tabitem 设置监听事件,
其实上面很多功能本来是在 mainactivity 中实现的,为了减少 activity 内部的代码量,抽取出来,到达低耦合,高内聚的效果。
ok,以上就是 微信6.1 tab 栏颜色渐变效果的实现全过程。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!