仿網易新聞的頁面(ViewPager作為RecyclerView的Header)

ShaunJohnso 10年前發布 | 36K 次閱讀 Android開發 移動開發

來自: http://blog.csdn.net//never_cxb/article/details/50520270


需求

>
想實現一個仿網易新聞的頁面,上面是輪播的圖片,下面是 RecyclerView 顯示新聞列表。

錯誤方法

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ...>
    <ViewPager ... />

<android.support.v7.widget.RecyclerView .../>

</LinearLayout></pre>

這樣布局 ViewPager 在 RecyclerView 的上面,如果不做特殊處理,當下滑 RecyclerView 加載更多內容的時候,ViewPager會固定不動。

正確的效果是下滑加載更多的時候,ViewPager 會滑出頁面,釋放空間供其他內容展示。

解決思路

方法有兩種

  • ViewPager作為 RecyclerView 的第0項,也就是 Header(本文采用該方法)
  • 利用ScrollView,重寫一些方法解決滑動沖突

總xml布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="

<android.support.v7.widget.RecyclerView  android:id="@+id/rcv_article_latest" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" />

</LinearLayout></pre>

很簡單,一個RecyclerView就行了

頭部 ViewPager 的viewholder_article_header.xml布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="

<!--ViewPager 熱門文章圖片展示-->
<FrameLayout  android:layout_width="match_parent" android:layout_height="200dp" android:background="@color/gray_light">

    <android.support.v4.view.ViewPager  android:id="@+id/vp_hottest" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorPrimary" />

    <LinearLayout  android:id="@+id/ll_hottest_indicator" android:layout_width="wrap_content" android:layout_height="20dp" android:layout_gravity="bottom|right" android:layout_marginBottom="5dp" android:layout_marginRight="10dp" android:layout_marginTop="5dp" android:gravity="center" android:orientation="horizontal" />
</FrameLayout>

</LinearLayout></pre>

FrameLayout里面的ViewPager和LinearLayout是覆蓋顯示的,實現在圖片的下方有個小圓點標記滑動到了第一張圖片。

新聞項 viewholder_article_item.xml 布局

<android.support.v7.widget.CardView  xmlns:android="

<LinearLayout  android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal">

    <com.非死book.drawee.view.SimpleDraweeView  android:id="@+id/rcv_article_photo" android:layout_width="100dp" android:layout_height="100dp" fresco:actualImageScaleType="centerInside" fresco:roundAsCircle="true" fresco:roundingBorderColor="@color/lightslategray" fresco:roundingBorderWidth="1dp" />


    <LinearLayout  android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="vertical">

        <TextView  android:id="@+id/rcv_article_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginTop="2dp" android:gravity="center" android:text="關于舉辦《經典音樂作品欣賞與人文審美》講座的通知" android:textColor="@color/primary_text" />
        <!-- 新聞 發布時間 來源 閱讀次數-->
        <LinearLayout  android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:gravity="center" android:orientation="horizontal">

            <TextView  android:id="@+id/rcv_article_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginRight="2dp" android:text="2015-01-09" />

            <TextView  android:id="@+id/rcv_article_source" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="2dp" android:layout_marginRight="2dp" android:text="科學研究院" />

            <TextView  android:id="@+id/rcv_article_readtimes" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="2dp" android:layout_marginRight="2dp" android:text="1129次" />

        </LinearLayout>


        <TextView  android:id="@+id/rcv_article_preview" android:layout_width="wrap_content" android:layout_height="0dp" android:layout_weight="1" android:layout_marginLeft="10dp" android:layout_marginTop="5dp" android:ellipsize="end" android:maxLines="2" android:text="講座主要內容:以中、西方音樂歷史中經典音樂作品為基礎,通過作曲家及作品創作背景、相關音樂文化史知識及音樂欣賞常識..." />

    </LinearLayout>
</LinearLayout>

</android.support.v7.widget.CardView></pre>

這篇文章 Android Material Design學習之RecyclerView代替 ListView http://blog.csdn.net/never_cxb/article/details/50495505實現了不加 ViewPager,利用 RecyclerView 展示新聞列表的功能。

RecyclerView 的適配器

/*  新聞列表的適配器  01-14 頭部是 ViewPager,下面是列表新聞  Created by tomchen on 1/11/16. */
public class ArticleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;

//頭部固定為 張圖片
private static final int NUM_IMAGE = 4;

//Handler 用到的參數值
private static final int UPTATE_VIEWPAGER = 0;

//新聞列表
private List<ItemArticle> articleList;

//設置當前 第幾個圖片 被選中
private int currentIndex = 0;

