1. 程式人生 > >Android 使GridView橫向水平滾動的實現方式

Android 使GridView橫向水平滾動的實現方式

Android為我們提供了豎直方向的滾動控制元件GridView,但如果我們想讓它水平滾動起來,就需要自己實現了。

以下使用的測試資料datas集合都為List<ResolveInfo>型別,用來儲存手機中的所有App

    public static List<ResolveInfo> getAppData(Context context) {
        PackageManager packageManager = context.getPackageManager();
        Intent mainIntent = new Intent(Intent.ACTION_MAIN, null
); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); return packageManager.queryIntentActivities(mainIntent, 0); }

一、單行橫向顯示

這裡寫圖片描述

實現思路

  • 在程式碼中動態設定GridView的NumColumns,使其等於GridView要顯示的資料集合大小。
  • 動態設定item項寬度,結合資料集合大小來設定GridView的總寬度。
  • 使用HorizontalScrollView包裹GridView

具體實現

關鍵程式碼

    /**
     * 將GridView改成單行橫向佈局
     */
private void changeGridView() { // item寬度 int itemWidth = DensityUtil.dip2px(this, 100); // item之間的間隔 int itemPaddingH = DensityUtil.dip2px(this, 1); int size = datas.size(); // 計算GridView寬度 int gridviewWidth = size * (itemWidth + itemPaddingH); LinearLayout.LayoutParams params = new
LinearLayout.LayoutParams( gridviewWidth, LinearLayout.LayoutParams.MATCH_PARENT); mContentGv.setLayoutParams(params); mContentGv.setColumnWidth(itemWidth); mContentGv.setHorizontalSpacing(itemPaddingH); mContentGv.setStretchMode(GridView.NO_STRETCH); mContentGv.setNumColumns(size); }

這裡用到的dip2px方法是根據手機的解析度從 dp 的單位 轉成為 px(畫素)

    /**
     * 根據手機的解析度從 dp 的單位 轉成為 px(畫素)
     * @param context   上下文
     * @param dpValue   dp值
     * @return  px值
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

在佈局檔案中,使用HorizontalScrollView包裹GridView

    <HorizontalScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="none">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <GridView
                android:id="@+id/gv_horizontal_gridview_line"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scrollbars="none"/>
        </LinearLayout>
    </HorizontalScrollView>

通過以上設定,再加上Adapter介面卡就能實現單行橫向滾動了,介面卡使用常規的實現方式就行,這裡就不貼了

二、多行橫向分頁顯示

這裡寫圖片描述

實現思路

  • 使用ViewPager實現左右翻頁效果。
  • 根據資料集合大小,計算出要顯示的頁數,並生成相應數量的GridView
  • GridView的Adapter介面卡中,動態分配GridView需要顯示的資料集合。
  • 使用List儲存多個GridView例項並傳入ViewPager介面卡中,一頁ViewPager對應一個GridView例項。

具體實現

資料量很多時,需要進行分頁,計算方式

需要頁數 = 總數量 ÷ 每頁顯示數量

有些整除不了的,就需要使用Math.ceil()函式,向上取整

關鍵程式碼

    /**
     * 獲取系統所有的應用程式,根據每頁需要顯示的item數量,生成相應數量的GridView頁面
     */
    private void initViews(List<ResolveInfo> datas) {
        int dataSize = datas.size();

        // (需要頁數 = 總數量 ÷ 每頁顯示數量)向上取整數
        int PageCount = (int) Math.ceil(dataSize / APP_SIZE);
        mGridViewList = new ArrayList<>();
        for (int i = 0; i <= PageCount; i++) {
            GridView appPage = new GridView(this);
            appPage.setAdapter(new HorizontalGvAdapter(this, datas, i));
            appPage.setNumColumns(4);
            appPage.setVerticalSpacing(1);
            appPage.setHorizontalSpacing(1);
            appPage.setHorizontalScrollBarEnabled(false);
            appPage.setVerticalScrollBarEnabled(false);
            mGridViewList.add(appPage);
        }

        if(dataSize % APP_SIZE == 0){
            mGridViewList.remove(mGridViewList.size()-1);
            PageCount--;
        }

        mGvPagerAdapter = new HorizontalGvPagerAdapter(mGridViewList);
        viewPager.setAdapter(mGvPagerAdapter);
        viewPager.addOnPageChangeListener(new MyPageChangeListener());

        addDot(PageCount);
    }

