多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > [置頂] 實現360手機助手TabHost的波紋效果

[置頂] 實現360手機助手TabHost的波紋效果

來源:程序員人生   發布時間:2016-07-15 09:38:02 閱讀次數:2525次

現在新版360手機助手的界面都做得挺漂亮的,在切換底部導航時的波紋效果也很好看,恰好最近看了個開源項目才了解到原來Drawable做動畫效果也怎樣好用,所以就仿照360實現了下帶波紋的TabHost。源代碼地址:https://github.com/Rukey7/XFragmentTabHost

先來看1下實現后的效果:


說明1下實現要點:

1. 由于我們項目之前用的是FragmentTabHost,所以我直接繼承FragmentTabHost來實現動畫效果更方便;

2. 波紋動畫的實現實際上是自定義帶動畫效果的Drawable,然后將Drawable設置為Tab菜單的背景;

3. 其它的就是1些Tab菜單切換的處理了。

1. 自定義波紋Drawable

自定義Drawable只要繼承Drawable并實現以下4個方法,同時實現Animatable接口

public class RippleDrawable extends Drawable implements Animatable { @Override public void draw(Canvas canvas) { // 繪圖 } @Override public void setAlpha(int alpha) { // 設置透明度 } @Override public void setColorFilter(ColorFilter colorFilter) { // 設置色彩過濾 } @Override public int getOpacity() { // 設置色彩格式 return PixelFormat.RGBA_8888; } @Override public void start() { // 啟動動畫 } @Override public void stop() { // 停止動畫 } @Override public boolean isRunning() { // 判斷動畫是不是運行 return false; } }
這幾個方法中最重要的就是draw()方法了,相信自定義過View的都知道我們圖形就是在這里繪制,這里也1樣,其它方法在這里影響不大,最后1個方法用來設置Drawable的色彩格式。要實現動畫Drawable需要實現Animatable接口,并實現3個方法以下,其實不實現這個接口也能做動畫效果,但還是實現比較好。

下面是全部波紋Drawable的實現代碼:

/** * Created by long on 2016/6/27. * 波紋Drawable */ public class RippleDrawable extends Drawable implements Animatable { /** * 3種模式:左側、中間和右側波紋 */ public static final int MODE_LEFT = 1; public static final int MODE_MIDDLE = 2; public static final int MODE_RIGHT = 3; private int mMode = MODE_MIDDLE; // 前風景和后風景畫筆 private Paint mPaintFront; private Paint mPaintBehind; // 用來繪制扇形的矩形框 private RectF mRect; // 目標View的寬高的1半 private int mHalfWidth; private int mHalfHeight; // 分散半徑 private int mRadius; // 前風景和背風景的分割距離 private int mDivideSpace; // 分散滿視圖需要的距離,中點到斜角的距離 private int mFullSpace; // 動畫控制 private ValueAnimator mValueAnimator; public RippleDrawable(int frontColor, int behindColor, int mode) { mPaintFront = new Paint(Paint.ANTI_ALIAS_FLAG); mPaintFront.setColor(frontColor); mPaintBehind = new Paint(Paint.ANTI_ALIAS_FLAG); mPaintBehind.setColor(behindColor); mRect = new RectF(); mMode = mode; } @Override public void draw(Canvas canvas) { if (mRadius > mHalfWidth) { int count = canvas.save(); canvas.drawCircle(mHalfWidth, mHalfHeight, mHalfWidth, mPaintBehind); canvas.restoreToCount(count); count = canvas.save(); canvas.drawCircle(mHalfWidth, mHalfHeight, mDivideSpace, mPaintFront); canvas.restoreToCount(count); } else if (mRadius > mDivideSpace) { int count = canvas.save(); canvas.drawCircle(mHalfWidth, mHalfHeight, mRadius, mPaintBehind); canvas.restoreToCount(count); count = canvas.save(); canvas.drawCircle(mHalfWidth, mHalfHeight, mDivideSpace, mPaintFront); canvas.restoreToCount(count); } else { canvas.drawCircle(mHalfWidth, mHalfHeight, mRadius, mPaintFront); } // 左右兩邊才進行扇形繪制 if (mMode != MODE_MIDDLE) { mRect.left = mHalfWidth - mRadius; mRect.right = mHalfWidth + mRadius; mRect.top = mHalfHeight - mRadius; mRect.bottom = mHalfHeight + mRadius; } if (mMode == MODE_LEFT) { canvas.drawArc(mRect, 90, 180, true, mPaintFront); } else if (mMode == MODE_RIGHT) { canvas.drawArc(mRect, ⑼0, 180, true, mPaintFront); } } @Override public void setAlpha(int alpha) { } @Override public void setColorFilter(ColorFilter colorFilter) { } @Override public int getOpacity() { return PixelFormat.RGBA_8888; } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); mHalfHeight = (bounds.bottom - bounds.top) / 2; mHalfWidth = (bounds.right - bounds.left) / 2; mDivideSpace = Math.max(mHalfHeight, mHalfWidth) * 3 / 4; mFullSpace = (int) Math.sqrt(mHalfWidth * mHalfWidth + mHalfHeight * mHalfHeight); // 屬性動畫 mValueAnimator = ValueAnimator.ofInt(0, mFullSpace); mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mRadius = (int) animation.getAnimatedValue(); invalidateSelf(); } }); mValueAnimator.setDuration(200); start(); } @Override public void start() { mValueAnimator.start(); } @Override public void stop() { mValueAnimator.end(); } @Override public boolean isRunning() { return mValueAnimator != null && mValueAnimator.isRunning(); } }
整體還是比較簡單的,主要就是繪圖那里需要繪制3個圖形,1個前風景的圓形、1個后風景的圓形和左右兩邊的扇形。在繪制前需要計算前風景和后風景繪制的半徑,中點都為Tab視圖的中心。這里需要實現onBoundsChange(Rect bounds)方法,在這里可以獲得到Tab菜單項的尺寸信息,這里的mDivideSpace是前風景圓形的半徑,也就是前景和后景的分割距離,而后風景圓形半徑為Tab項寬度的1半。最后就剩下左右兩邊需要填充Tab邊角的扇形半徑mFullSpace了,距離就是中心到Tab邊角點的距離了。

固然了,要實現動畫效果肯定不止這些,還有1個重要的ValueAnimator,通過它來控制波紋的分散半徑,用法還是很簡單的,用過屬性動畫的應當都不陌生。這里面需要注意的是里面調用了1個方法invalidateSelf() ,Drawable是通過這個方法來進行重繪的,它會重新調用draw()方法來實現波紋效果。
2. 實現擴大的FragmentTabHost

要實現擴大的FragmentTabHost需要繼承它并實現1個重要的方法setCurrentTab(int index),當FragmentTabHost在選擇Tab菜單時會調用該方法,在這方法里我們可以得到當前選中的項和之前選中的項,并做動畫處理。

在實現FragmentTabHost之前,我們的Tab菜單布局生成也通過這里實現,并提供方法讓外面調用,首先是菜單布局:

<?xml version="1.0" encoding="utf⑻"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="@dimen/tab_height" android:minWidth="@dimen/tab_min_width" android:paddingTop="@dimen/tab_padding_top_inactive" android:paddingBottom="@dimen/tab_padding_bottom" android:background="?selectableItemBackgroundBorderless"> <ImageView android:id="@+id/tab_icon" android:layout_width="@dimen/tab_icon" android:layout_height="@dimen/tab_icon" android:layout_gravity="center_horizontal"/> <TextView android:id="@+id/tab_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:lines="1" android:text="首頁" android:textColor="@color/colorInactive" android:textSize="@dimen/tab_text_size_inactive"/> </LinearLayout>
這個很簡單,就是圖標和標題,和正常使用沒區分。然后是Tab菜單類:

/** * Created by long on 2016/4/15. * Tab項 */ public class TabItem { private String title; private int imageRes; public TabItem(String title, int imageRes) { this.title = title; this.imageRes = imageRes; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getImageRes() { return imageRes; } public void setImageRes(int imageRes) { this.imageRes = imageRes; } }
一樣很簡單,和布局文件對應1個圖標和1個標題。

最后看下擴大FragmentTabHost的實現:

/** * Created by long on 2016/4/15. * 擴大TabHost */ public class XFragmentTabHost extends FragmentTabHost { private Context mContext; private List<View> mTabViews; private List<TabItem> mTabItems; // 字體激活色彩 private int mTextActiveColor; private int mTextInactiveColor; // 字體激活大小 private float mTextActiveSize; private float mTextInactiveSize; // 視圖激活對頂部的偏移 private int mViewActivePaddingTop; private int mViewInactivePaddingTop; // 波紋模式的前景色彩和后景色彩 private int mFrontColor; private int mBehindColor; // TabHost模式 private TabMode mTabMode; public XFragmentTabHost(Context context) { super(context); _init(context); } public XFragmentTabHost(Context context, AttributeSet attrs) { super(context, attrs); _init(context); } private void _init(Context context) { mTabViews = new ArrayList<>(); mTabItems = new ArrayList<>(); mContext = context; mTextActiveColor = ContextCompat.getColor(mContext, R.color.colorActive); mTextInactiveColor = ContextCompat.getColor(mContext, R.color.colorInactive); mFrontColor = ContextCompat.getColor(mContext, R.color.colorFront); mBehindColor = ContextCompat.getColor(mContext, R.color.colorBehind); mTextActiveSize = getResources().getDimension(R.dimen.tab_text_size_active); mTextInactiveSize = getResources().getDimension(R.dimen.tab_text_size_inactive); mViewActivePaddingTop = (int) getResources().getDimension(R.dimen.tab_padding_top_active); mViewInactivePaddingTop = (int) getResources().getDimension(R.dimen.tab_padding_top_inactive); mTabMode = TabMode.MoveToTop; } /** * 覆寫父類接口,并在這里做些動畫殊效 * @param index 當前選中的Tab項 */ @Override public void setCurrentTab(int index) { // 獲得之前選中的index int lastIndex = getCurrentTab(); super.setCurrentTab(index); // 選中不同的Tab項才做切換處理 if (lastIndex != index) { _switchTab(lastIndex, index); } } /** * 添加TabItem * @param item TabItem * @param fragClass fragment類名 * @param bundle 傳給fragment的參數 */ public void addTabItem(TabItem item, Class<?> fragClass, Bundle bundle) { mTabItems.add(item); View view = _getIndicator(item); mTabViews.add(view); this.addTab(newTabSpec(item.getTitle()).setIndicator(view), fragClass, bundle); } /** * 獲得TabItem視圖 * @param item TabItem * @return */ private View _getIndicator(TabItem item) { View view = LayoutInflater.from(mContext).inflate(R.layout.tab_indicator, null); ImageView imageView = (ImageView) view.findViewById(R.id.tab_icon); TextView title = (TextView) view.findViewById(R.id.tab_title); imageView.setImageResource(item.getImageRes()); title.setText(item.getTitle()); title.setTextColor(mTextInactiveColor); return view; } /** * 切換Tab * @param lastIndex 上1個選中索引 * @param nextIndex 下1個選中索引 */ private void _switchTab(int lastIndex, int nextIndex) { for (int i = 0; i < mTabViews.size(); i++) { if (i == lastIndex) { _doRipple(i, false); } else if (i == nextIndex) { _doRipple(i, true); } } } /** * 波紋處理 * @param index 索引 * @param isActivated 是不是激活 */ private void _doRipple(int index, boolean isActivated) { View view = mTabViews.get(index); View tabView = view.findViewById(R.id.tab_layout); TextView title = (TextView) view.findViewById(R.id.tab_title); if (index == 0) { _rippleDrawable(tabView, mFrontColor, mBehindColor, RippleDrawable.MODE_LEFT, isActivated); } else if (index == (mTabViews.size() - 1)){ _rippleDrawable(tabView, mFrontColor, mBehindColor, RippleDrawable.MODE_RIGHT, isActivated); } else { _rippleDrawable(tabView, mFrontColor, mBehindColor, RippleDrawable.MODE_MIDDLE, isActivated); } if (isActivated) { title.setTextColor(mTextActiveColor); } else { title.setTextColor(mTextInactiveColor); } } /** * 波紋動畫 * @param view * @param frontColor * @param behindColor * @param mode * @param isActivated */ @SuppressWarnings("deprecation") private void _rippleDrawable(final View view, int frontColor, int behindColor, int mode, boolean isActivated) { if (isActivated) { RippleDrawable rippleDrawable = new RippleDrawable(frontColor, behindColor, mode); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { view.setBackground(rippleDrawable); } else { view.setBackgroundDrawable(rippleDrawable); } } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { view.setBackground(null); } else { view.setBackgroundDrawable(null); } } } /** * 屬性設置 * @return */ public int getTextActiveColor() { return mTextActiveColor; } public void setTextActiveColor(int textActiveColor) { mTextActiveColor = textActiveColor; } public int getTextInactiveColor() { return mTextInactiveColor; } public void setTextInactiveColor(int textInactiveColor) { mTextInactiveColor = textInactiveColor; } public int getFrontColor() { return mFrontColor; } public void setFrontColor(int frontColor) { mFrontColor = frontColor; } public int getBehindColor() { return mBehindColor; } public void setBehindColor(int behindColor) { mBehindColor = behindColor; } }
其實也不會復雜,就是在切換Tab菜單時,對選中菜單設置背景為RippleDrawable,對之前的菜單背景設置為空,就這么簡單^ ^,使用的話大體和FragmentHost是基本1樣的,就添加Tab菜單使用上面實現的方法addTabItem(TabItem item, Class<?> fragClass, Bundle bundle) 就好了,具體下載源代碼查看。

這個TabHost實現還是不復雜,處理波紋效果外,源代碼里還有1些其它動畫效果,實現思路都1樣,有興趣也能夠自己定制些更好看的動畫效果~

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 色综合网亚洲精品久久 | 亚洲第一视频网 | 最近免费中文字幕大全高清mv | 一级淫片免费视频 | 国产亚洲精品福利在线 | 欧美特级午夜一区二区三区 | 校园春色欧美日韩 | 国产福利一区二区三区视频在线 | 7777精品久久久大香线蕉 | 欧美色视频日本 | 日韩拍拍拍| 国产在线精品一区二区高清不卡 | 久久久青草青青国产亚洲免观 | 亚洲免费大片 | jizzjizzjizz亚洲女| 色图片小说 | 意大利极品xxxxhd | 国产一区二区视频在线 | 国产成人精品免费视频大 | 人人澡人人擦人人免费 | 国产一国产一级毛片视频在线 | 91九色网址| 日韩毛片欧美一级a | 青青青青久久精品国产一百度 | 亚洲jizzjizz在线播放久 | 久久丝袜精品综合网站 | 在线观看91精品国产性色 | 亚洲欧美综合图片 | 一级做a爰片欧美一区 | 日本久久影视 | 成人亚洲精品一区 | 亚洲日本一区二区 | 免费成年人视频在线观看 | 欧美一区二区三区久久综 | 色老久久精品偷偷鲁一区 | 欧美日韩加勒比一区二区三区 | 亚洲精品视频在线观看视频 | 久久一区二区三区不卡 | 美美女高清毛片视频黄的一免费 | 日韩欧美印度一级毛片 | 午夜宅男网站 |