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;
    }
}
相关推荐
Frank_HarmonyOS2 小时前
【无标题】Android消息机制
android
凯文的内存4 小时前
Android14 OTA升级速度过慢问题解决方案
android·ota·update engine·系统升级·virtual ab
VinRichard4 小时前
Android 常用三方库
android
Aileen_0v05 小时前
【玩转OCR | 腾讯云智能结构化OCR在图像增强与发票识别中的应用实践】
android·java·人工智能·云计算·ocr·腾讯云·玩转腾讯云ocr
江上清风山间明月8 小时前
Flutter DragTarget拖拽控件详解
android·flutter·ios·拖拽·dragtarget
debug_cat11 小时前
AndroidStudio Ladybug中编译完成apk之后定制名字kts复制到指定目录
android·android studio
编程洪同学15 小时前
Spring Boot 中实现自定义注解记录接口日志功能
android·java·spring boot·后端
Clockwiseee18 小时前
PHP之伪协议
android·开发语言·php
小林爱18 小时前
【Compose multiplatform教程08】【组件】Text组件
android·java·前端·ui·前端框架·kotlin·android studio