]Android 仿網易新聞 ViewPager 實現圖片自動輪播

hcql1463 8年前發布 | 409K 次閱讀 Android開發 移動開發

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


前言

新聞 App 首頁最上方一般會循環播放熱點圖片,如下圖所示。

http://blog.csdn.net/never_cxb

本文主要介紹了利用 ViewPager 實現輪播圖片,圖片下方加上小圓點指示器標記當前位置,并利用 Timer+Handler 實現了自動輪播播放。

本文鏈接 http://blog.csdn.net/never_cxb/article/details/50515216 轉載請注明出處

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,ViewPager 顯示圖片,LinearLayout是小圓點指示器區域,標記現在滑到哪張圖片。

查看 xml 預覽圖,由于沒有圖片內容,當前只顯示出紅色矩形區域。

http://blog.csdn.net/never_cxb

新建javabean

首頁的圖片地址是新聞的一個屬性,我們新建一個ItemArticle類。

public class ItemArticle {
    // 新聞的 id
    private int index;
    // 新聞里的圖片 url
    private String imageUrl;

public ItemArticle(int index, String imageUrl) {
    this.index = index;
    this.imageUrl = imageUrl;
}

public int getIndex() {
    return index;
}

public void setIndex(int index) {
    this.index = index;
}

public String getImageUrl() {
    return imageUrl;
}

public void setImageUrl(String imageUrl) {
    this.imageUrl = imageUrl;
}

}</pre>

適配器 PagerAdapter

繼承自 android.support.v4.view.PagerAdapter,復寫4個方法

  • instantiateItem(ViewGroup, int)
  • destroyItem(ViewGroup, int, Object)
  • getCount()
  • isViewFromObject(View, Object)

public class HeaderAdapter extends PagerAdapter {
    private static final String LOG = "NEWS_LOG";

private Activity context;
private List<ItemArticle> articles;
private List<SimpleDraweeView> images = new ArrayList<SimpleDraweeView>();


public HeaderAdapter(Activity context, List<ItemArticle> articles) {
    this.context = context;
    if (articles == null || articles.size() == 0) {
        this.articles = new ArrayList<>();
    } else {
        this.articles = articles;
    }

    for (int i = 0; i < articles.size(); i++) {
        SimpleDraweeView image = new SimpleDraweeView(context);
        Uri uri = Uri.parse(articles.get(i).getImageUrl());
        image.setImageURI(uri);
        images.add(image);
    }
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    container.addView(images.get(position));
    return images.get(position);
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    container.removeView(images.get(position));
}

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

@Override
public boolean isViewFromObject(View view, Object object) {
    Log.i(LOG, "in isViewFromObject view: " + view + " object: "
            + object + " equal: " + (view == (View) object));
    return view == (View) object;
}

}</pre>

深入解析 isViewFromObject 方法

isViewFromObject(View view, Object object)的通用寫法是return view == (View) object;
其中(View) object可根據具體情形替換成LinearLayout等等。

查看 ViewPager 源代碼(戳這里

isViewFromObject是在infoForChild里被調用的,而且在該方法內會被調用mItems.size()次,mItems.size()是 ViewPager 里面圖片的個數。

static class ItemInfo {
    Object object;
    int position;
    boolean scrolling;
}

private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();

ItemInfo infoForChild(View child) { for (int i=0; i<mItems.size(); i++) { ItemInfo ii = mItems.get(i); if (mAdapter.isViewFromObject(child, ii.object)) { return ii; } } return null; }</pre>

ViewPager里面用了一個mItems 存儲每個page的信息(ItemInfo),當界面要展示或者發生變化時,需要依據page的當前信息來調整,但此時只能通過view來查找,遍歷mItems通過比較view和object來找到對應的ItemInfo。

Log.i(LOG, "in isViewFromObject view: " + view + " object: "

    + object + " equal: " + (view == (View) object));</pre> 

所以我們如果打印出 Log 的話,會看到isViewFromObject()被調用多次,只有1次返回 true (表示找到了對應的ItemInfo),其他返回 false。

01-17 10:15:21.207 I/NEWS_LOG﹕ in isViewFromObject 
view: SimpleDraweeView{holder=DraweeHolder{...} 
object: SimpleDraweeView{holder=DraweeHolder{...} 
equal: false

01-17 10:15:21.207 I/NEWS_LOG﹕ in isViewFromObject view: SimpleDraweeView{holder=DraweeHolder{...} object: SimpleDraweeView{holder=DraweeHolder{...} equal: false

01-17 10:15:21.207 I/NEWS_LOG﹕ in isViewFromObject view: SimpleDraweeView{holder=DraweeHolder{...} object: SimpleDraweeView{holder=DraweeHolder{...} equal: false

01-17 10:15:21.207 I/NEWS_LOG﹕ in isViewFromObject view: SimpleDraweeView{holder=DraweeHolder{...} object: SimpleDraweeView{....}} equal: true</pre>

增加底部小圓點指示器

輪播圖片的底部都會加上小圓點,指示當前訪問圖片的位置。

http://blog.csdn.net/never_cxb

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

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

for (int i = 0; i < mBottomImages.length; i++) { ImageView imageView = new ImageView(mAct); 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); }

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

}</pre>

上面這段代碼是小圓點的初始步驟,最開始是第0張圖片被選中,所以是第0張小圓點是藍色,其他小圓點是灰色。

addOnPageChangeListener 使得小圓點動態變化

切換圖片的時候,小圓點也要隨著改變,這需要利用ViewPager.OnPageChangeListener,主要是下面這個方法:

public abstract void onPageSelected (int position)

This method will be invoked when a new page becomes selected. Animation is not necessarily complete.

Parameters position Position index of the new selected page.</pre>

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

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

@Override
public void onPageScrollStateChanged(int state) {
}

});</pre>

onPageSelected()中,利用 for 循環,將當前選中位置對應的小圓點置為藍色,其他小圓點置為灰色。

自動播放

先定義一個 Handler,在主線程里面更新 UI

//定時輪播圖片,需要在主線程里面修改 UI
private Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case UPTATE_VIEWPAGER:
                if (msg.arg1 != 0) {
                    vpHottest.setCurrentItem(msg.arg1);
                } else {
                    //false 當從末頁調到首頁是,不顯示翻頁動畫效果,
                    vpHottest.setCurrentItem(msg.arg1, false);
                }
                break;
        }
    }
};

