Android自定义水波纹底部导航的实现

今天给大家带来一个自定义的底部导航,我不会做动图,只能搞一张图片给大家看看,大家见谅

这个就是自定义的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 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);
        }
    }
    @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中加入以下颜色

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
    <color name="black">#000000</color>
    <color name="white">#FFFFFF</color>
    <color name="custcolor">#FBD794</color>
    <color name="translate">#00000000</color>
    <color name="trans">#70000000</color>
    <color name="white_trans">#99ffffff</color>
    <color name="ef">#efefef</color>
    <color name="c4">#333333</color>
    <color name="c9">#999999</color>
    <color name="coloritem">#cccccc</color>
    <color name="black_bg">#66333333</color>
    <color name="c1">#111111</color>
    <color name="c6">#666666</color>
    <color name="c5">#eeeeee</color>
    <color name="c2">#ff6700</color>
    <color name="cfcc">#cccccc</color>
    <color name="app_color_f6">#f6f6f6</color>
    <color name="btn_not_click_color">#a8a8a8</color>
    <color name="btn_click_color">#dc5900</color>
    <color name="c7">#171717</color>
    <color name="c3">#cccccc</color>
    <color name="c33">#333333</color>
    <color name="test_blue">#338BFF</color>
    <color name="red1">#f94244</color>
    <color name="green1">#45cdd5</color>
    <color name="blue">#5e8cef</color>
    <color name="blue1">#4980f6</color>
    <color name="c_c1e4ff">#c1e4ff</color>
    <color name="c_eff4f8">#eff4f8</color>
    <color name="e5">#e5e5e5</color>
    <color name="f6">#f6f6f6</color>
    <color name="red_normal">#FF0000</color>
    <color name="red_pressed">#FF6666</color>
    <color name="blue_bg">#e6fffe</color>
    <color name="blue_text">#00e8df</color>
    <color name="org_bg">#ffefe5</color>
    <color name="green_bg">#e6fff2</color>
    <color name="green_text">#01e48c</color>
    <color name="red_bg">#ffeeee</color>
    <color name="red_text">#ff1901</color>
    <color name="org1">#fff4ea</color>
    <color name="org2">#ffa96e</color>
    <color name="org3">#ff8430</color>
    <color name="org4">#ff9d5c</color>
    <color name="org5">#ff802c</color>
    <color name="org6">#f2c279</color>
    <color name="org7">#fcf1ea</color>
    <color name="org8">#fed2c0</color>
    <color name="org9">#ffece5</color>
    <color name="green2">#68ffc4</color>
    <color name="green">#31c088</color>
    <color name="blue2_trans">#587ed6ff</color>
    <color name="blue2">#7ed6ff</color>
    <color name="blue3">#75fff7</color>
    <color name="blue5">#2d9ef5</color>
    <color name="photopicker_background">#181819</color>
    <color name="color1">#00a6b6</color>
    <color name="c99">#282e35</color>
    <!-- QR_Code -->
    <color name="viewfinder_mask">#60000000</color>
    <color name="result_view">#b0000000</color>
    <color name="possible_result_points">#c0ffff00</color>
    <color name="result_image_border">#ffffffff</color>
    <color name="result_minor_text">#ffc0c0c0</color>
    <color name="result_points">#c000ff00</color>
    <color name="system_bar_color">#fed952</color>
    <color name="red">#eb2127</color>
    <color name="c_red">#ff4200</color>
    <color name="red3">#f03030</color>
    <color name="c_f8">#f8f8f8</color>
    <color name="c_yellow">#daba7f</color>
    <color name="c_85">#858585</color>
    <color name="c_b2">#b2b2b2</color>
    <color name="c_light_purple">#58ffc8d1</color>
    <color name="c_purple">#ff6e6b</color>
    <color name="c_agent_position">#97a8bc</color>
    <color name="c_agent_name">#143660</color>
    <color name="c_44db09">#44db09</color>
    <color name="c_75">#757575</color>
    <color name="c_f5">#f5f5f5</color>
    <color name="c_76">#767676</color>
    <color name="c_d1">#d1d1d1</color>
    <color name="c_d2">#d2d2d2</color>
    <color name="c_d3">#d3d3d3</color>
    <color name="c_yellow3">#ffba16</color>
    <color name="c_mei_red">#f73954</color>
    <color name="yellow">#ffbd0a</color>
    <color name="org">#ff6600</color>
    <color name="org_trans">#fff6f3</color>
    <color name="c_d">#dddddd</color>
    <color name="c_e0">#e0e0e0</color>
    <color name="red7">#f70c38</color>
    <color name="green7">#47b81b</color>
    <color name="deal_bg">#38c595</color>
    <color name="f1">#f1f1f1</color>
    <color name="f5">#f5f5f5</color>
    <color name="c_3c5c8c">#3c5c8c</color>
    <color name="cus_phone_color">#094691</color>
    <color name="cus_follow_record_item_name_color">#002553</color>
    <color name="praise_item_default">#333333</color>
    <color name="praise_item_selector_default">#cccccc</color>
    <color name="default_clickable_color">#cccccc</color>
    <color name="praise_item">#8290AF</color>
    <color name="im_font_color_text_hint">#abb1b6</color>
    <color name="ffd1c0">#ffd1c0</color>
    <color name="fdf0eb">#fdf0eb</color>
    <color name="e4e9ec">#e4e9ec</color>
    <color name="d3">#d3d3d3</color>
    <color name="ffae00">#ffae00</color>
    <color name="e9eef3">#e9eef3</color>
    <color name="ecf0f4">#ECF0F4</color>
    <color name="ac677">#1ac677</color>
    <color name="ecf1f7">#ECF1F7</color>
    <color name="cace6">#3cace6</color>
    <color name="d282">#35d282</color>
    <color name="ff2a00">#ff2a00</color>
    <!-- 其它背景色或分割线相关 -->
    <color name="bg_content">#f3f3f3</color>
    <color name="divider_line">#d9d9d9</color>
    <!-- 字体色 -->
    <color name="text_title">#1b1b1b</color>
    <color name="text_content">#818181</color>
    <color name="text_sub">#919191</color>
    <color name="text_hint">#c7c7c7</color>
    <color name="text_black">#404040</color>
    <color name="main_tab_bg">#fcfcfc</color>
    <color name="black_need">#333333</color>
    <color name="orange_need">#f0420c</color>
    <color name="gray_need">#BEC0C1</color>
    <color name="dialog_content_textcolor">#909090</color>
    <color name="reply_text_color">#666666</color>
    <color name="contents_text">#ff000000</color>
    <color name="encode_view">#ffffffff</color>
    <color name="help_button_view">#ffcccccc</color>
    <color name="help_view">#ff404040</color>
    <color name="result_text">#ffffffff</color>
    <color name="sbc_header_text">#ff808080</color>
    <color name="sbc_header_view">#ffffffff</color>
    <color name="sbc_list_item">#fffff0e0</color>
    <color name="sbc_layout_view">#ffffffff</color>
    <color name="sbc_page_number_text">#ff000000</color>
    <color name="sbc_snippet_text">#ff4b4b4b</color>
    <color name="share_text">#ff000000</color>
    <color name="share_view">#ffffffff</color>
    <color name="status_view">#50000000</color>
    <color name="status_text">#ffffffff</color>
    <color name="transparent">#00000000</color>
    <color name="viewfinder_frame">#ff000000</color>
    <color name="viewfinder_laser">#ffff0000</color>
    <!-- 画TextView横线颜色 -->
    <color name="draw_line_color">#D4D4D4</color>
    <!-- 侧边仿通讯录26字母列表颜色 -->
    <color name="addresslist_textcolor">#A9A9A9</color>
    <color name="car_select_second_child">#999999</color>
    <color name="car_select_second_group_child_bg">#e1e1e1</color>
    <color name="map_poi_list_bg">#d3d3d3</color>
    <!-- 下拉PopupWindow -->
    <color name="pulldown_popupwindow_bg">#5191F2</color>
    <color name="butten_green">#1AAC19</color>
    <color name="check_butten_green">#19ad19</color>
    <color name="title1">#4a4a4a</color>
    <color name="title2">#a2a2a2</color>
    <color name="line">#d1d1d1</color>
    <color name="c_00c679">#00c679</color>
    <color name="F5F6F8">#F5F6F8</color>
    <color name="c65">#656565</color>
    <color name="fff0e5">#FFF0E5</color>
    <color name="f0f1f2">#F0F1F2</color>
    <color name="c7c9d1">#C7C9D1</color>
    <color name="f5f6f8">#f5f6f8</color>
    <color name="fd3341">#fd3341</color>
    <color name="dce1e5">#dce1e5</color>
    <color name="c_9498a4">#9498a4</color>
    <color name="c_202741">#202741</color>
    <color name="c_444861">#444861</color>
    <color name="c_e8">#e8e8e8</color>
    <color name="ff8963">#FF8963</color>
    <color name="c_2a3038">#2a3038</color>
    <color name="c_939">#9398AE</color>
    <color name="c_00a">#00A7FF</color>
    <color name="c_5c6166">#5c6166</color>
    <color name="c_e4e5e6">#e4e5e6</color>
    <color name="c_ffb452">#ffb452</color>
    <color name="c_ff7252">#ff7252</color>
    <color name="c8898A6">#8898A6</color>
    <color name="acb0b9">#ACB0B9</color>
    <color name="caccd0">#CACCD0</color>
    <color name="c_f0f1f3">#f0f1f3</color>
    <color name="eda95b40">#eda95b40</color>
    <color name="c_3257c5">#3257c5</color>
    <color name="c_9d9da3">#9d9da3</color>
    <color name="c_d7d8dc">#d7d8dc</color>
    <color name="c4c6cc">#c4c6cc</color>
    <color name="cdcfd5">#cdcfd5</color>
    <color name="d2d4d9">#d2d4d9</color>
    <color name="c_939499">#939499</color>
    <color name="c_616366">#616366</color>
    <color name="dc">#dcdcdc</color>
    <color name="cc0042ff">#cc0042ff</color>
    <color name="c_97a0ab">#97a0ab</color>
    <color name="ebba8f">#ebba8f</color>
    <color name="a86010">#a86010</color>
    <color name="colorActive">#00BCD4</color>
    <color name="colorInactive">#d2d0d1</color>
    <color name="colorFront">#4db6ac</color>
    <color name="colorBehind">#84ffff</color>
