Android 底部tab,使用recycleview实现

res/layout/activity_main.xml

c 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <!-- 用于显示Fragment的容器 -->
    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/rv_navigation"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <!-- 底部导航栏 -->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:overScrollMode="never"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

java/com/zs/test/MainActivity.java

c 复制代码
package com.zs.test;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.Toast;

import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;
import com.zs.test.bean.MainNavigation;
import com.zs.test.common.Global;
import com.zs.test.event.Event;
import com.zs.test.event.EventBus;
import com.zs.test.event.Subscribe;
import com.zs.test.fragment.MyFragment;
import com.zs.test.fragment.TestFragment;
import com.zs.test.fragment.WebViewFragment2;
import com.zs.test.ui.adapter.MainNavigationAdapter;

import java.util.List;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;

public class MainActivity extends AppCompatActivity {
    private boolean isBackPressedOnce = false; // 标志用户是否已经按过一次返回键
    private final Handler handler = new Handler(Looper.getMainLooper()); // 用于延时清除标志

    private WebViewFragment2 webViewFragment2;


    public static IWXAPI api;
    private RecyclerView mRvNavigation;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        EventBus.register(this);

        mRvNavigation = findViewById(R.id.rv_navigation);
        final MainNavigationAdapter adapter = new MainNavigationAdapter();
        adapter.setData(MainNavigation.obtainMainNav());
        adapter.attachToRecyclerView(mRvNavigation);
        adapter.setOnCheckedChangeListener(this::onCheckedChanged);
        //设置默认选中
        adapter.select(0);

        api = WXAPIFactory.createWXAPI(getApplicationContext(), Global.vxAppId, false);
        api.registerApp(Global.vxAppId);
    }

    private final Fragment[] mFragments = new Fragment[]{new TestFragment(), new MyFragment()};

    public void onCheckedChanged(@NonNull MainNavigationAdapter adapter, int position) {
        final String tagStart = "main_fragment_tag_";
        final List<Fragment> fragments = getSupportFragmentManager().getFragments();
        for (Fragment f : fragments) {
            if (f.getTag() != null && f.getTag().startsWith(tagStart)) {
                getSupportFragmentManager().beginTransaction().hide(f).commitAllowingStateLoss();
            }
        }

        final String tag = tagStart + position;
        Fragment current = getSupportFragmentManager().findFragmentByTag(tag);
        if (current != null) {
            getSupportFragmentManager().beginTransaction().show(current).commitAllowingStateLoss();
        } else {
            current = mFragments[position];
            getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, current, tag).commitAllowingStateLoss();
        }
    }

    private void handleBottomShow(boolean show) {
        if (show) {
            runOnUiThread(() -> {if (mRvNavigation != null) mRvNavigation.setVisibility(View.VISIBLE);});
        } else {
            runOnUiThread(() -> {if (mRvNavigation != null) mRvNavigation.setVisibility(View.GONE);});
        }
    }

    @Override
    public void onBackPressed() {
        // 检查当前显示的 Fragment
        final List<Fragment> fragments = getSupportFragmentManager().getFragments();
        for (Fragment fragment : fragments) {
            if (fragment instanceof TestFragment) {
                ((TestFragment) fragment).handleWebViewBackPress();
                return;
            }

            if (fragment instanceof MyFragment) {
                ((MyFragment) fragment).handleWebViewBackPress();
                return;
            }
        }

        if (isBackPressedOnce) {
            // 第二次按返回键,退出应用
            super.onBackPressed();
            finishAffinity(); // 结束所有活动,退出应用
            System.exit(0); // 彻底退出进程
        } else {
            // 第一次按返回键,提示用户
            isBackPressedOnce = true;
            Toast.makeText(this, "再按一次返回键退出程序", Toast.LENGTH_SHORT).show();

            // 延时2秒后重置标志
            handler.postDelayed(() -> isBackPressedOnce = false, 2000);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 清除所有回调,避免内存泄漏
        handler.removeCallbacksAndMessages(null);
        EventBus.unregister(this);
    }

    // 处理 RunGoalEvent 事件
    @Subscribe
    public void MainBottomShow(Event.MainBottomShow event) {
        handleBottomShow(event.isShow());
    }
}

res/menu/menu_main.xml

c 复制代码
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/tab_test"
        android:icon="@drawable/tab_statistics_selector"
        android:title="趣测"/>

    <item
        android:id="@+id/tab_my"
        android:icon="@drawable/tab_step_count_selector"
        android:title="我的"/>

</menu>

java/com/zs/test/base/BaseAdapter.java

c 复制代码
package com.zs.test.base;

import androidx.recyclerview.widget.RecyclerView;

