仿掘金框架之listview全解(一)

仿掘金框架之listview全解(一).jpg

文章首發:Android程序員日記javascript

做者:賢榆的魚java

測試閱讀時間:8minandroid

前言

有有很長一段時間沒有更新了,此次給你們帶來了一個框架Demo——仿掘金App,但願能夠經過這個Demo和你們分享一些控件的實際使用!此次呢先給你們帶來了自動隱藏佈局且帶下拉加載更多的listView!你們能夠先看看掘金app的效果圖,後面咱們把本身的放上來比較一下。
今天先放靜態的,明天放一張動的!git

掘金app

正文

經過仿掘金框架的這個listView你們能夠練習到一下幾個方面:程序員

  • [1] listView的基本用法github

  • [2] listView的viewHolder的複用優化及多條目微信

  • [3] listview添加headerView實現一些佈局和功能app

  • [4] listview經過footerview和滾動監聽實現上拉加載更多框架

  • [5] listview經過觸摸監聽事件實現上下bar的佈局隱藏功能異步

[ 1 ] listView的基本用法

首先在xml文件中配置listview

<ListView
        android:scrollbars="none"
         android:id="@+id/listView"
        android:fadingEdge="vertical"
        android:overScrollMode="never"
        android:dividerHeight="0.5dp"
        android:divider="#05999999"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true" />

上面用到的屬性:

  • android:overScrollMode="never" ——滾動越界的模式:ifcontentscroll和always纔會有越界效果!

  • android:dividerHeight="0.5dp" —— listview item之間間隔的高度

  • android:divider="#05999999" —— listview item之間間隔的背景或者顏色

  • android:fadingEdge="vertical" —— 上邊和下邊有黑色的陰影 值爲none的話就沒有陰影

  • android:scrollbars="none" —— 值爲horizontal|vertical的時候,會自動隱藏和顯示相應的滾動條,值爲none時隱藏滾動條。

部分經常使用屬性補充:

  • android:cacheColorHint="#00000000" —— 設置拖動背景色爲透明

  • android:fastScrollEnabled="true" —— 快速滾動效果屬性配置,在快速滾動的時候旁邊會出現一個小方塊的快速滾動效果,自動隱藏和顯示,

  • android:scrollbarTrackVertical="@color/colorAccent" —— 設置滾動條的背景色,只能設置引用不能直接填寫「#ffffff」

  • android:scrollbarThumbVertical="@color/colorPrimaryDark" —— 設置滾動條遊標的背景色,這裏只能引用不能直接填寫「#ffffff」

  • android:scrollbarStyle="outsideInset" —— 四個值的含義以下

    1. outsideInset : 該ScrollBar顯示在視圖(view)的邊緣,增長了view的padding. 若是可能的話,該ScrollBar僅僅覆蓋這個view的背景.

    • outsideOverlay : 該ScrollBar顯示在視圖(view)的邊緣,不增長view的padding,該ScrollBar將被半透明覆蓋

    • insideInset :該ScrollBar顯示在padding區域裏面,增長了控件的padding區域,該ScrollBar不會和視圖的內容重疊.

    • insideOverlay : 該ScrollBar顯示在內容區域裏面,不會增長了控件的padding區域,該ScrollBar以半透明的樣式覆蓋在視圖(view)的內容上.

  • android:footerDividersEnabled —— 當設爲false時,ListView將不會在各個footer之間繪製divider.默認爲true。

  • android:headerDividersEnabled —— 當設爲false時,ListView將不會在各個header之間繪製divider.默認爲true。

[ 2 ] ListView的複用優化及多條目

mutil_item.png

在activity中初始化listview並給他設置adapter

mlistView = (ListView) findViewById(R.id.listView);
mAdapter = new ViewHolderAdapter(this, mDates);
mlistView.setAdapter(mAdapter);

在adapter當中利用viewholder模式對listView的複用機制進行優化,在滑動的時候會更加的順暢。以及listview多條目的使用它。經過viewholder內部類以item佈局中的控件最爲變量,能夠有效的避免每次都經過findViewById()來實例化控件,從而達到複用控件實例,優化listview的目的!

