Android基本控件之listView(三)<用ListView實現分頁加載>

咱們以前討論了ListView的基本使用方法ListView的優化android

  今天咱們再來討論一個關於ListView的一個新的東西~就是分頁加載。那麼什麼是分頁加載呢?簡單點說,就是「下拉刷新」數組

 

咱們來簡單的構思一下咱們都須要些什麼東西app

  首先,咱們須要主界面佈局,而且在佈局文件中寫一個ListViewide

  而後,咱們須要ListView中每一個item的佈局佈局

  而後,咱們還須要一個當頁面加載的時候出現的那個旋轉的小圓圈(ProgressBar)的佈局,咱們能夠將其稱之爲「底部佈局(本身起的名字)。優化

  而後,咱們須要一個Activity,在其中咱們須要設置數據源和一系列操做。ui

  而後,咱們須要一個類,該類表明每一個item,存儲的是每一個item中的內容this

 

接下來,咱們來按照咱們上面的簡單的構思,來作一個具體點的構思spa

  主界面佈局文件我就不用多說了,就是裏面添加一個ListView線程

  而後,咱們來考慮item中的佈局,咱們想顯示一個頭像,一個名字,一個內容。咱們就定義一個ImageView、一個TextView(管名字)、一個TextView(管內容)

  而後,咱們須要在底部佈局中,添加一個ProgressBar (就是旋轉小圓圈,又叫圓形進度條)和一個TextView(用來寫「正在加載~」字樣)。

  而後,咱們須要一個表明item中的內容,因爲咱們上面已經說了,咱們想顯示一個頭像,一個名字,一個內容。 那麼,咱們在該類中就須要維護一個int類型的icon(圖片是int類型的),一個String類型的title(名字/標題),一個String類型的content(內容)

  而後,咱們須要Acitivty,在Activity中,咱們須要定義數據源,和一個適配器,還有一個存儲對象的list集合。而且讓Activity實現OnScrollListener接口,並實現其中的兩個方法 1.onScroll方法 2.onScrollStateChanged方法

 

咱們根據上面的構思,來作具體的實現

  咱們先來看咱們的主佈局文件

<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="application.smile.listview.MainActivity">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>

  而後,咱們來看每一個item中的佈局

  

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp">
    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:src="@mipmap/ic_launcher"/>
    <TextView
        android:id="@+id/tv_title"
        android:layout_marginTop="10dp"
        android:layout_toRightOf="@+id/iv_icon"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="這是標題"
        android:textSize="20sp"/>
    <TextView
        android:id="@+id/tv_content"
        android:layout_toRightOf="@+id/iv_icon"
        android:layout_below="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:text="這是內容"/>
</RelativeLayout>

  就是這樣的效果:

    

  接下來,咱們來看咱們底部佈局

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

    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="拼命加載中"/>
</LinearLayout>

  看到的效果就是這樣的:

    

  這樣,咱們的佈局文件就到這了。

接下來,咱們先來看建立的item的類

public class ItemContent {
    int icon;
    String title ;
    String content;
}