//context
private Context context;

private LayoutInflater mLayoutInflater;

private ImageView[] mCircleImages;//底部只是當前頁面的小圓點


public ArticleAdapter(Context context, List<ItemArticle> articleList) {
    this.context = context;

    //頭部viewpager圖片固定是7張,剩下的是列表的數據
    this.articleList = articleList;
    mLayoutInflater = LayoutInflater.from(context);
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    //理論上應該把最可能返回的 TYPE 放在前面
    View view = null;

    if (viewType == TYPE_ITEM) {
        view = mLayoutInflater.inflate(
                R.layout.viewholder_article_item, parent, false);
        return new ItemArticleViewHolder(view);
    }
    //頭部返回 ViewPager 實現的輪播圖片
    if (viewType == TYPE_HEADER) {
        view = mLayoutInflater.inflate(
                R.layout.viewholder_article_header, parent, false);
        return new HeaderArticleViewHolder(view);
    }

    return null;

// //可以拋出異常,沒有對應的View類型 // throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");

}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (holder instanceof ItemArticleViewHolder) {
        //轉型
        ItemArticleViewHolder newHolder = (ItemArticleViewHolder) holder;
        //注意RecyclerView第0項是 ViewPager 占據了0 1 2 3圖片
        //那么下面的列表展示是 RecyclerView 的第1項,從第4項開始
        ItemArticle article = articleList.get(position + NUM_IMAGE - 1);
        newHolder.rcvArticlePhoto.setImageURI(Uri.parse(article.getImageUrl()));
        newHolder.rcvArticleTitle.setText(article.getTitle());
        newHolder.rcvArticleDate.setText(article.getPublishDate());
        newHolder.rcvArticleSource.setText(article.getSource());
        //注意這個閱讀次數是 int 類型,需要轉化為 String 類型
        newHolder.rcvArticleReadtimes.setText(article.getReadTimes() + "次");
        newHolder.rcvArticlePreview.setText(article.getPreview());
    } else if (holder instanceof HeaderArticleViewHolder) {
        HeaderArticleViewHolder newHolder = (HeaderArticleViewHolder) holder;

        List<ItemArticle> headers = articleList.subList(0, NUM_IMAGE );
        HeaderImageAdapter imageAdapter = new HeaderImageAdapter(context, headers);

        setUpViewPager(newHolder.vpHottest, newHolder.llHottestIndicator, headers);

    }
}


private void setUpViewPager(final ViewPager vp, LinearLayout llBottom, final List<ItemArticle> headerArticles) {
    HeaderImageAdapter imageAdapter = new HeaderImageAdapter(context, headerArticles);
    //??這兒有些疑惑,Adapter 里面嵌套設置 Adapter 是否優雅?
    vp.setAdapter(imageAdapter);

    final Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPTATE_VIEWPAGER:
                    if (msg.arg1 != 0) {
                        vp.setCurrentItem(msg.arg1);
                    } else {
                        //false 當從末頁調到首頁是,不顯示翻頁動畫效果,
                        vp.setCurrentItem(msg.arg1, false);
                    }
                    break;
            }
        }
    };

    //下面是設置動畫切換的樣式
    vp.setPageTransformer(true, new RotateUpTransformer());

    //創建底部指示位置的導航欄
    final ImageView[] mCircleImages = new ImageView[headerArticles.size()];

    for (int i = 0; i < mCircleImages.length; i++) {
        ImageView imageView = new ImageView(context);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(10, 10);
        params.setMargins(5, 0, 5, 0);
        imageView.setLayoutParams(params);
        if (i == 0) {
            imageView.setBackgroundResource(R.drawable.indicator_select);
        } else {
            imageView.setBackgroundResource(R.drawable.indicator_not_select);
        }

        mCircleImages[i] = imageView;
        //把指示作用的原點圖片加入底部的視圖中
        llBottom.addView(mCircleImages[i]);

    }

    vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        //圖片左右滑動時候,將當前頁的圓點圖片設為選中狀態
        @Override
        public void onPageSelected(int position) {
            // 一定幾個圖片,幾個圓點,但注意是從0開始的
            int total = mCircleImages.length;
            for (int j = 0; j < total; j++) {
                if (j == position) {
                    mCircleImages[j].setBackgroundResource(R.drawable.indicator_select);
                } else {
                    mCircleImages[j].setBackgroundResource(R.drawable.indicator_not_select);
                }
            }

            //設置全局變量,currentIndex為選中圖標的 index
            currentIndex = position;
        }

        @Override
        public void onPageScrolled(int i, float v, int i1) {

        }

        @Override
        public void onPageScrollStateChanged(int state) {

            //實現切換到末尾后返回到第一張
            switch (state) {
                // 手勢滑動
                case ViewPager.SCROLL_STATE_DRAGGING:
                    break;

                // 界面切換中
                case ViewPager.SCROLL_STATE_SETTLING:
                    break;

                case ViewPager.SCROLL_STATE_IDLE:// 滑動結束,即切換完畢或者加載完畢
                    // 當前為最后一張,此時從右向左滑,則切換到第一張
                    if (vp.getCurrentItem() == vp.getAdapter()
                            .getCount() - 1) {
                        vp.setCurrentItem(0, false);
                    }
                    // 當前為第一張,此時從左向右滑,則切換到最后一張
                    else if (vp.getCurrentItem() == 0) {
                        vp.setCurrentItem(vp.getAdapter()
                                .getCount() - 1, false);
                    }
                    break;

                default:
                    break;
            }
        }
    });


    //設置自動輪播圖片,5s后執行,周期是5s

    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            Message message = new Message();
            message.what = UPTATE_VIEWPAGER;
            if (currentIndex == headerArticles.size() - 1) {
                currentIndex = -1;
            }
            message.arg1 = currentIndex + 1;
            mHandler.sendMessage(message);
        }
    }, 6000, 6000);
}

