介紹三個Android支持庫控件:TabLayout+ViewPager+RecyclerView

本文主要介紹如下三個Android支持庫控件的配合使用:

  • TabLayout:android.support.design.widget.TabLayout
  • ViewPager:android.support.v4.view.ViewPager
  • RecyclerView:android.support.v7.widget.RecyclerView

對于支持庫的使用,這里稍作介紹:
Android支持庫包含常用的v4、v7、v13以及v17,注解支持(annotation),設計支持(material design)等。
目前v4和v7已經根據不同的功能進行了拆分,以減小目標Apk的大小,可針對某項具體的功能引入特定的包,而不用整體引入support-v4包。
比如,以下為筆者常用的支持庫,讀者可以參考:

// 實現各種 UI 相關組件,例如 ViewPager、NestedScrollView 和 ExploreByTouchHelper。
supportCoreUi       : 'com.android.support:support-core-ui:24.2.0',
// 添加對使用片段封裝用戶界面和功能的支持,從而使應用能夠提供可以在大屏幕設備與小屏幕設備之間進行調節的布局。
// 此模塊依賴于 compat、core-utils、core-ui 和 media-compat。
supportFragment     : 'com.android.support:support-fragment:24.2.0',
// 此庫添加了對操作欄用戶界面設計模式的支持。此庫包含對 Material Design 用戶界面實現的支持。
supportAppCompat    : 'com.android.support:appcompat-v7:24.2.0',
// recyclerview 庫添加了 RecyclerView 類。此類能夠為 RecyclerView 小部件提供支持,
// RecyclerView 是一種通過提供有限的數據項窗口有效顯示大數據集的視圖。
supportRecyclerView : 'com.android.support:recyclerview-v7:24.2.0',
// 此庫添加了對 CardView 小部件的支持,讓您能夠在卡片內顯示信息,從而使應用具備一致的外觀。
supportCardView     : 'com.android.support:cardview-v7:24.2.0',
// 注解軟件包提供的 API 支持向應用中添加注解元數據。
supportAnnotations  : 'com.android.support:support-annotations:24.2.0',
// 設計軟件包提供的 API 支持向應用中添加 Material Design 組件和模式。
supportDesign       : 'com.android.support:design:24.2.0',

最后,需要注意,支持庫的版本需要保持一致,不然容易出現編譯錯誤。
更多關于支持庫的使用,請參考官方文檔

先上效果圖:

Tab頁切換效果圖

(左滑的比較慢,是為了看清效果)

一、TabLayout

TabLayout是design支持庫中引入的支持Tab頁的控件,配合ViewPager使用,實現Table頁面的滑動。使用時:

  1. 使用setTabMode(TabLayout.MODE_FIXED)來設置TabLayout的模式;
  2. 通過addTab來添加Tab頁面;
  3. 最后通過setupWithViewPager來關聯ViewPager.
// 設置TabLayout的模式
goodsTypeTl.setTabMode(TabLayout.MODE_FIXED);
// 添加Fragment顯示
for (int i = 0; i < 2; i++) {
    PickingTaskGoodsFragment itemFragment = PickingTaskGoodsFragment.newInstance(
            i == 0 ? unCompletedPickingSku : pickingCompletedSku);
    fragmentList.add(itemFragment);
    goodsTypeTl.addTab(goodsTypeTl.newTab().setText(titleList.get(i)));
}
// 實例化ViewPage的適配器
FragmentPagerAdapter fAdapter = new PickingFragmentPagerAdapter(
        getSupportFragmentManager(), fragmentList, titleList);
// viewpager加載adapter
goodsDetailVp.setAdapter(fAdapter);
// TabLayout加載viewpager
goodsTypeTl.setupWithViewPager(goodsDetailVp);

二、ViewPager

ViewPager是在v4包中引入的控件,在布局文件中,緊接著TabLayout進行布局。ViewPager繼承自ViewGroup,在使用時,最關鍵的是為其添加PagerAdapter,一般ViewPage會包含Fragment,那么這里PagerAdapter會使用FragmentPagerAdapter:

public abstract class FragmentPagerAdapter extends PagerAdapter

FragmentPagerAdapter是一個抽象類,在使用時一般繼承自FragmentPagerAdapter自定義實現:

public class PickingFragmentPagerAdapter extends FragmentPagerAdapter {

    private List<Fragment> fragmentList;
    private List<String> titleList;

    public PickingFragmentPagerAdapter(FragmentManager fm,
                                       List<Fragment> fragmentList, List<String> titleList) {
        super(fm);
        this.fragmentList = fragmentList;
        this.titleList = titleList;
    }

    @Override
    public Fragment getItem(int position) {
        return fragmentList.get(position);
    }

    @Override
    public int getCount() {
        return titleList.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titleList.get(position);
    }
}

getItem和getCount方法必須要求實現,分別返回List<Fragment>的內容就行了。

在實例化PickingFragmentPagerAdapter時,需要傳入FragmentManager,一般采用getSupportFragmentManager()。

三、RecyclerView

RecyclerView是v7支持庫中引入的控件,使用時需要依賴com.android.support:recyclerview-v7:22.2.1,使用RecyclerView需要注意兩點:

  1. 設置LayoutManager
recyclerView.setLayoutManager(new LinearLayoutManager(context));

當然,這里還可以設置GridLayoutManager

  1. 設置Adapter
