├── BgMoveViewPager ├── .gitignore ├── .idea │ ├── .name │ ├── compiler.xml │ ├── copyright │ │ └── profiles_settings.xml │ ├── encodings.xml │ ├── gradle.xml │ ├── modules.xml │ └── runConfigurations.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── zheblog │ │ │ └── bgmoveviewpager │ │ │ └── ApplicationTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── zheblog │ │ │ │ └── bgmoveviewpager │ │ │ │ ├── MainActivity.java │ │ │ │ ├── adapter │ │ │ │ ├── HorizontalPagerAdapter.java │ │ │ │ └── VerticalPagerAdapter.java │ │ │ │ ├── fragment │ │ │ │ ├── BottomFragment.java │ │ │ │ ├── HomeFragment.java │ │ │ │ ├── LeftFragment.java │ │ │ │ ├── PagerFragment.java │ │ │ │ └── RightFragment.java │ │ │ │ ├── interfaces │ │ │ │ ├── IScrollListener.java │ │ │ │ └── IViewPagerCurrent.java │ │ │ │ └── widget │ │ │ │ ├── FixedSpeedScroller.java │ │ │ │ ├── HorizontalViewPager.java │ │ │ │ └── VerticalViewPager.java │ │ └── res │ │ │ ├── layout │ │ │ ├── activity_main.xml │ │ │ ├── fragment_bottom.xml │ │ │ ├── fragment_home.xml │ │ │ ├── fragment_left.xml │ │ │ ├── fragment_pager.xml │ │ │ └── fragment_right.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── bg_bottom.png │ │ │ ├── ic_launcher.png │ │ │ ├── icon_bottom_line.png │ │ │ ├── icon_home.png │ │ │ ├── icon_home_line_bottom.png │ │ │ ├── icon_home_line_left.png │ │ │ ├── icon_home_line_right.png │ │ │ ├── icon_left_line.png │ │ │ └── icon_right_line.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── bg_bottom.png │ │ │ ├── bg_home.png │ │ │ ├── ic_launcher.png │ │ │ ├── icon_bottom_line.png │ │ │ ├── icon_home_line_bottom.png │ │ │ ├── icon_home_line_left.png │ │ │ ├── icon_home_line_right.png │ │ │ ├── icon_left_line.png │ │ │ └── icon_right_line.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-w820dp │ │ │ └── dimens.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── zheblog │ │ └── bgmoveviewpager │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── README.md └── gif └── BgMove.gif /BgMoveViewPager/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /BgMoveViewPager/.idea/.name: -------------------------------------------------------------------------------- 1 | BgMoveViewPager -------------------------------------------------------------------------------- /BgMoveViewPager/.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 | -------------------------------------------------------------------------------- /BgMoveViewPager/.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /BgMoveViewPager/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /BgMoveViewPager/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | -------------------------------------------------------------------------------- /BgMoveViewPager/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BgMoveViewPager/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.zheblog.bgmoveviewpager" 9 | minSdkVersion 19 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 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 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.3.0' 26 | } 27 | -------------------------------------------------------------------------------- /BgMoveViewPager/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 /android_tools/android-sdk-macosx/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 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/androidTest/java/com/zheblog/bgmoveviewpager/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/java/com/zheblog/bgmoveviewpager/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.app.FragmentActivity; 6 | 7 | import com.zheblog.bgmoveviewpager.adapter.VerticalPagerAdapter; 8 | import com.zheblog.bgmoveviewpager.fragment.BottomFragment; 9 | import com.zheblog.bgmoveviewpager.fragment.PagerFragment; 10 | import com.zheblog.bgmoveviewpager.interfaces.IScrollListener; 11 | import com.zheblog.bgmoveviewpager.interfaces.IViewPagerCurrent; 12 | import com.zheblog.bgmoveviewpager.widget.VerticalViewPager; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | public class MainActivity extends FragmentActivity implements IScrollListener,IViewPagerCurrent { 18 | 19 | private VerticalViewPager verticalViewPager; 20 | private VerticalPagerAdapter mAdapter; 21 | private List mFragments = new ArrayList<>(); 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_main); 27 | initView(); 28 | } 29 | 30 | private void initView() { 31 | verticalViewPager = (VerticalViewPager) findViewById(R.id.vertical_pager); 32 | PagerFragment pagerFragment = new PagerFragment(); 33 | BottomFragment bottomFragment = new BottomFragment(); 34 | mFragments.add(pagerFragment); 35 | mFragments.add(bottomFragment); 36 | mAdapter = new VerticalPagerAdapter(getSupportFragmentManager(), mFragments); 37 | verticalViewPager.setAdapter(mAdapter); 38 | } 39 | 40 | @Override 41 | public void canScrollView(boolean isCanScroll) { 42 | verticalViewPager.setScroll(isCanScroll); 43 | } 44 | 45 | @Override 46 | public void setCurrentPager(int position) { 47 | verticalViewPager.setCurrentItem(position); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/java/com/zheblog/bgmoveviewpager/adapter/HorizontalPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager.adapter; 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.view.ViewGroup; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Created by liuz on 16/6/16. 12 | */ 13 | public class HorizontalPagerAdapter extends FragmentPagerAdapter { 14 | 15 | private List list; 16 | 17 | public HorizontalPagerAdapter(FragmentManager fm, List list) { 18 | super(fm); 19 | this.list = list; 20 | } 21 | 22 | @Override 23 | public void destroyItem(ViewGroup container, int position, Object object) { 24 | container.removeView(list.get(position).getView()); 25 | } 26 | 27 | @Override 28 | public int getCount() { 29 | return list.size(); 30 | } 31 | 32 | @Override 33 | public int getItemPosition(Object object) { 34 | return super.getItemPosition(object); 35 | } 36 | 37 | @Override 38 | public Fragment getItem(int position) { 39 | return list.get(position); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/java/com/zheblog/bgmoveviewpager/adapter/VerticalPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager.adapter; 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.view.ViewGroup; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Created by liuz on 16/6/17. 12 | */ 13 | public class VerticalPagerAdapter extends FragmentPagerAdapter{ 14 | 15 | private List list; 16 | 17 | public VerticalPagerAdapter(FragmentManager fm, List list) { 18 | super(fm); 19 | this.list = list; 20 | } 21 | 22 | @Override 23 | public void destroyItem(ViewGroup container, int position, Object object) { 24 | container.removeView(list.get(position).getView()); 25 | } 26 | 27 | @Override 28 | public int getCount() { 29 | return list.size(); 30 | } 31 | 32 | @Override 33 | public Fragment getItem(int position) { 34 | return list.get(position); 35 | } 36 | 37 | @Override 38 | public int getItemPosition(Object object) { 39 | return super.getItemPosition(object); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/java/com/zheblog/bgmoveviewpager/fragment/BottomFragment.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager.fragment; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.RelativeLayout; 11 | 12 | import com.zheblog.bgmoveviewpager.R; 13 | import com.zheblog.bgmoveviewpager.interfaces.IViewPagerCurrent; 14 | 15 | /** 16 | * Created by liuz on 16/6/17. 17 | */ 18 | public class BottomFragment extends Fragment implements View.OnClickListener { 19 | 20 | private View contentView; 21 | private ImageView btnTop; 22 | 23 | private IViewPagerCurrent mViewPagerListener; 24 | 25 | @Override 26 | public void onAttach(Activity activity) { 27 | super.onAttach(activity); 28 | mViewPagerListener = (IViewPagerCurrent) activity; 29 | } 30 | 31 | @Override 32 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 33 | Bundle savedInstanceState) { 34 | contentView = inflater.inflate(R.layout.fragment_bottom, null); 35 | return contentView; 36 | } 37 | 38 | @Override 39 | public void onViewCreated(View view, Bundle savedInstanceState) { 40 | super.onViewCreated(view, savedInstanceState); 41 | initView(); 42 | } 43 | 44 | private void initView(){ 45 | btnTop = (ImageView) contentView.findViewById(R.id.btn_bottom_top); 46 | btnTop.setOnClickListener(this); 47 | } 48 | 49 | @Override 50 | public void onClick(View v) { 51 | mViewPagerListener.setCurrentPager(0); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/java/com/zheblog/bgmoveviewpager/fragment/HomeFragment.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager.fragment; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.LinearLayout; 11 | 12 | import com.zheblog.bgmoveviewpager.R; 13 | import com.zheblog.bgmoveviewpager.interfaces.IViewPagerCurrent; 14 | 15 | /** 16 | * Created by liuz on 16/6/17. 17 | */ 18 | public class HomeFragment extends Fragment { 19 | 20 | private View contentView; 21 | 22 | private ImageView btnLeft, btnRight, btnBottom; 23 | 24 | private View.OnClickListener mListener; 25 | 26 | @Override 27 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 28 | Bundle savedInstanceState) { 29 | contentView = inflater.inflate(R.layout.fragment_home, null); 30 | return contentView; 31 | } 32 | 33 | @Override 34 | public void onViewCreated(View view, Bundle savedInstanceState) { 35 | super.onViewCreated(view, savedInstanceState); 36 | initView(); 37 | } 38 | 39 | private void initView() { 40 | btnLeft = (ImageView) contentView.findViewById(R.id.btn_left); 41 | btnLeft.setOnClickListener(mListener); 42 | btnRight = (ImageView) contentView.findViewById(R.id.btn_right); 43 | btnRight.setOnClickListener(mListener); 44 | btnBottom = (ImageView) contentView.findViewById(R.id.btn_bottom); 45 | btnBottom.setOnClickListener(mListener); 46 | } 47 | 48 | public void setPagerOnClickListener(View.OnClickListener listener) { 49 | mListener = listener; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/java/com/zheblog/bgmoveviewpager/fragment/LeftFragment.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ImageView; 9 | 10 | import com.zheblog.bgmoveviewpager.R; 11 | 12 | /** 13 | * Created by liuz on 16/6/17. 14 | */ 15 | public class LeftFragment extends Fragment { 16 | 17 | private View contentView; 18 | private ImageView btnRight; 19 | private View.OnClickListener mListener; 20 | 21 | @Override 22 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 23 | Bundle savedInstanceState) { 24 | contentView = inflater.inflate(R.layout.fragment_left, null); 25 | return contentView; 26 | } 27 | 28 | @Override 29 | public void onViewCreated(View view, Bundle savedInstanceState) { 30 | super.onViewCreated(view, savedInstanceState); 31 | initView(); 32 | } 33 | 34 | private void initView(){ 35 | btnRight = (ImageView) contentView.findViewById(R.id.btn_left_right); 36 | btnRight.setOnClickListener(mListener); 37 | } 38 | 39 | public void setPagerOnClickListener(View.OnClickListener listener) { 40 | mListener = listener; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/java/com/zheblog/bgmoveviewpager/fragment/PagerFragment.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager.fragment; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.BitmapFactory; 6 | import android.os.Bundle; 7 | import android.support.v4.app.Fragment; 8 | import android.support.v4.view.ViewPager; 9 | import android.telephony.TelephonyManager; 10 | import android.util.Log; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | 15 | import com.zheblog.bgmoveviewpager.R; 16 | import com.zheblog.bgmoveviewpager.adapter.HorizontalPagerAdapter; 17 | import com.zheblog.bgmoveviewpager.interfaces.IScrollListener; 18 | import com.zheblog.bgmoveviewpager.interfaces.IViewPagerCurrent; 19 | import com.zheblog.bgmoveviewpager.widget.FixedSpeedScroller; 20 | import com.zheblog.bgmoveviewpager.widget.HorizontalViewPager; 21 | 22 | import java.lang.reflect.Field; 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | /** 27 | * Created by liuz on 16/6/17. 28 | */ 29 | public class PagerFragment extends Fragment implements ViewPager.OnPageChangeListener, View.OnClickListener { 30 | 31 | /** 32 | * 左侧fragment 33 | */ 34 | private final int INDEX_LEFT = 0; 35 | /** 36 | * 主页面fragment 37 | */ 38 | private final int INDEX_HOME = 1; 39 | /** 40 | * 右侧fragment 41 | */ 42 | private final int INDEX_RIGHT = 2; 43 | 44 | private View contentView; 45 | private HorizontalViewPager horizontalViewPager; 46 | private HorizontalPagerAdapter mAdapter; 47 | private IScrollListener mScrollListener; 48 | private IViewPagerCurrent mViewPagerListener; 49 | private List fragmentList = new ArrayList<>(); 50 | 51 | @Override 52 | public void onAttach(Activity activity) { 53 | super.onAttach(activity); 54 | mScrollListener = (IScrollListener) activity; 55 | mViewPagerListener = (IViewPagerCurrent) activity; 56 | } 57 | 58 | @Override 59 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 60 | Bundle savedInstanceState) { 61 | contentView = inflater.inflate(R.layout.fragment_pager, null); 62 | return contentView; 63 | } 64 | 65 | @Override 66 | public void onViewCreated(View view, Bundle savedInstanceState) { 67 | super.onViewCreated(view, savedInstanceState); 68 | horizontalViewPager = (HorizontalViewPager) contentView.findViewById(R.id.horizontal_pager); 69 | horizontalViewPager.setBackGround(BitmapFactory.decodeResource(getResources(), R.mipmap.bg_home)); 70 | initViewPagerSpeed(); 71 | LeftFragment leftFragment = new LeftFragment(); 72 | RightFragment rightFragment = new RightFragment(); 73 | HomeFragment homeFragment = new HomeFragment(); 74 | fragmentList.add(leftFragment); 75 | fragmentList.add(homeFragment); 76 | fragmentList.add(rightFragment); 77 | leftFragment.setPagerOnClickListener(this); 78 | rightFragment.setPagerOnClickListener(this); 79 | homeFragment.setPagerOnClickListener(this); 80 | mAdapter = new HorizontalPagerAdapter(getFragmentManager(), fragmentList); 81 | horizontalViewPager.setOffscreenPageLimit(fragmentList.size() - 1); 82 | horizontalViewPager.setAdapter(mAdapter); 83 | horizontalViewPager.setCurrentItem(INDEX_HOME); 84 | horizontalViewPager.setOnPageChangeListener(this); 85 | mScrollListener.canScrollView(true); 86 | } 87 | 88 | @Override 89 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 90 | 91 | } 92 | 93 | @Override 94 | public void onPageSelected(int position) { 95 | if (position != 1) { 96 | mScrollListener.canScrollView(false); 97 | } else { 98 | mScrollListener.canScrollView(true); 99 | } 100 | } 101 | 102 | @Override 103 | public void onPageScrollStateChanged(int state) { 104 | 105 | } 106 | 107 | /** 108 | * 设置ViewPager的滚动速度 109 | */ 110 | private void initViewPagerSpeed() { 111 | try { 112 | Field mScroller = ViewPager.class.getDeclaredField("mScroller"); 113 | mScroller.setAccessible(true); 114 | FixedSpeedScroller scroller = new FixedSpeedScroller(horizontalViewPager.getContext()); 115 | mScroller.set(horizontalViewPager, scroller); 116 | } catch (NoSuchFieldException e) { 117 | 118 | } catch (IllegalArgumentException e) { 119 | 120 | } catch (IllegalAccessException e) { 121 | 122 | } 123 | } 124 | 125 | @Override 126 | public void onClick(View v) { 127 | switch (v.getId()) { 128 | case R.id.btn_left: { 129 | horizontalViewPager.setCurrentItem(INDEX_LEFT); 130 | } 131 | break; 132 | case R.id.btn_right: { 133 | horizontalViewPager.setCurrentItem(INDEX_RIGHT); 134 | } 135 | break; 136 | case R.id.btn_bottom: { 137 | mViewPagerListener.setCurrentPager(INDEX_HOME); 138 | } 139 | break; 140 | case R.id.btn_left_right: { 141 | horizontalViewPager.setCurrentItem(INDEX_HOME); 142 | } 143 | break; 144 | case R.id.btn_right_left: { 145 | horizontalViewPager.setCurrentItem(INDEX_HOME); 146 | } 147 | break; 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/java/com/zheblog/bgmoveviewpager/fragment/RightFragment.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ImageView; 9 | 10 | import com.zheblog.bgmoveviewpager.R; 11 | 12 | /** 13 | * Created by liuz on 16/6/17. 14 | */ 15 | public class RightFragment extends Fragment { 16 | 17 | private View contentView; 18 | private ImageView btnLeft; 19 | 20 | private View.OnClickListener mListener; 21 | 22 | @Override 23 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 24 | Bundle savedInstanceState) { 25 | contentView = inflater.inflate(R.layout.fragment_right, null); 26 | return contentView; 27 | } 28 | 29 | @Override 30 | public void onViewCreated(View view, Bundle savedInstanceState) { 31 | super.onViewCreated(view, savedInstanceState); 32 | initView(); 33 | } 34 | 35 | private void initView(){ 36 | btnLeft = (ImageView) contentView.findViewById(R.id.btn_right_left); 37 | btnLeft.setOnClickListener(mListener); 38 | } 39 | 40 | public void setPagerOnClickListener(View.OnClickListener listener) { 41 | mListener = listener; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/java/com/zheblog/bgmoveviewpager/interfaces/IScrollListener.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager.interfaces; 2 | 3 | /** 4 | * Created by liuz on 16/6/17. 5 | */ 6 | public interface IScrollListener { 7 | 8 | void canScrollView(boolean isCanScroll); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/java/com/zheblog/bgmoveviewpager/interfaces/IViewPagerCurrent.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager.interfaces; 2 | 3 | /** 4 | * Created by liuz on 16/6/17. 5 | */ 6 | public interface IViewPagerCurrent { 7 | 8 | void setCurrentPager(int position); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/java/com/zheblog/bgmoveviewpager/widget/FixedSpeedScroller.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager.widget; 2 | 3 | import android.content.Context; 4 | import android.view.animation.Interpolator; 5 | import android.widget.Scroller; 6 | 7 | /** 8 | * Created by liuz on 16/6/17. 9 | * 控制viewpager滑动速度 10 | */ 11 | public class FixedSpeedScroller extends Scroller { 12 | private int mDuration = 800; // 默认为800ms 13 | 14 | public FixedSpeedScroller(Context context) { 15 | super(context); 16 | } 17 | 18 | public FixedSpeedScroller(Context context, Interpolator interpolator) { 19 | super(context, interpolator); 20 | } 21 | 22 | @Override 23 | public void startScroll(int startX, int startY, int dx, int dy, int duration) { 24 | super.startScroll(startX, startY, dx, dy, mDuration); 25 | } 26 | 27 | @Override 28 | public void startScroll(int startX, int startY, int dx, int dy) { 29 | super.startScroll(startX, startY, dx, dy, mDuration); 30 | } 31 | 32 | /** 33 | * 设置滑动时间 34 | * 35 | * @param time 36 | */ 37 | public void setmDuration(int time) { 38 | mDuration = time; 39 | } 40 | 41 | /** 42 | * 获得滑动时间 43 | * 44 | * @return 45 | */ 46 | public int getmDuration() { 47 | return mDuration; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/java/com/zheblog/bgmoveviewpager/widget/HorizontalViewPager.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.graphics.Rect; 8 | import android.support.v4.view.ViewPager; 9 | import android.util.AttributeSet; 10 | 11 | /** 12 | * 横向背景移动的ViewPager 13 | * Created by liuz on 16/6/16. 14 | */ 15 | public class HorizontalViewPager extends ViewPager { 16 | 17 | public HorizontalViewPager(Context context) { 18 | super(context); 19 | } 20 | 21 | public HorizontalViewPager(Context context, AttributeSet attrs) { 22 | super(context, attrs); 23 | } 24 | 25 | private Bitmap bg; 26 | private Paint b = new Paint(Paint.ANTI_ALIAS_FLAG); 27 | 28 | public void setBackGround(Bitmap paramBitmap) { 29 | this.bg = paramBitmap; 30 | this.b.setFilterBitmap(true); 31 | } 32 | 33 | @Override 34 | protected void dispatchDraw(Canvas canvas) { 35 | if (this.bg != null) { 36 | int width = this.bg.getWidth(); 37 | int height = this.bg.getHeight(); 38 | int count = getAdapter().getCount(); 39 | int x = getScrollX(); 40 | //子View中背景图片需要显示的宽度,放大背景图或缩小背景图。 41 | int n = height * getWidth() / getHeight(); 42 | //(width - n) / (count - 1)表示除去显示第一个ViewPager页面用去的背景宽度,剩余的ViewPager需要显示的背景图片的宽度。 43 | //getWidth()等于ViewPager一个页面的宽度,即手机屏幕宽度。在该计算中可以理解为滑动一个ViewPager页面需要滑动的像素值。 44 | //((width - n) / (count - 1)) / getWidth()也就表示ViewPager滑动一个像素时,背景图片滑动的宽度。 45 | //x * ((width - n) / (count - 1)) / getWidth()也就表示ViewPager滑动x个像素时,背景图片滑动的宽度。 46 | //背景图片滑动的宽度的宽度可以理解为背景图片滑动到达的位置。 47 | int w = (x+getWidth()) * ((width - n) / (count - 1)) / getWidth(); 48 | 49 | canvas.drawBitmap(this.bg, new Rect(w, 0, n + w, height), new Rect(x, 0, x + getWidth(), getHeight()), this.b); 50 | } 51 | super.dispatchDraw(canvas); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/java/com/zheblog/bgmoveviewpager/widget/VerticalViewPager.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.database.DataSetObserver; 6 | import android.graphics.Canvas; 7 | import android.graphics.Rect; 8 | import android.graphics.drawable.Drawable; 9 | import android.os.Build; 10 | import android.os.Parcel; 11 | import android.os.Parcelable; 12 | import android.os.SystemClock; 13 | import android.support.v4.os.ParcelableCompat; 14 | import android.support.v4.os.ParcelableCompatCreatorCallbacks; 15 | import android.support.v4.view.KeyEventCompat; 16 | import android.support.v4.view.MotionEventCompat; 17 | import android.support.v4.view.VelocityTrackerCompat; 18 | import android.support.v4.view.ViewCompat; 19 | import android.support.v4.view.ViewConfigurationCompat; 20 | import android.support.v4.widget.EdgeEffectCompat; 21 | import android.util.AttributeSet; 22 | import android.util.Log; 23 | import android.view.FocusFinder; 24 | import android.view.Gravity; 25 | import android.view.KeyEvent; 26 | import android.view.MotionEvent; 27 | import android.view.SoundEffectConstants; 28 | import android.view.VelocityTracker; 29 | import android.view.View; 30 | import android.view.ViewConfiguration; 31 | import android.view.ViewGroup; 32 | import android.view.ViewParent; 33 | import android.view.accessibility.AccessibilityEvent; 34 | import android.view.animation.Interpolator; 35 | import android.widget.Scroller; 36 | 37 | import com.zheblog.bgmoveviewpager.adapter.VerticalPagerAdapter; 38 | 39 | import java.lang.reflect.Method; 40 | import java.util.ArrayList; 41 | import java.util.Collections; 42 | import java.util.Comparator; 43 | 44 | /** 45 | * 垂直滑动的ViewPager 46 | * Created by liuz on 16/6/16. 47 | */ 48 | public class VerticalViewPager extends ViewGroup { 49 | 50 | private static final String TAG = "VerticalViewPager"; 51 | 52 | private static final boolean DEBUG = false; 53 | 54 | private static final boolean USE_CACHE = false; 55 | 56 | /** 57 | * ViewPager 58 | */ 59 | private static final int DEFAULT_OFFSCREEN_PAGES = 1; 60 | 61 | /** 62 | * 63 | */ 64 | private static final int MAX_SETTLE_DURATION = 600; // ms 65 | 66 | /** 67 | * 68 | */ 69 | private static final int MIN_DISTANCE_FOR_FLING = 25; // dips 70 | 71 | /** 72 | * layout attributes 73 | */ 74 | private static final int[] LAYOUT_ATTRS = new int[]{android.R.attr.layout_gravity}; 75 | 76 | /** 77 | * 78 | * 79 | */ 80 | static class ItemInfo { 81 | /** 82 | * item object 83 | */ 84 | Object object; 85 | 86 | /** 87 | *  88 | */ 89 | int position; 90 | 91 | /** 92 | * crolling 93 | */ 94 | boolean scrolling; 95 | } 96 | 97 | /** 98 | * ItemInfo temInfos 99 | */ 100 | private static final Comparator COMPARATOR = new Comparator() { 101 | @Override 102 | public int compare(ItemInfo lhs, ItemInfo rhs) { 103 | return lhs.position - rhs.position; 104 | } 105 | }; 106 | 107 | // XXX 108 | /** 109 | * Scroller 110 | */ 111 | private static final Interpolator sInterpolator = new Interpolator() { 112 | public float getInterpolation(float t) { 113 | t -= 1.0f; 114 | return t * t * t * t * t + 1.0f; 115 | } 116 | }; 117 | 118 | /** 119 | * View Page 120 | */ 121 | private final ArrayList mItems = new ArrayList(); 122 | 123 | /** 124 | * Vertical Pager Adapter 125 | */ 126 | private VerticalPagerAdapter mAdapter; 127 | 128 | /** 129 | * Index of currently displayed page. 130 | */ 131 | private int mCurItem; 132 | 133 | /** 134 | * Index of the displayed page that need to restore. 135 | */ 136 | private int mRestoredCurItem = -1; 137 | 138 | /** 139 | * Restored Adapter State 140 | */ 141 | private Parcelable mRestoredAdapterState = null; 142 | 143 | /** 144 | * Restored Class Loader 145 | */ 146 | private ClassLoader mRestoredClassLoader = null; 147 | 148 | /** 149 | * Scroller 150 | */ 151 | private Scroller mScroller; 152 | 153 | /** 154 | * PagerObserver 155 | */ 156 | private PagerObserver mObserver; 157 | 158 | /** 159 | * 160 | */ 161 | private int mPageMargin; 162 | 163 | /** 164 | * Margin Drawable object 165 | */ 166 | private Drawable mMarginDrawable; 167 | 168 | // XXX bounds 169 | /** 170 | * Left Page Bound 171 | */ 172 | private int mLeftPageBounds; 173 | 174 | /** 175 | * Right Page Bound 176 | */ 177 | private int mRightPageBounds; 178 | 179 | /** 180 | * Child Width Measure Spec 181 | */ 182 | private int mChildWidthMeasureSpec; 183 | 184 | /** 185 | * Child Height Measure Spec 186 | */ 187 | private int mChildHeightMeasureSpec; 188 | 189 | /** 190 | * onlayout true = yes , false = no 191 | */ 192 | private boolean mInLayout; 193 | 194 | /** 195 | * Scrolling Cache 196 | */ 197 | private boolean mScrollingCacheEnabled; 198 | 199 | /** 200 | * Populate Pending 201 | */ 202 | private boolean mPopulatePending; 203 | 204 | /** 205 | * Scrolling 206 | */ 207 | private boolean mScrolling; 208 | 209 | /** 210 | * Off screen page limit 211 | */ 212 | private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES; 213 | 214 | /** 215 | * is being dragged 216 | */ 217 | private boolean mIsBeingDragged; 218 | 219 | /** 220 | * is unabled to drag 221 | */ 222 | private boolean mIsUnableToDrag; 223 | 224 | /** 225 | * Touch 226 | */ 227 | private int mTouchSlop; 228 | 229 | // / XXX 230 | private float mInitialMotionY; 231 | 232 | /** 233 | * Position of the last motion event. 234 | */ 235 | private float mLastMotionX; 236 | private float mLastMotionY; 237 | 238 | /** 239 | * ID of the active pointer. This is used to retain consistency during drags/flings if multiple 240 | * pointers are used. 241 | */ 242 | private int mActivePointerId = INVALID_POINTER; 243 | /** 244 | * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}. 245 | */ 246 | private static final int INVALID_POINTER = -1; 247 | 248 | /** 249 | * Determines speed during touch scrolling 250 | */ 251 | private VelocityTracker mVelocityTracker; 252 | private int mMinimumVelocity; 253 | private int mMaximumVelocity; 254 | private int mFlingDistance; 255 | 256 | private boolean mFakeDragging; 257 | private long mFakeDragBeginTime; 258 | 259 | // XXX 260 | private EdgeEffectCompat mTopEdge; 261 | private EdgeEffectCompat mBottomEdge; 262 | 263 | private boolean mFirstLayout = true; 264 | private boolean mCalledSuper; 265 | private int mDecorChildCount; 266 | 267 | private OnPageChangeListener mOnPageChangeListener; 268 | private OnPageChangeListener mInternalPageChangeListener; 269 | private OnAdapterChangeListener mAdapterChangeListener; 270 | 271 | /** 272 | * Indicates that the pager is in an idle, settled state. The current page is fully in view and 273 | * no animation is in progress. 274 | */ 275 | public static final int SCROLL_STATE_IDLE = 0; 276 | 277 | /** 278 | * Indicates that the pager is currently being dragged by the user. 279 | */ 280 | public static final int SCROLL_STATE_DRAGGING = 1; 281 | 282 | /** 283 | * Indicates that the pager is in the process of settling to a final position. 284 | */ 285 | public static final int SCROLL_STATE_SETTLING = 2; 286 | 287 | private int mScrollState = SCROLL_STATE_IDLE; 288 | 289 | /** 290 | * Callback interface for responding to changing state of the selected page. 291 | */ 292 | public interface OnPageChangeListener { 293 | 294 | /** 295 | * This method will be invoked when the current page is scrolled, either as part of a 296 | * programmatically initiated smooth scroll or a user initiated touch scroll. 297 | * 298 | * @param position Position index of the first page currently being displayed. Page 299 | * position+1 will be visible if positionOffset is nonzero. 300 | * @param positionOffset Value from [0, 1) indicating the offset from the page at position. 301 | * @param positionOffsetPixels Value in pixels indicating the offset from position. 302 | */ 303 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels); 304 | 305 | /** 306 | * This method will be invoked when a new page becomes selected. Animation is not 307 | * necessarily complete. 308 | * 309 | * @param position Position index of the new selected page. 310 | */ 311 | public void onPageSelected(int position); 312 | 313 | /** 314 | * Called when the scroll state changes. Useful for discovering when the user begins 315 | * dragging, when the pager is automatically settling to the current page, or when it is 316 | * fully stopped/idle. 317 | * 318 | * @param state The new scroll state. 319 | * @see VerticalViewPager#SCROLL_STATE_IDLE 320 | * @see VerticalViewPager#SCROLL_STATE_DRAGGING 321 | * @see VerticalViewPager#SCROLL_STATE_SETTLING 322 | */ 323 | public void onPageScrollStateChanged(int state); 324 | } 325 | 326 | private boolean isScroll; 327 | 328 | public void setScroll(boolean scroll) { 329 | isScroll = scroll; 330 | } 331 | 332 | /** 333 | * Simple implementation of the {@link OnPageChangeListener} interface with stub implementations 334 | * of each method. Extend this if you do not intend to override every method of 335 | * {@link OnPageChangeListener}. 336 | */ 337 | public static class SimpleOnPageChangeListener implements OnPageChangeListener { 338 | @Override 339 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 340 | // This space for rent 341 | } 342 | 343 | @Override 344 | public void onPageSelected(int position) { 345 | // This space for rent 346 | } 347 | 348 | @Override 349 | public void onPageScrollStateChanged(int state) { 350 | // This space for rent 351 | } 352 | } 353 | 354 | /** 355 | * Used internally to monitor when adapters are switched. 356 | */ 357 | interface OnAdapterChangeListener { 358 | void onAdapterChanged(VerticalPagerAdapter oldAdapter, 359 | VerticalPagerAdapter newAdapter); 360 | } 361 | 362 | /** 363 | * Used internally to tag special types of child views that should be added as pager decorations 364 | * by default. 365 | */ 366 | interface Decor { 367 | } 368 | 369 | public VerticalViewPager(Context context) { 370 | super(context); 371 | initViewPager(); 372 | } 373 | 374 | public VerticalViewPager(Context context, AttributeSet attrs) { 375 | super(context, attrs); 376 | initViewPager(); 377 | } 378 | 379 | /** 380 | * 381 | */ 382 | void initViewPager() { 383 | setWillNotDraw(false); 384 | setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); 385 | setFocusable(true); 386 | final Context context = getContext(); 387 | mScroller = new Scroller(context, sInterpolator); 388 | final ViewConfiguration configuration = ViewConfiguration.get(context); 389 | mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); 390 | mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); 391 | mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 392 | // 393 | mTopEdge = new EdgeEffectCompat(context); 394 | mBottomEdge = new EdgeEffectCompat(context); 395 | 396 | final float density = context.getResources().getDisplayMetrics().density; 397 | mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density); 398 | } 399 | 400 | /** 401 | * Scroll State 402 | * 403 | * @param newState new state 404 | */ 405 | private void setScrollState(int newState) { 406 | if (mScrollState == newState) { 407 | return; 408 | } /* end of if */ 409 | 410 | mScrollState = newState; 411 | if (mOnPageChangeListener != null) { 412 | mOnPageChangeListener.onPageScrollStateChanged(newState); 413 | } /* end of if */ 414 | } 415 | 416 | /** 417 | * Set a PagerAdapter that will supply views for this pager as needed. 418 | * 419 | * @param adapter Adapter to use 420 | */ 421 | public void setAdapter(VerticalPagerAdapter adapter) { 422 | if (mAdapter != null) { 423 | //mAdapter.unregisterDataSetObserver(mObserver); 424 | mAdapter.startUpdate(this); 425 | for (int i = 0; i < mItems.size(); i++) { 426 | final ItemInfo ii = mItems.get(i); 427 | mAdapter.destroyItem(this, ii.position, ii.object); 428 | } /* end of for */ 429 | mAdapter.finishUpdate(this); 430 | mItems.clear(); 431 | removeNonDecorViews(); 432 | mCurItem = 0; 433 | scrollTo(0, 0); 434 | } /* end of if */ 435 | 436 | final VerticalPagerAdapter oldAdapter = mAdapter; 437 | mAdapter = adapter; 438 | 439 | if (mAdapter != null) { 440 | if (mObserver == null) { 441 | mObserver = new PagerObserver(); 442 | } /* end of if以下是添加反射代码 */ 443 | try { 444 | Method regMethod = mAdapter.getClass().getMethod("registerDataSetObserver", mObserver.getClass()); 445 | regMethod.invoke(mAdapter, mObserver); 446 | } catch (Exception e) { 447 | e.printStackTrace(); 448 | } 449 | //mAdapter.registerDataSetObserver(mObserver); 450 | mPopulatePending = false; 451 | if (mRestoredCurItem >= 0) { 452 | mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader); 453 | setCurrentItemInternal(mRestoredCurItem, false, true); 454 | mRestoredCurItem = -1; 455 | mRestoredAdapterState = null; 456 | mRestoredClassLoader = null; 457 | } else { 458 | populate(); 459 | } /* end of if */ 460 | } /* end of if */ 461 | 462 | if (mAdapterChangeListener != null && oldAdapter != adapter) { 463 | mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter); 464 | } /* end of if */ 465 | } 466 | 467 | /** 468 | * remove non decor view 469 | */ 470 | private void removeNonDecorViews() { 471 | for (int i = 0; i < getChildCount(); i++) { 472 | final View child = getChildAt(i); 473 | final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 474 | if (!lp.isDecor) { 475 | removeViewAt(i); 476 | i--; 477 | } /* end of if */ 478 | } /* end of for */ 479 | } 480 | 481 | /** 482 | * Retrieve the current adapter supplying pages. 483 | * 484 | * @return The currently registered PagerAdapter 485 | */ 486 | public VerticalPagerAdapter getAdapter() { 487 | return mAdapter; 488 | } 489 | 490 | /** 491 | * Set On Adapter Changer Listener 492 | * 493 | * @param listener listener 494 | */ 495 | void setOnAdapterChangeListener(OnAdapterChangeListener listener) { 496 | mAdapterChangeListener = listener; 497 | } 498 | 499 | /** 500 | * Set the currently selected page. If the ViewPager has already been through its first 501 | * layout there will be a smooth animated transition between the current item and the 502 | * specified item. 503 | * 504 | * @param item Item index to select 505 | */ 506 | public void setCurrentItem(int item) { 507 | mPopulatePending = false; 508 | setCurrentItemInternal(item, !mFirstLayout, false); 509 | } 510 | 511 | /** 512 | * Set the currently selected page. 513 | * 514 | * @param item Item index to select 515 | * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately 516 | */ 517 | public void setCurrentItem(int item, boolean smoothScroll) { 518 | mPopulatePending = false; 519 | setCurrentItemInternal(item, smoothScroll, false); 520 | } 521 | 522 | /** 523 | * @return curretnt item index 524 | */ 525 | public int getCurrentItem() { 526 | return mCurItem; 527 | } 528 | 529 | /** 530 | * Set current item internal 531 | * 532 | * @param item item index 533 | * @param smoothScroll is smooth scroll 534 | * @param always is always 535 | */ 536 | void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) { 537 | //设置切换速度,默认为800ms 538 | setCurrentItemInternal(item, smoothScroll, always, 800); 539 | } 540 | 541 | /** 542 | * Set current item internal 543 | * 544 | * @param item item index 545 | * @param smoothScroll is smooth scroll 546 | * @param always is always 547 | * @param velocity 548 | */ 549 | void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) { 550 | if (mAdapter == null || mAdapter.getCount() <= 0) { 551 | setScrollingCacheEnabled(false); 552 | return; 553 | } /* end of if */ 554 | if (!always && mCurItem == item && mItems.size() != 0) { 555 | setScrollingCacheEnabled(false); 556 | return; 557 | } /* end of if */ 558 | if (item < 0) { 559 | item = 0; // 560 | } else if (item >= mAdapter.getCount()) { 561 | item = mAdapter.getCount() - 1; // 562 | } /* end of if */ 563 | final int pageLimit = mOffscreenPageLimit; 564 | if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) { 565 | // We are doing a jump by more than one page. To avoid 566 | // glitches, we want to keep all current pages in the view 567 | // until the scroll ends. 568 | for (int i = 0; i < mItems.size(); i++) { 569 | mItems.get(i).scrolling = true; 570 | } /* end of for */ 571 | } /* end of if */ 572 | final boolean dispatchSelected = mCurItem != item; 573 | mCurItem = item; 574 | populate(); 575 | final int destY = (getHeight() + mPageMargin) * item; 576 | if (smoothScroll) { 577 | smoothScrollTo(0, destY, velocity); 578 | if (dispatchSelected && mOnPageChangeListener != null) { 579 | mOnPageChangeListener.onPageSelected(item); 580 | } /* end of if */ 581 | if (dispatchSelected && mInternalPageChangeListener != null) { 582 | mInternalPageChangeListener.onPageSelected(item); 583 | } /* end of if */ 584 | } else { 585 | if (dispatchSelected && mOnPageChangeListener != null) { 586 | mOnPageChangeListener.onPageSelected(item); 587 | } /* end of if */ 588 | if (dispatchSelected && mInternalPageChangeListener != null) { 589 | mInternalPageChangeListener.onPageSelected(item); 590 | } /* end of if */ 591 | completeScroll(); 592 | scrollTo(0, destY); 593 | } /* end of if */ 594 | } 595 | 596 | 597 | /** 598 | * Set a listener that will be invoked whenever the page changes or is incrementally 599 | * scrolled. See {@link OnPageChangeListener}. 600 | * 601 | * @param listener Listener to set 602 | */ 603 | public void setOnPageChangeListener(OnPageChangeListener listener) { 604 | mOnPageChangeListener = listener; 605 | } 606 | 607 | /** 608 | * Set a separate OnPageChangeListener for internal use by the support library. 609 | * 610 | * @param listener Listener to set 611 | * @return The old listener that was set, if any. 612 | */ 613 | OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener listener) { 614 | OnPageChangeListener oldListener = mInternalPageChangeListener; 615 | mInternalPageChangeListener = listener; 616 | return oldListener; 617 | } 618 | 619 | /** 620 | * Returns the number of pages that will be retained to either side of the 621 | * current page in the view hierarchy in an idle state. Defaults to 1. 622 | * 623 | * @return How many pages will be kept offscreen on either side 624 | * @see #setOffscreenPageLimit(int) 625 | */ 626 | public int getOffscreenPageLimit() { 627 | return mOffscreenPageLimit; 628 | } 629 | 630 | /** 631 | * Set the number of pages that should be retained to either side of the 632 | * current page in the view hierarchy in an idle state. Pages beyond this 633 | * limit will be recreated from the adapter when needed. 634 | *