/**
 * {@link RecyclerView}适配器基类
 */
public abstract class BaseAdapter extends RecyclerView.Adapter<BaseHolder> {
}

java/com/zs/test/base/BaseHolder.java

c 复制代码
package com.zs.test.base;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.collection.ArrayMap;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;

/**
 * RecyclerView ViewHolder
 */
public class BaseHolder extends RecyclerView.ViewHolder {

    private final Context mContext;
    private final SparseArray<View> mSparseArray;

    public BaseHolder(@NonNull View itemView) {
        super(itemView);
        mContext = itemView.getContext();
        mSparseArray = new SparseArray<>();
    }

    public BaseHolder(@LayoutRes int resource, @NonNull ViewGroup parent) {
        this(LayoutInflater.from(parent.getContext()).inflate(resource, parent, false));
    }

    /**
     * 设置 itemView最小高度
     */
    public BaseHolder setMinHeight(int minHeight) {
        itemView.setMinimumHeight(minHeight);
        return this;
    }

    public Context getContext() {
        return mContext;
    }

    @SuppressWarnings("all")
    public <T extends View> T findViewById(@IdRes int id) {
        View view = mSparseArray.get(id);
        if (view == null) {
            view = itemView.findViewById(id);
            mSparseArray.put(id, view);
        }
        return ((T) view);
    }

    public <T extends View> T findViewById(@IdRes int id, boolean visibility) {
        T view = findViewById(id);
        view.setVisibility(visibility ? View.VISIBLE : View.GONE);
        return view;
    }

    public <T extends View> T findViewById(@IdRes int id, int visibility) {
        T view = findViewById(id);
        view.setVisibility(visibility);
        return view;
    }

    public void setText(@IdRes int id, CharSequence text) {
        TextView textView = findViewById(id);
        textView.setText(text);
    }

    public void setText(@IdRes int id, int textResOrNum) {
        TextView textView = findViewById(id);
        try {
            textView.setText(getContext().getString(textResOrNum));
        } catch (Exception e) {
            textView.setText(String.valueOf(textResOrNum));
        }
    }

    public void setHint(@IdRes int id, CharSequence text) {
        TextView textView = findViewById(id);
        textView.setHint(text);
    }

    public void setHint(@IdRes int id, int textResOrNum) {
        TextView textView = findViewById(id);
        try {
            textView.setHint(getContext().getString(textResOrNum));
        } catch (Exception e) {
            textView.setHint(String.valueOf(textResOrNum));
        }
    }

    public ImageView findImageById(@IdRes int imageViewId) {
        return findViewById(imageViewId);
    }

    public ImageView findImageById(@IdRes int imageViewId, boolean visibility) {
        return findViewById(imageViewId, visibility);
    }

    public ImageView findImageById(@IdRes int imageViewId, int visibility) {
        return findViewById(imageViewId, visibility);
    }

    public void setVisibility(int id, int visibility) {
        findViewById(id).setVisibility(visibility);
    }

    public void setVisibility(int id, boolean visibility) {
        findViewById(id).setVisibility(visibility ? View.VISIBLE : View.GONE);
    }

    public void setImageResource(@IdRes int imageViewId, @DrawableRes int drawableRes) {
        ImageView imageView = findViewById(imageViewId);
        imageView.setImageResource(drawableRes);
    }

    private final ArrayMap<String, Drawable> mCircleDrawableMap = new ArrayMap<>();

    /**
     * 获取圆形的Drawable
     *
     * @param size  宽高
     * @param color 颜色
     */
    public Drawable getCircleDrawable(int size, int color) {
        String key = String.valueOf(size) + color;
        Drawable drawable = mCircleDrawableMap.get(key);
        if (drawable == null) {
            GradientDrawable gradientDrawable = new GradientDrawable();
            gradientDrawable.setShape(GradientDrawable.OVAL);
            if (size > 0) {
                gradientDrawable.setSize(size, size);
            }
            gradientDrawable.setColor(color);
            drawable = gradientDrawable;
            mCircleDrawableMap.put(key, drawable);
        }
        return drawable;
    }

    public Drawable getCircleDrawable(int size) {
        return getCircleDrawable(size, Color.parseColor("#EDEDED"));
    }

    public int getColor(@ColorRes int id) {
        return ContextCompat.getColor(getContext(), id);
    }

}

res/layout/item_main_navigation.xml

c 复制代码
<?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="wrap_content"
    android:gravity="center"
    android:minHeight="48dp"
    android:orientation="vertical">

    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/iv_icon"
        android:layout_width="wrap_content"
        android:layout_height="23dp"
        android:adjustViewBounds="true"
        android:scaleType="fitXY" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="3dp"
        android:textSize="12sp" />