@Override
public int getItemCount() {
    //因為多了一個頭部,所以是+1,但是頭部 ViewPager 占了7個
    //所以實際是少了6個
    return articleList.size() + 1 - NUM_IMAGE;
}

@Override
public int getItemViewType(int position) {
    if (position == 0)
        return TYPE_HEADER;
    else
        return TYPE_ITEM;
}


class HeaderArticleViewHolder extends RecyclerView.ViewHolder {

    //輪播的最熱新聞圖片
    @InjectView(R.id.vp_hottest)
    ViewPager vpHottest;
    //輪播圖片下面的小圓點
    @InjectView(R.id.ll_hottest_indicator)
    LinearLayout llHottestIndicator;

    //學院廣播信息
    @InjectView(R.id.tv_college_broadcast)
    TextView tvCollegeBroadcast;

    public HeaderArticleViewHolder(View itemView) {
        super(itemView);
        ButterKnife.inject(this, itemView);
    }
}

class ItemArticleViewHolder extends RecyclerView.ViewHolder {

    @InjectView(R.id.rcv_article_photo)
    SimpleDraweeView rcvArticlePhoto;
    @InjectView(R.id.rcv_article_title)
    TextView rcvArticleTitle;
    @InjectView(R.id.rcv_article_date)
    TextView rcvArticleDate;
    @InjectView(R.id.rcv_article_source)
    TextView rcvArticleSource;
    @InjectView(R.id.rcv_article_readtimes)
    TextView rcvArticleReadtimes;
    @InjectView(R.id.rcv_article_preview)
    TextView rcvArticlePreview;

    public ItemArticleViewHolder(View itemView) {
        super(itemView);
        ButterKnife.inject(this, itemView);
    }
}


}</pre>

ItemArticleViewHolder是列表展示的新聞項的 ViewHolder,對應了上面的 viewholder_article_item.xml。

HeaderArticleViewHolder 是頭部 ViewPager 的 ViewHolder, 對應viewholder_article_header.xml

Note

  • 本文上面的 ViewPager 輪播4幅圖片,所以getItemCount()需要復寫
  • List headers = articleList.subList(0, NUM_IMAGE );得到頭部圖片的數據
  • ItemArticle article = articleList.get(position + NUM_IMAGE - 1);得到下面新聞項的數據
  • getItemViewType(int position)根據position判斷是不是頭部ViewPager
  • onCreateViewHolder(ViewGroup parent, int viewType)根據viewType生成頭部圖片或者下面新聞項的ViewHolder

疑惑及后續計劃

除了將 ViewPager 作為 RecyclerView 第一項,還有一張方法就是利用ScrollView,要復寫一些方法,可以參考這篇文章
Doing it right: vertical ScrollView with ViewPager and ListViewhttps://pristalovpavel.wordpress.com/2014/12/26/doing-it-right-vertical-scrollview-with-viewpager-and-listview/

參考文章

Placing ViewPager as a row in ListView http://stackoverflow.com/questions/14920459/placing-viewpager-as-a-row-in-listview
ListView inside ViewPager inside ScrollViewhttp://stackoverflow.com/questions/23429151/listview-inside-viewpager-inside-scrollview
RecyclerView:實現帶header的grid
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0722/3214.html

</div>

 本文由用戶 ShaunJohnso 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!