635 | *

This is offered as an optimization. If you know in advance the number 636 | * of pages you will need to support or have lazy-loading mechanisms in place 637 | * on your pages, tweaking this setting can have benefits in perceived smoothness 638 | * of paging animations and interaction. If you have a small number of pages (3-4) 639 | * that you can keep active all at once, less time will be spent in layout for 640 | * newly created view subtrees as the user pages back and forth.

641 | *

642 | *

You should keep this limit low, especially if your pages have complex layouts. 643 | * This setting defaults to 1.

644 | * 645 | * @param limit How many pages will be kept offscreen in an idle state. 646 | */ 647 | public void setOffscreenPageLimit(int limit) { 648 | if (limit < DEFAULT_OFFSCREEN_PAGES) { 649 | Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + 650 | DEFAULT_OFFSCREEN_PAGES); 651 | limit = DEFAULT_OFFSCREEN_PAGES; 652 | } /* end of if */ 653 | if (limit != mOffscreenPageLimit) { 654 | mOffscreenPageLimit = limit; 655 | populate(); 656 | } /* end of if */ 657 | } 658 | 659 | //XXX 660 | 661 | /** 662 | * Set the margin between pages. 663 | * 664 | * @param marginPixels Distance between adjacent pages in pixels 665 | * @see #getPageMargin() 666 | * @see #setPageMarginDrawable(Drawable) 667 | * @see #setPageMarginDrawable(int) 668 | */ 669 | public void setPageMargin(int marginPixels) { 670 | final int oldMargin = mPageMargin; 671 | mPageMargin = marginPixels; 672 | 673 | final int height = getHeight(); 674 | recomputeScrollPosition(height, height, marginPixels, oldMargin); 675 | 676 | requestLayout(); 677 | } 678 | 679 | /** 680 | * Return the margin between pages. 681 | * 682 | * @return The size of the margin in pixels 683 | */ 684 | public int getPageMargin() { 685 | return mPageMargin; 686 | } 687 | 688 | /** 689 | * Set a drawable that will be used to fill the margin between pages. 690 | * 691 | * @param d Drawable to display between pages 692 | */ 693 | public void setPageMarginDrawable(Drawable d) { 694 | mMarginDrawable = d; 695 | if (d != null) refreshDrawableState(); 696 | setWillNotDraw(d == null); 697 | invalidate(); 698 | } 699 | 700 | /** 701 | * Set a drawable that will be used to fill the margin between pages. 702 | * 703 | * @param resId Resource ID of a drawable to display between pages 704 | */ 705 | public void setPageMarginDrawable(int resId) { 706 | setPageMarginDrawable(getContext().getResources().getDrawable(resId)); 707 | } 708 | 709 | @Override 710 | protected boolean verifyDrawable(Drawable who) { 711 | return super.verifyDrawable(who) || who == mMarginDrawable; 712 | } 713 | 714 | @Override 715 | protected void drawableStateChanged() { 716 | super.drawableStateChanged(); 717 | final Drawable d = mMarginDrawable; 718 | if (d != null && d.isStateful()) { 719 | d.setState(getDrawableState()); 720 | } /* end of if */ 721 | } 722 | 723 | // We want the duration of the page snap animation to be influenced by the distance that 724 | // the screen has to travel, however, we don't want this duration to be effected in a 725 | // purely linear fashion. Instead, we use this method to moderate the effect that the distance 726 | // of travel has on the overall snap duration. 727 | float distanceInfluenceForSnapDuration(float f) { 728 | f -= 0.5f; // center the values about 0. 729 | f *= 0.3f * Math.PI / 2.0f; 730 | return (float) Math.sin(f); 731 | } 732 | 733 | /** 734 | * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. 735 | * 736 | * @param x the number of pixels to scroll by on the X axis 737 | * @param y the number of pixels to scroll by on the Y axis 738 | */ 739 | void smoothScrollTo(int x, int y) { 740 | smoothScrollTo(x, y, 0); 741 | } 742 | 743 | //XXX 744 | 745 | /** 746 | * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. 747 | * 748 | * @param x the number of pixels to scroll by on the X axis 749 | * @param y the number of pixels to scroll by on the Y axis 750 | * @param velocity the velocity associated with a fling, if applicable. (0 otherwise) 751 | */ 752 | void smoothScrollTo(int x, int y, int velocity) { 753 | if (getChildCount() == 0) { 754 | // Nothing to do. 755 | setScrollingCacheEnabled(false); 756 | return; 757 | } /* end of if */ 758 | int sx = getScrollX(); 759 | int sy = getScrollY(); 760 | int dx = x - sx; //distance of x 761 | int dy = y - sy; //distance of y 762 | if (dx == 0 && dy == 0) { 763 | completeScroll(); 764 | setScrollState(SCROLL_STATE_IDLE); 765 | return; 766 | } /* end of if */ 767 | 768 | setScrollingCacheEnabled(true); 769 | mScrolling = true; 770 | setScrollState(SCROLL_STATE_SETTLING); 771 | 772 | final int height = getHeight(); 773 | final int halfHeight = height / 2; // 774 | final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dy) / height); 775 | final float distance = halfHeight + halfHeight * 776 | distanceInfluenceForSnapDuration(distanceRatio); 777 | 778 | int duration = 0; 779 | velocity = Math.abs(velocity); 780 | if (velocity > 0) { 781 | duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); 782 | } else { 783 | final float pageDelta = (float) Math.abs(dy) / (height + mPageMargin); 784 | duration = (int) ((pageDelta + 1) * 100); 785 | } /* end of if */ 786 | duration = Math.min(duration, MAX_SETTLE_DURATION); 787 | 788 | mScroller.startScroll(sx, sy, dx, dy, duration); 789 | invalidate(); 790 | } 791 | 792 | /** 793 | * View Pager 794 | * 795 | * @param position 796 | * @param index index 797 | */ 798 | void addNewItem(int position, int index) { 799 | ItemInfo ii = new ItemInfo(); 800 | ii.position = position; 801 | ii.object = mAdapter.instantiateItem(this, position); 802 | if (index < 0) { 803 | mItems.add(ii); 804 | } else { 805 | mItems.add(index, ii); 806 | } /* end of if */ 807 | } 808 | 809 | /** 810 | * This method only gets called if our observer is attached, so mAdapter is non-null 811 | */ 812 | void dataSetChanged() { 813 | // This method only gets called if our observer is attached, so mAdapter is non-null. 814 | 815 | boolean needPopulate = mItems.size() < 3 && mItems.size() < mAdapter.getCount(); 816 | int newCurrItem = -1; 817 | 818 | boolean isUpdating = false; 819 | for (int i = 0; i < mItems.size(); i++) { 820 | final ItemInfo ii = mItems.get(i); 821 | final int newPos = mAdapter.getItemPosition(ii.object); 822 | 823 | if (newPos == VerticalPagerAdapter.POSITION_UNCHANGED) { 824 | // the position of the given item has not changed 825 | continue; 826 | } /* end of if */ 827 | 828 | if (newPos == VerticalPagerAdapter.POSITION_NONE) { 829 | // the item is no longer present in the adapter. 830 | mItems.remove(i); 831 | i--; 832 | 833 | if (!isUpdating) { 834 | mAdapter.startUpdate(this); 835 | isUpdating = true; 836 | } /* end of if */ 837 | 838 | mAdapter.destroyItem(this, ii.position, ii.object); 839 | needPopulate = true; 840 | 841 | if (mCurItem == ii.position) { 842 | // Keep the current item in the valid range 843 | newCurrItem = Math.max(0, Math.min(mCurItem, mAdapter.getCount() - 1)); 844 | } /* end of if */ 845 | continue; 846 | } /* end of if */ 847 | 848 | if (ii.position != newPos) { 849 | if (ii.position == mCurItem) { 850 | // Our current item changed position. Follow it. 851 | newCurrItem = newPos; 852 | } /* end of if */ 853 | 854 | ii.position = newPos; 855 | needPopulate = true; 856 | } /* end of if */ 857 | } /* end of for */ 858 | 859 | if (isUpdating) { 860 | mAdapter.finishUpdate(this); 861 | } /* end of if */ 862 | 863 | Collections.sort(mItems, COMPARATOR); // item 864 | 865 | if (newCurrItem >= 0) { 866 | // TODO This currently causes a jump. 867 | setCurrentItemInternal(newCurrItem, false, true); 868 | needPopulate = true; 869 | } /* end of if */ 870 | if (needPopulate) { 871 | populate(); 872 | requestLayout(); 873 | } /* end of if */ 874 | } 875 | 876 | /** 877 | * 878 | */ 879 | void populate() { 880 | if (mAdapter == null) { 881 | return; 882 | } /* end of if */ 883 | 884 | // Bail now if we are waiting to populate. This is to hold off 885 | // on creating views from the time the user releases their finger to 886 | // fling to a new position until we have finished the scroll to 887 | // that position, avoiding glitches from happening at that point. 888 | if (mPopulatePending) { 889 | if (DEBUG) Log.i(TAG, "populate is pending, skipping for now..."); 890 | return; 891 | } /* end of if */ 892 | 893 | // Also, don't populate until we are attached to a window. This is to 894 | // avoid trying to populate before we have restored our view hierarchy 895 | // state and conflicting with what is restored. 896 | if (getWindowToken() == null) { 897 | return; 898 | } /* end of if */ 899 | 900 | mAdapter.startUpdate(this); 901 | 902 | final int pageLimit = mOffscreenPageLimit; 903 | final int startPos = Math.max(0, mCurItem - pageLimit); 904 | final int N = mAdapter.getCount(); 905 | final int endPos = Math.min(N - 1, mCurItem + pageLimit); 906 | 907 | if (DEBUG) Log.v(TAG, "populating: startPos=" + startPos + " endPos=" + endPos); 908 | 909 | // Add and remove pages in the existing list. 910 | int lastPos = -1; 911 | for (int i = 0; i < mItems.size(); i++) { 912 | ItemInfo ii = mItems.get(i); 913 | if ((ii.position < startPos || ii.position > endPos) && !ii.scrolling) { 914 | if (DEBUG) Log.i(TAG, "removing: " + ii.position + " @ " + i); 915 | mItems.remove(i); 916 | i--; 917 | mAdapter.destroyItem(this, ii.position, ii.object); 918 | } else if (lastPos < endPos && ii.position > startPos) { 919 | // The next item is outside of our range, but we have a gap 920 | // between it and the last item where we want to have a page 921 | // shown. Fill in the gap. 922 | lastPos++; 923 | if (lastPos < startPos) { 924 | lastPos = startPos; 925 | } /* end of if */ 926 | while (lastPos <= endPos && lastPos < ii.position) { 927 | if (DEBUG) Log.i(TAG, "inserting: " + lastPos + " @ " + i); 928 | addNewItem(lastPos, i); 929 | lastPos++; 930 | i++; 931 | } /* end of while */ 932 | } /* end of if */ 933 | lastPos = ii.position; 934 | } /* end of if */ 935 | 936 | // Add any new pages we need at the end. 937 | lastPos = mItems.size() > 0 ? mItems.get(mItems.size() - 1).position : -1; 938 | if (lastPos < endPos) { 939 | lastPos++; 940 | lastPos = lastPos > startPos ? lastPos : startPos; 941 | while (lastPos <= endPos) { 942 | if (DEBUG) Log.i(TAG, "appending: " + lastPos); 943 | addNewItem(lastPos, -1); 944 | lastPos++; 945 | } /* end of while */ 946 | } /* end of if */ 947 | 948 | if (DEBUG) { 949 | Log.i(TAG, "Current page list:"); 950 | for (int i = 0; i < mItems.size(); i++) { 951 | Log.i(TAG, "#" + i + ": page " + mItems.get(i).position); 952 | } /* end of for */ 953 | } /* end of if */ 954 | 955 | ItemInfo curItem = null; 956 | for (int i = 0; i < mItems.size(); i++) { 957 | if (mItems.get(i).position == mCurItem) { 958 | curItem = mItems.get(i); 959 | break; 960 | } /* end of if */ 961 | } /* end of for */ 962 | mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null); 963 | 964 | mAdapter.finishUpdate(this); 965 | 966 | if (hasFocus()) { 967 | View currentFocused = findFocus(); 968 | ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null; 969 | if (ii == null || ii.position != mCurItem) { 970 | for (int i = 0; i < getChildCount(); i++) { 971 | View child = getChildAt(i); 972 | ii = infoForChild(child); 973 | if (ii != null && ii.position == mCurItem) { 974 | if (child.requestFocus(FOCUS_FORWARD)) { 975 | break; 976 | } /* end of if */ 977 | } /* end of if */ 978 | } /* end of for */ 979 | } /* end of if */ 980 | } /* end of if */ 981 | } 982 | 983 | /** 984 | * This is the persistent state that is saved by ViewPager. Only needed 985 | * if you are creating a sublass of ViewPager that must save its own 986 | * state, in which case it should implement a subclass of this which 987 | * contains that state. 988 | */ 989 | public static class SavedState extends BaseSavedState { 990 | int position; 991 | Parcelable adapterState; 992 | ClassLoader loader; 993 | 994 | public SavedState(Parcelable superState) { 995 | super(superState); 996 | } 997 | 998 | @Override 999 | public void writeToParcel(Parcel out, int flags) { 1000 | super.writeToParcel(out, flags); 1001 | out.writeInt(position); 1002 | out.writeParcelable(adapterState, flags); 1003 | } 1004 | 1005 | @Override 1006 | public String toString() { 1007 | return "FragmentPager.SavedState{" 1008 | + Integer.toHexString(System.identityHashCode(this)) 1009 | + " position=" + position + "}"; 1010 | } 1011 | 1012 | public static final Creator CREATOR 1013 | = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks() { 1014 | @Override 1015 | public SavedState createFromParcel(Parcel in, ClassLoader loader) { 1016 | return new SavedState(in, loader); 1017 | } 1018 | 1019 | @Override 1020 | public SavedState[] newArray(int size) { 1021 | return new SavedState[size]; 1022 | } 1023 | }); 1024 | 1025 | SavedState(Parcel in, ClassLoader loader) { 1026 | super(in); 1027 | if (loader == null) { 1028 | loader = getClass().getClassLoader(); 1029 | } /* end of if */ 1030 | position = in.readInt(); 1031 | adapterState = in.readParcelable(loader); 1032 | this.loader = loader; 1033 | } 1034 | } 1035 | 1036 | @Override 1037 | public Parcelable onSaveInstanceState() { 1038 | Parcelable superState = super.onSaveInstanceState(); 1039 | SavedState ss = new SavedState(superState); 1040 | ss.position = mCurItem; 1041 | if (mAdapter != null) { 1042 | ss.adapterState = mAdapter.saveState(); 1043 | } /* end of if */ 1044 | return ss; 1045 | } 1046 | 1047 | @Override 1048 | public void onRestoreInstanceState(Parcelable state) { 1049 | if (!(state instanceof SavedState)) { 1050 | super.onRestoreInstanceState(state); 1051 | return; 1052 | } /* end of if */ 1053 | 1054 | SavedState ss = (SavedState) state; 1055 | super.onRestoreInstanceState(ss.getSuperState()); 1056 | 1057 | if (mAdapter != null) { 1058 | mAdapter.restoreState(ss.adapterState, ss.loader); 1059 | setCurrentItemInternal(ss.position, false, true); 1060 | } else { 1061 | mRestoredCurItem = ss.position; 1062 | mRestoredAdapterState = ss.adapterState; 1063 | mRestoredClassLoader = ss.loader; 1064 | } /* end of if */ 1065 | } 1066 | 1067 | @Override 1068 | public void addView(View child, int index, ViewGroup.LayoutParams params) { 1069 | if (!checkLayoutParams(params)) { 1070 | params = generateLayoutParams(params); 1071 | } /* end of if */ 1072 | final LayoutParams lp = (LayoutParams) params; 1073 | lp.isDecor |= child instanceof Decor; 1074 | if (mInLayout) { 1075 | if (lp != null && lp.isDecor) { 1076 | throw new IllegalStateException("Cannot add pager decor view during layout"); 1077 | } /* end of if */ 1078 | addViewInLayout(child, index, params); 1079 | child.measure(mChildWidthMeasureSpec, mChildHeightMeasureSpec); 1080 | } else { 1081 | super.addView(child, index, params); 1082 | } /* end of if */ 1083 | 1084 | if (USE_CACHE) { 1085 | if (child.getVisibility() != GONE) { 1086 | child.setDrawingCacheEnabled(mScrollingCacheEnabled); 1087 | } else { 1088 | child.setDrawingCacheEnabled(false); 1089 | } /* end of if */ 1090 | } /* end of if */ 1091 | } 1092 | 1093 | /** 1094 | * 鍙栧緱鐩墠瑭查爜鐨剓@link ItemInfo} 1095 | * 1096 | * @param child child {@link View} object 1097 | * @return return {@link ItemInfo} if is view from object, other return null 1098 | */ 1099 | ItemInfo infoForChild(View child) { 1100 | for (int i = 0; i < mItems.size(); i++) { 1101 | ItemInfo ii = mItems.get(i); 1102 | if (mAdapter.isViewFromObject(child, ii.object)) { 1103 | return ii; 1104 | } /* end of if */ 1105 | } /* end of for */ 1106 | return null; 1107 | } 1108 | 1109 | ItemInfo infoForAnyChild(View child) { 1110 | ViewParent parent; 1111 | while ((parent = child.getParent()) != this) { 1112 | if (parent == null || !(parent instanceof View)) { 1113 | return null; 1114 | } /* end of if */ 1115 | child = (View) parent; 1116 | } /* end of while */ 1117 | return infoForChild(child); 1118 | } 1119 | 1120 | @Override 1121 | protected void onAttachedToWindow() { 1122 | super.onAttachedToWindow(); 1123 | mFirstLayout = true; 1124 | } 1125 | 1126 | @Override 1127 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1128 | // For simple implementation, or internal size is always 0. 1129 | // We depend on the container to specify the layout size of 1130 | // our view. We can't really know what it is since we will be 1131 | // adding and removing different arbitrary views and do not 1132 | // want the layout to change as this happens. 1133 | setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), 1134 | getDefaultSize(0, heightMeasureSpec)); 1135 | 1136 | // Children are just made to fill our space. 1137 | int childWidthSize = getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); 1138 | int childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); 1139 | 1140 | /* 1141 | * Make sure all children have been properly measured. Decor views first. 1142 | * Right now we cheat and make this less complicated by assuming decor 1143 | * views won't intersect. We will pin to edges based on gravity. 1144 | */ 1145 | int size = getChildCount(); 1146 | for (int i = 0; i < size; ++i) { 1147 | final View child = getChildAt(i); 1148 | if (child.getVisibility() != GONE) { 1149 | final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1150 | if (lp != null && lp.isDecor) { 1151 | final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK; 1152 | final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK; 1153 | Log.d(TAG, "gravity: " + lp.gravity + " hgrav: " + hgrav + " vgrav: " + vgrav); 1154 | int widthMode = MeasureSpec.AT_MOST; 1155 | int heightMode = MeasureSpec.AT_MOST; 1156 | boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM; 1157 | boolean consumeHorizontal = hgrav == Gravity.LEFT || hgrav == Gravity.RIGHT; 1158 | 1159 | if (consumeVertical) { 1160 | widthMode = MeasureSpec.EXACTLY; 1161 | } else if (consumeHorizontal) { 1162 | heightMode = MeasureSpec.EXACTLY; 1163 | } /* end of if */ 1164 | 1165 | final int widthSpec = MeasureSpec.makeMeasureSpec(childWidthSize, widthMode); 1166 | final int heightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, heightMode); 1167 | child.measure(widthSpec, heightSpec); 1168 | 1169 | if (consumeVertical) { 1170 | childHeightSize -= child.getMeasuredHeight(); 1171 | } else if (consumeHorizontal) { 1172 | childWidthSize -= child.getMeasuredWidth(); 1173 | } /* end of if */ 1174 | } /* end of if */ 1175 | } /* end of if */ 1176 | } /* end of for */ 1177 | 1178 | mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY); 1179 | mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY); 1180 | 1181 | // Make sure we have created all fragments that we need to have shown. 1182 | mInLayout = true; 1183 | populate(); 1184 | mInLayout = false; 1185 | 1186 | // Page views next. 1187 | size = getChildCount(); 1188 | for (int i = 0; i < size; ++i) { 1189 | final View child = getChildAt(i); 1190 | if (child.getVisibility() != GONE) { 1191 | if (DEBUG) Log.v(TAG, "Measuring #" + i + " " + child 1192 | + ": " + mChildWidthMeasureSpec); 1193 | 1194 | final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1195 | if (lp == null || !lp.isDecor) { 1196 | child.measure(mChildWidthMeasureSpec, mChildHeightMeasureSpec); 1197 | } /* end of if */ 1198 | } /* end of if */ 1199 | } /* end of for */ 1200 | } 1201 | 1202 | // XXX鍨傜洿 1203 | @Override 1204 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 1205 | super.onSizeChanged(w, h, oldw, oldh); 1206 | 1207 | // Make sure scroll position is set correctly. 1208 | if (h != oldh) { 1209 | recomputeScrollPosition(h, oldh, mPageMargin, mPageMargin); 1210 | } /* end of if */ 1211 | } 1212 | 1213 | // XXX鍨傜洿 1214 | 1215 | /** 1216 | * 閲嶆柊瑷堢畻Scroll浣嶇疆 1217 | * 1218 | * @param height 楂樺害 1219 | * @param oldHeight 鑸婇珮搴� 1220 | * @param margin 閭婄晫 1221 | * @param oldMargin 鑸婇倞鐣� 1222 | */ 1223 | private void recomputeScrollPosition(int height, int oldHeight, int margin, int oldMargin) { 1224 | final int heightWithMargin = height + margin; 1225 | if (oldHeight > 0) { 1226 | final int oldScrollPos = getScrollY(); 1227 | final int oldwwm = oldHeight + oldMargin; 1228 | final int oldScrollItem = oldScrollPos / oldwwm; 1229 | final float scrollOffset = (float) (oldScrollPos % oldwwm) / oldwwm; 1230 | final int scrollPos = (int) ((oldScrollItem + scrollOffset) * heightWithMargin); 1231 | scrollTo(getScrollX(), scrollPos); 1232 | if (!mScroller.isFinished()) { 1233 | // We now return to your regularly scheduled scroll, already in progress. 1234 | final int newDuration = mScroller.getDuration() - mScroller.timePassed(); 1235 | mScroller.startScroll(0, scrollPos, mCurItem * heightWithMargin, 0, newDuration); 1236 | } /* end of if */ 1237 | } else { 1238 | int scrollPos = mCurItem * heightWithMargin; 1239 | if (scrollPos != getScrollY()) { 1240 | completeScroll(); 1241 | scrollTo(getScrollX(), scrollPos); 1242 | } /* end of if */ 1243 | } /* end of if */ 1244 | } 1245 | 1246 | // XXX 鍨傜洿 1247 | @Override 1248 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 1249 | mInLayout = true; 1250 | populate(); 1251 | mInLayout = false; 1252 | 1253 | final int count = getChildCount(); 1254 | int width = r - l; 1255 | int height = b - t; 1256 | int paddingLeft = getPaddingLeft(); 1257 | int paddingTop = getPaddingTop(); 1258 | int paddingRight = getPaddingRight(); 1259 | int paddingBottom = getPaddingBottom(); 1260 | final int scrollY = getScrollY(); 1261 | 1262 | int decorCount = 0; 1263 | 1264 | for (int i = 0; i < count; i++) { 1265 | final View child = getChildAt(i); 1266 | if (child.getVisibility() != GONE) { 1267 | final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1268 | ItemInfo ii; 1269 | int childLeft = 0; 1270 | int childTop = 0; 1271 | if (lp.isDecor) { 1272 | //XXX isDecor鐐篺alse锛屾毇鏅傛矑鐢ㄥ埌 1273 | final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK; 1274 | final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK; 1275 | switch (hgrav) { 1276 | default: 1277 | childLeft = paddingLeft; 1278 | break; 1279 | case Gravity.LEFT: 1280 | childLeft = paddingLeft; 1281 | paddingLeft += child.getMeasuredWidth(); 1282 | break; 1283 | case Gravity.CENTER_HORIZONTAL: 1284 | childLeft = Math.max((width - child.getMeasuredWidth()) / 2, 1285 | paddingLeft); 1286 | break; 1287 | case Gravity.RIGHT: 1288 | childLeft = width - paddingRight - child.getMeasuredWidth(); 1289 | paddingRight += child.getMeasuredWidth(); 1290 | break; 1291 | } /* end of switch */ 1292 | switch (vgrav) { 1293 | default: 1294 | childTop = paddingTop; 1295 | break; 1296 | case Gravity.TOP: 1297 | childTop = paddingTop; 1298 | paddingTop += child.getMeasuredHeight(); 1299 | break; 1300 | case Gravity.CENTER_VERTICAL: 1301 | childTop = Math.max((height - child.getMeasuredHeight()) / 2, 1302 | paddingTop); 1303 | break; 1304 | case Gravity.BOTTOM: 1305 | childTop = height - paddingBottom - child.getMeasuredHeight(); 1306 | paddingBottom += child.getMeasuredHeight(); 1307 | break; 1308 | } /* end of switch */ 1309 | 1310 | //XXX 绱�寗y杌哥Щ鍕曡窛闆� 1311 | childTop += scrollY; 1312 | decorCount++; 1313 | child.layout(childLeft, childTop, 1314 | childLeft + child.getMeasuredWidth(), 1315 | childTop + child.getMeasuredHeight()); 1316 | } else if ((ii = infoForChild(child)) != null) { 1317 | //XXX 瑷堢畻ViewPager姣忎竴闋佺殑閭婄晫 1318 | int toff = (height + mPageMargin) * ii.position; 1319 | childLeft = paddingLeft; 1320 | childTop = paddingTop + toff; 1321 | 1322 | if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object 1323 | + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth() 1324 | + "x" + child.getMeasuredHeight()); 1325 | child.layout(childLeft, childTop, 1326 | childLeft + child.getMeasuredWidth(), 1327 | childTop + child.getMeasuredHeight()); 1328 | } /* end of if */ 1329 | } /* end of if */ 1330 | } /* end of for */ 1331 | 1332 | //XXX 瑷畾宸﹀彸閭婄晫 1333 | mLeftPageBounds = paddingLeft; 1334 | mRightPageBounds = width - paddingRight; 1335 | mDecorChildCount = decorCount; 1336 | mFirstLayout = false; 1337 | } 1338 | 1339 | @Override 1340 | public void computeScroll() { 1341 | if (DEBUG) Log.i(TAG, "computeScroll: finished=" + mScroller.isFinished()); 1342 | if (!mScroller.isFinished()) { 1343 | if (mScroller.computeScrollOffset()) { 1344 | if (DEBUG) Log.i(TAG, "computeScroll: still scrolling"); 1345 | int oldX = getScrollX(); 1346 | int oldY = getScrollY(); 1347 | int x = mScroller.getCurrX(); 1348 | int y = mScroller.getCurrY(); 1349 | 1350 | if (oldX != x || oldY != y) { 1351 | scrollTo(x, y); 1352 | pageScrolled(y); 1353 | } /* end of if */ 1354 | 1355 | // Keep on drawing until the animation has finished. 1356 | invalidate(); 1357 | return; 1358 | } /* end of if */ 1359 | } /* end of if */ 1360 | 1361 | // Done with scroll, clean up state. 1362 | completeScroll(); 1363 | } 1364 | 1365 | /** 1366 | * page scrolled 1367 | * 1368 | * @param ypos 1369 | */ 1370 | private void pageScrolled(int ypos) { 1371 | final int heightWithMargin = getHeight() + mPageMargin; 1372 | final int position = ypos / heightWithMargin; 1373 | final int offsetPixels = ypos % heightWithMargin; 1374 | final float offset = (float) offsetPixels / heightWithMargin; 1375 | 1376 | mCalledSuper = false; 1377 | onPageScrolled(position, offset, offsetPixels); 1378 | if (!mCalledSuper) { 1379 | throw new IllegalStateException("onPageScrolled did not call superclass implementation"); 1380 | } /* end of if */ 1381 | } 1382 | 1383 | //XXX 1384 | 1385 | /** 1386 | * This method will be invoked when the current page is scrolled, either as part 1387 | * of a programmatically initiated smooth scroll or a user initiated touch scroll. 1388 | * If you override this method you must call through to the superclass implementation 1389 | * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled 1390 | * returns. 1391 | * 1392 | * @param position Position index of the first page currently being displayed. 1393 | * Page position+1 will be visible if positionOffset is nonzero. 1394 | * @param offset Value from [0, 1) indicating the offset from the page at position. 1395 | * @param offsetPixels Value in pixels indicating the offset from position. 1396 | */ 1397 | protected void onPageScrolled(int position, float offset, int offsetPixels) { 1398 | // Offset any decor views if needed - keep them on-screen at all times. 1399 | if (mDecorChildCount > 0) { 1400 | final int scrollY = getScrollY(); 1401 | int paddingTop = getPaddingTop(); 1402 | int paddingBottom = getPaddingBottom(); 1403 | final int height = getHeight(); 1404 | final int childCount = getChildCount(); 1405 | for (int i = 0; i < childCount; i++) { 1406 | final View child = getChildAt(i); 1407 | final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1408 | if (!lp.isDecor) continue; 1409 | 1410 | final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK; 1411 | int childTop = 0; 1412 | switch (vgrav) { 1413 | default: 1414 | childTop = paddingTop; 1415 | break; 1416 | case Gravity.TOP: 1417 | childTop = paddingTop; 1418 | paddingTop += child.getHeight(); 1419 | break; 1420 | case Gravity.CENTER_HORIZONTAL: 1421 | childTop = Math.max((height - child.getMeasuredHeight()) / 2, 1422 | paddingTop); 1423 | break; 1424 | case Gravity.BOTTOM: 1425 | childTop = height - paddingBottom - child.getMeasuredHeight(); 1426 | paddingBottom += child.getMeasuredHeight(); 1427 | break; 1428 | } /* end of switch */ 1429 | childTop += scrollY; 1430 | 1431 | final int childOffset = childTop - child.getTop(); 1432 | if (childOffset != 0) { 1433 | child.offsetTopAndBottom(childOffset); 1434 | } /* end of if */ 1435 | } /* end of for */ 1436 | } /* end of for */ 1437 | 1438 | if (mOnPageChangeListener != null) { 1439 | mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels); 1440 | } /* end of if */ 1441 | if (mInternalPageChangeListener != null) { 1442 | mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels); 1443 | } /* end of if */ 1444 | mCalledSuper = true; 1445 | } 1446 | 1447 | /** 1448 | * ScrollScroll 1449 | * Scroll populate 1450 | */ 1451 | private void completeScroll() { 1452 | boolean needPopulate = mScrolling; 1453 | if (needPopulate) { 1454 | // Done with scroll, no longer want to cache view drawing. 1455 | setScrollingCacheEnabled(false); 1456 | mScroller.abortAnimation(); 1457 | int oldX = getScrollX(); 1458 | int oldY = getScrollY(); 1459 | int x = mScroller.getCurrX(); 1460 | int y = mScroller.getCurrY(); 1461 | if (oldX != x || oldY != y) { 1462 | scrollTo(x, y); 1463 | } /* end of if */ 1464 | setScrollState(SCROLL_STATE_IDLE); 1465 | } /* end of if */ 1466 | mPopulatePending = false; 1467 | mScrolling = false; 1468 | for (int i = 0; i < mItems.size(); i++) { 1469 | ItemInfo ii = mItems.get(i); 1470 | if (ii.scrolling) { 1471 | needPopulate = true; 1472 | ii.scrolling = false; 1473 | } /* end of if */ 1474 | } /* end of for */ 1475 | if (needPopulate) { 1476 | populate(); 1477 | } /* end of if */ 1478 | } 1479 | 1480 | 1481 | // 1482 | @Override 1483 | public boolean onInterceptTouchEvent(MotionEvent ev) { 1484 | /* 1485 | * This method JUST determines whether we want to intercept the motion. 1486 | * If we return true, onMotionEvent will be called and we do the actual 1487 | * scrolling there. 1488 | */ 1489 | if (!isScroll) { 1490 | return false; 1491 | } 1492 | 1493 | final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; 1494 | 1495 | // Always take care of the touch gesture being complete. 1496 | if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { 1497 | // Release the drag. 1498 | if (DEBUG) Log.v(TAG, "Intercept done!"); 1499 | mIsBeingDragged = false; 1500 | mIsUnableToDrag = false; 1501 | mActivePointerId = INVALID_POINTER; 1502 | if (mVelocityTracker != null) { 1503 | mVelocityTracker.recycle(); 1504 | mVelocityTracker = null; 1505 | } /* end of if */ 1506 | return false; 1507 | } /* end of if */ 1508 | 1509 | // Nothing more to do here if we have decided whether or not we 1510 | // are dragging. 1511 | if (action != MotionEvent.ACTION_DOWN) { 1512 | if (mIsBeingDragged) { 1513 | if (DEBUG) Log.v(TAG, "Intercept returning true!"); 1514 | return true; 1515 | } /* end of if */ 1516 | if (mIsUnableToDrag) { 1517 | if (DEBUG) Log.v(TAG, "Intercept returning false!"); 1518 | return false; 1519 | } /* end of if */ 1520 | } /* end of if */ 1521 | 1522 | switch (action) { 1523 | case MotionEvent.ACTION_MOVE: { 1524 | /* 1525 | * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 1526 | * whether the user has moved far enough from his original down touch. 1527 | */ 1528 | 1529 | /* 1530 | * Locally do absolute value. mLastMotionX is set to the x value 1531 | * of the down event. 1532 | */ 1533 | final int activePointerId = mActivePointerId; 1534 | if (activePointerId == INVALID_POINTER) { 1535 | // If we don't have a valid id, the touch down wasn't on content. 1536 | break; 1537 | } /* end of if */ 1538 | 1539 | final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId); 1540 | final float x = MotionEventCompat.getX(ev, pointerIndex); 1541 | final float xDiff = Math.abs(x - mLastMotionX); 1542 | final float y = MotionEventCompat.getY(ev, pointerIndex); 1543 | final float dy = y - mLastMotionY; 1544 | final float yDiff = Math.abs(dy); 1545 | 1546 | if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff); 1547 | 1548 | if (canScroll(this, false, (int) dy, (int) x, (int) y)) { 1549 | // Nested view has scrollable area under this point. Let it be handled there. 1550 | mInitialMotionY = mLastMotionY = y; 1551 | mLastMotionX = x; 1552 | return false; 1553 | } /* end of if */ 1554 | if (yDiff > mTouchSlop && yDiff > xDiff) { 1555 | if (DEBUG) Log.v(TAG, "Starting drag!"); 1556 | mIsBeingDragged = true; 1557 | setScrollState(SCROLL_STATE_DRAGGING); 1558 | mLastMotionY = y; 1559 | setScrollingCacheEnabled(true); 1560 | } else { 1561 | if (xDiff > mTouchSlop) { 1562 | // The finger has moved enough in the horizontal 1563 | // direction to be counted as a drag... abort 1564 | // any attempt to drag vertically, to work correctly 1565 | // with children that have scrolling containers. 1566 | if (DEBUG) Log.v(TAG, "Starting unable to drag!"); 1567 | mIsUnableToDrag = true; 1568 | } /* end of if */ 1569 | } /* end of if */ 1570 | break; 1571 | } /* end of case */ 1572 | 1573 | case MotionEvent.ACTION_DOWN: { 1574 | /* 1575 | * Remember location of down touch. 1576 | * ACTION_DOWN always refers to pointer index 0. 1577 | */ 1578 | mLastMotionX = ev.getX(); 1579 | mLastMotionY = mInitialMotionY = ev.getY(); 1580 | mActivePointerId = MotionEventCompat.getPointerId(ev, 0); 1581 | 1582 | if (mScrollState == SCROLL_STATE_SETTLING) { 1583 | // Let the user 'catch' the pager as it animates. 1584 | mIsBeingDragged = true; 1585 | mIsUnableToDrag = false; 1586 | setScrollState(SCROLL_STATE_DRAGGING); 1587 | } else { 1588 | completeScroll(); 1589 | mIsBeingDragged = false; 1590 | mIsUnableToDrag = false; 1591 | } /* end of if */ 1592 | 1593 | if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY 1594 | + " mIsBeingDragged=" + mIsBeingDragged 1595 | + "mIsUnableToDrag=" + mIsUnableToDrag); 1596 | break; 1597 | } /* end of case */ 1598 | 1599 | case MotionEventCompat.ACTION_POINTER_UP: 1600 | onSecondaryPointerUp(ev); 1601 | break; 1602 | } /* end of switch */ 1603 | 1604 | if (!mIsBeingDragged) { 1605 | // Track the velocity as long as we aren't dragging. 1606 | // Once we start a real drag we will track in onTouchEvent. 1607 | if (mVelocityTracker == null) { 1608 | mVelocityTracker = VelocityTracker.obtain(); 1609 | } /* end of if */ 1610 | mVelocityTracker.addMovement(ev); 1611 | } /* end of if */ 1612 | 1613 | /* 1614 | * The only time we want to intercept motion events is if we are in the 1615 | * drag mode. 1616 | */ 1617 | return mIsBeingDragged; 1618 | } 1619 | 1620 | 1621 | // XXX 鍨傜洿 1622 | @Override 1623 | public boolean onTouchEvent(MotionEvent ev) { 1624 | if (mFakeDragging) { 1625 | // A fake drag is in progress already, ignore this real one 1626 | // but still eat the touch events. 1627 | // (It is likely that the user is multi-touching the screen.) 1628 | return true; 1629 | } /* end of if */ 1630 | 1631 | if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { 1632 | // Don't handle edge touches immediately -- they may actually belong to one of our 1633 | // descendants. 1634 | return false; 1635 | } /* end of if */ 1636 | 1637 | if (mAdapter == null || mAdapter.getCount() == 0) { 1638 | // Nothing to present or scroll; nothing to touch. 1639 | return false; 1640 | } /* end of if */ 1641 | 1642 | if (mVelocityTracker == null) { 1643 | mVelocityTracker = VelocityTracker.obtain(); 1644 | } /* end of if */ 1645 | mVelocityTracker.addMovement(ev); 1646 | 1647 | final int action = ev.getAction(); 1648 | boolean needsInvalidate = false; 1649 | 1650 | switch (action & MotionEventCompat.ACTION_MASK) { 1651 | case MotionEvent.ACTION_DOWN: { 1652 | /* 1653 | * If being flinged and user touches, stop the fling. isFinished 1654 | * will be false if being flinged. 1655 | */ 1656 | completeScroll(); 1657 | 1658 | // Remember where the motion event started 1659 | mLastMotionY = mInitialMotionY = ev.getY(); 1660 | mActivePointerId = MotionEventCompat.getPointerId(ev, 0); 1661 | break; 1662 | } /* end of case */ 1663 | case MotionEvent.ACTION_MOVE: 1664 | if (!mIsBeingDragged) { 1665 | final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); 1666 | final float x = MotionEventCompat.getX(ev, pointerIndex); 1667 | final float xDiff = Math.abs(x - mLastMotionX); 1668 | final float y = MotionEventCompat.getY(ev, pointerIndex); 1669 | final float yDiff = Math.abs(y - mLastMotionY); 1670 | if (DEBUG) 1671 | Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff); 1672 | if (yDiff > mTouchSlop && yDiff > xDiff) { 1673 | if (DEBUG) Log.v(TAG, "Starting drag!"); 1674 | mIsBeingDragged = true; 1675 | mLastMotionY = y; 1676 | setScrollState(SCROLL_STATE_DRAGGING); 1677 | setScrollingCacheEnabled(true); 1678 | } /* end of if */ 1679 | } /* end of if */ 1680 | if (mIsBeingDragged) { 1681 | // Scroll to follow the motion event 1682 | final int activePointerIndex = MotionEventCompat.findPointerIndex( 1683 | ev, mActivePointerId); 1684 | final float y = MotionEventCompat.getY(ev, activePointerIndex); 1685 | final float deltaY = mLastMotionY - y; 1686 | mLastMotionY = y; 1687 | float oldScrollY = getScrollY(); 1688 | float scrollY = oldScrollY + deltaY; 1689 | final int height = getHeight(); 1690 | final int heightWithMargin = height + mPageMargin; 1691 | 1692 | final int lastItemIndex = mAdapter.getCount() - 1; 1693 | final float topBound = Math.max(0, (mCurItem - 1) * heightWithMargin); 1694 | final float bottomBound = 1695 | Math.min(mCurItem + 1, lastItemIndex) * heightWithMargin; 1696 | if (scrollY < topBound) { 1697 | if (topBound == 0) { 1698 | float over = -scrollY; 1699 | needsInvalidate = mTopEdge.onPull(over / height); 1700 | } /* end of if */ 1701 | scrollY = topBound; 1702 | } else if (scrollY > bottomBound) { 1703 | if (bottomBound == lastItemIndex * heightWithMargin) { 1704 | float over = scrollY - bottomBound; 1705 | needsInvalidate = mBottomEdge.onPull(over / height); 1706 | } /* end of if */ 1707 | scrollY = bottomBound; 1708 | } /* end of if */ 1709 | // Don't lose the rounded component 1710 | mLastMotionY += scrollY - (int) scrollY; 1711 | scrollTo(getScrollX(), (int) scrollY); 1712 | pageScrolled((int) scrollY); 1713 | } /* end of if */ 1714 | break; 1715 | case MotionEvent.ACTION_UP: 1716 | if (mIsBeingDragged) { 1717 | final VelocityTracker velocityTracker = mVelocityTracker; 1718 | velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 1719 | int initialVelocity = (int) VelocityTrackerCompat.getYVelocity( 1720 | velocityTracker, mActivePointerId); 1721 | mPopulatePending = true; 1722 | final int heightWithMargin = getHeight() + mPageMargin; 1723 | final int scrollY = getScrollY(); 1724 | final int currentPage = scrollY / heightWithMargin; 1725 | final float pageOffset = (float) (scrollY % heightWithMargin) / heightWithMargin; 1726 | final int activePointerIndex = 1727 | MotionEventCompat.findPointerIndex(ev, mActivePointerId); 1728 | final float y = MotionEventCompat.getY(ev, activePointerIndex); 1729 | final int totalDelta = (int) (y - mInitialMotionY); 1730 | int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity, 1731 | totalDelta); 1732 | setCurrentItemInternal(nextPage, true, true, initialVelocity); 1733 | 1734 | mActivePointerId = INVALID_POINTER; 1735 | endDrag(); 1736 | needsInvalidate = mTopEdge.onRelease() | mBottomEdge.onRelease(); 1737 | } /* end of if */ 1738 | break; 1739 | case MotionEvent.ACTION_CANCEL: 1740 | if (mIsBeingDragged) { 1741 | setCurrentItemInternal(mCurItem, true, true); 1742 | mActivePointerId = INVALID_POINTER; 1743 | endDrag(); 1744 | needsInvalidate = mTopEdge.onRelease() | mBottomEdge.onRelease(); 1745 | } /* end of if */ 1746 | break; 1747 | case MotionEventCompat.ACTION_POINTER_DOWN: { 1748 | final int index = MotionEventCompat.getActionIndex(ev); 1749 | final float y = MotionEventCompat.getY(ev, index); 1750 | mLastMotionY = y; 1751 | mActivePointerId = MotionEventCompat.getPointerId(ev, index); 1752 | break; 1753 | } /* end of case */ 1754 | case MotionEventCompat.ACTION_POINTER_UP: 1755 | onSecondaryPointerUp(ev); 1756 | mLastMotionY = MotionEventCompat.getY(ev, 1757 | MotionEventCompat.findPointerIndex(ev, mActivePointerId)); 1758 | break; 1759 | } /* end of switch */ 1760 | if (needsInvalidate) { 1761 | invalidate(); 1762 | } /* end of if */ 1763 | return true; 1764 | } 1765 | 1766 | // XXX 1767 | 1768 | /** 1769 | * @param currentPage current page index 1770 | * @param pageOffset page 1771 | * @param velocity 1772 | * @param deltaY 1773 | * @return target page 1774 | */ 1775 | private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaY) { 1776 | int targetPage; 1777 | if (Math.abs(deltaY) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) { 1778 | targetPage = velocity > 0 ? currentPage : currentPage + 1; 1779 | } else { 1780 | targetPage = (int) (currentPage + pageOffset + 0.5f); 1781 | } /* end of if */ 1782 | 1783 | return targetPage; 1784 | } 1785 | 1786 | @Override 1787 | public void draw(Canvas canvas) { 1788 | // XXX 1789 | super.draw(canvas); 1790 | boolean needsInvalidate = false; 1791 | 1792 | final int overScrollMode = ViewCompat.getOverScrollMode(this); 1793 | if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS || 1794 | (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && 1795 | mAdapter != null && mAdapter.getCount() > 1)) { 1796 | if (!mTopEdge.isFinished()) { 1797 | final int restoreCount = canvas.save(); 1798 | final int width = getWidth() - getPaddingLeft() - getPaddingRight(); 1799 | 1800 | canvas.rotate(270); 1801 | canvas.translate(-width + getPaddingLeft(), 0); 1802 | mTopEdge.setSize(width, getHeight()); 1803 | needsInvalidate |= mTopEdge.draw(canvas); 1804 | canvas.restoreToCount(restoreCount); 1805 | } /* end of if */ 1806 | if (!mBottomEdge.isFinished()) { 1807 | final int restoreCount = canvas.save(); 1808 | final int width = getWidth() - getPaddingLeft() - getPaddingRight(); 1809 | final int height = getHeight(); 1810 | final int itemCount = mAdapter != null ? mAdapter.getCount() : 1; 1811 | 1812 | canvas.rotate(180); 1813 | canvas.translate(-width + getPaddingLeft(), -itemCount * (height + mPageMargin) + mPageMargin); 1814 | mBottomEdge.setSize(width, height); 1815 | needsInvalidate |= mBottomEdge.draw(canvas); 1816 | canvas.restoreToCount(restoreCount); 1817 | } /* end of if */ 1818 | } else { 1819 | mTopEdge.finish(); 1820 | mBottomEdge.finish(); 1821 | } /* end of if */ 1822 | 1823 | if (needsInvalidate) { 1824 | // Keep animating 1825 | invalidate(); 1826 | } /* end of if */ 1827 | } 1828 | 1829 | @Override 1830 | protected void onDraw(Canvas canvas) { 1831 | super.onDraw(canvas); 1832 | 1833 | //XXX 1834 | //Draw the margin drawable if needed. 1835 | if (mPageMargin > 0 && mMarginDrawable != null) { 1836 | final int scrollY = getScrollY(); 1837 | final int height = getHeight(); 1838 | final int offset = scrollY % (height + mPageMargin); 1839 | if (offset != 0) { 1840 | // Pages fit completely when settled; we only need to draw when in between 1841 | final int top = scrollY - offset + height; 1842 | mMarginDrawable.setBounds(mLeftPageBounds, top, mRightPageBounds, top + mPageMargin); 1843 | mMarginDrawable.draw(canvas); 1844 | } /* end of if */ 1845 | } /* end of if */ 1846 | } 1847 | 1848 | /** 1849 | * Start a fake drag of the pager. 1850 | *