總數量 ÷ 每頁顯示數量剛好被整除時,會出現一頁空白頁的情況,這個時候需要去掉多出來的那一頁

        if(dataSize % APP_SIZE == 0){
            mGridViewList.remove(mGridViewList.size()-1);
            PageCount--;
        }

Adapter在建立初期就要對顯示的資料進行控制,因為這裡每個GridView都有一個單獨的Adapter,所以需要對其顯示的datas進行動態計算
通過傳入構造方法的資料進行動態計算,可以得出資料開始載入的位置、結束載入的位置
HorizontalGvAdapter的構造方法:

    /**
     * 所有應用資料
     */
    private List<ResolveInfo> mAppDatas = new ArrayList<ResolveInfo>();

    public HorizontalGvAdapter(Context context, List<ResolveInfo> list, int page) {
        this.mContext = context;

        // 開始載入的位置
        int pageStart = page * HorizontalGridViewAct.APP_SIZE;
        // 結束載入的位置
        int pageEnd = pageStart + HorizontalGridViewAct.APP_SIZE;

        while ((pageStart < list.size()) && (pageStart < pageEnd)) {
            mAppDatas.add(list.get(pageStart));
            pageStart++;
        }
    }

如果需要加小圓點的話,可以先在佈局中用一個空LinearLayout當小圓點的容器

<?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="match_parent"
              android:background="#ffffff"
              android:orientation="vertical">
    <android.support.v4.view.ViewPager
        android:id="@+id/vp_horizontal_gridview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_gravity="center"
        android:background="#c5c5c5"
        android:scaleType="fitXY"/>

    <!-- 底部小圓點 -->
    <LinearLayout
        android:id="@+id/ll_dot_container"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="#4b4b4b"
        android:layout_gravity="bottom"
        android:gravity="center"
        android:orientation="horizontal"/>
</LinearLayout>

然後在程式碼中用List<View>來儲存建立的小圓點

    // 放圓點的list
    private List<View> dotViewsList;

    /**
     * 建立指定數量的圓點
     * @param dotNumber viewPager的數量
     */
    private void addDot(int dotNumber) {
        if (null == dotViewsList) {
            dotViewsList = new ArrayList<View>();
        }
        LinearLayout dotLayout = (LinearLayout) findViewById(R.id.ll_dot_container);
        for (int i = 0; i <= dotNumber; i++) {
            ImageView dotView = new ImageView(this);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                    FrameLayout.LayoutParams.WRAP_CONTENT,
                    FrameLayout.LayoutParams.WRAP_CONTENT);

            // 圓點與圓點之間的距離
            params.leftMargin = 10;
            params.rightMargin = 10;

            // 圓點的大小
            params.height = 15;
            params.width = 15;

            dotLayout.addView(dotView, params);
            dotViewsList.add(dotView);
        }
        // 設定圓點預設選中第一個
        setDotShow(0);
    }

動態新增完小圓點後,就可以設定它們的選中狀態了,這裡只需要更改對應小圓點的圖片顯示就行

    /**
     * 顯示底部圓點導航
     * @param position 選中哪個圓點
     */
    private void setDotShow(int position){
        if (dotViewsList == null){
            return;
        }
        for (int i = 0; i < dotViewsList.size(); i++) {
            if (i == position) {
                dotViewsList.get(position).setBackgroundResource(R.drawable.ic_dot_on);
            } else {
                dotViewsList.get(i).setBackgroundResource(R.drawable.ic_dot_off);
            }
        }
    }

小圓點新增好後,需要根據ViewPager的頁面位置設定圓點的選中狀態,所以還需要新增一個監聽器,監聽ViewPager當前是第幾個頁面,然後根據這個頁面的值來改變小圓點的選中位置

    /**
     * ViewPager的監聽器 動態改變小圓點的選中位置
     */
    private class MyPageChangeListener implements ViewPager.OnPageChangeListener {

        @Override
        public void onPageScrollStateChanged(int arg0) {
        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
        }

        @Override
        public void onPageSelected(int pos) {
            setDotShow(pos);
        }

    }

三、總結

以上就是讓GridView橫向滾動的實現方式,其實我們完全可以不必這麼麻煩,Google在support-v7包中為我們提供了RecyclerView控制元件,切換橫向和豎直滾動只需要讓佈局管理器使用setOrientation方法設定一下就好,非常便捷,如果專案允許,建議使用RecyclerView來實現此類需求。