</resources>

然后是我们的布局

<?xml version="1.0" encoding="utf-8"?>
<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>

布局中呢会有一些图片这个图片的话就自己搞吧然后在res文件下创建一个dimen

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="tab_elevation">8dp</dimen>
    <dimen name="tab_shadow_height">3dp</dimen>
    <dimen name="tab_shadow_height_without_colored_background">4dp</dimen>
    <dimen name="tab_min_width">104dp</dimen>
    <dimen name="tab_max_width">168dp</dimen>
    <dimen name="tab_height">56dp</dimen>
    <dimen name="tab_line_width">1dp</dimen>
    <dimen name="tab_icon">24dp</dimen>
    <dimen name="tab_padding_top_active">3dp</dimen>
    <dimen name="tab_padding_top_inactive">6dp</dimen>
    <dimen name="tab_padding_top_inactive_without_text">16dp</dimen>
    <dimen name="tab_padding_bottom">3dp</dimen>
    <dimen name="tab_padding_left">12dp</dimen>
    <dimen name="tab_padding_right">12dp</dimen>
    <dimen name="tab_text_size_active">14sp</dimen>
    <dimen name="tab_text_size_inactive">12sp</dimen>
</resources>

我们的底部导航的样式就搞完了,接下里就是我们的动画效果写一个自定义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的一个布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical">
    <FrameLayout
        android:id="@+id/relate_tab_content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@drawable/shadow" />
    <com.example.map.tab.XFragmentTabHost
        android:id="@android:id/tabhost"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="0dp"
            android:layout_height="0dp" />
    </com.example.map.tab.XFragmentTabHost>
