編輯:關於Android編程
相信大家都見到了微信圖標顏色漸變的過程,是不是感覺很牛逼?不得不說微信團隊確實是很厲害的團隊,不管是從設計還是開發人員。
今天我帶大家來看看,微信 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); } }
注釋有點少,額,不是少,是壓根就沒有,其實,這個類的代碼不需要注釋,我相信大家都能看懂,我就講下他的作用吧,
其實上面很多功能本來是在 MainActivity 中實現的,為了減少 Activity 內部的代碼量,抽取出來,到達低耦合,高內聚的效果。
Ok,以上就是 微信6.1 tab 欄顏色漸變效果的實現全過程。
以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持本站!
Android源代碼在編譯之前,要先對編譯環境進行初始化,其中最主要就是指定編譯的類型和目標設備的型號。Android的編譯類型主要有eng、userdebug和user
進程等待一個進程在終止時會關閉所有的文件描述符,釋放在用戶空間分配出來的內存,但它的PCB還保留著,而且內核中還保存著一些信息,如果是正常終止,則保存著退出狀態,如果是異
簡介KVO是一套當目標對象的屬性值改變時觀察者對象能夠接受到通知的機制。必須先理解KVC才能更好的理解KVO,前者是後者的實現基礎。這樣的通信機制在MVC設計模式很是常見
又是興趣系列 網上有很多自動強紅包的例子和代碼,筆者也是做了一些優化。 先說說自己的兩個個優勢 1.可以在聊天界面自動強不依賴於通知欄推送 2.可以在屏幕熄滅的時候的時候