1851 | *

A fake drag can be useful if you want to synchronize the motion of the ViewPager 1852 | * with the touch scrolling of another view, while still letting the ViewPager 1853 | * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.) 1854 | * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call 1855 | * {@link #endFakeDrag()} to complete the fake drag and fling as necessary. 1856 | *

1857 | *

During a fake drag the ViewPager will ignore all touch events. If a real drag 1858 | * is already in progress, this method will return false. 1859 | * 1860 | * @return true if the fake drag began successfully, false if it could not be started. 1861 | * @see #fakeDragBy(float) 1862 | * @see #endFakeDrag() 1863 | */ 1864 | public boolean beginFakeDrag() { 1865 | if (mIsBeingDragged) { 1866 | return false; 1867 | } /* end of if */ 1868 | mFakeDragging = true; 1869 | setScrollState(SCROLL_STATE_DRAGGING); 1870 | // XXX 1871 | mInitialMotionY = mLastMotionY = 0; 1872 | if (mVelocityTracker == null) { 1873 | mVelocityTracker = VelocityTracker.obtain(); 1874 | } else { 1875 | mVelocityTracker.clear(); 1876 | } /* end of if */ 1877 | final long time = SystemClock.uptimeMillis(); 1878 | final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0); 1879 | mVelocityTracker.addMovement(ev); 1880 | ev.recycle(); 1881 | mFakeDragBeginTime = time; 1882 | return true; 1883 | } 1884 | 1885 | /** 1886 | * End a fake drag of the pager. 1887 | * 1888 | * @see #beginFakeDrag() 1889 | * @see #fakeDragBy(float) 1890 | */ 1891 | public void endFakeDrag() { 1892 | //XXX 1893 | if (!mFakeDragging) { 1894 | throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first."); 1895 | } /* end of if */ 1896 | 1897 | final VelocityTracker velocityTracker = mVelocityTracker; 1898 | velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 1899 | int initialVelocity = (int) VelocityTrackerCompat.getXVelocity( 1900 | velocityTracker, mActivePointerId); 1901 | mPopulatePending = true; 1902 | final int totalDelta = (int) (mLastMotionY - mInitialMotionY); 1903 | final int scrollY = getScrollY(); 1904 | final int heightWithMargin = getHeight() + mPageMargin; 1905 | final int currentPage = scrollY / heightWithMargin; 1906 | final float pageOffset = (float) (scrollY % heightWithMargin) / heightWithMargin; 1907 | int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity, totalDelta); 1908 | setCurrentItemInternal(nextPage, true, true, initialVelocity); 1909 | endDrag(); 1910 | 1911 | mFakeDragging = false; 1912 | } 1913 | 1914 | 1915 | // XXX 1916 | 1917 | /** 1918 | * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first. 1919 | * 1920 | * @param yOffset Offset in pixels to drag by. 1921 | * @see #beginFakeDrag() 1922 | * @see #endFakeDrag() 1923 | */ 1924 | public void fakeDragBy(float yOffset) { 1925 | if (!mFakeDragging) { 1926 | throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first."); 1927 | } /* end of if */ 1928 | 1929 | mLastMotionY += yOffset; 1930 | float scrollY = getScrollY() - yOffset; 1931 | final int height = getHeight(); 1932 | final int heightWithMargin = height + mPageMargin; 1933 | 1934 | final float topBound = Math.max(0, (mCurItem - 1) * heightWithMargin); 1935 | final float bottomBound = 1936 | Math.min(mCurItem + 1, mAdapter.getCount() - 1) * heightWithMargin; 1937 | if (scrollY < topBound) { 1938 | scrollY = topBound; 1939 | } else if (scrollY > bottomBound) { 1940 | scrollY = bottomBound; 1941 | } /* end of if */ 1942 | // Don't lose the rounded component 1943 | mLastMotionY += scrollY - (int) scrollY; 1944 | scrollTo(getScrollX(), (int) scrollY); 1945 | pageScrolled((int) scrollY); 1946 | 1947 | // Synthesize an event for the VelocityTracker. 1948 | final long time = SystemClock.uptimeMillis(); 1949 | final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE, 1950 | 0, mLastMotionY, 0); 1951 | mVelocityTracker.addMovement(ev); 1952 | ev.recycle(); 1953 | } 1954 | 1955 | /** 1956 | * Returns true if a fake drag is in progress. 1957 | * 1958 | * @return true if currently in a fake drag, false otherwise. 1959 | * @see #beginFakeDrag() 1960 | * @see #fakeDragBy(float) 1961 | * @see #endFakeDrag() 1962 | */ 1963 | public boolean isFakeDragging() { 1964 | return mFakeDragging; 1965 | } 1966 | 1967 | // XXX 1968 | 1969 | /** 1970 | * on secondary pointer up 1971 | * This was our active pointer going up. Choose a new active pointer and adjust accordingly. 1972 | * 1973 | * @param ev MotionEvent 1974 | */ 1975 | private void onSecondaryPointerUp(MotionEvent ev) { 1976 | final int pointerIndex = MotionEventCompat.getActionIndex(ev); 1977 | final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); 1978 | if (pointerId == mActivePointerId) { 1979 | // This was our active pointer going up. Choose a new 1980 | // active pointer and adjust accordingly. 1981 | final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 1982 | mLastMotionY = MotionEventCompat.getY(ev, newPointerIndex); 1983 | mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); 1984 | if (mVelocityTracker != null) { 1985 | mVelocityTracker.clear(); 1986 | } /* end of if */ 1987 | } /* end of if */ 1988 | } 1989 | 1990 | /** 1991 | * end drag 1992 | */ 1993 | private void endDrag() { 1994 | mIsBeingDragged = false; 1995 | mIsUnableToDrag = false; 1996 | 1997 | if (mVelocityTracker != null) { 1998 | mVelocityTracker.recycle(); 1999 | mVelocityTracker = null; 2000 | } /* end of if */ 2001 | } 2002 | 2003 | /** 2004 | * ScrollingCacheEnabled 2005 | * 2006 | * @param enabled enabled or disabled 2007 | */ 2008 | private void setScrollingCacheEnabled(boolean enabled) { 2009 | if (mScrollingCacheEnabled != enabled) { 2010 | mScrollingCacheEnabled = enabled; 2011 | if (USE_CACHE) { 2012 | final int size = getChildCount(); 2013 | for (int i = 0; i < size; ++i) { 2014 | final View child = getChildAt(i); 2015 | if (child.getVisibility() != GONE) { 2016 | child.setDrawingCacheEnabled(enabled); 2017 | } /* end of if */ 2018 | } /* end of for */ 2019 | } /* end of if */ 2020 | } /* end of if */ 2021 | } 2022 | 2023 | //XXX 2024 | 2025 | /** 2026 | * Tests scrollability within child views of v given a delta of dy. 2027 | * 2028 | * @param v View to test for vertical scrollability 2029 | * @param checkV Whether the view v passed should itself be checked for scrollability (true), 2030 | * or just its children (false). 2031 | * @param dy Delta scrolled in pixels 2032 | * @param x X coordinate of the active touch point 2033 | * @param y Y coordinate of the active touch point 2034 | * @return true if child views of v can be scrolled by delta of dx. 2035 | */ 2036 | protected boolean canScroll(View v, boolean checkV, int dy, int x, int y) { 2037 | if (v instanceof ViewGroup) { 2038 | final ViewGroup group = (ViewGroup) v; 2039 | final int scrollX = v.getScrollX(); 2040 | final int scrollY = v.getScrollY(); 2041 | final int count = group.getChildCount(); 2042 | // Count backwards - let topmost views consume scroll distance first. 2043 | for (int i = count - 1; i >= 0; i--) { 2044 | // This will not work for transformed views in Honeycomb+ 2045 | final View child = group.getChildAt(i); 2046 | if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() && 2047 | y + scrollY >= child.getTop() && y + scrollY < child.getBottom() && 2048 | canScroll(child, true, dy, x + scrollX - child.getLeft(), 2049 | y + scrollY - child.getTop())) { 2050 | return true; 2051 | } /* end of if */ 2052 | } /* end of for */ 2053 | } /* end of if */ 2054 | 2055 | return checkV && ViewCompat.canScrollVertically(v, -dy); 2056 | } 2057 | 2058 | @Override 2059 | public boolean dispatchKeyEvent(KeyEvent event) { 2060 | // Let the focused view and/or our descendants get the key first 2061 | return super.dispatchKeyEvent(event) || executeKeyEvent(event); 2062 | } 2063 | 2064 | 2065 | //XXX 2066 | 2067 | /** 2068 | * You can call this function yourself to have the scroll view perform 2069 | * scrolling from a key event, just as if the event had been dispatched to 2070 | * it by the view hierarchy. 2071 | * 2072 | * @param event The key event to execute. 2073 | * @return Return true if the event was handled, else false. 2074 | */ 2075 | public boolean executeKeyEvent(KeyEvent event) { 2076 | boolean handled = false; 2077 | if (event.getAction() == KeyEvent.ACTION_DOWN) { 2078 | switch (event.getKeyCode()) { 2079 | case KeyEvent.KEYCODE_DPAD_UP: 2080 | handled = arrowScroll(FOCUS_UP); 2081 | break; 2082 | case KeyEvent.KEYCODE_DPAD_DOWN: 2083 | handled = arrowScroll(FOCUS_DOWN); 2084 | break; 2085 | case KeyEvent.KEYCODE_TAB: 2086 | if (Build.VERSION.SDK_INT >= 11) { 2087 | // The focus finder had a bug handling FOCUS_FORWARD and FOCUS_BACKWARD 2088 | // before Android 3.0. Ignore the tab key on those devices. 2089 | if (KeyEventCompat.hasNoModifiers(event)) { 2090 | handled = arrowScroll(FOCUS_FORWARD); 2091 | } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) { 2092 | handled = arrowScroll(FOCUS_BACKWARD); 2093 | } /* end of if */ 2094 | } /* end of if */ 2095 | break; 2096 | } /* end of switch */ 2097 | } /* end of if */ 2098 | return handled; 2099 | } 2100 | 2101 | //XXX 2102 | 2103 | /** 2104 | * Page keypad 2105 | * 2106 | * @param direction 2107 | * @return handled 2108 | */ 2109 | public boolean arrowScroll(int direction) { 2110 | View currentFocused = findFocus(); 2111 | if (currentFocused == this) currentFocused = null; 2112 | 2113 | boolean handled = false; 2114 | 2115 | View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, 2116 | direction); 2117 | if (nextFocused != null && nextFocused != currentFocused) { 2118 | if (direction == View.FOCUS_UP) { 2119 | // If there is nothing to the left, or this is causing us to 2120 | // jump to the down, then what we really want to do is page up. 2121 | if (currentFocused != null && nextFocused.getTop() >= currentFocused.getTop()) { 2122 | handled = pageUp(); 2123 | } else { 2124 | handled = nextFocused.requestFocus(); 2125 | } /* end of if */ 2126 | } else if (direction == View.FOCUS_DOWN) { 2127 | // If there is nothing to the right, or this is causing us to 2128 | // jump to the left, then what we really want to do is page right. 2129 | if (currentFocused != null && nextFocused.getTop() <= currentFocused.getTop()) { 2130 | handled = pageDown(); 2131 | } else { 2132 | handled = nextFocused.requestFocus(); 2133 | } /* end of if */ 2134 | } /* end of if */ 2135 | } else if (direction == FOCUS_UP || direction == FOCUS_BACKWARD) { 2136 | // Trying to move left and nothing there; try to page. 2137 | handled = pageUp(); 2138 | } else if (direction == FOCUS_DOWN || direction == FOCUS_FORWARD) { 2139 | // Trying to move right and nothing there; try to page. 2140 | handled = pageDown(); 2141 | } /* end of if */ 2142 | if (handled) { 2143 | playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction)); 2144 | } /* end of if */ 2145 | return handled; 2146 | } 2147 | 2148 | //XXX 2149 | boolean pageUp() { 2150 | if (mCurItem > 0) { 2151 | setCurrentItem(mCurItem - 1, true); 2152 | return true; 2153 | } /* end of if */ 2154 | return false; 2155 | } 2156 | 2157 | //XXX 2158 | boolean pageDown() { 2159 | if (mAdapter != null && mCurItem < (mAdapter.getCount() - 1)) { 2160 | setCurrentItem(mCurItem + 1, true); 2161 | return true; 2162 | } /* end of if */ 2163 | return false; 2164 | } 2165 | 2166 | /** 2167 | * We only want the current page that is being shown to be focusable. 2168 | */ 2169 | @Override 2170 | public void addFocusables(ArrayList views, int direction, int focusableMode) { 2171 | final int focusableCount = views.size(); 2172 | 2173 | final int descendantFocusability = getDescendantFocusability(); 2174 | 2175 | if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { 2176 | for (int i = 0; i < getChildCount(); i++) { 2177 | final View child = getChildAt(i); 2178 | if (child.getVisibility() == VISIBLE) { 2179 | ItemInfo ii = infoForChild(child); 2180 | if (ii != null && ii.position == mCurItem) { 2181 | child.addFocusables(views, direction, focusableMode); 2182 | } /* end of if */ 2183 | } /* end of if */ 2184 | } /* end of for */ 2185 | } /* end of if */ 2186 | 2187 | // we add ourselves (if focusable) in all cases except for when we are 2188 | // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is 2189 | // to avoid the focus search finding layouts when a more precise search 2190 | // among the focusable children would be more interesting. 2191 | if ( 2192 | descendantFocusability != FOCUS_AFTER_DESCENDANTS || 2193 | // No focusable descendants 2194 | (focusableCount == views.size())) { 2195 | // Note that we can't call the superclass here, because it will 2196 | // add all views in. So we need to do the same thing View does. 2197 | if (!isFocusable()) { 2198 | return; 2199 | } /* end of if */ 2200 | if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE && 2201 | isInTouchMode() && !isFocusableInTouchMode()) { 2202 | return; 2203 | } /* end of if */ 2204 | if (views != null) { 2205 | views.add(this); 2206 | } /* end of if */ 2207 | } /* end of if */ 2208 | } 2209 | 2210 | /** 2211 | * We only want the current page that is being shown to be touchable. 2212 | */ 2213 | @Override 2214 | public void addTouchables(ArrayList views) { 2215 | // Note that we don't call super.addTouchables(), which means that 2216 | // we don't call View.addTouchables(). This is okay because a ViewPager 2217 | // is itself not touchable. 2218 | for (int i = 0; i < getChildCount(); i++) { 2219 | final View child = getChildAt(i); 2220 | if (child.getVisibility() == VISIBLE) { 2221 | ItemInfo ii = infoForChild(child); 2222 | if (ii != null && ii.position == mCurItem) { 2223 | child.addTouchables(views); 2224 | } /* end of if */ 2225 | } /* end of if */ 2226 | } /* end of for */ 2227 | } 2228 | 2229 | /** 2230 | * We only want the current page that is being shown to be focusable. 2231 | */ 2232 | @Override 2233 | protected boolean onRequestFocusInDescendants(int direction, 2234 | Rect previouslyFocusedRect) { 2235 | int index; 2236 | int increment; 2237 | int end; 2238 | int count = getChildCount(); 2239 | if ((direction & FOCUS_FORWARD) != 0) { 2240 | index = 0; 2241 | increment = 1; 2242 | end = count; 2243 | } else { 2244 | index = count - 1; 2245 | increment = -1; 2246 | end = -1; 2247 | } /* end of if */ 2248 | for (int i = index; i != end; i += increment) { 2249 | View child = getChildAt(i); 2250 | if (child.getVisibility() == VISIBLE) { 2251 | ItemInfo ii = infoForChild(child); 2252 | if (ii != null && ii.position == mCurItem) { 2253 | if (child.requestFocus(direction, previouslyFocusedRect)) { 2254 | return true; 2255 | } /* end of if */ 2256 | } /* end of if */ 2257 | } /* end of if */ 2258 | } /* end of for */ 2259 | return false; 2260 | } 2261 | 2262 | @Override 2263 | public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 2264 | // ViewPagers should only report accessibility info for the current page, 2265 | // otherwise things get very confusing. 2266 | 2267 | // TODO: Should this note something about the paging container? 2268 | 2269 | final int childCount = getChildCount(); 2270 | for (int i = 0; i < childCount; i++) { 2271 | final View child = getChildAt(i); 2272 | if (child.getVisibility() == VISIBLE) { 2273 | final ItemInfo ii = infoForChild(child); 2274 | if (ii != null && ii.position == mCurItem && 2275 | child.dispatchPopulateAccessibilityEvent(event)) { 2276 | return true; 2277 | } /* end of if */ 2278 | } /* end of if */ 2279 | } /* end of for */ 2280 | 2281 | return false; 2282 | } 2283 | 2284 | @Override 2285 | protected ViewGroup.LayoutParams generateDefaultLayoutParams() { 2286 | return new LayoutParams(); 2287 | } 2288 | 2289 | @Override 2290 | protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 2291 | return generateDefaultLayoutParams(); 2292 | } 2293 | 2294 | @Override 2295 | protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 2296 | return p instanceof LayoutParams && super.checkLayoutParams(p); 2297 | } 2298 | 2299 | @Override 2300 | public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { 2301 | return new LayoutParams(getContext(), attrs); 2302 | } 2303 | 2304 | private class PagerObserver extends DataSetObserver { 2305 | @Override 2306 | public void onChanged() { 2307 | dataSetChanged(); 2308 | } 2309 | 2310 | @Override 2311 | public void onInvalidated() { 2312 | dataSetChanged(); 2313 | } 2314 | } 2315 | 2316 | /** 2317 | * Layout parameters that should be supplied for views added to a 2318 | * ViewPager. 2319 | */ 2320 | public static class LayoutParams extends ViewGroup.LayoutParams { 2321 | /** 2322 | * true if this view is a decoration on the pager itself and not 2323 | * a view supplied by the adapter. 2324 | */ 2325 | public boolean isDecor; 2326 | 2327 | /** 2328 | * Where to position the view page within the overall ViewPager 2329 | * container; constants are defined in {@link Gravity}. 2330 | */ 2331 | public int gravity; 2332 | 2333 | public LayoutParams() { 2334 | super(FILL_PARENT, FILL_PARENT); 2335 | } 2336 | 2337 | public LayoutParams(Context context, AttributeSet attrs) { 2338 | super(context, attrs); 2339 | 2340 | final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS); 2341 | gravity = a.getInteger(0, Gravity.NO_GRAVITY); 2342 | a.recycle(); 2343 | } 2344 | } 2345 | } 2346 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/layout/fragment_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | 21 | 22 | 23 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/layout/fragment_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/layout/fragment_pager.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/layout/fragment_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xhdpi/bg_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/bg_bottom.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_bottom_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_bottom_line.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home_line_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home_line_bottom.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home_line_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home_line_left.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home_line_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home_line_right.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_left_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_left_line.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_right_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_right_line.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/bg_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/bg_bottom.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/bg_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/bg_home.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_bottom_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_bottom_line.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_home_line_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_home_line_bottom.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_home_line_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_home_line_left.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_home_line_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_home_line_right.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_left_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_left_line.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_right_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_right_line.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #00000000 4 | #FF000000 5 | #FFF9F9F9 6 | #FFE9E9E9 7 | #FF666666 8 | #99333333 9 | #FF999999 10 | #FFCCCCCC 11 | 12 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BgMoveViewPager 3 | 4 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /BgMoveViewPager/app/src/test/java/com/zheblog/bgmoveviewpager/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.zheblog.bgmoveviewpager; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /BgMoveViewPager/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.1.2' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /BgMoveViewPager/gradle.properties: -------------------------------------------------------------------------------- 1 | # IDE (e.g. Android Studio) users: 2 | # Gradle settings configured through the IDE *will override* 3 | # any settings specified in this file. 4 | 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | 8 | # Specifies the JVM arguments used for the daemon process. 9 | # The setting is particularly useful for tweaking memory settings. 10 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 11 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 12 | 13 | # When configured, Gradle will run in incubating parallel mode. 14 | # This option should only be used with decoupled projects. More details, visit 15 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 16 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /BgMoveViewPager/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /BgMoveViewPager/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /BgMoveViewPager/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /BgMoveViewPager/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /BgMoveViewPager/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BgMoveViewPager # 2 | 背景图片跟随手势滑动的ViewPager,可各方向滑动或点击切换页面。 3 | 4 | 原效果参考 [ANA Portuguese Airports](https://play.google.com/store/apps/details?id=com.innovagency.ana)(google play地址) 5 | 6 | ### 效果图 ### 7 | 8 | 9 | ### 组成 ### 10 | 项目由一个Activity和五个fragment组成。 11 | 12 | ### 主要代码介绍 ### 13 | 1、横向背景移动的ViewPager中,重写dispatchDraw方法: 14 | 15 | ```` 16 | @Override 17 | protected void dispatchDraw(Canvas canvas) { 18 | if (this.bg != null) { 19 | int width = this.bg.getWidth(); 20 | int height = this.bg.getHeight(); 21 | int count = getAdapter().getCount(); 22 | int x = getScrollX(); 23 | //子View中背景图片需要显示的宽度,放大背景图或缩小背景图。 24 | int n = height * getWidth() / getHeight(); 25 | //(width - n) / (count - 1)表示除去显示第一个ViewPager页面用去的背景宽度,剩余的ViewPager需要显示的背景图片的宽度。 26 | //getWidth()等于ViewPager一个页面的宽度,即手机屏幕宽度。在该计算中可以理解为滑动一个ViewPager页面需要滑动的像素值。 27 | //((width - n) / (count - 1)) / getWidth()也就表示ViewPager滑动一个像素时,背景图片滑动的宽度。 28 | //x * ((width - n) / (count - 1)) / getWidth()也就表示ViewPager滑动x个像素时,背景图片滑动的宽度。 29 | //背景图片滑动的宽度的宽度可以理解为背景图片滑动到达的位置。 30 | int w = (x+getWidth()) * ((width - n) / (count - 1)) / getWidth(); 31 | canvas.drawBitmap(this.bg, new Rect(w, 0, n + w, height), new Rect(x, 0, x + getWidth(), getHeight()), this.b); 32 | } 33 | super.dispatchDraw(canvas); 34 | } 35 | ```` 36 | 2、IScrollListener中控制是否可以方向滚动 37 | 38 | ```` 39 | void canScrollView(boolean isCanScroll); 40 | ```` 41 | 3、添加FixedSpeedScroller类(继承Scroller),控制ViewPager调用setCurrentItem方法时的滚动速度。 42 | 43 | ```` 44 | private int mDuration = 800; // 默认为800ms 45 | @Override 46 | public void startScroll(int startX, int startY, int dx, int dy, int duration) { 47 | super.startScroll(startX, startY, dx, dy, mDuration); 48 | } 49 | 50 | @Override 51 | public void startScroll(int startX, int startY, int dx, int dy) { 52 | super.startScroll(startX, startY, dx, dy, mDuration); 53 | } 54 | ```` 55 | 56 | ### 最后 ### 57 | 58 | 如果对您有帮助请Star,有问题随时联系我,谢谢. 59 | 60 | ### 关于我 ### 61 | QQ交流群: 496946393 62 | 63 | 邮箱: nh_zhe@163.com 64 | 65 | [简书](http://www.jianshu.com/users/550d52af9d72/latest_articles) 66 | 67 | [个人博客](http://www.zheblog.com) -------------------------------------------------------------------------------- /gif/BgMove.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/gif/BgMove.gif --------------------------------------------------------------------------------