現在網上關於ViewPager的文章已經琳琅滿目了,很多都是關於ViewPager和Fragment的,大概大家都覺得他們才是最佳拍檔。 但是本文是關於:ViewPager+View+TabLayout+SwipeRefreshLayout的最佳實踐,而且還加入了ViewPager切換的動畫效果。
大家如果想看專案實踐執行的視訊,可以切換到ViewPager最佳實踐。
下面我來帶大家實踐一下,首先看下需要的檔案。
上面就是我們需要的專案檔案。<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.lwj.viewpager.ViewPagerActivity">
<android.support.design.widget.TabLayout
android:id="@+id/toolbar_tab"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_gravity="bottom"
android:background="#ffffff"
android:fillViewport="true"
android:paddingLeft="5dp"
android:visibility="invisible"
app:tabIndicatorColor="#96a8d0"
app:tabIndicatorHeight="3.0dp"
app:tabPaddingEnd="0dp"
app:tabPaddingStart="0dp"
app:tabPaddingTop="0dp"
app:tabSelectedTextColor="#96a8d0"
app:tabTextColor="#666666">
</android.support.design.widget.TabLayout>
<android.support.v4.view.ViewPager
android:id="@+id/my_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
複製程式碼
主介面的佈局很簡單,一個TabLayout,一個ViewPager。你可以在這裡設定TabLayout的顯示樣式。 需要說明一點,TabLayout是在android.support.design包下的,所以專案中要新增這個包,在Dependencies中新增就可以了。
public class ViewPagerActivity extends BaseActivity {
private ViewPager mViewPager;
private TabLayout mTabLayout;
private TabItemView mSelectBarItem;//記錄選擇中的tablayout的item
private List<View> mViews = new ArrayList<>();//viewpager每個頁面的view
private MyViewPagerAdapter mViewPagerAdapter;
private String[] mIsLoads;//0:未載入,1:已載入
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_pager);
initView();
getPageTitle();
}
/**
* 初始化View
*/
private void initView() {
mViewPager = (ViewPager)this.findViewById(R.id.my_viewpager);
mTabLayout = (TabLayout)this.findViewById(R.id.toolbar_tab);
mTabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
mViewPagerAdapter = new MyViewPagerAdapter(mViews);
mViewPager.setAdapter(mViewPagerAdapter);
//設定viewpager的滑動監聽器
mViewPager.addOnPageChangeListener(mPagerChangeListener);
//設定viewpager切換動畫
mViewPager.setPageTransformer(true, new ZoomOutPageTransformer());
}
/**
* 獲取頁面的title
*/
public void getPageTitle() {
List<String> titles = new ArrayList<>();
for (int i = 0; i < 15; i++) {
String title = "Title "+i;
titles.add(title);
}
initViewPages(titles);
}
/**
* 根據多少個title建立多少個view
* @param titles
* 這裡必須建立完成各個頁面的view並且改變viewpager才能初始化tablayout的item,否則出錯
*/
private void initViewPages(List<String> titles) {
if(titles != null && titles.size() > 0)
{
mIsLoads = new String[titles.size()];
for(int i = 0; i < titles.size(); i++)
{
ListPage page = new ListPage(ViewPagerActivity.this);
mIsLoads[i] = "0";
mViews.add(page);
}
mViewPagerAdapter.notifyDataSetChanged();
addData(0);
}
initTab(titles);
}
/**
* 初始化各個頁面的資料
* @param position
*/
private void addData(int position) {
if(mIsLoads != null && mIsLoads[position].equals("0"))
{
mIsLoads[position] = "1";
if(mViews != null && mViews.size() > 0)
{
if (mViews.get(position) != null){
((ListPage)mViews.get(position)).firstLoadData();
}
}
}
}
/**
* 初始化tablayout的item
* 有多少個title就建立多少個item
* @param titles
*/
private void initTab(List<String> titles)
{
mTabLayout.setVisibility(View.VISIBLE);
ColorStateList color = mTabLayout.getTabTextColors();
if(titles != null && titles.size() > 0)
{
mTabLayout.setupWithViewPager(mViewPager);
for(int i = 0; i < titles.size(); i++)
{
String title = titles.get(i);
TabLayout.Tab item = mTabLayout.getTabAt(i);
TabItemView tabItem = new TabItemView(ViewPagerActivity.this);
tabItem.setText(title);
if(i == 0)
{
mSelectBarItem = tabItem;
tabItem.setTextColor(0xff809fd8);
}
else
{
tabItem.setTextColor(color);
}
item.setCustomView(tabItem);
}
}
}
/**
* ViewPager監聽器
*/
private ViewPager.OnPageChangeListener mPagerChangeListener = new ViewPager.OnPageChangeListener()
{
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{
}
@Override
public void onPageSelected(final int position)
{
//改變tablayout選中item的顏色
mSelectBarItem.setTextColor(mTabLayout.getTabTextColors());
//滑動到頁面才能初始化資料,不需要預載入
addData(position);
}
@Override
public void onPageScrollStateChanged(int state)
{
}
};
}
複製程式碼
這是activity,主要做的操作就是初始化ViewPager、TabLayout和資料,還有viewpager的設定。具體的程式碼已經註釋清楚了。
public class TabItemView extends FrameLayout {
private TextView mText;
public TabItemView(@NonNull Context context) {
super(context);
init(context);
}
public TabItemView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TabItemView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
View v = LayoutInflater.from(context).inflate(R.layout.tab_text, null);
addView(v);
mText = (TextView) v.findViewById(R.id.tv_toolbar);
}
/**
* 設定選中的顏色
* @param color
*/
public void setTextColor(ColorStateList color)
{
mText.setTextColor(color);
}
/**
* 設定選中的顏色
* @param color
*/
public void setTextColor(int color)
{
mText.setTextColor(color);
}
/**
* 設定item的文字
* @param text
*/
public void setText(String text)
{
mText.setText(text);
}
}
複製程式碼
設定TabLayout的item,你可以在裡面設定item的樣式。
<?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:orientation="vertical">
<TextView
android:id="@+id/tv_toolbar"
android:gravity="center"
android:paddingLeft="20dp"
android:paddingTop="2dp"
android:paddingRight="20dp"
android:paddingBottom="2dp"
android:textColor="@color/text_color_selector"
android:textSize="15dp"
android:layout_centerInParent="true"
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
</LinearLayout>
複製程式碼
這是TabLayout的item佈局檔案,你可以在這裡編輯你想要的佈局。
public class MyViewPagerAdapter extends PagerAdapter {
private List<View> mViews = null;
public MyViewPagerAdapter(List<View> datas)
{
this.mViews = datas;
}
@Override
public int getCount()
{
return mViews == null ? 0 : mViews.size();
}
/**
* 判斷是否使用快取, 如果true, 使用快取
* view 就是拖動的物件
* object 進來的物件
*/
@Override
public boolean isViewFromObject(View view, Object object)
{
return view == object;
}
/**
* 銷燬物件
* position 就是被銷燬的物件的索引
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object)
{
container.removeView(mViews.get(position));
}
/**
* 載入item
* position 被載入的item的索引
*/
@Override
public Object instantiateItem(ViewGroup container, int position)
{
container.addView(mViews.get(position));
return mViews.get(position);
}
}
複製程式碼
這是viewpager的adapter,具體的方法使用在檔案中已經註釋說明了。
接下來我們看viewpager頁面所需要的view部分。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list"
android:divider="@null"
android:dividerHeight="0dp"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
複製程式碼
佈局真的很簡單,一個是google的下拉重新整理,一個是用來顯示列表的ListView。
public class ListPage extends FrameLayout {
private LinearLayout layout;
private SwipeRefreshLayout mSwipeRefreshLayout;
private ListView mListView;
private Handler mH = new Handler();
private ListAdapter mAdapter;
private List<String> datas = new ArrayList<>();
private String[] lists = new String[]{"好聽麼","你是誰",
"你從哪裡來","要到哪裡去","android開發","javaweb開發","ios開發","php開發","哈哈",
"聽風","下雨了","嘩啦啦","風呼呼","牛魔王","孫悟空","齊天大聖","豬八戒","無能為力","沙僧","唐僧"};
public ListPage(@NonNull Context context) {
super(context);
initialize(context);
}
public ListPage(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initialize(context);
}
public ListPage(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context);
}
private void initialize(Context context) {
initView(context);
initListener();
}
/**
* 初始化view
* @param context
*/
private void initView(Context context) {
int MP = LayoutParams.MATCH_PARENT;
LayoutParams fl = new LayoutParams(MP,MP);
layout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.list_layout,null);
addView(layout,fl);
mSwipeRefreshLayout = (SwipeRefreshLayout)layout.findViewById(R.id.swipe);
mListView = (ListView)layout.findViewById(R.id.list);
mAdapter = new ListAdapter(context,datas);
mListView.setAdapter(mAdapter);
}
/**
* 第一次進來載入資料
*/
public void firstLoadData(){
setData(getData());
}
/**
* 獲取資料
*/
public List<String> getData(){
Random r = new Random();
List<String> mDatas = new ArrayList<>();
for (int i = 0; i < 50; i++) {
String data = lists[r.nextInt(20)];
mDatas.add(data);
}
return mDatas;
}
private void initListener() {
/**
* 下拉重新整理
*/
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
getNetData();
}
});
}
/**
* 下拉重新整理獲取資料
*/
public void getNetData() {
new Thread(new Runnable() {
@Override
public void run() {
final List<String> strs = getData();
mSwipeRefreshLayout.isRefreshing();
// mSwipeRefreshLayout.setEnabled(false);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mH.post(new Runnable() {
@Override
public void run() {
setData(strs);
}
});
}
}).start();
}
/**
* 設定資料
* @param data
*/
public void setData(List<String> data) {
mSwipeRefreshLayout.setRefreshing(false);
if (data != null && data.size() > 0){
if (datas != null && datas.size() > 0){
datas.clear();
}
datas.addAll(data);
mAdapter.notifyDataSetChanged();
}
}
}
複製程式碼
這裡主要是實現ListView列表和SwipeRefreshLayout的下拉重新整理。
public class ListAdapter extends BaseAdapter {
private List<String> datas;
private Context mContext;
public ListAdapter(Context mContext,List<String> datas) {
this.datas = datas;
this.mContext = mContext;
}
@Override
public int getCount() {
return datas == null ? 0 : datas.size();
}
@Override
public Object getItem(int i) {
return datas == null ? null : datas.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
if (convertView == null && !(convertView instanceof ItemLayout)){
convertView = new ItemLayout(mContext);
}
((ItemLayout)convertView).setData(datas.get(position));
return convertView;
}
public static class ItemLayout extends LinearLayout{
public String tagStr;
private Button mButton;
public ItemLayout(Context context) {
super(context);
initView(context);
}
private void initView(Context context) {
int MP = LinearLayout.LayoutParams.MATCH_PARENT;
int WC = LinearLayout.LayoutParams.WRAP_CONTENT;
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(MP, WC);
LinearLayout layout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.list_item_layout,null);
addView(layout,params);
mButton = (Button)layout.findViewById(R.id.text);
}
public void setData(String str){
if (str == null || str == tagStr){
return;
}else{
tagStr = str;
mButton.setText(str);
}
}
}
}
複製程式碼
這裡大家應該很熟悉了,這是ListView的adapter,這裡直接顯示列表,沒有做過多的需求。
<?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="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/text"
android:layout_margin="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
複製程式碼
這是adapter的item。
最後我們看看實現viewpager頁面切換的動畫,這裡用的是google官網上的。
public class ZoomOutPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.9f;
private static final float MIN_ALPHA = 0.5f;
@Override
public void transformPage(View page, float position) {
int pageWidth = page.getWidth();
int pageHeight = page.getHeight();
if (position < -1)
{ // [-Infinity,-1)
// This page is way off-screen to the left.
page.setAlpha(0);
} else if (position <= 1) //a頁滑動至b頁 ; a頁從 0.0 -1 ;b頁從1 ~ 0.0
{ // [-1,1]
// Modify the default slide transition to shrink the page as well
float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horzMargin = pageWidth * (1 - scaleFactor) / 2;
if (position < 0)
{
page.setTranslationX(horzMargin - vertMargin / 2);
} else
{
page.setTranslationX(-horzMargin + vertMargin / 2);
}
// Scale the page down (between MIN_SCALE and 1)
page.setScaleX(scaleFactor);
page.setScaleY(scaleFactor);
// Fade the page relative to its size.
page.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE)
/ (1 - MIN_SCALE) * (1 - MIN_ALPHA));
} else
{ // (1,+Infinity]
// This page is way off-screen to the right.
page.setAlpha(0);
}
}
}
複製程式碼
mViewPager.setPageTransformer(true, new ZoomOutPageTransformer());這樣簡簡單單的一句話就可以實現viewpager的頁面切換動畫了。 關於更多的ViewPager切換動畫,大家可以參考鴻洋大神的文章 Android 實現個性的ViewPager切換動畫 實戰PageTransformer(相容Android3.0以下)
最後提供一個簡單封裝的ViewPager,可以設定不允許ViewPager左右滑動
public class CustomViewPager extends ViewPager {
//控制Viewpager是否可以左右滑動,預設不能滑動
private boolean isCanScroll = true;
public CustomViewPager(Context context) {
super(context);
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScanScroll(boolean isCanScroll) {
this.isCanScroll = isCanScroll;
}
@Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
}
@Override
public boolean onTouchEvent(MotionEvent arg0) {
// TODO Auto-generated method stub
if (isCanScroll) {
return false;
} else {
return super.onTouchEvent(arg0);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (isCanScroll) {
return false;
} else {
return super.onInterceptTouchEvent(ev);
}
}
@Override
public void setCurrentItem(int item, boolean smoothScroll) {
// TODO Auto-generated method stub
super.setCurrentItem(item, smoothScroll);
}
@Override
public void setCurrentItem(int item) {
// TODO Auto-generated method stub
super.setCurrentItem(item);
}
}
複製程式碼