利用 Timer 實現每隔 5s 向 Handler 發送message來更新圖片

// 設置自動輪播圖片,5s后執行,周期是5s
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        Message message = new Message();
        message.what = UPTATE_VIEWPAGER;
        if (autoCurrIndex == headerArticles.size() - 1) {
            autoCurrIndex = -1;
        }
        message.arg1 = autoCurrIndex + 1;
        mHandler.sendMessage(message);
    }
}, 5000, 5000);

為了使得滑到最后一頁后能滑到首頁,我們對于autoCurrIndex == headerArticles.size() - 1進行了處理。

完整代碼

基于上面的分析,我們實現了自動輪播圖片

http://blog.csdn.net/never_cxb

public class MasterArticleFragment extends Fragment {

private static final String ARTICLE_LATEST_PARAM = "param";

private static final int UPTATE_VIEWPAGER = 0;

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

//存儲的參數
private String mParam;

//獲取 fragment 依賴的 Activity,方便使用 Context
private Activity mAct;

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

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

private Timer timer = new Timer(); //為了方便取消定時輪播,將 Timer 設為全局

//定時輪播圖片,需要在主線程里面修改 UI
private Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case UPTATE_VIEWPAGER:
                if (msg.arg1 != 0) {
                    vpHottest.setCurrentItem(msg.arg1);
                } else {
                    //false 當從末頁調到首頁是,不顯示翻頁動畫效果,
                    vpHottest.setCurrentItem(msg.arg1, false);
                }
                break;
        }
    }
};

public static MasterArticleFragment newInstance(String param) {
    MasterArticleFragment fragment = new MasterArticleFragment();
    Bundle args = new Bundle();
    args.putString(ARTICLE_LATEST_PARAM, param);
    fragment.setArguments(args);
    return fragment;
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {
        mParam = savedInstanceState.getString(ARTICLE_LATEST_PARAM);
    }
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.fragment_one_master, container, false);
    mAct = getActivity();
    ButterKnife.inject(this, view);
    return view;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    new ImageTask().execute();
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    ButterKnife.reset(this);
}


private void setUpViewPager(final List<ItemArticle> headerArticles) {
    HeaderAdapter imageAdapter = new HeaderAdapter(mAct, headerArticles);
    vpHottest.setAdapter(imageAdapter);

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

    for (int i = 0; i < mBottomImages.length; i++) {
        ImageView imageView = new ImageView(mAct);
        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);
        }

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

    }

    vpHottest.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

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

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

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

          @Override
          public void onPageScrollStateChanged(int state) {
          }
      }
    );

    // 設置自動輪播圖片,5s后執行,周期是5s
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            Message message = new Message();
            message.what = UPTATE_VIEWPAGER;
            if (autoCurrIndex == headerArticles.size() - 1) {
                autoCurrIndex = -1;
            }
            message.arg1 = autoCurrIndex + 1;
            mHandler.sendMessage(message);
        }
    }, 5000, 5000);
}


class ImageTask extends AsyncTask<String, Void, List<ItemArticle>> {
    @Override
    protected List<ItemArticle> doInBackground(String... params) {
        List<ItemArticle> articles = new ArrayList<ItemArticle>();
        articles.add(
                new ItemArticle(1123, "http://***20151231105648_11790.jpg"));
        articles.add(
                new ItemArticle(1123, "http://***20151230152544_36663.jpg"));
        articles.add(
                new ItemArticle(1123, "http://***20151229204329_75030.jpg"));
        articles.add(
                new ItemArticle(1123, "http://***20151221151031_36136.jpg"));
        return articles;
    }

    @Override
    protected void onPostExecute(List<ItemArticle> articles) {
        //這兒的 是 url 的集合
        super.onPostExecute(articles);
        setUpViewPager(articles);

    }
}

}</pre>

一些知識點

schedule和scheduleAtFixedRate方法

(1)schedule方法:下一次執行時間相對于 上一次 實際執行完成的時間點 ,因此執行時間會不斷延后。保持間隔時間的穩定
(2)scheduleAtFixedRate方法:下一次執行時間相對于上一次開始的 時間點 ,因此執行時間不會延后,存在并發性 。保持執行頻率的穩定。

參考文章

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