今天给大家带来一个自定义的底部导航,我不会做动图,只能搞一张图片给大家看看,大家见谅
这个就是自定义的tablayout底部搞好的样式了
TabLayout提供了一个水平布局用于展示tabs,继承自HorizontalScrollView。一般与Viewpager结合使用实现页面和标签联动的效果,是时下APP中非常常用的一个控件
首先我们需要创建一个类或者是模块都可以
package com.example.map.tab; import android.content.Context; import android.os.Bundle; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import androidx.core.content.ContextCompat; import androidx.fragment.app.FragmentTabHost; import com.example.map.R; import java.util.ArrayList; import java.util.List; /** * Created by long on 2022/5/10. * 扩展TabHost */ public class XFragmentTabHost extends FragmentTabHost { private Context mContext; private ListmTabViews; private List 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); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); // 部分机型TabHost带有分割线,同一将分割线设为透明 getTabWidget().setDividerDrawable(android.R.color.transparent); } /** * 添加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 上一个选中索引 * @param nextIndex 下一个选中索引 */ private void _switchTab(int lastIndex, int nextIndex) { for (int i = 0; i < mTabViews.size(); i++) { if (i == lastIndex) { _switchView(i, false); } else if (i == nextIndex) { _switchView(i, true); } } } /** * 切换视图 * @param index 索引 * @param isActivated 是否激活 */ private void _switchView(int index, boolean isActivated) { switch (mTabMode) { case MoveToTop: _doMoveToTop(index, isActivated); break; case ClipDrawable: _doClipDrawable(index, isActivated); break; case Ripple: _doRipple(index, isActivated); break; } } /** * 背景展开处理 * @param index 索引 * @param isActivated 是否激活 */ private void _doClipDrawable(int index, boolean isActivated) { View view = mTabViews.get(index); View tabView = view.findViewById(R.id.tab_layout); if (isActivated) { TabAnimHelper.clipDrawable(tabView, mTabItems.get(index).getDrawable(), true); } else { TabAnimHelper.clipDrawable(tabView, mTabItems.get(index).getDrawable(), false); } } /** * 波纹处理 * @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) { TabAnimHelper.rippleDrawable(tabView, mFrontColor, mBehindColor, RippleDrawable.MODE_LEFT, isActivated); } else if (index == (mTabViews.size() - 1)){ TabAnimHelper.rippleDrawable(tabView, mFrontColor, mBehindColor, RippleDrawable.MODE_RIGHT, isActivated); } else { TabAnimHelper.rippleDrawable(tabView, mFrontColor, mBehindColor, RippleDrawable.MODE_MIDDLE, isActivated); } if (isActivated) { title.setTextColor(mTextActiveColor); } else { title.setTextColor(mTextInactiveColor); } } /** * 上移动画处理 * @param index 索引 * @param isActivated 是否激活 */ private void _doMoveToTop(int index, boolean isActivated) { View view = mTabViews.get(index); TextView title = (TextView) view.findViewById(R.id.tab_title); ImageView icon = (ImageView) view.findViewById(R.id.tab_icon); if (isActivated) { TabAnimHelper.changeTextColor(title, mTextInactiveColor, mTextActiveColor); TabAnimHelper.changeTextSize(title, mTextInactiveSize, mTextActiveSize); TabAnimHelper.changeTopPadding(view, mViewInactivePaddingTop, mViewActivePaddingTop); TabAnimHelper.changeImageSize(icon, 1.0f, 1.1f); } else { TabAnimHelper.changeTextColor(title, mTextActiveColor, mTextInactiveColor); TabAnimHelper.changeTextSize(title, mTextActiveSize, mTextInactiveSize); TabAnimHelper.changeTopPadding(view, mViewActivePaddingTop, mViewInactivePaddingTop); TabAnimHelper.changeImageSize(icon, 1.1f, 1.0f); } } /** * 属性设置 * @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; } public TabMode getTabMode() { return mTabMode; } public void setTabMode(TabMode tabMode) { mTabMode = tabMode; } /** * Tab的模式 * MoveToTop:向上偏移 * Ripple:波纹 * ClipDrawable:逐步展示背景 */ public enum TabMode { MoveToTop(1), Ripple(2), ClipDrawable(3); private int tabMode; TabMode(int tabMode) { this.tabMode = tabMode; } public int getTabMode() { return tabMode; } } }
然后在res下的value中的color中加入以下颜色
#3F51B5 #303F9F #FF4081 #000000 #FFFFFF #FBD794 #00000000 #70000000 #99ffffff #efefef #333333 #999999 #cccccc #66333333 #111111 #666666 #eeeeee #ff6700 #cccccc #f6f6f6 #a8a8a8 #dc5900 #171717 #cccccc #333333 #338BFF #f94244 #45cdd5 #5e8cef #4980f6 #c1e4ff #eff4f8 #e5e5e5 #f6f6f6 #FF0000 #FF6666 #e6fffe #00e8df #ffefe5 #e6fff2 #01e48c #ffeeee #ff1901 #fff4ea #ffa96e #ff8430 #ff9d5c #ff802c #f2c279 #fcf1ea #fed2c0 #ffece5 #68ffc4 #31c088 #587ed6ff #7ed6ff #75fff7 #2d9ef5 #181819 #00a6b6 #282e35 #60000000 #b0000000 #c0ffff00 #ffffffff #ffc0c0c0 #c000ff00 #fed952 #eb2127 #ff4200 #f03030 #f8f8f8 #daba7f #858585 #b2b2b2 #58ffc8d1 #ff6e6b #97a8bc #143660 #44db09 #757575 #f5f5f5 #767676 #d1d1d1 #d2d2d2 #d3d3d3 #ffba16 #f73954 #ffbd0a #ff6600 #fff6f3 #dddddd #e0e0e0 #f70c38 #47b81b #38c595 #f1f1f1 #f5f5f5 #3c5c8c #094691 #002553 #333333 #cccccc #cccccc #8290AF #abb1b6 #ffd1c0 #fdf0eb #e4e9ec #d3d3d3 #ffae00 #e9eef3 #ECF0F4 #1ac677 #ECF1F7 #3cace6 #35d282 #ff2a00 #f3f3f3 #d9d9d9 #1b1b1b #818181 #919191 #c7c7c7 #404040 #fcfcfc #333333 #f0420c #BEC0C1 #909090 #666666 #ff000000 #ffffffff #ffcccccc #ff404040 #ffffffff #ff808080 #ffffffff #fffff0e0 #ffffffff #ff000000 #ff4b4b4b #ff000000 #ffffffff #50000000 #ffffffff #00000000 #ff000000 #ffff0000 #D4D4D4 #A9A9A9 #999999 #e1e1e1 #d3d3d3 #5191F2 #1AAC19 #19ad19 #4a4a4a #a2a2a2 #d1d1d1 #00c679 #F5F6F8 #656565 #FFF0E5 #F0F1F2 #C7C9D1 #f5f6f8 #fd3341 #dce1e5 #9498a4 #202741 #444861 #e8e8e8 #FF8963 #2a3038 #9398AE #00A7FF #5c6166 #e4e5e6 #ffb452 #ff7252 #8898A6 #ACB0B9 #CACCD0 #f0f1f3 #eda95b40 #3257c5 #9d9da3 #d7d8dc #c4c6cc #cdcfd5 #d2d4d9 #939499 #616366 #dcdcdc #cc0042ff #97a0ab #ebba8f #a86010 #00BCD4 #d2d0d1 #4db6ac #84ffff
然后是我们的布局
布局中呢会有一些图片这个图片的话就自己搞吧然后在res文件下创建一个dimen
8dp 3dp 4dp 104dp 168dp 56dp 1dp 24dp 3dp 6dp 16dp 3dp 12dp 12dp 14sp 12sp
我们的底部导航的样式就搞完了,接下里就是我们的动画效果写一个自定义Drawable
package com.example.map.tab; import android.animation.ValueAnimator; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; /** * Created by long on 2022/5/10. * 波纹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的宽高的一半 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, -90, 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(); } }
然后创建我们的TabFragment
package com.example.map.tab; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import com.example.map.R; /** * Created by long on 2022/5/10. */ public class TabFragment extends Fragment { public static final String FRAG_KEY = "FragKey"; private TextView mFragTabText; private void assignViews(View view) { mFragTabText = (TextView) view.findViewById(R.id.frag_tab_text); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_tab, null); assignViews(view); return view; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (getArguments() != null) { String title = getArguments().getString(FRAG_KEY); mFragTabText.setText(title); } } }
然后创建一个动画的帮助类
package com.example.map.tab; import android.animation.AnimatorSet; import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.graphics.drawable.ClipDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.widget.ImageView; import android.widget.TextView; /** * Created by long on 2022/5/10. * Tab动画帮助类 */ public class TabAnimHelper { /** * 改变Tab的顶部偏移 * @param view * @param fromPadding * @param toPadding */ public static void changeTopPadding(final View view, int fromPadding, int toPadding) { ValueAnimator animator = ValueAnimator.ofFloat(fromPadding, toPadding); animator.setDuration(150); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { float animatedValue = (float) valueAnimator.getAnimatedValue(); view.setPadding(view.getPaddingLeft(), (int) animatedValue, view.getPaddingRight(), view.getPaddingBottom()); } }); animator.start(); } /** * 改变字体大小 * @param textView * @param from * @param to */ public static void changeTextSize(final TextView textView, float from, float to) { ValueAnimator textSizeChangeAnimator = ValueAnimator.ofFloat(from, to); textSizeChangeAnimator.setDuration(150); textSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, (float) valueAnimator.getAnimatedValue()); } }); textSizeChangeAnimator.start(); } /** * 改变字体颜色 * @param textView * @param fromColor * @param toColor */ public static void changeTextColor(final TextView textView, int fromColor, int toColor) { ValueAnimator changeTextColorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), fromColor, toColor); changeTextColorAnimation.setDuration(150); changeTextColorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animator) { textView.setTextColor((Integer) animator.getAnimatedValue()); } }); changeTextColorAnimation.start(); } /** * 改变视图大小 * @param image * @param fromScale * @param toScale */ public static void changeImageSize(ImageView image, float fromScale, float toScale) { ObjectAnimator scaleX; ObjectAnimator scaleY; scaleX = ObjectAnimator.ofFloat(image, "scaleX", fromScale, toScale); scaleY = ObjectAnimator.ofFloat(image, "scaleY", fromScale, toScale); AnimatorSet set = new AnimatorSet(); set.setDuration(150); set.playTogether(scaleX, scaleY); set.start(); } /** * 从中心展开Drawable * @param image * @param drawable * @param isActivated */ @SuppressWarnings("deprecation") public static void clipDrawable(final View image, Drawable drawable, boolean isActivated) { if (drawable == null) { return; } if (isActivated) { final ClipDrawable scaleDrawable = new ClipDrawable(drawable, Gravity.CENTER, ClipDrawable.HORIZONTAL | ClipDrawable.VERTICAL); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { image.setBackground(scaleDrawable); } else { image.setBackgroundDrawable(scaleDrawable); } image.setBackgroundDrawable(scaleDrawable); ValueAnimator animator = ValueAnimator.ofInt(0, 10000); animator.setDuration(200); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { scaleDrawable.setLevel((Integer) animation.getAnimatedValue()); } }); animator.start(); } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { image.setBackground(null); } else { image.setBackgroundDrawable(null); } } } /** * 波纹动画 * @param view * @param frontColor * @param behindColor * @param mode * @param isActivated */ @SuppressWarnings("deprecation") public static 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); } } } }
还有一个item的实体类
package com.example.map.tab; import android.graphics.drawable.Drawable; /** * Created by long on 2022/5/10. * Tab项 */ public class TabItem { private String title; private Drawable drawable; private int imageRes; public TabItem(String title, Drawable drawable, int imageRes) { this.title = title; this.drawable = drawable; this.imageRes = imageRes; } public TabItem(String title, int imageRes) { this.title = title; this.drawable = null; this.imageRes = imageRes; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Drawable getDrawable() { return drawable; } public void setDrawable(Drawable drawable) { this.drawable = drawable; } public int getImageRes() { return imageRes; } public void setImageRes(int imageRes) { this.imageRes = imageRes; } }
然后在我们的Activity中进行一个引用,首先在是我们Activity的一个布局
然后在我们的Activity中进行一个应用控件
package com.example.map.ui.activity; import android.graphics.Color; import android.os.Bundle; import android.util.Log; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.bumptech.glide.Glide; import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.request.RequestOptions; import com.example.map.api.ApiService; import com.example.map.base.GlideImageLoader; import com.example.map.bean.WallPaperResponse; import com.example.map.ui.fragment.Fragment1; import com.example.map.ui.fragment.Fragment2; import com.example.map.ui.fragment.Fragment3; import com.example.map.ui.fragment.Fragment4; import com.example.map.ui.fragment.Fragment5; import com.example.map.R; import com.example.map.tab.TabFragment; import com.example.map.tab.TabItem; import com.example.map.tab.XFragmentTabHost; import com.llw.network.NetworkApi; import com.llw.network.observer.BaseObserver; import com.llw.network.utils.KLog; import com.youth.banner.Banner; import com.youth.banner.BannerConfig; import com.youth.banner.Transformer; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class QihooActivity extends AppCompatActivity { private XFragmentTabHost mTabHost; //底部导航的名称 String[] mTabTitle = new String[]{"首页", "游戏", "软件", "应用圈", "管理"}; //文字上的图片 int[] mImageResId = new int[]{R.drawable.sel_360_home, R.drawable.sel_360_game, R.drawable.sel_360_software, R.drawable.sel_360_app, R.drawable.sel_360_mag}; //创建的几个fragment Class[] mFragClass = new Class[]{Fragment1.class, Fragment2.class, Fragment3.class, Fragment4.class, Fragment5.class}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_qihoo); initTabHost(); } private void initTabHost() { mTabHost = (XFragmentTabHost) findViewById(android.R.id.tabhost); mTabHost.setup(this, getSupportFragmentManager(), R.id.relate_tab_content); mTabHost.setTabMode(XFragmentTabHost.TabMode.Ripple); mTabHost.setTextActiveColor(Color.WHITE); mTabHost.setTextInactiveColor(Color.GRAY); // mTabHost.setFrontColor(Color.RED); // mTabHost.setBehindColor(Color.GREEN); for (int i = 0; i < mFragClass.length; i++) { Bundle bundle = new Bundle(); bundle.putString(TabFragment.FRAG_KEY, mTabTitle[i]); mTabHost.addTabItem(new TabItem(mTabTitle[i], mImageResId[i]), mFragClass[i], bundle); } } }
这样我们的一个自定义的水波纹动画的底部导航就完成了
到此这篇关于Android自定义水波纹底部导航的实现的文章就介绍到这了,更多相关Android水波纹底部导航内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!