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;
    }
}
相关推荐
雨白6 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹8 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空10 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭10 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日11 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安11 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑11 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟15 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡17 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0017 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体