Android 仿微信底部漸變Tab效果
先來看一下效果圖

除了第三個的發(fā)現(xiàn)Tab有所差別外,其他的基本還原了微信的底部Tab漸變效果
每個Tab都是一個自定義View,根據(jù)ImageView的tint屬性來實現(xiàn)顏色漸變效果,tint屬性的使用可以看我的上一篇文章
我將自定義View命名為ShadeView,包含四個自定義屬性
意思分別為圖標(biāo)、背景色、底部文本、底部文本大小
<declare-styleable name="ShadeView">
<attr name="icon" format="reference" />
<attr name="color" format="color" />
<attr name="text" format="string" />
<attr name="text_size" format="dimension" />
</declare-styleable>
ShadeView的定義如下,主要是進(jìn)行繪圖操作,并向外提供改變透明度和圖標(biāo)的方法
public class ShadeView extends View {
/**
* 圖標(biāo)
*/
private Bitmap iconBitmap;
/**
* 圖標(biāo)背景色
*/
private int iconBackgroundColor;
/**
* 圖標(biāo)默認(rèn)背景色
*/
private final int DEFAULT_ICON_BACKGROUND_COLOR = 0x3CAF36;
/**
* 圖標(biāo)底部文本
*/
private String text;
/**
* 圖標(biāo)底部文字默認(rèn)大?。╯p)
*/
private final int DEFAULT_TEXT_SIZE = 12;
/**
* 圖標(biāo)底部文字默認(rèn)顏色
*/
private final int DEFAULT_TEXT_COLOR = 0x2B2B2B;
/**
* 圖標(biāo)繪制范圍
*/
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);
//因為圖標(biāo)是正方形且需要居中顯示的,所以View的大小去掉padding和文字所占空間后,
//剩余的空間的寬和高的最小值才是圖標(biāo)的邊長
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;
//獲取圖標(biāo)的繪制范圍
iconRect = new Rect(left, top, left + bitmapSide, top + bitmapSide);
}
@Override
protected void onDraw(Canvas canvas) {
//進(jìn)一取整
int alpha = (int) Math.ceil((255 * mAlpha));
//繪制原圖標(biāo)
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模式下的圖標(biāo)
*
* @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);
//在圖標(biāo)背后先繪制一層iconBackgroundColor顏色的背景
canvas.drawRect(iconRect, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
paint.setAlpha(255);
//在mBitmap上繪制以iconBackgroundColor顏色為Dst,DST_IN模式下的圖標(biāo)
canvas.drawBitmap(iconBitmap, null, iconRect, paint);
}
/**
* 繪制默認(rèn)狀態(tài)下的字體
*
* @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);
}
/**
* 繪制滑動到該標(biāo)簽時的字體
*
* @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);
}
/**
* 設(shè)置圖標(biāo)透明度并重繪
*
* @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();
}
}
/**
* 判斷當(dāng)前是否為UI線程,是則直接重繪,否則調(diào)用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";
/**
* 保存狀態(tài)
*
* @return Parcelable
*/
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(STATE_INSTANCE, super.onSaveInstanceState());
bundle.putFloat(STATE_ALPHA, mAlpha);
return bundle;
}
/**
* 恢復(fù)狀態(tài)
*
* @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);
}
}
}
然后在布局文件中聲明使用,這里不需要每個自定義屬性都使用到,因為我也提供了默認(rèn)值
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/id_viewpager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@drawable/tab_background"
android:orientation="horizontal"
android:paddingBottom="3dp"
android:paddingTop="1dp">
<com.example.zy.myapplication.ShadeView
android:id="@+id/id_indicator_one"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
app:icon="@drawable/weixin"
app:text="微信" />
<com.example.zy.myapplication.ShadeView
android:id="@+id/id_indicator_two"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
app:icon="@drawable/address_book"
app:text="通訊錄" />
<com.example.zy.myapplication.ShadeView
android:id="@+id/id_indicator_three"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
app:icon="@drawable/discover"
app:text="發(fā)現(xiàn)" />
<com.example.zy.myapplication.ShadeView
android:id="@+id/id_indicator_four"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
app:icon="@drawable/me"
app:text="我" />
</LinearLayout>
</LinearLayout>
因為主界面是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進(jìn)行滑動監(jiān)聽,根據(jù)滑動偏移量來動態(tài)改變透明度alpha,從而實現(xiàn)顏色漸變效果
public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener, View.OnClickListener {
private List<TabFragment> tabFragments;
private List<ShadeView> 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[]{"微信", "通訊錄", "發(fā)現(xiàn)", "我"};
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狀態(tài)
*/
private void resetTabsStatus() {
for (int i = 0; i < tabIndicators.size(); i++) {
tabIndicators.get(i).setIconAlpha(0);
}
}
/**
* 如果是直接點擊圖標(biāo)來跳轉(zhuǎn)頁面的話,position值為0到3,positionOffset一直為0.0
* 如果是通過滑動來跳轉(zhuǎn)頁面的話
* 假如是從第一頁滑動到第二頁
* 在這個過程中,positionOffset從接近0逐漸增大到接近1.0,滑動完成后又恢復(fù)到0.0,而position只有在滑動完成后才從0變?yōu)?
* 假如是從第二頁滑動到第一頁
* 在這個過程中,positionOffset從接近1.0逐漸減小到0.0,而position一直是0
*
* @param position 當(dāng)前頁面索引
* @param positionOffset 偏移量
* @param positionOffsetPixels 偏移量
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.e("TAG", "position==" + position);
Log.e("TAG", "positionOffset==" + positionOffset);
Log.e("TAG", "positionOffsetPixels==" + positionOffsetPixels);
if (positionOffset > 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) {
}
}
總結(jié)
以上所述是小編給大家介紹的Android 仿微信底部漸變Tab效果,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
- Android 使用FragmentTabHost實現(xiàn)底部菜單功能
- Android中修改TabLayout底部導(dǎo)航條Indicator長短的方法
- Android TabWidget底部顯示效果
- Android編程實現(xiàn)將tab選項卡放在屏幕底部的方法
- Android中TabLayout+ViewPager 簡單實現(xiàn)app底部Tab導(dǎo)航欄
- Android design包自定義tablayout的底部導(dǎo)航欄的實現(xiàn)方法
- 關(guān)注Ionic底部導(dǎo)航按鈕tabs在android情況下浮在上面的處理
- Android仿微信底部實現(xiàn)Tab選項卡切換效果
- android 選項卡(TabHost)如何放置在屏幕的底部
- Android TabLayout 實現(xiàn)底部Tab的示例代碼
相關(guān)文章
Android編程實現(xiàn)WebView添加進(jìn)度條的方法
這篇文章主要介紹了Android編程實現(xiàn)WebView添加進(jìn)度條的方法,涉及Android WebView界面及控件功能相關(guān)操作技巧,需要的朋友可以參考下2017-02-02
android使用service和activity獲取屏幕尺寸的方法
這篇文章主要介紹了android使用service和activity獲取屏幕尺寸的方法,實例分析了基于service和activity兩種方法獲取屏幕尺寸的相關(guān)技巧,非常簡單實用,需要的朋友可以參考下2015-08-08
Android實現(xiàn)頂部底部雙導(dǎo)航界面功能
這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)頂部\底部雙導(dǎo)航界面功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-09-09
Android Studio 4.0 新功能中的Live Layout Inspector詳解
這篇文章主要介紹了Android Studio 4.0 新功能中的Live Layout Inspector,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06
Android 進(jìn)度條按鈕ProgressButton的實現(xiàn)代碼
這篇文章主要介紹了Android 進(jìn)度條按鈕實現(xiàn)(ProgressButton)代碼,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值 ,需要的朋友可以參考下2018-10-10
詳解Android——藍(lán)牙技術(shù) 帶你實現(xiàn)終端間數(shù)據(jù)傳輸
藍(lán)牙技術(shù)在智能硬件方面有很多用武之地,本篇文章主要介紹了Android——藍(lán)牙技術(shù),實現(xiàn)兩個終端間數(shù)據(jù)的傳輸,有興趣的朋友可以了解一下。2016-12-12
Flutter開發(fā)通用頁面Loading組件示例詳解
這篇文章主要為大家介紹了Flutter開發(fā)通用頁面Loading組件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11