而後,咱們再來看Activity中的實現

  這裏是最重要的了……由於全部的實現都在Activity中,咱們仍是有必要再細一些的講解

  首先,咱們須要實現接口,而後設置數據源(就是圖片和名字),建立一個list集合用來存放剛剛建立的類的對象(就是ItemContent),再維護一個適配器

  而後,咱們在onCreate(...)方法中去執行咱們的老套路找到控件、而後更新數據、而後設置適配器,而後設置點擊事件(這裏有兩個事件,一個是滾動事件,一個是點擊事件),這裏還要加一個,就是獲得咱們的底部佈局,並加入到ListView中

  而後,咱們在更新數據的方法中去設置每次加載多少條數據。就是經過一個循環來控制每次加載多少條數據,每次都建立一個ItemContent(建立的item類)的對象,併爲其賦值,最後添加到List集合中,這裏須要作一個操做,由於咱們是模擬,因此沒有不少的數據,那麼咱們就須要讓咱們現有的數據來循環使用,咱們須要一個控制變量(至關於一個指針),當這個控制變量的大小和數據數組的長度一致的時候,就將控制變量置零,讓咱們從數據數組的開始繼續讀取,從而實現數據的循環使用。

  而後咱們來看咱們重寫的兩個方法中的onScroll方法

    該方法用於監聽屏幕滾動

    該方法中有四個參數

      第一個參數AbsListView類型的view   該參數是正在滾動的視圖

      第二個參數int類型的firstVisibleItem 該參數是第一個可見的item的索引

      第三個參數int類型的visibleItemCount 該參數是可見的item的數量

      第四個參數int類型的totalItemCount 該參數是列表適配器中的項目數(咱們這裏用不到)

    而後,咱們來看這個方法中,咱們須要作些什麼

      咱們須要定義一個int類型的變量,用來標記咱們能看見的最後一個item的索引 ,而後咱們經過<「第一個可見的item的索引」 + 「可見的item的數量」 -1> 來得到咱們想要的最後一個item的索引。 

      這裏爲何要-1呢?咱們來仔細的考慮一下。所謂索引也就是下標,咱們知道下標是從0開始的,若是咱們不-1 ,咱們就會多獲得一條數據,每次就不是10個了,而是11個。因此,咱們須要-1 來控制,咱們每次都拿到10條數據

    這樣,咱們這個方法就完成了。

  而後,咱們來看重寫的兩個方法中的onScrollStateChanged方法

    該方法用於監聽屏幕滾動的狀態

    該方法中有兩個參數

      第一個參數:AbsListView類型的view  該參數是正在滾動的視圖

      第二個參數:int類型的scrollState   該參數是當前滾動的狀態

        該參數,能夠有三個值與之匹配

            1.SCROLL_STATE_FLING  表明:用戶已經使用觸摸來滾動,手指已經離開屏幕,可是視圖還在慣性的滾動

            2.SCROLL_STATE_IDLE  表明:視圖不滾動,而且手指鬆開

            3.SCROLL_STATE_TOUCH_SCROLL  表明:用戶正在使用觸摸來滾動,而且手指還在屏幕上

    而後,咱們來看這個方法中,咱們須要作些什麼

      咱們須要經過線程來模擬加載的時間,可是在此以前,咱們須要先判斷「滾動的狀態」 是否是處於「視圖再也不滾動而且手指鬆開的狀態」而且判斷能看到的最後一條item的索引是否是適配器的長度

      若是這兩個條件都知足,證實已經滾動到了最下面一條,須要更新數據

      因而咱們開啓一個線程而且睡上1秒,睡完要更新數據,而且顯示到ListView上,可是在安卓中想要更新UI就必須在UI線程中就行,不能夠在子線程中進行,那麼咱們想要個更新UI就用上了Handler 這個類

      咱們要在成員位置建立一個Handler對象,並重寫handleMessage方法,在方法中作更新UI的操做,那麼咱們如何讓子線程與HandleMessage方法連接上呢?咱們須要在子線程中經過Handler對象,去發送一個一個消息,並附上一個標誌

      而後,咱們在HandleMessage中去判斷這個標誌,若是成功就更新UI

  接下來,咱們就來看咱們自定義的適配器。仍是以前的套路,繼承BaseAdapter,並重寫方法

    再重寫方法以前,咱們須要維護一個佈局填充器,並在構造方法中給他賦值

    而後咱們來看重寫的方法

      第一個方法getCount方法   這個須要返回總共有多少個item,這裏就是list集合的size

      第二個方法:getItem方法  這個就是返回每一個item,這裏就是經過list集合的get方法得到item並返回

      第三個方法:getItemId方法 這個就是返回每一個item的id,這裏就是直接返回position

      第四個方法:getView方法 這個方法中就是具體的狀況了

        首先建立一個類取名爲ViewHolder並在其中聲明item中的三個控件

        而後在getView方法中維護一個ViewHolder

        而後判斷convetView是否爲空,若是爲空就經過佈局填充器得到item的佈局。

          並實例化ViewHolder,經過convertView找到控件並賦值給ViewHolder的相應的控件。

          而後設置一個標記,把ViewHolder放進去。

        若是不爲空,就經過標記取出ViewHolder,並給每一個空間設置數據源。

        最後返回convertView

而後,咱們來上代碼:

