1. 程式人生 > >底下選單欄的實現

底下選單欄的實現

這兩天計劃做一個新的專案,但是當我剛開始動工的時候,發現自己連做過的功能都不知道怎麼實現,所以現在先做一個總結和整理,以後要用的時候可以方便使用.

有兩種方式實現:1.自定義. 2.用系統提供

1.自定義方式

①寫好整體佈局

分為兩大部分,上面用幀佈局來展示fragment,下面為組合控制元件作為Tab進行主頁面的切換.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width
="match_parent" android:layout_height="match_parent" android:orientation="vertical" >
<!--幀佈局,用於放置Fragment--> <FrameLayout android:id="@+id/top_bar" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" />
<!--紅色線條--> <View android:layout_width="match_parent" android:layout_height="4dp" android:background="#BB1F35" /> <!-- 組合控制元件 --> <include android:id="@+id/bottom_bar" layout="@layout/bottom_bar" /> </LinearLayout>

②組合控制元件,Tab的製作

根據自己的需要進行設計,自定義控制元件

Java程式碼

public class BottomBar extends LinearLayout implements View.OnClickListener {

    private ImageView frag_main_iv;
    private TextView frag_main;

    private ImageView frag_category_iv;
    private TextView frag_category;

    private ImageView frag_shopcar_iv;
    private TextView frag_shopcar;

    private ImageView frag_mine_iv;
    private TextView frag_mine;
    private IBottomBarItemClickListener mListener;
    private int mCurrentTabId=-1;

    //程式設計師自己new控制元件的時候使用的
    public BottomBar(Context context) {
        super(context);
    }

    //將控制元件放到xml佈局中 系統在xml佈局檔案中載入控制元件的時候就會預設呼叫該構造器
    public BottomBar(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    //獲取子控制元件  並且為子控制元件設定樣式
    //該方法用來告訴我們 佈局轉換成控制元件已經轉換完畢了
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        findViewById(R.id.frag_main_ll).setOnClickListener(this);
        findViewById(R.id.frag_category_ll).setOnClickListener(this);
        findViewById(R.id.frag_shopcar_ll).setOnClickListener(this);
        findViewById(R.id.frag_mine_ll).setOnClickListener(this);
        frag_main_iv = (ImageView) findViewById(R.id.frag_main_iv);
        frag_category_iv = (ImageView) findViewById(R.id.frag_category_iv);
        frag_shopcar_iv = (ImageView) findViewById(R.id.frag_shopcar_iv);
        frag_mine_iv = (ImageView) findViewById(R.id.frag_mine_iv);
        frag_main = (TextView) findViewById(R.id.frag_main);
        frag_category = (TextView) findViewById(R.id.frag_category);
        frag_shopcar = (TextView) findViewById(R.id.frag_shopcar);
        frag_mine = (TextView) findViewById(R.id.frag_mine);
        //模擬使用者點選了首頁
        findViewById(R.id.frag_main_ll).performClick();
    }

    /**
     * Indicators 指示器
     */
    private void changeIndicators(int viewId) {
        frag_main_iv.setSelected(viewId == R.id.frag_main_ll);
        frag_main.setSelected(viewId == R.id.frag_main_ll);
        frag_category_iv.setSelected(viewId == R.id.frag_category_ll);
        frag_category.setSelected(viewId == R.id.frag_category_ll);

        frag_shopcar_iv.setSelected(viewId == R.id.frag_shopcar_ll);
        frag_shopcar.setSelected(viewId == R.id.frag_shopcar_ll);
        frag_mine_iv.setSelected(viewId == R.id.frag_mine_ll);
        frag_mine.setSelected(viewId == R.id.frag_mine_ll);
    }

    @Override
    public void onClick(View view) {
        //如果當前是點選某個按鈕了 就要做一個攔截
        //每次都要獲取點選的item的id的優化
        int mTabId=view.getId();
        if (mCurrentTabId==mTabId){
            return;
        }
        switch (mTabId) {
            case R.id.frag_main_ll:
            case R.id.frag_category_ll:
            case R.id.frag_shopcar_ll:
            case R.id.frag_mine_ll:
                changeIndicators(mTabId);
                if (mListener != null) {
                    mListener.onItemClick(mTabId);
                }
                break;
        }
        //記錄當前點選的是哪個View
        mCurrentTabId=mTabId;
    }

    public void setIBottomBarItemClickListener(IBottomBarItemClickListener listener) {
        this.mListener = listener;
    }
}

xml程式碼

