国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

ViewPager 全面剖析及使用詳解

2019-11-09 17:11:04
字體:
供稿:網(wǎng)友

ViewPager在開發(fā)中的使用頻率非常的高,所以在此做個(gè)總結(jié)。主要包括以下幾方面:

ViewPager的簡(jiǎn)介和作用ViewPager的適配器ViewPager的翻頁動(dòng)畫簡(jiǎn)化ViewPager的使用ViewPager結(jié)合第三方庫(kù)實(shí)現(xiàn)小圓點(diǎn)指示器效果ViewPager結(jié)合design庫(kù)實(shí)現(xiàn)tab切換基于ViewPager實(shí)現(xiàn)廣告輪播控件

按照慣例,先上個(gè)效果圖

demo.gif

基礎(chǔ)篇


1.ViewPager的簡(jiǎn)介和作用ViewPager是android擴(kuò)展包v4包中的類,這個(gè)類可以讓用戶左右切換當(dāng)前的view1)ViewPager類直接繼承了ViewGroup類,所有它是一個(gè)容器類,可以在其中添加其他的view類。2)ViewPager類需要一個(gè)PagerAdapter適配器類給它提供數(shù)據(jù)。3)ViewPager經(jīng)常和Fragment一起使用,并且提供了專門的FragmentPagerAdapter和FragmentStatePagerAdapter類供Fragment中的ViewPager使用。

2.ViewPager的適配器簡(jiǎn)介中提到了PagerAdapter,和ListView等控件使用一樣,需要ViewPager設(shè)置PagerAdapter來完成頁面和數(shù)據(jù)的綁定,這個(gè)PagerAdapter是一個(gè)基類適配器,我們經(jīng)常用它來實(shí)現(xiàn)app引導(dǎo)圖,它的子類有FragmentPagerAdapter和FragmentStatePagerAdapter,這兩個(gè)子類適配器用于和Fragment一起使用,在安卓應(yīng)用中它們就像listview一樣出現(xiàn)的頻繁。

實(shí)現(xiàn)一個(gè)最基本的PagerAdapter,《必須實(shí)現(xiàn)四個(gè)方法》,在代碼里有注釋