recyclerView.setAdapter(new PickingTaskGoodsAdapter(pickingTaskDParams, mListener));

RecyclerView的Adapter是需要重點關注的。其中,需要實現的方法有三個:onCreateViewHolderonBindViewHoldergetItemCount

  • onCreateViewHolder:這個方法的返回值是ViewHolder,而這個ViewHolder一般都需要自定義,當然,默認RecyclerView也有ViewHolder。
View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.out_picking_goods_detail_item, parent, false);
return new ViewHolder(view, new MySkuItemData());

View是直接通過Inflater提取出來的(注意item的根ViewGroup的height不能設置為match parent,不然多行item無法正常顯示),ViewHolder的入參需要傳入View。ViewHolder的定義有點講究,與ListView中有不一致:

public class ViewHolder extends RecyclerView.ViewHolder {
    final View mView;

    AutofitTextView goodsNumberTv;
    TextView goodsNameTv;
    TextView goodsLeftNumberTv;
    ImageView skuState;
    Button pickingSku;

    ViewHolder(View view) {
        super(view);
        mView = view;

        goodsNumberTv = (AutofitTextView) view.findViewById(R.id.out_picking_task_goods_number_tv);
        goodsNameTv = (TextView) view.findViewById(R.id.out_picking_task_goods_name_tv);
        goodsLeftNumberTv = (TextView) view.findViewById(R.id.out_picking_task_goods_left_number_tv);
        skuState = (ImageView) view.findViewById(R.id.out_picking_task_goods_sku_state_iv);
        pickingSku = (Button) view.findViewById(R.id.out_picking_task_goods_picking_btn);
    }
}

首先,需要定義一個帶參構造器,第一個參數一定是View,ViewHolder也需要包含View的域,另外,ViewHolder可以包含各個View的監聽器,而這種監聽器一般都需要自定義,因為其中會包含重要的參數。

  • onBindViewHolder是具體實現數據更新的地方,onBindViewHolder的入參為ViewHolder holder, int position,因此,首先通過position獲取數據,然后對ViewHolder的控件依次設置:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    PickingTaskDParam dParam = taskDParamList.get(position);
    holder.goodsNumberTv.setText(dParam.getSkuNo());
    holder.goodsNameTv.setText(dParam.getSkuName());
    holder.goodsLeftNumberTv.setText(String.valueOf(dParam.getPlannedPickQty().intValue()));

    if (dParam.getOptStatus() == 1 || dParam.getOptStatus() == 2) {
        // 大小設置
        holder.pickingSku.setVisibility(View.GONE);
        holder.skuState.setVisibility(View.VISIBLE);
    } else {
        holder.skuState.setVisibility(View.GONE);
        if (pickingVisible) {
            holder.pickingSku.setVisibility(View.VISIBLE);
            holder.pickingSku.setOnClickListener(new MyItemOnClickListener(dParam));
        } else {
            holder.pickingSku.setVisibility(View.GONE);
        }
    }
}
  • getItemCount就直接返回數據List的size()就可以啦。

RecyclerView添加分割:分為設置垂直方向距離和設置分隔條兩種方式:

垂直方向距離:

public class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration {

    private final int verticalSpaceHeight;

    public VerticalSpaceItemDecoration(int verticalSpaceHeight) {
        this.verticalSpaceHeight = verticalSpaceHeight;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
            RecyclerView.State state) {
        outRect.bottom = verticalSpaceHeight;
    }
}

分隔條:

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};

    private Drawable divider;

    /**
     * Default divider will be used
     */
    public DividerItemDecoration(Context context) {
        final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS);
        divider = styledAttributes.getDrawable(0);
        styledAttributes.recycle();
    }

    /**
     * Custom divider will be used
     */
    public DividerItemDecoration(Context context, int resId) {
        divider = ContextCompat.getDrawable(context, resId);
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();

        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);

            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();

            int top = child.getBottom() + params.bottomMargin;
            int bottom = top + divider.getIntrinsicHeight();

            divider.setBounds(left, top, right, bottom);
            divider.draw(c);
        }
    }
}

以上關于分隔條內容可參考Stack Overflow:How to add dividers and spaces between items in RecyclerView?

四、One more thing

最后,復習下Fragment和Activity的數據傳遞:

  • 從Activity到Fragment:Bundle,通過Fragment的newInstance方法:
public static PickingTaskGoodsFragment newInstance(List<PickingTaskDParam> pickingTaskDParams) {
    PickingTaskGoodsFragment fragment = new PickingTaskGoodsFragment();
    Bundle args = new Bundle();
    args.putParcelable(PICKING_TASK_D_PARAM, Parcels.wrap(pickingTaskDParams));
    fragment.setArguments(args);
    return fragment;
}
  • 從Fragment到Activity:接口回調,在Fragment中定義接口,讓包含該Fragment的Activity必須實現接口:
@Override
public void onAttach(Context context) {
    super.onAttach(context);
    if (context instanceof OnListFragmentInteractionListener) {
        mListener = (OnListFragmentInteractionListener) context;
    } else {
        throw new RuntimeException(context.toString()
                + " must implement OnListFragmentInteractionListener");
    }
}
@Override
public void onDetach() {
    super.onDetach();
    mListener = null;
}
public interface OnListFragmentInteractionListener {

    void onListFragmentInteraction(PickingTaskDParam item);
}

以上。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容