public class MainActivity extends AppCompatActivity implements AbsListView.OnScrollListener {
            //用來判斷點擊的是否是「正在加載」的item,true表明是,false表明不是
    private boolean flag = false;
            //用於Handler返回的標誌
    private static final int SEND_OK = 0X1;
    //存儲文字的數組
    private String[] names = {"郭嘉", "黃月英", "華佗", "劉備", "陸遜", "呂布", "呂蒙", "馬超", "司馬懿", "孫權",
            "孫尚香", "夏侯惇", "許褚", "楊修", "張飛", "趙雲", "甄姬", "周瑜", "諸葛亮"};
    private List<ItemContent> project = new ArrayList<>();
    //存儲圖片的數組
    private int[] images = {R.mipmap.guojia, R.mipmap.huangyueying, R.mipmap.huatuo, R.mipmap.liubei, R.mipmap.luxun, R.mipmap.lvbu, R.mipmap.lvmeng,
            R.mipmap.machao, R.mipmap.simayi, R.mipmap.sunquan, R.mipmap.sunshangxiang, R.mipmap.xiahoudun, R.mipmap.xuchu, R.mipmap.yangxiu,
            R.mipmap.zhangfei, R.mipmap.zhaoyun, R.mipmap.zhenji, R.mipmap.zhouyu, R.mipmap.zhugeliang};
    private MyAdapter myAdapter;
    //建立一個Handler對象
    private Handler handler = new Handler() {
            //當子線程調用sendMessage方法時會自動調用該方法
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
                //根據標誌作相應的操做
            switch (msg.what) {
                case SEND_OK:
                        //更新ui
                    myAdapter.notifyDataSetChanged();
                        //將「正在加載」的標誌,設置爲沒在加載
                    flag = false;
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //找到控件
        ListView listView = (ListView) findViewById(R.id.listView);
        //更新數據
        initData();
        //找到底部佈局
        View bottom_layout = getLayoutInflater().inflate(R.layout.bottom_layout, null);
        //設置到listView上
        listView.addFooterView(bottom_layout);

        //設置適配器
        myAdapter = new MyAdapter(this);
        listView.setAdapter(myAdapter);

        listView.setOnScrollListener(this);
        //設置點擊事件
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                //彈出吐司
                if (!flag) {
                        //若是點擊的不是「正在加載」就彈出這個吐司
                    Toast.makeText(MainActivity.this, "點我了?我是" + names[position % 19], Toast.LENGTH_SHORT).show();
                } else {
                        //若是點擊的是「正在加載」就彈出這個吐司
                    Toast.makeText(MainActivity.this, "加載中....", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
    //控制數據循環的控制變量
    int count = 0;

    public void initData() {
            //經過循環控制每次獲取10條數據
        for (int i = 0; i < 10; i++) {
                //實例化一個item的類的對象
            ItemContent item = new ItemContent();
                //判斷,若是控制變量的長度大於或者等於數據數組的長度,就置零
            if (count >= images.length) {
                count = 0;
            }
                //賦值
            item.icon = images[count];
            item.title = names[count];
            item.content = "我是" + names[count];
                //添加到list集合
            project.add(item);
                //控制變量自增
            count++;
        }
    }

    /**
     * 用於監聽ListView滾動狀態的變化
     *
     * @param view        正在報告滾動狀態的視圖
     * @param scrollState 當前的滾動狀態
     */
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (!flag) {
            /**
             *SCROLL_STATE_FLING:   用戶已經使用觸摸來滾動,手指已經離開屏幕,可是視圖還在慣性的滾動
             *SCROLL_STATE_IDLE:    視圖不滾動,而且手指鬆開
             * SCROLL_STATE_TOUCH_SCROLL:   用戶正在使用觸摸來滾動,而且他們的手指仍在屏幕上
             */
            if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE && visibleLastItem == myAdapter.getCount()) {

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        initData();
                        handler.sendEmptyMessage(SEND_OK);
                    }
                }).start();
                flag = true;
            }
        }
    }

    private int visibleLastItem = 0;  //能看到的最後一個單元格的索引

    /**
     * 用於監聽ListView屏幕滾動
     *
     * @param view             正在報告滾動狀態的視圖
     * @param firstVisibleItem 第一個可見單元格的索引
     * @param visibleItemCount 可見單元格的數量
     * @param totalItemCount   列表適配器中的項目數
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        //最後能看到的視圖索引 = 第一個能夠看見的單元格索引 +  可見單元格的數量 - 1;
        //由於是從0開始的,可是數量是從1開始的,因此 須要減一才能夠作索引
        visibleLastItem = firstVisibleItem + visibleItemCount -1;
    }

    /**
     * 自定義適配器,繼承BaseAdapter
     */
    class MyAdapter extends BaseAdapter {
        //維護一個佈局填充器,爲了獲得每一個item 的佈局
        private LayoutInflater layoutInflater;

        public MyAdapter(Context context) {
            //經過構造方法獲取佈局填充器對象
            layoutInflater = LayoutInflater.from(context);
        }

        //該方法是總共有多少個item
        @Override
        public int getCount() {
            return project.size();
        }

        //該方法是獲得每一個item的值
        @Override
        public Object getItem(int position) {
            return project.get(position);
        }

        //該方法是獲得每一個item的id
        @Override
        public long getItemId(int position) {
            return position;
        }

        //該方法是得到視圖,也是這些裏面最重要的方法
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            if (convertView == null) {
                //首先,咱們經過佈局填充器得到item的佈局
                convertView = layoutInflater.inflate(R.layout.item_layout, null);
                //實例化ViewHolder
                viewHolder = new ViewHolder();
                //根據item的佈局找到圖片
                viewHolder.iv_icon = (ImageView) convertView.findViewById(iv_icon);
                //根據item的佈局找到標題
                viewHolder.tv_title = (TextView) convertView.findViewById(tv_title);
                //根據item的佈局找到內容
                viewHolder.tv_content = (TextView) convertView.findViewById(tv_content);
                //設置tag標記
                convertView.setTag(viewHolder);
            }
            viewHolder = (ViewHolder) convertView.getTag();
            //設置圖片源
            viewHolder.iv_icon.setImageResource(project.get(position).icon);
            //設置標題文字
            viewHolder.tv_title.setText(project.get(position).title);
            //設置內容文字
            viewHolder.tv_content.setText(project.get(position).content);
            //返回item的佈局
            return convertView;
        }

        class ViewHolder {
            ImageView iv_icon;
            TextView tv_title;
            TextView tv_content;
        }
    }
}

 

接下來,咱們來看一下運行的結果吧~

  

這樣,咱們的ListView的分頁加載就完成了

 

讓程序寫入生命,將代碼融入靈魂

                    -------smile、zj

相關文章
相關標籤/搜索