而這裏用到的多條目通常狀況下是爲增長listview條目的多樣樣行,讓listview表現的更加豐富!固然還有一個狀況就是爲了突出數據的結構分層!咱們這裏就是爲了凸顯結構,加了一個文章分類title!

public class ViewHolderAdapter extends BaseAdapter {
    private final Context mContext;
    private List<NewsItem> mDates=new ArrayList<>();
    private static final int TITLE=0;
    private static final int CONTENT=1;

    public ViewHolderAdapter(Context mcontext, List<NewsItem> newsItems) {
        this.mContext=mcontext;
        //添加分類的標題
        mDates.add(new NewsItem("熱門文章"));
        mDates.addAll(1,newsItems);
    }
    @Override
    public int getItemViewType(int position) {
        if(position==0){//position爲0是itemType爲分類Title
            return TITLE;
        }else{
            return CONTENT;
        }
    }
    //返回listview條目類型的總數,咱們這隻有文章和分類title兩種!
    @Override
    public int getViewTypeCount() {
        return 2;
    }
    //返回listview中的條目數據總數
    @Override
    public int getCount() {
            return mDates==null?0:mDates.size();
    }
    //返回條目位置對應的內容
    @Override
    public NewsItem getItem(int position) {
        return mDates.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }
    //在getView當中對多條目的條目類型進行區分並分別作處理!
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        NewsItem item = getItem(position);
        ContentViewHolder contentViewHolder;
        TieleViewHolder tieleViewHolder;
        int type = getItemViewType(position);
        if(convertView==null){
            switch (type){
                case TITLE:
                    tieleViewHolder=new TieleViewHolder();
                    convertView=View.inflate(mContext,R.layout.lv_item_title,null);
                    tieleViewHolder.tv_title= (TextView) convertView.findViewById(R.id.tv_top_title);
                    tieleViewHolder.tv_title.setText(item.getTitle());
                    //綁定一個viewHolder方便在對convertView複用時也對viewHolder進行復用
                    convertView.setTag(tieleViewHolder);
                    break;
                case CONTENT:
                    contentViewHolder=new ContentViewHolder();
                    convertView=View.inflate(mContext,R.layout.lv_item,null);
                    contentViewHolder.iamge= (ImageView) convertView.findViewById(R.id.iv_image);
                    contentViewHolder.tv_articleInfo= (TextView) convertView.findViewById(R.id.tv_info);
                    contentViewHolder.tv_title= (TextView) convertView.findViewById(R.id.tv_title);
                    contentViewHolder.iamge.setImageResource(item.getImage());
                    contentViewHolder.tv_title.setText(item.getTitle());
                    contentViewHolder.tv_articleInfo.setText(item.getArtcileInfo());
                    convertView.setTag(contentViewHolder);
                    break;
            }
        }else{
            switch (type){
                case TITLE:
                    tieleViewHolder= (TieleViewHolder) convertView.getTag();
                    tieleViewHolder.tv_title.setText(item.getTitle());
                    break;
                case CONTENT:
                    //複用綁定的ViewHolder
                    contentViewHolder= (ContentViewHolder) convertView.getTag();
                    contentViewHolder.iamge.setImageResource(item.getImage());
                    contentViewHolder.tv_title.setText(item.getTitle());
                    contentViewHolder.tv_articleInfo.setText(item.getArtcileInfo());
            }
        }
        return convertView;
    }
    public void addDate(List<NewsItem> newsItems, boolean isHead) {
        if(mDates==null){
            this.mDates =newsItems;
        }else{
            if (isHead){
                mDates.addAll(1,newsItems);
            }else{
                mDates.addAll(getCount(),newsItems);
            }
        }
    }
    //建立兩個不一樣itemType對應的ViewHolder內部類
    public class ContentViewHolder {
        ImageView iamge;
        TextView tv_title;
        TextView tv_articleInfo;
    }
    public class TieleViewHolder {
        TextView tv_title;
    }
}