public class AdapterViewpager extends PagerAdapter { PRivate List<View> mViewList; public AdapterViewpager(List<View> mViewList) { this.mViewList = mViewList; } @Override public int getCount() {//必須實(shí)現(xiàn) return mViewList.size(); } @Override public boolean isViewFromObject(View view, Object object) {//必須實(shí)現(xiàn) return view == object; } @Override public Object instantiateItem(ViewGroup container, int position) {//必須實(shí)現(xiàn),實(shí)例化 container.addView(mViewList.get(position)); return mViewList.get(position); } @Override public void destroyItem(ViewGroup container, int position, Object object) {//必須實(shí)現(xiàn),銷毀 container.removeView(mViewList.get(position)); }}

實(shí)現(xiàn)一個(gè)最基本的FragmentPagerAdapter

public class AdapterFragment extends FragmentPagerAdapter { private List<Fragment> mFragments; public AdapterFragment(FragmentManager fm, List<Fragment> mFragments) { super(fm); this.mFragments = mFragments; } @Override public Fragment getItem(int position) {//必須實(shí)現(xiàn) return mFragments.get(position); } @Override public int getCount() {//必須實(shí)現(xiàn) return mFragments.size(); } @Override public CharSequence getPageTitle(int position) {//選擇性實(shí)現(xiàn) return mFragments.get(position).getClass().getSimpleName(); }}

FragmentStatePagerAdapter的實(shí)現(xiàn)和FragmentPagerAdapter的實(shí)現(xiàn)一樣就不在寫了

3個(gè)適配器的基本實(shí)現(xiàn)講完了是不是很簡(jiǎn)單,那他們的區(qū)別是什么呢?PagerAdapter是基類適配器是一個(gè)通用的ViewPager適配器,相比PagerAdapter,F(xiàn)ragmentPagerAdapter和FragmentStatePagerAdapter更專注于每一頁是Fragment的情況,而這兩個(gè)子類適配器使用情況也是有區(qū)別的。FragmentPagerAdapter適用于頁面比較少的情況,F(xiàn)ragmentStatePagerAdapter適用于頁面比較多的情況。為什么?簡(jiǎn)單分析下兩個(gè)適配器的源碼就可以知道了。

FragmentStatePagerAdapter

@Override public Object instantiateItem(ViewGroup container, int position) { // If we already have this item instantiated, there is nothing // to do. This can happen when we are restoring the entire pager // from its saved state, where the fragment manager has already // taken care of restoring the fragments we previously had instantiated. if (mFragments.size() > position) { Fragment f = mFragments.get(position);//fragment被釋放后這里得到的null值 if (f != null) { return f; } } if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } Fragment fragment = getItem(position);//fragment被釋放后或者是初次進(jìn)入頁面拿到新的Fragment實(shí)例 if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment); if (mSavedState.size() > position) { Fragment.SavedState fss = mSavedState.get(position); if (fss != null) { fragment.setInitialSavedState(fss); } } while (mFragments.size() <= position) { mFragments.add(null); } fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); mFragments.set(position, fragment); mCurTransaction.add(container.getId(), fragment);//新的Fragment實(shí)例 是add上去的 return fragment; } @Override public void destroyItem(ViewGroup container, int position, Object object) { Fragment fragment = (Fragment) object; if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object + " v=" + ((Fragment)object).getView()); while (mSavedState.size() <= position) { mSavedState.add(null); } mSavedState.set(position, fragment.isAdded() ? mFragmentManager.saveFragmentInstanceState(fragment) : null); mFragments.set(position, null);//真正釋放了fragment實(shí)例 mCurTransaction.remove(fragment); }

FragmentPagerAdapter

@Override public Object instantiateItem(ViewGroup container, int position) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } final long itemId = getItemId(position); // Do we already have this fragment? String name = makeFragmentName(container.getId(), itemId); Fragment fragment = mFragmentManager.findFragmentByTag(name); if (fragment != null) { if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); mCurTransaction.attach(fragment);//因?yàn)閒ragment實(shí)例沒有被真正釋放,所以可以直接attach效率高 } else { fragment = getItem(position);//初始化頁面的時(shí)候拿到fragment的實(shí)例 if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment); mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));//add上去 } if (fragment != mCurrentPrimaryItem) { fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); } return fragment; } @Override public void destroyItem(ViewGroup container, int position, Object object) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object + " v=" + ((Fragment)object).getView()); mCurTransaction.detach((Fragment)object);//并沒有真正釋放fragment對(duì)象只是detach }

從源碼中我們可以看出FragmentStatePagerAdapter中fragment實(shí)例在destroyItem的時(shí)候被真正釋放,所以FragmentStatePagerAdapter省內(nèi)存。FragmentPagerAdapter中的fragment實(shí)例在destroyItem的時(shí)候并沒有真正釋放fragment對(duì)象只是detach,所以FragmentPagerAdapter消耗更多的內(nèi)存,帶來的好處就是效率更高一些。所以得出這樣的結(jié)論:FragmentPagerAdapter適用于頁面比較少的情況,F(xiàn)ragmentStatePagerAdapter適用于頁面比較多的情況,因此不同的場(chǎng)合選擇合適的適配器才是正確的做法

3.ViewPager的翻頁動(dòng)畫為ViewPager設(shè)置適配器后,就可以正常使用了,接下來我們?yōu)閂iewPager增加翻頁動(dòng)畫,畢竟人的審美會(huì)疲勞,加上一些動(dòng)畫交互會(huì)提高不少逼格~~,ViewPager提供了PageTransformer接口用于實(shí)現(xiàn)翻頁動(dòng)畫。官方提供了PageTransformer的實(shí)現(xiàn)例子。

public class DepthPageTransformer implements ViewPager.PageTransformer { private static final float MIN_SCALE = 0.75f; public void transformPage(View view, float position) { Log.d("DepthPageTransformer", view.getTag() + " , " + position + ""); int pageWidth = view.getWidth(); if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. view.setAlpha(0); } else if (position <= 0) { // [-1,0] // Use the default slide transition when moving to the left page view.setAlpha(1); view.setTranslationX(0); view.setScaleX(1); view.setScaleY(1); } else if (position <= 1) { // (0,1] // Fade the page out. view.setAlpha(1 - position); // Counteract the default slide transition view.setTranslationX(pageWidth * -position); // Scale the page down (between MIN_SCALE and 1) float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position)); view.setScaleX(scaleFactor); view.setScaleY(scaleFactor); } else { // (1,+Infinity] // This page is way off-screen to the right. view.setAlpha(0); } }} public class ZoomOutPageTransformer implements ViewPager.PageTransformer { private static final float MIN_SCALE = 0.85f; private static final float MIN_ALPHA = 0.5f; @SuppressLint("NewApi") public void transformPage(View view, float position) { int pageWidth = view.getWidth(); int pageHeight = view.getHeight(); Log.e("TAG", view + " , " + position + ""); if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. view.setAlpha(0); } else if (position <= 1) { // [-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) { view.setTranslationX(horzMargin - vertMargin / 2); } else { view.setTranslationX(-horzMargin + vertMargin / 2); } // Scale the page down (between MIN_SCALE and 1) view.setScaleX(scaleFactor); view.setScaleY(scaleFactor); // Fade the page relative to its size. view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA)); } else { // (1,+Infinity] // This page is way off-screen to the right. view.setAlpha(0); } }}

實(shí)現(xiàn)翻頁動(dòng)畫的關(guān)鍵就是重寫transformPage方法,方法里有兩個(gè)參數(shù)view和position,理解這兩個(gè)參數(shù)非常重要。假設(shè)有三個(gè)頁面view1,view2,view3從左至右在viewPager中顯示

往左滑動(dòng)時(shí):view1,view2,view3的position都是不斷變小的。 view1的position: 0 → -1 → 負(fù)無窮大 view2的position: 1 → 0 → -1 view3的position: 1 → 0往右滑動(dòng)時(shí):view1,view2,view3的position都是不斷變大的。 view1的position: -1 → 0 view2的position: -1 → 0 → 1 view3的position: 0 → 1→ 正無窮大當(dāng)position是正負(fù)無窮大時(shí)view就離開屏幕視野了。因此最核心的控制邏輯是在[-1,0]和(0,1]這兩個(gè)區(qū)間,通過設(shè)置透明度,平移,旋轉(zhuǎn),縮放等動(dòng)畫組合可以實(shí)現(xiàn)各式各樣的頁面變化效果。

4.簡(jiǎn)化ViewPager的使用

不會(huì)偷懶的程序猿不是好程序員

這里只是做了最簡(jiǎn)單的封裝,可以根據(jù)需要調(diào)整

PagerAdapter簡(jiǎn)化

public class QuickPageAdapter<T extends View> extends PagerAdapter { private List<T> mList; public QuickPageAdapter(List<T> mList) { this.mList = mList; } @Override public int getCount() { return mList.size(); } @Override public boolean isViewFromObject(View view, Object object) { return object == view; } @Override public Object instantiateItem(ViewGroup container, int position) { container.addView(mList.get(position)); return mList.get(position); } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(mList.get(position)); }}

使用它,這樣不用每次都寫個(gè)適配器List<View> views = new ArrayList<>();...mViewPager.setAdapter(new QuickPageAdapter<View>(views));

FragmentPagerAdapter簡(jiǎn)化

public class QuickFragmentPageAdapter<T extends Fragment> extends FragmentPagerAdapter { private List<T> mList; private String[] mStrings; /** * @param fm * @param list * @param titles PageTitles */ public QuickFragmentPageAdapter(FragmentManager fm, List<T> list, String[] titles) { super(fm); mList = list; mStrings = titles; } @Override public Fragment getItem(int position) { return mList.get(position); } @Override public int getCount() { return mList.size(); } @Override public CharSequence getPageTitle(int position) { return mStrings == null ? super.getPageTitle(position) : mStrings[position]; }}

FragmentStatePagerAdapter封裝類似FragmentPagerAdapter就不寫了,基本使用講完了。

5.補(bǔ)充一個(gè)知識(shí)點(diǎn)mViewPager.setOffscreenPageLimit()//這個(gè)方法是用來控制fragment不重新走生命周期的個(gè)數(shù)的,打個(gè)比方一共4個(gè)fragment頁面,如果mViewPager.setOffscreenPageLimit(3),那么所有的fragment都只走一次生命周期,如果是mViewPager.setOffscreenPageLimit(2),那么其中有一個(gè)fragment會(huì)在切換的時(shí)候重新走一遍生命周期,F(xiàn)ragmentStatePagerAdapter和FragmentPagerAdapter都是這樣,但是FragmentPagerAdapter設(shè)置setOffscreenPageLimit不影響fragment緩存的個(gè)數(shù),而FragmentStatePagerAdapter緩存的fragment實(shí)例個(gè)數(shù)就是setOffscreenPageLimit設(shè)置的值+1。另外setOffscreenPageLimit的缺省值是1,設(shè)置0是無效的會(huì)被強(qiáng)制賦值成1。

private static final int DEFAULT_OFFSCREEN_PAGES = 1; public void setOffscreenPageLimit(int limit) { if (limit < DEFAULT_OFFSCREEN_PAGES) { Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + DEFAULT_OFFSCREEN_PAGES); limit = DEFAULT_OFFSCREEN_PAGES;//強(qiáng)制賦值為1 } if (limit != mOffscreenPageLimit) { mOffscreenPageLimit = limit; populate(); } }

提高篇


1.ViewPager結(jié)合第三方庫(kù)實(shí)現(xiàn)小圓點(diǎn)指示器效果https://github.com/ongakuer/CircleIndicator

screenshot.gif

使用看官方文檔很簡(jiǎn)單。

看一下實(shí)現(xiàn)思路

public void setViewPager(ViewPager viewPager) { mViewpager = viewPager; if (mViewpager != null && mViewpager.getAdapter() != null) { mLastPosition = -1; createIndicators(); mViewpager.removeOnPageChangeListener(mInternalPageChangeListener); mViewpager.addOnPageChangeListener(mInternalPageChangeListener);//綁定上內(nèi)部實(shí)現(xiàn)的PageChangeListener mInternalPageChangeListener.onPageSelected(mViewpager.getCurrentItem()); } } private final OnPageChangeListener mInternalPageChangeListener = new OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) {//這里是動(dòng)畫的核心 if (mViewpager.getAdapter() == null || mViewpager.getAdapter().getCount() <= 0) { return; } if (mAnimatorIn.isRunning()) { mAnimatorIn.end(); mAnimatorIn.cancel(); } if (mAnimatorOut.isRunning()) { mAnimatorOut.end(); mAnimatorOut.cancel(); } View currentIndicator; if (mLastPosition >= 0 && (currentIndicator = getChildAt(mLastPosition)) != null) {//頁面離開屏幕時(shí)指示器動(dòng)畫 currentIndicator.setBackgroundResource(mIndicatorUnselectedBackgroundResId); mAnimatorIn.setTarget(currentIndicator); mAnimatorIn.start(); } View selectedIndicator = getChildAt(position); if (selectedIndicator != null) {//頁面進(jìn)入屏幕時(shí)指示器動(dòng)畫 selectedIndicator.setBackgroundResource(mIndicatorBackgroundResId); mAnimatorOut.setTarget(selectedIndicator); mAnimatorOut.start(); } mLastPosition = position; } @Override public void onPageScrollStateChanged(int state) { } };

2.ViewPager結(jié)合design庫(kù)實(shí)現(xiàn)tab切換在design庫(kù)中有個(gè)TabLayout可以為viewPager加上Tab標(biāo)題頭

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.administrator.viewpager.MainActivity"> <android.support.design.widget.TabLayout android:id="@+id/mTabLayout" android:layout_width="match_parent" android:layout_height="wrap_content"></android.support.design.widget.TabLayout> <android.support.v4.view.ViewPager android:id="@+id/mViewPager" android:layout_width="match_parent" android:layout_height="match_parent"></android.support.v4.view.ViewPager></LinearLayout>mTabLayout.setupWithViewPager(mViewPager);//一行代碼完成綁定

更多高級(jí)的用法包括tab中添加icon等請(qǐng)轉(zhuǎn)至這里 傳送門

3.基于ViewPager實(shí)現(xiàn)廣告輪播控件https://github.com/daimajia/AndroidImageSlider

imageSlider.gif

源碼分析,省略了部分代碼:

public class SliderLayout extends RelativeLayout{ private InfiniteViewPager mViewPager;//這個(gè)ViewPager只是修改了setPageTransformer方法去掉了if (Build.VERSION.SDK_INT >= 11) 的限制,結(jié)合NineOldDroid庫(kù)讓動(dòng)畫兼容低版本 /** * InfiniteViewPager adapter. */ private SliderAdapter mSliderAdapter;//這個(gè)是PagerAdapter /** * {@link com.daimajia.slider.library.Tricks.ViewPagerEx} indicator. */ private PagerIndicator mIndicator;//頁面指示器 /** * A timer and a TimerTask using to cycle the {@link com.daimajia.slider.library.Tricks.ViewPagerEx}. */ private Timer mCycleTimer;//用于輪播的定時(shí)器 private TimerTask mCycleTask; /** * For resuming the cycle, after user touch or click the {@link com.daimajia.slider.library.Tricks.ViewPagerEx}. */ private Timer mResumingTimer; private TimerTask mResumingTask; /** * {@link com.daimajia.slider.library.Tricks.ViewPagerEx} 's transformer */ private BaseTransformer mViewPagerTransformer;//PageTransformer的封裝用于控制頁面翻頁效果 public SliderLayout(Context context, AttributeSet attrs, int defStyle) {//核心代碼,用于初始化ViewPager super(context, attrs, defStyle); mContext = context; LayoutInflater.from(context).inflate(R.layout.slider_layout, this, true); final TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs,R.styleable.SliderLayout, defStyle,0); mTransformerSpan = attributes.getInteger(R.styleable.SliderLayout_pager_animation_span, 1100); mTransformerId = attributes.getInt(R.styleable.SliderLayout_pager_animation, Transformer.Default.ordinal()); mAutoCycle = attributes.getBoolean(R.styleable.SliderLayout_auto_cycle,true); int visibility = attributes.getInt(R.styleable.SliderLayout_indicator_visibility,0); for(PagerIndicator.IndicatorVisibility v: PagerIndicator.IndicatorVisibility.values()){ if(v.ordinal() == visibility){ mIndicatorVisibility = v; break; } } mSliderAdapter = new SliderAdapter(mContext); PagerAdapter wrappedAdapter = new InfinitePagerAdapter(mSliderAdapter); mViewPager = (InfiniteViewPager)findViewById(R.id.daimajia_slider_viewpager); mViewPager.setAdapter(wrappedAdapter); mViewPager.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_UP: recoverCycle(); break; } return false; } }); attributes.recycle(); setPresetIndicator(PresetIndicators.Center_Bottom); setPresetTransformer(mTransformerId); setSliderTransformDuration(mTransformerSpan,null); setIndicatorVisibility(mIndicatorVisibility); if(mAutoCycle){ startAutoCycle(); } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN://手指按下時(shí)候暫停輪播 pauseAutoCycle(); break; } return false; } /** * preset transformers and their names */ public enum Transformer{//PageTransformer枚舉 Default("Default"), Accordion("Accordion"), Background2Foreground("Background2Foreground"), CubeIn("CubeIn"), DepthPage("DepthPage"), Fade("Fade"), FlipHorizontal("FlipHorizontal"), FlipPage("FlipPage"), Foreground2Background("Foreground2Background"), RotateDown("RotateDown"), RotateUp("RotateUp"), Stack("Stack"), Tablet("Tablet"), ZoomIn("ZoomIn"), ZoomOutSlide("ZoomOutSlide"), ZoomOut("ZoomOut"); private final String name; private Transformer(String s){ name = s; } public String toString(){ return name; } public boolean equals(String other){ return (other == null)? false:name.equals(other); } };}

通過分析我們可以對(duì)SliderLayout實(shí)現(xiàn)思路小結(jié)一下:1.內(nèi)部持有一個(gè)修改過的ViewPager控件,可以兼容低版本的頁面轉(zhuǎn)換動(dòng)畫2.內(nèi)部有一個(gè)實(shí)現(xiàn)了PagerAdapter的SliderAdapter適配器3.內(nèi)部持有一個(gè)PagerIndicator 頁面指示器可供選擇4.維護(hù)一個(gè)定時(shí)任務(wù)用于控制輪播5.對(duì)手勢(shì)事件進(jìn)行處理暫停輪播,繼續(xù)輪播6.提供了很多缺省的PageTransformer方便調(diào)用

最后在構(gòu)造函數(shù)中初始化ViewPager。

http://www.jianshu.com/p/e5abbda4a71c


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 石阡县| 内乡县| 海原县| 湘乡市| 万源市| 库伦旗| 万源市| 永顺县| 石河子市| 泸水县| 招远市| 屏山县| 同仁县| 卢龙县| 景泰县| 九龙城区| 盐池县| 扎赉特旗| 柞水县| 沈丘县| 温宿县| 康平县| 大渡口区| 虹口区| 县级市| 云林县| 盱眙县| 伊川县| 凤山市| 宜兰市| 探索| 辽阳县| 肃北| 腾冲县| 兴义市| 缙云县| 鄯善县| 奉贤区| 南城县| 麟游县| 扎兰屯市|