<?xml version="1.0" encoding="utf-8"?>
<com.it520.jdmall03.ui.BottomBar
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="55dp"
    android:orientation="horizontal"  >

    <LinearLayout
        android:id="@+id/frag_main_ll" 
        style="@style/bottom_ll_style" >

        <ImageView
            android:id="@+id/frag_main_iv"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:src="@drawable/home_bot_bar" />

        <TextView
            android:id="@+id/frag_main"
            style="@style/bottom_text_style"
            android:text="@string/frag_main" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/frag_category_ll" 
        style="@style/bottom_ll_style" >

        <ImageView
            android:id="@+id/frag_category_iv"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:src="@drawable/category_bot_bar" />

        <TextView
            android:id="@+id/frag_category"
            style="@style/bottom_text_style"
            android:text="@string/frag_category" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/frag_shopcar_ll" 
        style="@style/bottom_ll_style" >

        <ImageView
            android:id="@+id/frag_shopcar_iv"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:src="@drawable/shopcar_bot_bar" />

        <TextView
            android:id="@+id/frag_shopcar"
            style="@style/bottom_text_style"
            android:text="@string/frag_shopcar" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/frag_mine_ll"
        style="@style/bottom_ll_style" >

        <ImageView
            android:id="@+id/frag_mine_iv"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:src="@drawable/mine_bot_bar" />

        <TextView
            android:id="@+id/frag_mine"
            style="@style/bottom_text_style"
            android:text="@string/frag_mine" />
    </LinearLayout>

</com.it520.jdmall03.ui.BottomBar>

效果如下

這裡寫圖片描述

整體效果如下

這裡寫圖片描述

每一個bottom都設定了背景選擇器,當選中時會更變顏色,可以更好地看出是哪個按鈕被選中,程式碼中還引用了Velues資料夾中資源件裡的屬性,這裡就不一一展示了.

③功能的實現

佈局設計完成後要進行功能的實現,就是按下不同的Tab,頁面進行切換
首先要建立好需要的Fragment,然後通過點選Tab按鈕進行Fragment的切換
程式碼如下:

public class MainActivity extends BaseActivity implements IBottomBarItemClickListener {

    private ArrayList<BaseFragment> mFragments;
    private BottomBar mBottomBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setBackgroundDrawable(null);
        setContentView(R.layout.activity_main);
        initViews();
        initFragments();
        changeFragment(mFragments.get(0));
    }

    private void initViews() {
        mBottomBar =(BottomBar) findViewById(R.id.bottom_bar);
        mBottomBar.setIBottomBarItemClickListener(this);
    }

    private void initFragments(){
        mFragments=new ArrayList<>();
        mFragments.add(new HomeFragment());
        mFragments.add(new CategoryFragment());
        mFragments.add(new ShopcarFragment());
        mFragments.add(new MyJdFragment());
    }

    /**
     * 點選底部欄切換Fragment--->事務
     * */
    private void changeFragment(BaseFragment f){
        FragmentManager fManager = getSupportFragmentManager();
        FragmentTransaction transaction = fManager.beginTransaction();
        //add 往容器裡面不斷的新增東西  此刻你可以認為容器就是一個佇列
        //remove  不斷的往容器裡面移除Fragment
        // show/hide   就是容器裡面已經有某個Fragment了 只能顯示隱藏
        //                  此刻Fragment的生命週期是沒變化     onHiddenChanged
        //replace  不管容器裡面有多少Fragment 都會銷燬掉  再新增新的Fragment
        //attach/detach  處理是否關聯Fragment內部的佈局  Fragment還在
        transaction.replace(R.id.top_bar,f);
        transaction.commitAllowingStateLoss();
    }

    /**
     * 底部欄點選的item方法回撥
     * */
    @Override
    public void onItemClick(int viewId) {
        switch (viewId) {
            case R.id.frag_main_ll:
                changeFragment(mFragments.get(0));
                break;
            case R.id.frag_category_ll:
                changeFragment(mFragments.get(1));
                break;
            case R.id.frag_shopcar_ll:
                changeFragment(mFragments.get(2));
                break;
            case R.id.frag_mine_ll:
                changeFragment(mFragments.get(3));
                break;
        }
    }

}

完成,終效果如下:
這裡寫圖片描述

2.利用系統提供的控制元件FragmentTabHost

①寫好主頁的佈局

分為兩部分,上面用FrameLayout來展示Fragment,下面為v4包下的FragmentTabHost控制元件,這是google提供的選單欄控制元件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <FrameLayout
        android:id="@+id/main_framelayout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        />

    <android.support.v4.app.FragmentTabHost
        android:id="@+id/main_fragmenttabhost"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    </android.support.v4.app.FragmentTabHost>
</LinearLayout>

②功能實現

