ViewPager(懒加载)+Fragment+自定义View

本文介绍如何使用ViewPager结合Fragment构建主界面,讨论了底部导航的两种实现方式,作者倾向于使用ViewPager+Fragment的方式,因为它允许灵活地切换滑动功能。此外,文章还提到了对Fragment的封装,以增强扩展性和维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主界面的UI与设计

现在一般的项目都是底部几个按钮对应这几个fragment这种情况,这种大体上可以有2种写法

  1. FragmentTabHost/(RadiuGroup+RadiuButton)等等一些底部按钮+Fragment来展示
  2. 一些底部按钮+ViewPager+Fragment来实现

这两种我个人更喜欢使用的是第二种,并且在最近的项目中也是用第二种方式来实现的,如果产品说底部按钮对应的页面是不能够滑动的,那么两种没什么区别,(viewPager可以禁止滑动来实现)可后来如果产品又说需要滑动了,那如果用第一种实现的就傻了,而第二种默认就是可以滑动的只需要简单的修改一下就可以

public class ViewPagerMain  extends ViewPager{
    private boolean mIsScroll=true;//通过这个值来判断是否需要禁止滑动

    public ViewPagerMain(Context context) {
        super(context);
    }

    public ViewPagerMain(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (mIsScroll==true){
            return super.onInterceptTouchEvent(ev);
        }else{
            return mIsScroll;
        }


    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mIsScroll==true){
            return super.onTouchEvent(ev);
        }else{
            return mIsScroll;
        }

    }

    public void setIsScroll(boolean isScroll){
        mIsScroll = isScroll;
    }
}

至于底部的那些按钮我并没有使用上面的一些Android原生控件,而是通过
布局文件自己摆放,我个人认为这样的话扩展性更高一些,当然这只是一部分原因后面的代码大家可以看到这么写的好处
底部xml

<LinearLayout
    android:id="@+id/main_bottom_root"
    android:layout_width="match_parent"
    android:layout_height="@dimen/y96"
    android:background="#fff"
    >
    <LinearLayout
        android:id="@+id/home_root"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="vertical"
        android:gravity="center"
        >
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:gravity="center"
            >
            <ImageView
                android:id="@+id/iv_home"
                style="@style/bottom_iv"
                android:background="@drawable/home_selector"
                />
            <TextView
                android:id="@+id/tv_home"
                style="@style/main_tv"
                android:text="晒e晒"
                />
        </LinearLayout>

    </LinearLayout>
    <LinearLayout
        android:id="@+id/rb_message"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="vertical"
        android:gravity="center"
        >
        <ImageView
            android:id="@+id/iv_message"
            style="@style/bottom_iv"
            android:background="@drawable/message_selector" />
        <TextView
            android:id="@+id/tv_message"
            style="@style/main_tv"
            android:text="消息"
            />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/rb_service"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="vertical"
        android:gravity="center"

        >

        <ImageView
            style="@style/bottom_iv"
            android:background="@drawable/service_selector"
            android:id="@+id/iv_service" />

        <TextView
            android:id="@+id/tv_service"
            style="@style/main_tv"
            android:text="服务"
            />

    </LinearLayout>
    <LinearLayout
        android:id="@+id/rb_find"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="vertical"
        android:gravity="center"
        >
        <ImageView
            android:id="@+id/iv_find"
            style="@style/bottom_iv"
            android:background="@drawable/find_selector" />
        <TextView
            android:id="@+id/tv_find"
            style="@style/main_tv"
            android:text="发现"
            />

    </LinearLayout>
    <LinearLayout
        android:id="@+id/rb_my"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="vertical"
        android:gravity="center"
        >
        <ImageView
            android:id="@+id/iv_mine"
            style="@style/bottom_iv"
            android:background="@drawable/mine_selector" />
        <TextView
            android:id="@+id/tv_mine"
            style="@style/main_tv"
            android:text="我的"
            />
    </LinearLayout>

</LinearLayout>


/**
 * 监听底部按钮的切换状态,mMainBottomRoot为底部按钮的根布局,这样封装的话如果底部按钮的数量有变动
 * 这段代码不用发生什么变动,而且下一个项目用到的话直接复制粘贴
private void setBottomListener() {
    int childCount = mMainBottomRoot.getChildCount();
    for (int i = 0; i <childCount ; i++) {
        //获得子布局,为每个子view设置点击事件
        LinearLayout child = (LinearLayout) mMainBottomRoot.getChildAt(i);
        child.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //通过mMainBottomRoot来获得点击view对应的索引值
                int indexOfChild = mMainBottomRoot.indexOfChild(view);
                //根据索引来改变UI效果
                changeUI(indexOfChild);
                //viewPager也跳到对应的Fragment中
                mViewpagerMain.setCurrentItem(indexOfChild, false);
            }
        });
    }
}

/**
 *  当viewPager切换的时候更改底部按钮的UI状态
 * @param position
 */
private void changeUI(int position) {
    for (int i = 0; i <mMainBottomRoot.getChildCount() ; i++) {
        if (i==position){
            setEnable(mMainBottomRoot.getChildAt(i),false);
        }else{
            setEnable(mMainBottomRoot.getChildAt(i),true);
        }


    }
}