</LinearLayout>

然后在我们的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水波纹底部导航内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android实现水波纹特效

    最近需要做个类似于水波纹动画的效果,思考了一下不需要UI切个动态图,Android原生的技术利用动画或者自定义控件都可以实现,下面上个图类似于这样的效果 下面请看第一种动画实现,这种方式较为简单些,就是利用3个ImageView不断地做缩放和渐变的动画. 布局文件定义一下 <RelativeLayout android:id="@+id/rl" android:layout_width="match_parent" android:layout_height=

  • Android实现水波纹效果实例代码

    效果图 attrs.xml 自定义属性 <declare-styleable name="RippleAnimationView"> <attr name="ripple_anim_color" format="color" /> <!-- 水波纹填充类型 --> <attr name="ripple_anim_type" format="enum"> <

  • Android 通过自定义view实现水波纹效果案例详解

    在实际的开发中,很多时候还会遇到相对比较复杂的需求,比如产品妹纸或UI妹纸在哪看了个让人兴奋的效果,兴致高昂的来找你,看了之后目的很明确,当然就是希望你能给她: 在这样的关键时候,身子板就一定得硬了,可千万别说不行,爷们儿怎么能说不行呢: 好了,为了让大家都能给妹纸们想要的,后面会逐渐分享一些比较比较不错的效果,目的只有一个,通过自定义view实现我们所能实现的动效: 今天主要分享水波纹效果: 标准正余弦水波纹: 非标准圆形液柱水波纹: 虽说都是水波纹,但两者在实现上差异是比较大的,一个通过正余

  • android实现简单底部导航栏

    本文实例为大家分享了android实现底部导航栏的具体代码,供大家参考,具体内容如下 常见的底部导航栏 动态效果 实现步骤 1.底部导航栏样式 我们应该在项目的res文件夹下新建一个menu文件夹,用来装menu布局文件 <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"&g

  • Android如何自定义View实现横向的双水波纹进度条

    目录 思路分析 功能实现 1.绘制圆角背景和圆角矩形边框 2.通过贝塞尔曲线实现双水波 3.设置动画使进度和水波纹变化 结语 网上垂直的水波纹进度条很多,但横向的很少,将垂直的水波纹改为水平的还遇到了些麻烦,现在完善后发布出来,希望遇到的人少躺点坑. 思路分析 整体效果可分为三个,绘制圆角背景和圆角矩形,绘制第一条和第二条水波浪,根据自定义进度变化效果. 功能实现 1.绘制圆角背景和圆角矩形边框 圆角矩形边框: private RectF rectBorder; if (rectBorder =

  • Android实现渐变色水波纹效果

    本文实例为大家分享了Android实现渐变色水波纹效果的具体代码,供大家参考,具体内容如下 项目中使用到的效果,效果图如下: 代码实现: public class WaveView extends View { private Paint mPaint, mCriclePaint, mTextPaint; // 倾斜或旋转.快速变化,当在屏幕上画一条直线时, 横竖不会出现锯齿, // 但是当斜着画时, 就会出现锯齿的效果,所以需要设置抗锯齿 private DrawFilter mDrawFil

  • Android实现水波纹扩散效果

    本文实例为大家分享了Android实现水波纹扩散效果的具体代码,供大家参考,具体内容如下 先上图 囧!没有图片所以就拿了小安代替了. 先看一下如何使用这个View. <jianpan.com.mybutton.view.RippleDiffuse android:layout_width="200dp" android:layout_height="200dp" app:btn_img_res="@drawable/rd" app:ripp

  • Android实现底部导航栏效果

    目前网上主流的文章都是用底部的 RadioGroup + 页面部分的 Fragment 实现导航栏切换页面效果的. 然而底部的 RadioGroup 是如此麻烦,每个按钮的图片和文字部分都要做一个 selector 用于表示选中和非选中两种状态时的样式. 另外 Fragment 也有很多坑,先不管大家是否已熟练掌握,反正我是看着看着就学不下去了,所以我另辟蹊径用 Activity 的方式实现了伪 Fragment 的效果. 这里我们就来做一个三个按钮的底部导航栏. 因为我们这里是用三个 Acti

  • Android 自定义球型水波纹带圆弧进度效果(实例代码)

    需求 如下,实现一个圆形水波纹,带进度,两层水波纹需要渐变显示,且外围有一个圆弧进度. 思路 外围圆弧进度:可以通过canvas.drawArc()实现.由于圆弧需要实现渐变,可以通过给画笔设置shader(SweepGradient)渲染,为了保证圆弧起始的颜色值始终一致,需要动态调整shader的参数.具体参见 SweepGradient(centerX.toFloat(), centerY.toFloat(), circleColors[0], floatArrayOf(0f, value

  • Android自定义水波纹底部导航的实现

    今天给大家带来一个自定义的底部导航,我不会做动图,只能搞一张图片给大家看看,大家见谅 这个就是自定义的tablayout底部搞好的样式了 TabLayout提供了一个水平布局用于展示tabs,继承自HorizontalScrollView.一般与Viewpager结合使用实现页面和标签联动的效果,是时下APP中非常常用的一个控件 首先我们需要创建一个类或者是模块都可以 package com.example.map.tab; import android.content.Context; imp

  • Android自定义水波纹动画Layout实例代码

    话不多说,我们先来看看效果: Hi前辈搜索预览 这一张是<Hi前辈>的搜索预览图,你可以在这里下载这个APP查看更多效果: http://www.wandoujia.com/apps/com.superlity.hiqianbei LSearchView 这是一个MD风格的搜索框,集成了ripple动画以及search时的loading,使用很简单,如果你也需要这样的搜索控件不妨来试试:https://github.com/onlynight/LSearchView RippleEverywh

  • Android design包自定义tablayout的底部导航栏的实现方法

    以前做项目大多用的radiobutton,今天用tablayout来做一个tab切换页面的的效果. 实现的效果就是类似QQ.微信的页面间(也就是Fragment间)的切换.如图: 布局只要一个tablayout <android.support.design.widget.TabLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id=&

  • Android仿水波纹流量球进度条控制器

    仿水波纹流球进度条控制器,Android实现高端大气的主流特效,供大家参考,具体内容如下 效果图: CircleView 这里主要是实现中心圆以及水波特效 package com.lgl.circleview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.gra

  • Android实现水波纹效果

    一.效果 点击开始: 点击停止: 二.在MainActivity中 import android.graphics.Paint; import android.os.Bundle; import android.support.v4.view.animation.LinearOutSlowInInterpolator; import android.support.v7.app.AppCompatActivity; import android.view.View; import android

  • Android仿微信页面底部导航效果代码实现

    大家在参考本地代码的时候要根据需要适当的修改,里面有冗余代码小编没有删除.好了,废话不多说了,一切让代码说话吧! 关键代码如下所示: .java里面的主要代码 public class MainActivity extends BaseActivity implements TabChangeListener { private Fragment[] fragments; private FragZaiXianYuYue fragZaiXianYuYue; private FragDaoLuJi

  • Android实现水波纹点击效果

    Android实现水波纹点击效果只在Android5.0以上版本有效,水波纹点击效果代码供大家参考,具体内容如下 圆角背景的水波纹效果(如上图) 1. 定义一个普通圆角背景的xml; rounded_corners.xml <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"

  • Android自定义View实现字母导航栏的代码

    思路分析: 1.自定义View实现字母导航栏 2.ListView实现联系人列表 3.字母导航栏滑动事件处理 4.字母导航栏与中间字母的联动 5.字母导航栏与ListView的联动 效果图: 首先,我们先甩出主布局文件,方便后面代码的说明 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/re

随机推荐