在MainActivity中找到控制元件,按照使用方法給控制元件新增自己需要的樣式

item佈局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
    android:gravity="center_horizontal">

    <ImageView
        android:id="@+id/item_tab_iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/tab_my_selected"/>

    <TextView
        android:id="@+id/item_tab_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="我的"/>
</LinearLayout>

java程式碼

public class MainActivity extends AppCompatActivity {

    private FrameLayout mMainFramelayout;
    private MyFragmentTabHost mMainTabhost;

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

        initView();
        initTabHost();
    }

    //tabHost切換tab
    private void initTabHost() {
        //最基本使用方式
        //        //1.初始化,傳入需要的引數
        //        mMainTabhost.setup(getApplicationContext(), getSupportFragmentManager(), R.id.main_framelayout);
        //        //2.開始設定tabhost
        //        //設定標籤,用來表示不同的tab
        //        TabHost.TabSpec tabSpec = mMainTabhost.newTabSpec("1");
        //        //設定指示器,即為顯示按鈕的佈局
        //        tabSpec.setIndicator("新聞");
        //        NewsFragment newsFragment = new NewsFragment();
        //        //引數:標籤,fragment物件,需要傳入的資料
        //        mMainTabhost.addTab(tabSpec, newsFragment.getClass(), null);
        //
        //利用for迴圈進行新增可以減少程式碼量
        mMainTabhost.setup(getApplicationContext(), getSupportFragmentManager(), R.id.main_framelayout);
        int[] resIds = {R.drawable.tab_news, R.drawable.tab_va, R.drawable.tab_topic, R.drawable.tab_my};
        String[] tabTexts = {"新聞", "直播", "話題", "我的"};
        Class[] fragmentClazz = {new NewsFragment().getClass(), new VaFragment().getClass(), new TopicFragment().getClass(), new MeFragment().getClass()};
        for (int i = 0; i < resIds.length; i++) {
            TabHost.TabSpec tabSpec = mMainTabhost.newTabSpec(String.valueOf(i));
            View inflate = View.inflate(getApplicationContext(), R.layout.item_tab, null);
            tabSpec.setIndicator(inflate);
            ImageView ivTab = (ImageView) inflate.findViewById(R.id.item_tab_iv);
            TextView tvTab = (TextView) inflate.findViewById(R.id.item_tab_tv);
            ivTab.setImageResource(resIds[i]);
            tvTab.setText(tabTexts[i]);

            mMainTabhost.addTab(tabSpec, fragmentClazz[i], null);
        }

    }

    private void initView() {
        mMainFramelayout = (FrameLayout) findViewById(R.id.main_framelayout);
        mMainTabhost = (MyFragmentTabHost) findViewById(R.id.main_tabhost);
    }
}

基本使用已經完成,效果如下圖:

這裡寫圖片描述

③優化

通過觀察原始碼發現,點選tab切換Fragment的方式為detach和attach兩個方法,這樣會導致每次切換Fragment時都會重新載入,不能保留客戶在介面上的操作,而我的業務需求是最好能保留客戶的操作,所以我重寫了這個類,並做了一點修改.

首先建立一個類MyFragmentTabHost,繼承TabHost,然後找到系統提供的FragmentTabHost類,全部複製一份,貼上到自己的類中,再進行修改下面方法,把detach和attach,改為hide和show

 @Nullable
    private FragmentTransaction doTabChanged(@Nullable String tag,
            @Nullable FragmentTransaction ft) {
        final TabInfo newTab = getTabInfoForTag(tag);
        if (mLastTab != newTab) {
            if (ft == null) {
                ft = mFragmentManager.beginTransaction();
            }

            if (mLastTab != null) {
                if (mLastTab.fragment != null) {
//                    ft.detach(mLastTab.fragment);
                    ==ft.hide(mLastTab.fragment)==;
                }
            }

            if (newTab != null) {
                if (newTab.fragment == null) {
                    newTab.fragment = Fragment.instantiate(mContext,
                            newTab.clss.getName(), newTab.args);
                    ft.add(mContainerId, newTab.fragment, newTab.tag);
                } else {
//                    ft.attach(newTab.fragment);
                    ==ft.show(newTab.fragment);==

                }
            }

修改完成後,再在佈局中修改引用的控制元件型別,改成自己的類就可以了.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.simon.newwangyi.activity.MainActivity">

    <FrameLayout
        android:id="@+id/main_framelayout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        />

    <com.simon.newwangyi.view.MyFragmentTabHost
        android:id="@+id/main_tabhost"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    </com.simon.newwangyi.view.MyFragmentTabHost>
</LinearLayout>

完成.