[ 3 ] ListView添加HeaderView

ListView經過添加HeaderView其實能夠實現不少的效果,好比下拉刷新的動畫就能夠放在HeadView當中。固然此次仿掘金的app用了SwipeRefreshLayout實現了下拉刷新,後面會針對這個寫一篇的你們也別急!而這個app中依然也用到了headerView。這裏是添加了一個頭佈局。以下圖:

headerview.png

咱們在[ 2 ] 的初始化listview代碼以後設置adapter以前加上添加headerView的代碼

header = LayoutInflater.from(this).inflate(R.layout.lv_headerview, mlistView, false);
//header = getLayoutInflater().inflate(R.layout.lv_headerview,null,true);
//header=View.inflate(this,R.layout.lv_headerview,null);
mlistView.addHeaderView(header);

注 :

關於lv_headerview.xml的代碼你們能夠在文末的github地址中獲取到,這裏就不貼出來了!不過上面代碼中也有要注意的地方,我寫了三種方式實例化headerView!第一種和第二種是同樣!而第三種方法省去了第三個參數!
你們在實例化的時候須要注意的是,若是你的headerview有預留的一段空隙,像個人不居中的同樣,那麼你的第二個參數須要填寫listview的實例對象,而第三個參數須要填寫false!不然heaaderview下面的空白就會消失(上面的第2、三個方法就會有該現象)!還有一個就第二個參數填寫了listview實例,而第三個對象填寫了true!這個時候,就會報錯崩潰報以下錯誤:

Caused by: java.lang.UnsupportedOperationException: addView(View, LayoutParams) is not supported in AdapterView

[ 4 ] 經過footerview和滾動監聽實現上拉加載更多

footerview.png

思路:

Step1:先加載一個帶加載標示的footerview

Step2:實現listview的滾動監聽事件

Step3:判斷是否爲最後一個條目?是,就顯示footerview而且異步加載數據!加載完畢隱藏footerveiw。

代碼實現:

在添加headerview後面添加footerview並實例化footerview裏面loading標示

footview = LayoutInflater.from(this).inflate(R.layout.lv_footview, mlistView, false);
mLoadMore = (TextView) footview.findViewById(R.id.tv_loadmore);
mlistView.addFooterView(footview);

實現listview的滾動監聽事件,並在onscroll()方法中完成異步加載數據的操做

mlistView.setOnScrollListener(new AbsListView.OnScrollListener(){
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
        }
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            //使用isLoading布爾值做爲標識符避免每次觸底時進行屢次加載
            if(view.getLastVisiblePosition()==view.getCount()-1&&!isLoading){
                mLoadMore.setVisibility(View.VISIBLE);
                isLoading=true;
                //new 一個一部任務執行異步任務加載操做!
                new AsyncTask<Void, Void, List<NewsItem>>() {
                    @Override
                    protected List<NewsItem> doInBackground(Void... params) {
                        SystemClock.sleep(1500);
                        List<NewsItem> loadItems=refreshDate("Old");
                        return loadItems;
                    }
                    @Override
                    protected void onPostExecute(List<NewsItem> loadItems) {
                        isLoading=false;
                        mLoadMore.setVisibility(View.GONE);
                        mAdapter.addDate(loadItems,false);
                        mAdapter.notifyDataSetChanged();
                    }
                }.execute();
            }
        }
    });

最後咱們來看一下咱們的效果圖:
仿掘金app.png

該項目的github地址:https://github.com/luorenyu/JuejinMoudel.git

後記

因爲字數的限制,因此整篇不能放在一篇裏面!那就先分享前面四個點!第五點就明天繼續分享!若是着急的也可先直接去上面的git地址下載源碼本身!若是有任何問題,歡迎你們在個人微信公衆號【Android程序員日記】裏問我!
本篇未完待續,明天繼續分享!


喜歡的能夠關注個人微信公衆號!
Android程序員日記

相關文章
相關標籤/搜索