/**
 * 更改底部按钮UI状态,如果是view直接setEnable,如果是viewGroup递归遍历其中的view setEnable
 * @param childAt
 * @param b
 */
private void setEnable(View childAt, boolean b) {

    childAt.setEnabled(b);
    if (childAt instanceof ViewGroup){
        for (int i = 0; i < ((ViewGroup) childAt).getChildCount(); i++) {
            setEnable(((ViewGroup) childAt).getChildAt(i),b);
        }
    }
}

至于Fragment 我这里对Fragment展示的布局做了进一步封装,因为现在项目的主界面大体上不外呼刚进来时候弹个ProgressBar,DialogFragmen等与用户进行交互,当数据请求回来再隐藏销毁,如果数据请求失败再展示个失败的界面
这里我对它又进行了一次封装,不仅减少了代码的书写也加强了扩展性和维护性
/**
* BaseFragment
*/

public abstract class MyBaseFragment extends Fragment {

    protected Context context;
    protected StateLayout stateLayout;
    protected boolean isFirst = true;
    protected boolean isPrepared;
    private Unbinder mUnbinder;


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        context = getActivity();
        //防止Fragment重复加载
        if (stateLayout == null) {
            // 说明这个Fragemnt的onCreateView方法是第一次执行
            View view = getContentView();
            mUnbinder = ButterKnife.bind(this, view);
            //对Fragment展示的布局进一步封装
            stateLayout = StateLayout.newInstance(context, view);
            initView();
            initListener();
            //
            isPrepared = true;
        } else {
            ViewGroup parent = (ViewGroup) stateLayout.getParent();
            if (parent != null) {
                parent.removeView(stateLayout);
            }
        }
        return stateLayout;
    }


    /**
     * Fragment当前状态是否可见
     */
    protected boolean isVisible;

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        if (isVisibleToUser) {
            isVisible = true;
            LogUtil.e(getClass().getName()+"__________setUserVisibleHint");
            onVisible();
        } else {
            isVisible = false;
            onInvisible();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        LogUtil.e(getClass().getName()+"__________onResume");
        onVisible();
    }

    /**
     * 对initData再进一步的封装
     */
    protected void onVisible() {
        //保证ViewPager能够实现懒加载的方式并保证只有第一次进入这个Fragment的时候才进行数据的加载
        LogUtil.e("isFirst _____________" + isFirst);
        LogUtil.e("isVisible _____________" + isVisible);
        LogUtil.e("isPrepared _____________" + isPrepared);
        if (!isFirst || !isVisible || !isPrepared) {
            return;
        }
        initData();
        isFirst = false;
    }


    /**
     * 不可见
     */
    protected void onInvisible() {


    }


    /**
     * 查找View,增加这个方法是为了略强转
     *
     * @param id
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T findView(int id) {
        T view = (T) stateLayout.findViewById(id);
        return view;
    }




    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mUnbinder!=null){
            mUnbinder.unbind();
        }

    }

    /**
     * 返回Fragment自己的名称
     */
    protected  CharSequence getTitle(){
        return getClass().getName();
    };

    /**
     * 初始化View相关的代码写在这个方法中
     */
    public abstract void initView();

    /**
     * 初始化Listener的代码写在这个方法中
     */
    public abstract void initListener();

    /**
     * 初始化数据的代码写在这个方法中
     */
    public abstract void initData();

    /**
     * 返回正常的界面想要展示的View或View的Id
     */
    public abstract View getContentView();
}
public class StateLayout extends FrameLayout {

    private View loadingView;
    private View failView;
    private View emptyView;
    private View contentView;

    public StateLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 创建一个StateLayout实现
     * @param contentView 正常想要展示的View
     */
    public static StateLayout newInstance(Context context, View contentView) {
        StateLayout stateLayout = (StateLayout) LayoutInflater.from(context).inflate(R.layout.state_layout, null);
        stateLayout.contentView=contentView;
        // StateLayout inflate之后就有3个状态的View了,还需要第四种状态
        stateLayout.addView(contentView);
        //当添加完成后暂时给隐藏掉
        contentView.setVisibility(View.GONE);
        return stateLayout;
    }



    /**
     * 渲染完毕后展示获取view对象
     */
    @Override
    protected void onFinishInflate() {
        loadingView = findViewById(R.id.loadingView);
        failView = findViewById(R.id.failView);
        emptyView = findViewById(R.id.emptyView);

        showLoadingView();
    }

    /** 显示正在加载的View */
    public void showLoadingView() {
        showView(loadingView);
    }

    /** 显示失败的View */
    public void showFailView() {
        showView(failView);
    }

    /** 显示加载为空的View */
    public void showEmptyView() {
        showView(emptyView);
    }

    /** 显示正常界面的View */
    public void showContentView() {
        showView(contentView);
    }

    /** 
     * 显示指定的View,并且隐藏其它的View
     * @param view 指定要显示的View
     */
    private void showView(View view) {

        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i); //
            child.setVisibility(view == child ? View.VISIBLE : View.GONE);
        }
    }

}

具体的我就不细说了,
有需要的话可以
下来看看https://github.com/wangrun1992/MainUiFrame

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值