├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── markdown-navigator.xml ├── markdown-navigator │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── dqr │ │ └── www │ │ └── viewpagerfragmentad │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── dqr │ │ │ └── www │ │ │ └── viewpagerfragmentad │ │ │ ├── MainActivity.java │ │ │ ├── PagerAdapter.java │ │ │ └── TestFragment.java │ └── res │ │ ├── layout │ │ ├── activity_main.xml │ │ └── test_fragment_layout.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── dqr │ └── www │ └── viewpagerfragmentad │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── viewpage_fragment.gif /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 35 | 36 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ViewPagerFragmentAD 2 | Android Viewpager+fragment 动态添加删除Fragment 复用FragmentManager中缓存的Fragment 3 | 4 | #### 方案1: 5 | 6 | [动态更新Viewpager+fragment](http://stackoverflow.com/questions/10396321/remove-fragment-page-from-viewpager-in-android) 7 | 这种方法可以实现动态新增删除,但是有一个缺点 会重建所有的Fragment 造成性能浪费, 8 | 比如目前项目中Fragment中是有网络数据处理的 重新创建Fragment的话将重复调用服务器接口, 9 | 对于用户体验 服务器访问等都造成了 不良后果 遂优化如方案2 10 | 11 | #### 方案2: 12 | [ViewPager 详解(二)---详解四大函数](http://blog.csdn.net/harvic880925/article/details/38487149) 13 | [动态更新Viewpager+fragment](http://stackoverflow.com/questions/10396321/remove-fragment-page-from-viewpager-in-android) 14 | 15 | 根据方案1与以上资料,可知: 16 | ###### 1.每个滑动页面都对应一个Key,而且这个Key值是用来唯一追踪这个页面的,也就是说每个滑动页面都与一个唯一的Key一一对应 17 | ###### 2.在FragmentPagerAdapter中 每次在instantiateItem中调用getItemId()的时候,都会是不同的id。适配器发现找不到之前的碎片,就会重新调用getItem来新建碎片。这个方法是适配器用来组装tag的一部分。只要改变了它,也就改变了tag。 18 | ###### 3.每次调用notifyDataSetChanged()时,都会激活getItemPosition(Object object)方法,该方法会遍历viewpager的所有缓存的item,为每个item返回一个状态值(POSITION_NONE/POSITION_UNCHANGED),如果是none,那么该item会被destroyItem(ViewGroup container, int position, Object object)方法remove掉,然后重新加载,如果是unchanged,就不会重新加载,默认是unchanged,所以如果我们不重写getItemPosition(Object object),就无法看到刷新效果。 19 | 20 | ##### 基于以上得到思路: 21 | ##### 1.为每一个Fragment保存一个对应的key(使用SparseArray) 22 | mTestFragments = new SparseArray<>(); 23 | mTestFragments.put(key++,TestFragment.newInstance("第一")); 24 | mTestFragments.put(key++,TestFragment.newInstance("第二")); 25 | mTestFragments.put(key++,TestFragment.newInstance("第三")); 26 | mTestFragments.put(key++,TestFragment.newInstance("第四")); 27 | mTestFragments.put(key++,TestFragment.newInstance("第五")); 28 | ##### 2.重写getItemId(int position) 返回对应的key 29 | @Override 30 | public long getItemId(int position) { 31 | Log.d(TAG, "position: "+position+" getItemId:"+mTestFragments.keyAt(position)); 32 | return mTestFragments.keyAt(position); 33 | } 34 | 35 | ##### 3.重写getItem(int position) 36 | 37 | @Override 38 | public Fragment getItem(int position) { 39 | Log.d(TAG, "getItem: "+position); 40 | TestFragment testFragment=mTestFragments.valueAt(position); 41 | testFragment.tag=mTestFragments.keyAt(position); 42 | return testFragment; 43 | } 44 | 45 | 46 | ##### 4.重写getItemPosition 否刷新页面 47 | @Override 48 | public int getItemPosition(Object object) { 49 | return POSITION_NONE; 50 | 51 | } 52 | 53 | ##### 5.新增和删除调用 54 | 55 | //删除 56 | mTestFragments.removeAt(mCurPos); 57 | mPagerAdapter.notifyDataSetChanged(); 58 | 59 | //新增 60 | mTestFragments.put(key++,TestFragment.newInstance("第"+key)); 61 | mPagerAdapter.notifyDataSetChanged(); 62 | 63 | ##### 经过以上处理后,新增和删除都只会影响当前Fragment的生命周期,其他已经缓存的Fragment都不会重新创建,而是直接在FragmentManager中读取缓存复用 64 | 65 | ![image](https://github.com/bux-git/ViewPagerFragmentAD/raw/master/viewpage_fragment.gif) -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "com.dqr.www.viewpagerfragmentad" 8 | minSdkVersion 19 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 29 | testCompile 'junit:junit:4.12' 30 | } 31 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\DQRAndroid\adt-bundle-windows-x86_64-20140702\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/dqr/www/viewpagerfragmentad/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.dqr.www.viewpagerfragmentad; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.dqr.www.viewpagerfragmentad", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/dqr/www/viewpagerfragmentad/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.dqr.www.viewpagerfragmentad; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.view.ViewPager; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.util.Log; 7 | import android.util.SparseArray; 8 | import android.view.View; 9 | import android.widget.Button; 10 | 11 | public class MainActivity extends AppCompatActivity { 12 | 13 | 14 | private Button mBtnDelete; 15 | private Button mBtnAdd; 16 | private ViewPager mViewPager; 17 | private SparseArray mTestFragments; 18 | private PagerAdapter mPagerAdapter; 19 | 20 | private int key; 21 | private int mCurPos; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_main); 27 | mViewPager = (ViewPager) findViewById(R.id.pager); 28 | mBtnDelete = (Button) findViewById(R.id.btn_delete); 29 | mBtnAdd = (Button) findViewById(R.id.btn_add); 30 | 31 | mTestFragments = new SparseArray<>(); 32 | mTestFragments.put(key++,TestFragment.newInstance("第一")); 33 | mTestFragments.put(key++,TestFragment.newInstance("第二")); 34 | mTestFragments.put(key++,TestFragment.newInstance("第三")); 35 | mTestFragments.put(key++,TestFragment.newInstance("第四")); 36 | mTestFragments.put(key++,TestFragment.newInstance("第五")); 37 | mPagerAdapter = new PagerAdapter(getSupportFragmentManager(), mTestFragments); 38 | 39 | mViewPager.setAdapter(mPagerAdapter); 40 | mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 41 | @Override 42 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 43 | 44 | } 45 | 46 | @Override 47 | public void onPageSelected(int position) { 48 | mCurPos=position; 49 | Log.d("sort:", "onPageSelected: "+position); 50 | } 51 | 52 | @Override 53 | public void onPageScrollStateChanged(int state) { 54 | 55 | } 56 | }); 57 | mBtnDelete.setOnClickListener(new View.OnClickListener() { 58 | @Override 59 | public void onClick(View v) { 60 | mTestFragments.removeAt(mCurPos); 61 | mPagerAdapter.notifyDataSetChanged(); 62 | } 63 | }); 64 | 65 | mBtnAdd.setOnClickListener(new View.OnClickListener() { 66 | @Override 67 | public void onClick(View v) { 68 | mTestFragments.put(key++,TestFragment.newInstance("第"+key)); 69 | mPagerAdapter.notifyDataSetChanged(); 70 | } 71 | }); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/dqr/www/viewpagerfragmentad/PagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.dqr.www.viewpagerfragmentad; 2 | 3 | import android.support.v4.app.Fragment; 4 | import android.support.v4.app.FragmentManager; 5 | import android.support.v4.app.FragmentPagerAdapter; 6 | import android.util.Log; 7 | import android.util.SparseArray; 8 | import android.view.ViewGroup; 9 | 10 | /** 11 | * Description: 12 | * Author:LiuYM 13 | * Date: 2017-05-10 10:44 14 | */ 15 | 16 | public class PagerAdapter extends FragmentPagerAdapter { 17 | private static final String TAG = "PagerAdapter"; 18 | 19 | SparseArray mTestFragments; 20 | 21 | 22 | public PagerAdapter(FragmentManager fm, SparseArray testFragments) { 23 | super(fm); 24 | this.mTestFragments = testFragments; 25 | } 26 | 27 | @Override 28 | public Fragment getItem(int position) { 29 | Log.d(TAG, "getItem: "+position); 30 | TestFragment testFragment=mTestFragments.valueAt(position); 31 | return testFragment; 32 | } 33 | 34 | @Override 35 | public int getCount() { 36 | return mTestFragments.size(); 37 | } 38 | 39 | /** 40 | * 返回每一个item所有对应的key,在instantiateItem中 会根据这个item去查找是否已经存在这个item 41 | * 如果不存在则调用getItem创建 42 | * 存在则在FragmentManager中去find 43 | * 详情查看instantiateItem源码 44 | * @param position 45 | * @return 46 | */ 47 | @Override 48 | public long getItemId(int position) { 49 | Log.d(TAG, "position: "+position+" getItemId:"+mTestFragments.keyAt(position)); 50 | return mTestFragments.keyAt(position); 51 | } 52 | 53 | @Override 54 | public Object instantiateItem(ViewGroup container, int position) { 55 | Log.d(TAG, "instantiateItem: "+position); 56 | return super.instantiateItem(container, position); 57 | } 58 | 59 | @Override 60 | public void destroyItem(ViewGroup container, int position, Object object) { 61 | super.destroyItem(container, position, object); 62 | } 63 | 64 | @Override 65 | public int getItemPosition(Object object) { 66 | return POSITION_NONE; 67 | 68 | 69 | } 70 | 71 | @Override 72 | public void setPrimaryItem(ViewGroup container, int position, Object object) { 73 | super.setPrimaryItem(container, position, object); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/dqr/www/viewpagerfragmentad/TestFragment.java: -------------------------------------------------------------------------------- 1 | package com.dqr.www.viewpagerfragmentad; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v4.app.Fragment; 6 | import android.util.Log; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.TextView; 11 | 12 | /** 13 | * Description: 14 | * Author:LiuYM 15 | * Date: 2017-05-10 10:37 16 | */ 17 | 18 | public class TestFragment extends Fragment { 19 | 20 | private static final String TAG = "TestFragment"; 21 | private TextView mTextView; 22 | private String text; 23 | 24 | public static TestFragment newInstance(String text){ 25 | TestFragment testFragment = new TestFragment(); 26 | Bundle bundle = new Bundle(); 27 | bundle.putString("text", text); 28 | testFragment.setArguments(bundle); 29 | return testFragment; 30 | } 31 | 32 | @Override 33 | public void setUserVisibleHint(boolean isVisibleToUser) { 34 | super.setUserVisibleHint(isVisibleToUser); 35 | text=getArguments().getString("text"); 36 | Log.d(TAG+text, "setUserVisibleHint: "+isVisibleToUser); 37 | } 38 | 39 | @Override 40 | public void onCreate(@Nullable Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | Log.d(TAG+toString(), "onCreate: "+text); 43 | } 44 | 45 | @Nullable 46 | @Override 47 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 48 | View view = inflater.inflate(R.layout.test_fragment_layout, container, false); 49 | mTextView = (TextView) view.findViewById(R.id.tv_test); 50 | 51 | mTextView.setText(text); 52 | Log.d(TAG+toString(), "onCreateView: "); 53 | return view; 54 | } 55 | 56 | @Override 57 | public void onStart() { 58 | super.onStart(); 59 | Log.d(TAG+toString(), "onStart: "); 60 | } 61 | 62 | @Override 63 | public void onResume() { 64 | super.onResume(); 65 | Log.d(TAG+toString(), "onResume: "); 66 | } 67 | 68 | @Override 69 | public void onPause() { 70 | super.onPause(); 71 | Log.d(TAG+toString(), "onPause: "); 72 | } 73 | 74 | @Override 75 | public void onStop() { 76 | super.onStop(); 77 | Log.d(TAG+toString(), "onStop: "); 78 | } 79 | 80 | @Override 81 | public void onDestroyView() { 82 | super.onDestroyView(); 83 | Log.d(TAG+toString(), "onDestroyView: "); 84 | } 85 | 86 | @Override 87 | public void onDestroy() { 88 | super.onDestroy(); 89 | Log.d(TAG+toString(), "onDestroy: "); 90 | } 91 | 92 | @Override 93 | public String toString() { 94 | return text; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 |