</LinearLayout>

java/com/zs/test/bean/MainNavigation.java

c 复制代码
package com.zs.test.bean;

import androidx.annotation.DrawableRes;

import com.zs.test.R;

import java.util.Arrays;
import java.util.List;

/**
 * 底部导航数据
 */
public class MainNavigation {

    /**
     * 获取主页的
     */
    public static List<MainNavigation> obtainMainNav() {
        return Arrays.asList(
                new MainNavigation("趣测", R.drawable.ic_statistics_unselected, R.drawable.ic_statistics_selected),
                new MainNavigation("我的", R.drawable.ic_step_count_unselected, R.drawable.ic_step_count_selected)
        );
    }

    /**
     * 按钮名称
     */
    private final String buttonName;

    /**
     * 图标选中图标
     */
    @DrawableRes
    private final int drawableNormal, drawableSelect;

    public MainNavigation(String buttonName, int drawableNormal, int drawableSelect) {
        this.buttonName = buttonName;
        this.drawableNormal = drawableNormal;
        this.drawableSelect = drawableSelect;
    }

    public int getDrawableNormal() {
        return drawableNormal;
    }

    public int getDrawableSelect() {
        return drawableSelect;
    }

    public String getButtonName() {
        return buttonName;
    }
}

java/com/zs/test/ui/adapter/MainNavigationAdapter.java

c 复制代码
package com.zs.test.ui.adapter;

import android.graphics.Color;
import android.view.ViewGroup;
import android.widget.TextView;

import com.zs.test.R;
import com.zs.test.base.BaseAdapter;
import com.zs.test.base.BaseHolder;
import com.zs.test.bean.MainNavigation;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

/**
 * 底部导航
 */
public class MainNavigationAdapter extends BaseAdapter {

    private RecyclerView mRecyclerView;

    /**
     * 数据
     */
    private final List<MainNavigation> mData = new ArrayList<>();

    /**
     * 当前选中的
     */
    private int mSelect = -1;

    /**
     * 选中监听
     */
    private OnCheckedChangeListener mOnCheckedChangeListener;

    public void setData(List<MainNavigation> data) {
        mData.clear();
        if (data != null) mData.addAll(data);
        notifyDataSetChanged();
        if (mRecyclerView != null) attachToRecyclerView(mRecyclerView);
    }

    public void select(int position) {
        if (mSelect == position) {
            return;
        }

        final int old = mSelect;
        mSelect = position;
        if (old >= 0 && old < mData.size()) notifyItemChanged(old);
        if (mSelect >= 0 && mSelect < mData.size()) notifyItemChanged(mSelect);
        if (mOnCheckedChangeListener != null && mSelect >= 0) {
            mOnCheckedChangeListener.onCheckedChanged(this, mSelect);
        }
    }

    public interface OnCheckedChangeListener {
        void onCheckedChanged(@NonNull MainNavigationAdapter adapter, int position);
    }

    /**
     * 设置选中监听
     */
    public void setOnCheckedChangeListener(OnCheckedChangeListener l) {
        mOnCheckedChangeListener = l;
        if (mOnCheckedChangeListener != null && mSelect >= 0) {
            mOnCheckedChangeListener.onCheckedChanged(this, mSelect);
        }
    }

    @NonNull
    @Override
    public BaseHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new BaseHolder(R.layout.item_main_navigation, parent);
    }

    @Override
    public void onBindViewHolder(@NonNull BaseHolder holder, int position) {
        final MainNavigation item = mData.get(position);

        //按钮图标
        holder.setImageResource(R.id.iv_icon, position == mSelect ? item.getDrawableSelect() : item.getDrawableNormal());

        //按钮文本
        final TextView tvName = holder.findViewById(R.id.tv_name);
        tvName.setText(item.getButtonName());
        tvName.setTextColor(Color.parseColor(position == mSelect ? "#574DBE" : "#999999"));

        //点击事件
        holder.itemView.setOnClickListener(v -> select(position));
    }

    @Override
    public int getItemCount() {
        return mData.size();
    }

    public void attachToRecyclerView(@NonNull RecyclerView recyclerView) {
        final int spanCount = Math.max(mData.size(), 1);
        recyclerView.setLayoutManager(new GridLayoutManager(recyclerView.getContext(), spanCount, GridLayoutManager.VERTICAL, false));
        recyclerView.setAdapter(this);
        mRecyclerView = recyclerView;
    }
}
相关推荐
Kapaseker1 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴1 小时前
Android17 为什么重写 MessageQueue
android
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android