├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── listen │ │ └── test_mogu_view │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── listen │ │ │ └── test_mogu_view │ │ │ ├── MainActivity.java │ │ │ └── viewpager │ │ │ ├── MoguViewPager.java │ │ │ ├── MoguViewPagerAdapter.java │ │ │ ├── MoguViewPagerTag1.java │ │ │ ├── TransforView.java │ │ │ ├── TransforViewTag1.java │ │ │ ├── TransforViewTag2.java │ │ │ └── ViewModel.java │ └── res │ │ ├── drawable-xhdpi │ │ ├── four_bg.png │ │ ├── four_bottom_1.png │ │ ├── four_bottom_2.png │ │ ├── four_bottom_3.png │ │ ├── four_top.png │ │ ├── one_bottom.png │ │ ├── one_bottom_bg.png │ │ ├── one_top.png │ │ ├── three_1.png │ │ ├── three_2.png │ │ ├── three_3.png │ │ ├── three_4.png │ │ ├── three_5.png │ │ ├── three_6.png │ │ ├── two_1.png │ │ ├── two_2.png │ │ ├── two_3.png │ │ ├── two_bg.png │ │ ├── two_bottom.png │ │ └── two_top.png │ │ ├── drawable │ │ └── select_btn.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── guide_view_four.xml │ │ ├── guide_view_one.xml │ │ ├── guide_view_three.xml │ │ ├── guide_view_two.xml │ │ └── layout_mogu_viewpager.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── listen │ └── test_mogu_view │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties └── settings.gradle /README.md: -------------------------------------------------------------------------------- 1 | 2 | README -> [高仿蘑菇街欢迎页](https://www.jianshu.com/p/9dd4027c2107) 3 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion "25.0.0" 6 | defaultConfig { 7 | applicationId "com.listen.test_mogu_view" 8 | minSdkVersion 16 9 | targetSdkVersion 24 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:24.2.1' 28 | testCompile 'junit:junit:4.12' 29 | } 30 | -------------------------------------------------------------------------------- /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 /Users/lisong/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/listen/test_mogu_view/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.listen.test_mogu_view; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.listen.test_mogu_view", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/listen/test_mogu_view/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.listen.test_mogu_view; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.Window; 6 | import android.view.WindowManager; 7 | 8 | public class MainActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | requestWindowFeature(Window.FEATURE_NO_TITLE); 14 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 15 | setContentView(R.layout.activity_main); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/listen/test_mogu_view/viewpager/MoguViewPager.java: -------------------------------------------------------------------------------- 1 | package com.listen.test_mogu_view.viewpager; 2 | 3 | import android.content.Context; 4 | import android.support.v4.view.ViewPager; 5 | import android.util.AttributeSet; 6 | import android.view.View; 7 | import android.widget.RelativeLayout; 8 | 9 | import com.listen.test_mogu_view.R; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | 15 | public class MoguViewPager extends RelativeLayout { 16 | 17 | private MoguViewPagerAdapter mAdapter; 18 | private ViewPager mViewPager; 19 | private TransforView mTransforView; 20 | private List mViewList = new ArrayList<>(); 21 | private int[] mLayouts = new int[] {R.layout.guide_view_one, R.layout.guide_view_two, R.layout.guide_view_three, 22 | R.layout.guide_view_four}; 23 | 24 | public MoguViewPager(Context context) { 25 | super(context); 26 | init(); 27 | } 28 | 29 | public MoguViewPager(Context context, AttributeSet attrs) { 30 | super(context, attrs); 31 | init(); 32 | } 33 | 34 | private void init() { 35 | inflate(getContext(), R.layout.layout_mogu_viewpager, this); 36 | 37 | mViewPager = (ViewPager) this.findViewById(R.id.viewpager); 38 | mTransforView = (TransforView) this.findViewById(R.id.transfor_view); 39 | 40 | { 41 | /** 初始化4个页面 */ 42 | for (int i = 0; i < mLayouts.length; i++) { 43 | View view = View.inflate(getContext(), mLayouts[i], null); 44 | mViewList.add(view); 45 | } 46 | } 47 | 48 | mAdapter = new MoguViewPagerAdapter(mViewList, getContext()); 49 | mViewPager.setAdapter(mAdapter); 50 | mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 51 | @Override 52 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 53 | mTransforView.transfor(position, positionOffset, positionOffsetPixels); 54 | } 55 | 56 | @Override 57 | public void onPageSelected(int position) { 58 | 59 | } 60 | 61 | @Override 62 | public void onPageScrollStateChanged(int state) { 63 | 64 | } 65 | }); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/listen/test_mogu_view/viewpager/MoguViewPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.listen.test_mogu_view.viewpager; 2 | 3 | import android.content.Context; 4 | import android.support.v4.view.PagerAdapter; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import java.util.List; 9 | 10 | public class MoguViewPagerAdapter extends PagerAdapter { 11 | 12 | private List mViewList; 13 | private Context mContext; 14 | 15 | public MoguViewPagerAdapter(List mImageViewList, Context context) { 16 | mViewList = mImageViewList; 17 | mContext = context; 18 | } 19 | 20 | @Override 21 | public void destroyItem(ViewGroup container, int position, Object object) { 22 | container.removeView(mViewList.get(position)); 23 | } 24 | 25 | @Override 26 | public Object instantiateItem(ViewGroup container, int position) { 27 | View view = mViewList.get(position); 28 | container.addView(view); 29 | return view; 30 | } 31 | 32 | @Override 33 | public int getCount() { 34 | return mViewList == null ? 0 : mViewList.size(); 35 | } 36 | 37 | @Override 38 | public boolean isViewFromObject(View view, Object object) { 39 | return view == object; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/listen/test_mogu_view/viewpager/MoguViewPagerTag1.java: -------------------------------------------------------------------------------- 1 | package com.listen.test_mogu_view.viewpager; 2 | 3 | import android.content.Context; 4 | import android.support.v4.view.ViewPager; 5 | import android.util.AttributeSet; 6 | import android.view.View; 7 | import android.widget.RelativeLayout; 8 | 9 | import com.listen.test_mogu_view.R; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | 15 | public class MoguViewPagerTag1 extends RelativeLayout { 16 | 17 | private MoguViewPagerAdapter mAdapter; 18 | private ViewPager mViewPager; 19 | private List mViewList = new ArrayList<>(); 20 | private int[] mLayouts = new int[] {R.layout.guide_view_one, R.layout.guide_view_two, R.layout.guide_view_three, 21 | R.layout.guide_view_four}; 22 | 23 | public MoguViewPagerTag1(Context context) { 24 | super(context); 25 | init(); 26 | } 27 | 28 | public MoguViewPagerTag1(Context context, AttributeSet attrs) { 29 | super(context, attrs); 30 | init(); 31 | } 32 | 33 | private void init() { 34 | inflate(getContext(), R.layout.layout_mogu_viewpager, this); 35 | mViewPager = (ViewPager) this.findViewById(R.id.viewpager); 36 | { 37 | /** 初始化4个页面 */ 38 | for (int i = 0; i < mLayouts.length; i++) { 39 | View view = View.inflate(getContext(), mLayouts[i], null); 40 | mViewList.add(view); 41 | } 42 | } 43 | 44 | mAdapter = new MoguViewPagerAdapter(mViewList, getContext()); 45 | mViewPager.setAdapter(mAdapter); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/listen/test_mogu_view/viewpager/TransforView.java: -------------------------------------------------------------------------------- 1 | package com.listen.test_mogu_view.viewpager; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.RectF; 8 | import android.support.annotation.NonNull; 9 | import android.util.AttributeSet; 10 | import android.util.Log; 11 | import android.view.View; 12 | 13 | import com.listen.test_mogu_view.R; 14 | 15 | 16 | public class TransforView extends View { 17 | 18 | // ///////////////////////////////////////////////////////////////////////// 19 | // 页面切换时的,白色矩形背景的变化参数 20 | // ///////////////////////////////////////////////////////////////////////// 21 | /** 22 | * 第1页->第2页 23 | */ 24 | public static final float FIRST_HEIGHT = 0.4f;// 第1个页面高度缩放比例,正:放大,负:缩小 25 | public final int FIRST_TOP1 = -dp2px(30);// 第1个页面top移动距离,正:下移,负:上移 26 | public final int FIRST_TOP2 = dp2px(60);// 第1个页面top移动距离,正:下移,负:上移 27 | public static final float FIRST_RATE = 0.5f;// 在偏移50%处,进行下一页的显示 28 | /** 29 | * 第2页->第3页 30 | */ 31 | public static final float SECOND_WIDTH = -0.15f;// 第2个页面宽度缩放比例,正:放大,负:缩小 32 | public final int SECOND_TOP = -dp2px(20);// 第2个页面top移动距离比例,正:下移,负:上移 33 | public static final float SECOND_RATE = 0.5f;// 在偏移50%处,进行下一页的显示 34 | /** 35 | * 第3页->第4页 36 | */ 37 | public static final float THIRD_WIDTH = -0.1f;// 第3个页面宽度缩放比例,正:放大,负:缩小 38 | public static final float THIRD_HEIGHT = -0.1f;// 第3个页面高度缩放比例,正:放大,负:缩小 39 | public static final int THIRD_DEGREE = -10;// 第3个页面角度调整,正:顺时针,负:逆时针 40 | public static final float THIRD_RATE = 0.5f;// 在偏移50%处,进行下一页的显示 41 | 42 | // ///////////////////////////////////////////////////////////////////////// 43 | // 白色圆角矩形背景 44 | // ///////////////////////////////////////////////////////////////////////// 45 | private Paint mRectBgPaint; 46 | private int mRectBgDefaultCorner = dp2px(5);// 初始圆角5dp 47 | private float mRectBgCurrentDegree = 0;// 默认不旋转 48 | 49 | /** 50 | * 第1页初始化 51 | * default=默认初始宽高,left,top,如果是2,3,4页,则表示变化后的值 52 | * current=当前宽高,left,top,会根据偏移量变化 53 | */ 54 | private float mPage1RectBgDefaultWidth = dp2px(260); 55 | private float mPage1RectBgDefaultHeight = dp2px(230); 56 | private float mPage1RectBgDefaultLeft = getScreenWidth() / 2 - mPage1RectBgDefaultWidth / 2; 57 | private float mPage1RectBgDefaultTop = dp2px(80); 58 | 59 | private float mRectBgCurrentWidth = mPage1RectBgDefaultWidth; 60 | private float mRectBgCurrentHeight = mPage1RectBgDefaultHeight; 61 | private float mRectBgCurrentLeft = mPage1RectBgDefaultLeft; 62 | private float mRectBgCurrentTop = mPage1RectBgDefaultTop; 63 | 64 | /** 65 | * 第1页->第2页 66 | * 在第1页的基础上进行变化 67 | * 1.height放大 68 | * 2.top先上移n,在下移n*2 69 | */ 70 | private float mPage2RectBgDefaultWidth = mPage1RectBgDefaultWidth; 71 | private float mPage2RectBgDefaultHeight = mPage1RectBgDefaultHeight * (1 + FIRST_HEIGHT); 72 | private float mPage2RectBgDefaultLeft = mPage1RectBgDefaultLeft; 73 | private float mPage2RectBgDefaultTop = mPage1RectBgDefaultTop + FIRST_TOP1 + FIRST_TOP2; 74 | 75 | /** 76 | * 第2页->第3页 77 | * 在第2页的基础上进行变化 78 | * 1.宽度缩小 79 | * 2.top上移 80 | */ 81 | private float mPage3RectBgDefaultWidth = mPage2RectBgDefaultWidth * (1 + SECOND_WIDTH); 82 | private float mPage3RectBgDefaultHeight = mPage2RectBgDefaultHeight; 83 | private float mPage3RectBgDefaultLeft = getScreenWidth() / 2 - mPage3RectBgDefaultWidth / 2; 84 | private float mPage3RectBgDefaultTop = mPage2RectBgDefaultTop + SECOND_TOP; 85 | 86 | /** 87 | * 第3页->第4页 88 | * 在第3页的基础上进行变化 89 | * 1.宽度缩小 90 | * 2.高度缩小 91 | * 2.逆时针旋转 92 | */ 93 | private float mPage4RectBgDefaultWidth = mPage3RectBgDefaultWidth * (1 + THIRD_WIDTH); 94 | private float mPage4RectBgDefaultHeight = mPage3RectBgDefaultHeight * (1 + THIRD_HEIGHT); 95 | private float mPage4RectBgDefaultLeft = getScreenWidth() / 2 - mPage4RectBgDefaultWidth / 2; 96 | private float mPage4RectBgDefaultTop = mPage3RectBgDefaultTop; 97 | private float mPage4ModelDefaultWidth = (mPage4RectBgDefaultWidth - padding() * 4) / 3; 98 | 99 | // ///////////////////////////////////////////////////////////////////////// 100 | // 第1页,view元素 101 | // ///////////////////////////////////////////////////////////////////////// 102 | private ViewModel mPage1Top;// 顶部模特图 103 | private ViewModel mPage1Bottom;// 底部文案图 104 | private ViewModel mPage1BottomBg;// 底部背景图 105 | 106 | // ///////////////////////////////////////////////////////////////////////// 107 | // 第2页,view元素 108 | // ///////////////////////////////////////////////////////////////////////// 109 | private int[] mPage2ModelResources = new int[] {R.drawable.two_1, R.drawable.two_2, R.drawable.two_3}; 110 | private ViewModel mPage2Top;// 顶部文案图 111 | private ViewModel[] mPage2Center = new ViewModel[mPage2ModelResources.length];// 中间3张模特图 112 | private ViewModel mPage2Bottom;// 底部文案图 113 | private ViewModel[] mPage2Split = new ViewModel[2];// 2张裂变背景图 114 | 115 | // ///////////////////////////////////////////////////////////////////////// 116 | // 第3页,view元素 117 | // ///////////////////////////////////////////////////////////////////////// 118 | private int[] mPage3ModelResources = new int[] {R.drawable.three_1, R.drawable.three_2, R.drawable.three_3, 119 | R.drawable.three_4, R.drawable.three_5, R.drawable.three_6}; 120 | private ViewModel[] mPage3Model = new ViewModel[mPage3ModelResources.length];// 6张模特图 121 | private float mPage3ModelDefaultWidth = (mPage3RectBgDefaultWidth - padding() * 4) / 3;// 每张模特图的默认宽度 122 | private float mPage3ModelDefaultHeight = (mPage3RectBgDefaultHeight - padding() * 3) / 2;// 每张模特图的默认高度 123 | 124 | // ///////////////////////////////////////////////////////////////////////// 125 | // 第4页,view元素 126 | // ///////////////////////////////////////////////////////////////////////// 127 | private int[] mPage4ModelResources = new int[] {R.drawable.four_bottom_1, R.drawable.four_bottom_2, 128 | R.drawable.four_bottom_3}; 129 | private ViewModel mPage4Top;// 顶部模特图 130 | private ViewModel[] mPage4Model = new ViewModel[mPage4ModelResources.length];// 底部3张模特图 131 | private ViewModel[] mPage4Split = new ViewModel[2];// 2张裂变背景图 132 | // private float mPage4SplitTargeOffset = 133 | 134 | /** 135 | * 当前页数索引 136 | */ 137 | private int mCurrentPageIndex; 138 | 139 | public TransforView(Context context) { 140 | super(context); 141 | init(); 142 | } 143 | 144 | public TransforView(Context context, AttributeSet attrs) { 145 | super(context, attrs); 146 | init(); 147 | } 148 | 149 | public TransforView(Context context, AttributeSet attrs, int defStyle) { 150 | super(context, attrs, defStyle); 151 | init(); 152 | } 153 | 154 | public void transfor(int position, float positionOffset, int positionOffsetPixels) { 155 | Log.e("ls_debug_log", "p=" + position + ",p_offset=" + positionOffset + ",p_px=" + positionOffsetPixels); 156 | mCurrentPageIndex = position; 157 | calculateOffset(position, positionOffset, positionOffsetPixels); 158 | } 159 | 160 | private void init() { 161 | { 162 | /** 第1页,初始化矩形背景 */ 163 | mRectBgPaint = new Paint(); 164 | mRectBgPaint.setAntiAlias(true); // 设置画笔为无锯齿 165 | mRectBgPaint.setColor(Color.BLACK); // 设置画笔颜色 166 | mRectBgPaint.setStyle(Paint.Style.FILL); // 空心效果 167 | } 168 | { 169 | /** 第1页,顶部模特图 */ 170 | mPage1Top = new ViewModel(getContext(), R.drawable.one_top).alpha(255) 171 | // 宽度为矩形背景宽度,减去左,右,各5dp边距 172 | .width(mPage1RectBgDefaultWidth - padding() * 2) 173 | // 高度为矩形背景的高度,减去上,中,下各5边距后剩余的高度的2/3 174 | .height((mPage1RectBgDefaultHeight - padding() * 3) / 3 * 2) 175 | .left(mPage1RectBgDefaultLeft + padding()) 176 | .top(mPage1RectBgDefaultTop + padding()) 177 | .create(); 178 | 179 | /** 第1页,底部文案图 */ 180 | mPage1Bottom = new ViewModel(getContext(), R.drawable.one_bottom).alpha(255) 181 | // 宽度为矩形背景宽度,减去左,右,各5dp边距 182 | .width(mPage1RectBgDefaultWidth - padding() * 2) 183 | // 高度为矩形背景的高度,减去上,中,下各5dp边距后剩余的高度的1/3 184 | .height((mPage1RectBgDefaultHeight - padding() * 3) / 3) 185 | .left(mPage1RectBgDefaultLeft + padding()) 186 | // top距离=顶部模特图top+height+5dp边距 187 | .top(mPage1Top.defaultTop + mPage1Top.defaultHeight + padding()) 188 | .create(); 189 | 190 | /** 第1页,底部背景图 */ 191 | mPage1BottomBg = 192 | new ViewModel(getContext(), R.drawable.one_bottom_bg).alpha(255) 193 | .width(mPage1RectBgDefaultWidth - padding() * 2) 194 | // .height()// 不传则按宽度比例缩放 195 | .left(mPage1RectBgDefaultLeft + padding()) 196 | // top距离=矩形背景top+height+5dp边距 197 | .top(mPage1RectBgDefaultTop + mPage1RectBgDefaultHeight + padding()) 198 | .create(); 199 | } 200 | 201 | { 202 | 203 | /** 第2页,顶部文案图 */ 204 | mPage2Top = new ViewModel(getContext(), R.drawable.two_top).width(mPage2RectBgDefaultWidth - padding() * 2) 205 | // .height()// 不传则按宽度比例缩放 206 | .left(mPage2RectBgDefaultLeft + padding()) 207 | .top(mPage2RectBgDefaultTop + padding()) 208 | .create(); 209 | 210 | /** 第2页,中间3张模特图 */ 211 | for (int i = 0; i < mPage2ModelResources.length; i++) { 212 | mPage2Center[i] = new ViewModel(getContext(), mPage2ModelResources[i]); 213 | if (i == 0) { 214 | /** 第1张模特图 */ 215 | mPage2Center[i].width((mPage2RectBgDefaultWidth - dp2px(15)) / 3 * 2) 216 | .height(mPage2Center[i].defaultWidth + dp2px(5)) 217 | .left(mPage2RectBgDefaultLeft + padding()) 218 | .top(mPage2Top.defaultTop + mPage2Top.defaultHeight + padding()) 219 | .create(); 220 | } else if (i == 1) { 221 | /** 第2张模特图 */ 222 | mPage2Center[i].width((mPage2RectBgDefaultWidth - dp2px(15)) / 3) 223 | .height(mPage2Center[i].defaultWidth) 224 | .left(mPage2Center[0].defaultLeft + mPage2Center[0].defaultWidth + padding()) 225 | .top(mPage2Top.defaultTop + mPage2Top.defaultHeight + padding()) 226 | .create(); 227 | } else if (i == 2) { 228 | /** 第3张模特图 */ 229 | mPage2Center[i].width((mPage2RectBgDefaultWidth - dp2px(15)) / 3) 230 | .height(mPage2Center[i].defaultWidth) 231 | .left(mPage2Center[0].defaultLeft + mPage2Center[0].defaultWidth + padding()) 232 | .top(mPage2Center[1].defaultTop + mPage2Center[1].defaultHeight + padding()) 233 | .create(); 234 | } 235 | } 236 | 237 | /** 第2页,底部文案图 */ 238 | mPage2Bottom = 239 | new ViewModel(getContext(), R.drawable.two_bottom).width(mPage2RectBgDefaultWidth - padding() * 2) 240 | .height( 241 | mPage2RectBgDefaultHeight - mPage2Top.defaultHeight - mPage2Center[0].defaultHeight - padding() 242 | * 4) 243 | .left(mPage2RectBgDefaultLeft + padding()) 244 | .top(mPage2Center[0].defaultTop + mPage2Center[0].defaultHeight + padding()) 245 | .create(); 246 | 247 | /** 第2页,裂变背景图 */ 248 | for (int i = 0; i < 2; i++) { 249 | mPage2Split[i] = 250 | new ViewModel(getContext(), R.drawable.two_bg).width(mPage2RectBgDefaultWidth) 251 | .height(mPage2RectBgDefaultHeight) 252 | .left(mPage2RectBgDefaultLeft) 253 | .top(mPage2RectBgDefaultTop) 254 | .create(); 255 | 256 | } 257 | } 258 | 259 | { 260 | /** 第3页,6张模特图 */ 261 | for (int i = 0; i < mPage3Model.length; i++) { 262 | if (i < mPage3Model.length / 2) { 263 | /** 第3页,第1排3张模特图 */ 264 | mPage3Model[i] = 265 | new ViewModel(getContext(), mPage3ModelResources[i]).width(mPage3ModelDefaultWidth) 266 | .height(mPage3ModelDefaultHeight) 267 | .left(mPage3RectBgDefaultLeft + mPage3ModelDefaultWidth * i + padding() * (i + 1)) 268 | .top(mPage3RectBgDefaultTop + padding()) 269 | .create(); 270 | } else { 271 | /** 第3页,第2排3张模特图 */ 272 | mPage3Model[i] = 273 | new ViewModel(getContext(), mPage3ModelResources[i]).width(mPage3ModelDefaultWidth) 274 | .height(mPage3ModelDefaultHeight) 275 | .left( 276 | mPage3RectBgDefaultLeft + mPage3ModelDefaultWidth * (i - mPage3Model.length / 2) 277 | + padding() * ((i - mPage3Model.length / 2) + 1)) 278 | // 与第1排第区别在于top需要加上第1排的height 279 | .top(mPage3RectBgDefaultTop + mPage3Model[i - 1].defaultHeight + padding() * 2) 280 | .create(); 281 | } 282 | mPage3Model[i].paint.setAntiAlias(true); 283 | 284 | } 285 | } 286 | 287 | { 288 | /** 第4页,顶部模特图 */ 289 | mPage4Top = 290 | new ViewModel(getContext(), R.drawable.four_top).width(mPage4RectBgDefaultWidth - padding() * 2) 291 | .left(mPage4RectBgDefaultLeft + padding()) 292 | .top(mPage4RectBgDefaultTop + padding()) 293 | .create(); 294 | mPage4Top.paint.setAntiAlias(true); 295 | 296 | /** 第4页,底部3张模特图 */ 297 | for (int i = 0; i < mPage4ModelResources.length; i++) { 298 | mPage4Model[i] = 299 | new ViewModel(getContext(), mPage4ModelResources[i]).width(mPage4ModelDefaultWidth) 300 | .height(mPage4RectBgDefaultHeight - padding() * 3 - mPage4Top.defaultHeight) 301 | .left(mPage4RectBgDefaultLeft + mPage4ModelDefaultWidth * i + padding() * (i + 1)) 302 | .top(mPage4Top.defaultTop + mPage4Top.defaultHeight + padding()); 303 | mPage4Model[i].paint.setAntiAlias(true); 304 | } 305 | 306 | /** 第4页,2张裂变背景图 */ 307 | for (int i = 0; i < mPage4Split.length; i++) { 308 | mPage4Split[i] = new ViewModel(getContext(), R.drawable.four_bg) 309 | .width(mPage4RectBgDefaultWidth) 310 | .height(mPage4RectBgDefaultHeight) 311 | .left(mPage4RectBgDefaultLeft) 312 | .top(mPage4RectBgDefaultTop); 313 | 314 | } 315 | } 316 | } 317 | 318 | /** 319 | * @desc 根据viewPager偏移,修改view的宽,高,left,top 320 | * @author listen 321 | */ 322 | private void calculateOffset(int position, float positionOffset, int positionOffsetPixels) { 323 | if (positionOffset <= 0) { 324 | return; 325 | } 326 | if (fromPage1ToPage2(position)) { 327 | /** 第1页,底部背景图,根据页面向左偏移 */ 328 | mPage1BottomBg.currentLeft = mPage1BottomBg.defaultLeft - positionOffsetPixels; 329 | 330 | if (positionOffset < FIRST_RATE) { 331 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里重新设置一次,保证第2页的view不可见 */ 332 | hidePage2Views(); 333 | 334 | /** 第1页,在0->50%区间偏移 */ 335 | /** 矩形背景,高度放大40%,向上移动30dp */ 336 | transformRectBgFrom1To2Before(positionOffset); 337 | 338 | /** 第1页,渐渐隐顶部图,底部图;透明度渐变消失,偏移到50%时完全消失 */ 339 | stepByHidePage1Views(positionOffset); 340 | 341 | } else { 342 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次。保证第1页的view不可见 */ 343 | hidePage1Views(); 344 | 345 | /** 第1页,在50%->100%区间偏移 */ 346 | /** 矩形背景,上移30dp后,向下偏移60dp */ 347 | transformRectBgFrom1To2After(positionOffset); 348 | 349 | /** 第2页,渐渐显示顶部,3张模特图,底部图 */ 350 | stepByShowPage2Views(positionOffset); 351 | 352 | } 353 | } else if (fromPage2ToPage3(position)) { 354 | /** 矩形背景,宽度缩小15%,上移20dp */ 355 | transformRectBgFrom2To3(positionOffset); 356 | 357 | if (positionOffset < SECOND_RATE) { 358 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次。保证第3页滑回到第2页时,第3页的view不可见 */ 359 | hidePage3Views(); 360 | /** 第2页,在0->50%区间偏移,渐渐隐藏顶部,中间,底部,裂变背景图 */ 361 | stepByHidePage2Views(positionOffset, positionOffsetPixels); 362 | } else { 363 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次,保证第2页的view不可见 */ 364 | hidePage2Views(); 365 | /** 第2页,在50->100%区间偏移,渐渐显示第3页,6张模特图 */ 366 | stepByShowPage3Views(positionOffset); 367 | } 368 | } else if (fromPage3ToPage4(position)) { 369 | 370 | /** 背景矩形的宽度,高度减少10%,逆时针旋转10度 */ 371 | transformRectBgFrom3To4(positionOffset); 372 | 373 | if (positionOffset < THIRD_RATE) { 374 | 375 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次,保证第4页的view不可见 */ 376 | hidePage4Views(); 377 | 378 | /** 渐渐缩放,隐藏第3页,6张模特图 */ 379 | stepByHidePage3Views(positionOffset); 380 | 381 | } else { 382 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次,保证第3页的view缩放完成 */ 383 | scaleHidePage3Views(); 384 | 385 | /** 渐渐显示第4页,顶部图,底部3张模特图,分裂背景图 */ 386 | stepByShowPage4Views(positionOffset); 387 | } 388 | } 389 | postInvalidate(); 390 | } 391 | 392 | private void stepByShowPage4Views(float positionOffset) { 393 | /** 显示第4页,裂变背景图,并向左右平移 */ 394 | float offset = (mPage4RectBgDefaultWidth + dp2px(40)) * ((positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE))); 395 | for (int i = 0; i < mPage4Split.length; i++) { 396 | mPage4Split[i].matrix.reset(); 397 | mPage4Split[i].matrix.postScale(mPage4RectBgDefaultWidth / mPage4Split[i].bitmap.getWidth(), mPage4RectBgDefaultHeight / mPage4Split[i].bitmap.getHeight()); 398 | 399 | float currentLeft = 0; 400 | if (i == 0) { 401 | // 左移 402 | currentLeft = mPage4RectBgDefaultLeft - offset; 403 | } else if (i == 1) { 404 | // 右移 405 | currentLeft = mPage4RectBgDefaultLeft + offset; 406 | } 407 | 408 | // 平移 409 | mPage4Split[i].matrix.postTranslate(currentLeft, mPage4RectBgDefaultTop); 410 | // 旋转角度 411 | mPage4Split[i].matrix.postRotate(THIRD_DEGREE, currentLeft + mPage4RectBgDefaultWidth/2, 412 | mPage4RectBgDefaultTop + mPage4RectBgDefaultHeight/2); 413 | 414 | mPage4Split[i].alpha((int) (255 * ((positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE))))); 415 | } 416 | 417 | /** 显示第4页,顶部模特图 */ 418 | mPage4Top.alpha((int) (255 * ((positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE))))); 419 | 420 | /** 显示第4页,底部3张模特图 */ 421 | for (int i = 0; i < mPage4Model.length; i++) { 422 | if (i == 0) { 423 | mPage4Model[i].currentWidth = 424 | mPage4Model[i].defaultWidth * ((positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE))); 425 | mPage4Model[i].currentHeight = 426 | mPage4Model[i].defaultHeight * ((positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE))); 427 | mPage4Model[i].alpha((int) (255 * (positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE)))); 428 | } else { 429 | if (mPage4Model[i - 1].currentWidth >= mPage4ModelDefaultWidth / 2) { 430 | mPage4Model[i].currentWidth = 431 | mPage4Model[i].defaultWidth * ((mPage4Model[i - 1].widthRate() - 0.5f) * 2); 432 | mPage4Model[i].currentHeight = 433 | mPage4Model[i].defaultHeight * ((mPage4Model[i - 1].widthRate() - 0.5f) * 2); 434 | mPage4Model[i].currentLeft = 435 | mPage4Model[i - 1].currentLeft + mPage4Model[i - 1].currentWidth + padding(); 436 | mPage4Model[i].alpha((int) (255 * (mPage4Model[i - 1].widthRate() - 0.5f) * 2)); 437 | } 438 | } 439 | } 440 | } 441 | 442 | private void transformRectBgFrom3To4(float positionOffset) { 443 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次,保证变化完成 */ 444 | mRectBgCurrentWidth = mPage3RectBgDefaultWidth; 445 | mRectBgCurrentTop = mPage3RectBgDefaultTop; 446 | 447 | /** 调整第4页,背景矩形的宽高和角度 */ 448 | /** 背景矩形的宽度,在第3页调整宽度的基础上进行缩小 */ 449 | mRectBgCurrentWidth = mPage3RectBgDefaultWidth * (1 + THIRD_WIDTH * positionOffset); 450 | mRectBgCurrentLeft = getScreenWidth() / 2 - mRectBgCurrentWidth / 2; 451 | /** 背景矩形的高度,在第2页调整高度的基础上进行缩小 */ 452 | mRectBgCurrentHeight = mPage3RectBgDefaultHeight * (1 + THIRD_HEIGHT * positionOffset); 453 | /** 背景矩形逆时针旋转 */ 454 | mRectBgCurrentDegree = THIRD_DEGREE * positionOffset; 455 | } 456 | 457 | private void stepByHidePage3Views(float positionOffset) { 458 | /** 隐藏第3页6张模特图 */ 459 | /** 从第1排,第3-1张开始,依次缩放 */ 460 | for (int i = mPage3ModelResources.length / 2 - 1; i >= 0; i--) { 461 | if (i == mPage3ModelResources.length / 2 - 1) { 462 | /** 如果是第1排,第3张,则开始缩放 */ 463 | mPage3Model[i].currentHeight = 464 | mPage3Model[i].defaultHeight * (1 - positionOffset * (1 / (1 - THIRD_RATE))); 465 | mPage3Model[i].currentWidth = 466 | mPage3Model[i].defaultWidth * (1 - positionOffset * (1 / (1 - THIRD_RATE))); 467 | } else { 468 | /** 如果是第1排,第1/2张,则判断后1张缩放到一半的时候开始自己的缩放 */ 469 | if (mPage3Model[i + 1].currentHeight <= mPage3Model[i + 1].defaultHeight / 2) { 470 | mPage3Model[i].currentHeight = mPage3Model[i].defaultHeight * mPage3Model[i + 1].heightRate() * 2; 471 | mPage3Model[i].currentWidth = mPage3Model[i].defaultWidth * mPage3Model[i + 1].heightRate() * 2; 472 | } else { 473 | mPage3Model[i].currentHeight = mPage3Model[i].defaultHeight; 474 | mPage3Model[i].currentWidth = mPage3Model[i].defaultWidth; 475 | } 476 | } 477 | 478 | /** 跳转left,top,实现居中缩放 */ 479 | mPage3Model[i].currentLeft = 480 | mPage3Model[i].defaultLeft + mPage3Model[i].defaultWidth / 2 - mPage3Model[i].currentWidth / 2; 481 | mPage3Model[i].currentTop = 482 | mPage3Model[i].defaultTop + mPage3Model[i].defaultHeight / 2 - mPage3Model[i].currentHeight / 2; 483 | } 484 | 485 | /** 从第1排,第4-6张开始,依次缩放 */ 486 | for (int i = mPage3ModelResources.length / 2; i < mPage3ModelResources.length; i++) { 487 | if (i == mPage3ModelResources.length / 2) { 488 | /** 如果是第2排,第1张,则开始缩放 */ 489 | mPage3Model[i].currentHeight = 490 | mPage3Model[i].defaultHeight * (1 - positionOffset * (1 / (1 - THIRD_RATE))); 491 | mPage3Model[i].currentWidth = 492 | mPage3Model[i].defaultWidth * (1 - positionOffset * (1 / (1 - THIRD_RATE))); 493 | } else { 494 | /** 如果是第2排,第5/6张,则判断前1张缩放到一半的时候开始自己的缩放 */ 495 | if (mPage3Model[i - 1].currentHeight <= mPage3Model[i - 1].defaultHeight / 2) { 496 | mPage3Model[i].currentHeight = mPage3Model[i].defaultHeight * mPage3Model[i - 1].heightRate() * 2; 497 | mPage3Model[i].currentWidth = mPage3Model[i].defaultWidth * mPage3Model[i - 1].heightRate() * 2; 498 | } else { 499 | mPage3Model[i].currentHeight = mPage3Model[i].defaultHeight; 500 | mPage3Model[i].currentWidth = mPage3Model[i].defaultWidth; 501 | } 502 | } 503 | 504 | /** 跳转left,top,实现居中缩放 */ 505 | mPage3Model[i].currentLeft = 506 | mPage3Model[i].defaultLeft + mPage3Model[i].defaultWidth / 2 - mPage3Model[i].currentWidth / 2; 507 | mPage3Model[i].currentTop = 508 | mPage3Model[i].defaultTop + mPage3Model[i].defaultHeight / 2 - mPage3Model[i].currentHeight / 2; 509 | } 510 | } 511 | 512 | private void transformRectBgFrom1To2Before(float positionOffset) { 513 | /** 矩形背景,高度放大40% */ 514 | /** 515 | * 偏移到50%的时候height需要放大40%,defaultHeight=400,targetHeight=400*1.4=560 516 | * 517 | * offset=0 518 | * 400 * (1 + 0.4 * 0 * (1 / 0.5)) = 400 519 | * 520 | * offset=0.25 521 | * 400 * (1 + 0.4 * 0.25 * (1 / 0.5)) = 400 * 1.2 = 480 522 | * 523 | * offset=0.5 524 | * 400 * (1 + 0.4 * 0.5 * (1 / 0.5)) = 400 * 1.4 = 560 525 | * 526 | */ 527 | mRectBgCurrentHeight = 528 | (int) (mPage1RectBgDefaultHeight * (1 + FIRST_HEIGHT * positionOffset * (1 / FIRST_RATE))); 529 | /** 矩形背景,向上移动30dp */ 530 | mRectBgCurrentTop = (int) (mPage1RectBgDefaultTop + (FIRST_TOP1 * positionOffset * (1 / FIRST_RATE))); 531 | } 532 | 533 | private void stepByHidePage1Views(float positionOffset) { 534 | /** 535 | * 偏移到50%的时候alpha需要为0,view不可见 536 | * 537 | * offset=0 538 | * 255-(255*0.0*(1/0.5)) = 0 539 | * 540 | * offset=0.25 541 | * 255-(255*0.25*(1/0.5)) = 127 542 | * 543 | * offset=0.5 544 | * 255-(255*0.5*(1/0.5)) = 255 545 | */ 546 | mPage1Top.alpha((int) (255 - (255 * positionOffset * (1 / FIRST_RATE)))); 547 | mPage1Bottom.alpha((int) (255 - (255 * positionOffset * (1 / FIRST_RATE)))); 548 | 549 | /** 第1页,顶部图向下移动 */ 550 | mPage1Top.currentTop = mPage1Top.defaultTop + (FIRST_TOP2 + FIRST_TOP1) * positionOffset * (1 / FIRST_RATE); 551 | 552 | /** 第1页,底部图跟随顶部图向下移动 */ 553 | mPage1Bottom.currentTop = mPage1Top.currentTop + mPage1Top.defaultHeight + padding(); 554 | } 555 | 556 | private void transformRectBgFrom1To2After(float positionOffset) { 557 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次,保证变化完成 */ 558 | mRectBgCurrentHeight = mPage2RectBgDefaultHeight; 559 | mRectBgCurrentTop = mPage1RectBgDefaultTop + FIRST_TOP1; 560 | /** 第1页,在50%->100%区间偏移 */ 561 | /** 矩形背景,在上上偏移30dp后,向下偏移60dp */ 562 | mRectBgCurrentTop = 563 | (int) (mPage1RectBgDefaultTop + FIRST_TOP1 + (FIRST_TOP2 * (positionOffset - FIRST_RATE) * 1.0 / (1 - FIRST_RATE))); 564 | } 565 | 566 | private void stepByShowPage2Views(float positionOffset) { 567 | /** 第2页,顶部图,跟随矩形背景下移 */ 568 | mPage2Top.currentTop = mRectBgCurrentTop + padding(); 569 | 570 | /** 第2页,底部图,跟随矩形背景下移 */ 571 | mPage2Bottom.currentTop = mPage2Center[0].currentTop + mPage2Center[0].defaultHeight + padding(); 572 | 573 | /** 第2页,计算裂变背景图的偏移px,并修改透明度渐变显示 */ 574 | float offset = 575 | (mPage1RectBgDefaultWidth + dp2px(15)) * ((positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE))); 576 | mPage2Split[0].currentLeft = mPage2Split[0].defaultLeft - offset; 577 | mPage2Split[1].currentLeft = mPage2Split[0].defaultLeft + offset; 578 | /** 579 | * 偏移到50%的时候alpha需要为0,偏移到100%,alpha需要为255,不过此时positionOffset的取值=0.5~1 580 | * 581 | * offset=0.5 582 | * 255 * (0.5 - 0.5) * (1 / (1 - 0.5)))=255 * 0 = 0 583 | * 584 | * offset=0.75 585 | * 255 * (0.75 - 0.5) * (1 / (1 - 0.5)))=255 * 0.5 = 127.5 586 | * 587 | * offset=1 588 | * 255 * (1 - 0.5) * (1 / (1 - 0.5)))=255 * 1 = 255 589 | */ 590 | mPage2Split[0].alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE)))); 591 | mPage2Split[1].alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE)))); 592 | 593 | /** 第2页,顶部,底部图,透明度渐变显示,偏移量达到100%,完成显示 */ 594 | mPage2Top.alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE)))); 595 | mPage2Bottom.alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE)))); 596 | 597 | /** 第2页,显示中间3张模特图 */ 598 | for (int i = 0; i < mPage2Center.length; i++) { 599 | if (i == 0) { 600 | /** 第2页,显示第1张模特图 */ 601 | mPage2Center[i].currentWidth = 602 | mPage2Center[i].defaultWidth * (positionOffset - FIRST_RATE) * 1 / (1 - FIRST_RATE); 603 | mPage2Center[i].currentHeight = 604 | mPage2Center[i].defaultHeight * (positionOffset - FIRST_RATE) * 1 / (1 - FIRST_RATE); 605 | mPage2Center[i].alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE)))); 606 | mPage2Center[i].currentTop = mPage2Top.currentTop + mPage2Top.currentHeight + padding(); 607 | } else { 608 | /** 第2,3张模特图,在前1张显示到一半时才显示 */ 609 | if (mPage2Center[i - 1].currentWidth >= mPage2Center[i - 1].defaultWidth / 2) { 610 | float rate = mPage2Center[i - 1].widthRate() - 0.5f; 611 | mPage2Center[i].currentWidth = mPage2Center[i].defaultWidth * (rate * 2); 612 | mPage2Center[i].currentHeight = mPage2Center[i].defaultHeight * (rate * 2); 613 | /** 第2,3张模特图,需要根据第1张图计算left */ 614 | mPage2Center[i].currentLeft = 615 | mPage2Center[0].currentLeft + mPage2Center[0].currentWidth + padding(); 616 | mPage2Center[i].currentTop = mPage2Top.currentTop + mPage2Top.currentHeight + padding(); 617 | if (i == 2) { 618 | /** 第3张模特图,根据第2张图计算top */ 619 | mPage2Center[i].currentTop = 620 | mPage2Center[1].currentTop + mPage2Center[1].currentHeight + padding(); 621 | } 622 | mPage2Center[i].alpha((int) (255 * (positionOffset * rate * 2))); 623 | } else { 624 | mPage2Center[i].alpha(0); 625 | } 626 | } 627 | } 628 | } 629 | 630 | private void transformRectBgFrom2To3(float positionOffset) { 631 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次,保证变化完成 */ 632 | mRectBgCurrentHeight = mPage2RectBgDefaultHeight; 633 | mRectBgCurrentTop = mPage2RectBgDefaultTop; 634 | 635 | /** 矩形背景,宽度缩小15% */ 636 | mRectBgCurrentWidth = (int) (mPage2RectBgDefaultWidth * (1 + SECOND_WIDTH * positionOffset)); 637 | mRectBgCurrentLeft = getScreenWidth() / 2 - mRectBgCurrentWidth / 2; 638 | 639 | /** 矩形背景,上移20dp */ 640 | mRectBgCurrentTop = (int) (mPage2RectBgDefaultTop + (SECOND_TOP * positionOffset)); 641 | } 642 | 643 | private void stepByHidePage2Views(float positionOffset, int positionOffsetPixels) { 644 | /** 裂变背景图,跟随滑动,向左偏移至消失 */ 645 | mPage2Split[0].currentLeft = 646 | (mPage2Split[0].defaultLeft - mPage1RectBgDefaultWidth - dp2px(15)) - positionOffsetPixels 647 | * (1 / SECOND_RATE); 648 | mPage2Split[1].currentLeft = 649 | (mPage2Split[1].defaultLeft + mPage1RectBgDefaultWidth + dp2px(15)) - positionOffsetPixels 650 | * (1 / SECOND_RATE); 651 | mPage2Split[0].alpha((int) (255 - (255 * positionOffset * (1 / SECOND_RATE)))); 652 | mPage2Split[1].alpha((int) (255 - (255 * positionOffset * (1 / SECOND_RATE)))); 653 | 654 | /** 顶部图,3张模特图,底部图,跟随矩形背景上移 */ 655 | mPage2Top.currentTop = mRectBgCurrentTop + padding(); 656 | mPage2Center[0].currentTop = mPage2Top.currentTop + mPage2Top.currentHeight + padding(); 657 | mPage2Center[1].currentTop = mPage2Center[0].currentTop; 658 | mPage2Center[2].currentTop = mPage2Center[1].currentTop + mPage2Center[1].currentHeight; 659 | mPage2Bottom.currentTop = mPage2Center[0].currentTop + mPage2Center[0].currentHeight + padding(); 660 | 661 | /** 渐渐减少透明度,隐藏第2页的顶部图,3张模特图,底部图 */ 662 | mPage2Top.alpha((int) (255 - (255 * positionOffset * (1 / SECOND_RATE)))); 663 | mPage2Bottom.alpha((int) (255 - (255 * positionOffset * (1 / SECOND_RATE)))); 664 | for (ViewModel viewModel : mPage2Center) { 665 | viewModel.alpha((int) (255 - (255 * positionOffset * (1 / SECOND_RATE)))); 666 | } 667 | 668 | /** 因为矩形背景变窄了,所以渐渐减少第2页顶部图,底部图的宽度,实现跟随矩形背景宽度变化 */ 669 | mPage2Top.currentWidth = mRectBgCurrentWidth - padding() * 2; 670 | mPage2Top.currentLeft = mRectBgCurrentLeft + padding(); 671 | mPage2Bottom.currentWidth = mRectBgCurrentWidth - padding() * 2; 672 | mPage2Bottom.currentLeft = mRectBgCurrentLeft + padding(); 673 | mPage2Bottom.currentLeft = mRectBgCurrentLeft + padding(); 674 | 675 | /** 因为矩形背景变窄了,所以渐渐减少第2,3张模特图的宽高,left和top,实现跟随矩形背景宽度变化 */ 676 | mPage2Center[0].currentLeft = mRectBgCurrentLeft + padding(); 677 | mPage2Center[1].currentWidth = mRectBgCurrentWidth - padding() * 3 - mPage2Center[0].defaultWidth; 678 | mPage2Center[1].currentHeight = mPage2Center[1].currentWidth; 679 | mPage2Center[1].currentLeft = mPage2Center[0].currentLeft + mPage2Center[0].defaultWidth + padding(); 680 | 681 | mPage2Center[2].currentWidth = mRectBgCurrentWidth - padding() * 3 - mPage2Center[0].defaultWidth; 682 | mPage2Center[2].currentHeight = mPage2Center[2].currentWidth; 683 | mPage2Center[2].currentLeft = mPage2Center[0].currentLeft + mPage2Center[0].defaultWidth + padding(); 684 | mPage2Center[2].currentTop = mPage2Center[1].currentTop + mPage2Center[1].currentHeight + padding(); 685 | } 686 | 687 | private void stepByShowPage3Views(float positionOffset) { 688 | /** 第2页,在50->100%区间偏移,显示第3页,6张模特图 */ 689 | for (int i = 0; i < mPage3Model.length; i++) { 690 | if (i == 0) { 691 | /** 第1张模特图先显示 */ 692 | if (mPage3Model[i].paint.getAlpha() < 255) { 693 | mPage3Model[i].alpha((int) (255 * (positionOffset - SECOND_RATE) * (1 / (1 - SECOND_RATE)))); 694 | } 695 | } else { 696 | /** 其他模特图在前1张显示50%透明度的时候再依次展示 */ 697 | if (mPage3Model[i - 1].paint.getAlpha() >= 255 / 2) { 698 | float rate = mPage3Model[i - 1].paint.getAlpha() / 255.0f - 0.5f; 699 | mPage3Model[i].alpha((int) (255 * (rate * 2))); 700 | } else { 701 | mPage3Model[i].alpha(0); 702 | } 703 | } 704 | 705 | /** 6张模特图,跟随矩形背景上移 */ 706 | if (i < mPage3ModelResources.length / 2) { 707 | /** 第1排,3张模特图的top计算 */ 708 | mPage3Model[i].currentTop = mRectBgCurrentTop + padding(); 709 | } else { 710 | /** 第1排,3张模特图的top,需要加上第一排的height */ 711 | mPage3Model[i].currentTop = mRectBgCurrentTop + mPage3ModelDefaultHeight + padding() * 2; 712 | } 713 | } 714 | } 715 | 716 | private boolean isPage4(int currentPageIndex) { 717 | return currentPageIndex == 3; 718 | } 719 | 720 | private boolean fromPage3ToPage4(int position) { 721 | return position == 2; 722 | } 723 | 724 | private boolean fromPage2ToPage3(int position) { 725 | return position == 1; 726 | } 727 | 728 | private boolean fromPage1ToPage2(int position) { 729 | return position == 0; 730 | } 731 | 732 | private void hidePage4Views() { 733 | mPage4Top.alpha(0); 734 | 735 | for (ViewModel viewModel : mPage4Model) { 736 | viewModel.currentWidth = 0; 737 | viewModel.currentHeight = 0; 738 | viewModel.alpha(0); 739 | } 740 | 741 | for (ViewModel viewModel : mPage4Split) { 742 | viewModel.alpha(0); 743 | } 744 | } 745 | 746 | private void hidePage3Views() { 747 | for (int i = 0; i < mPage3Model.length; i++) { 748 | mPage3Model[i].alpha(0); 749 | } 750 | } 751 | 752 | private void scaleHidePage3Views() { 753 | for (int i = 0; i < mPage3Model.length; i++) { 754 | mPage3Model[i].currentWidth = 0; 755 | mPage3Model[i].currentHeight = 0; 756 | } 757 | } 758 | 759 | private void hidePage1Views() { 760 | mPage1Top.alpha(0); 761 | mPage1Bottom.alpha(0); 762 | } 763 | 764 | private void hidePage2Views() { 765 | mPage2Top.alpha(0); 766 | mPage2Bottom.alpha(0); 767 | 768 | for (ViewModel viewModel : mPage2Center) { 769 | viewModel.alpha(0); 770 | } 771 | 772 | for (ViewModel viewModel : mPage2Split) { 773 | viewModel.alpha(0); 774 | } 775 | } 776 | 777 | public void drawBitmap(Canvas canvas, ViewModel viewModel) { 778 | canvas.drawBitmap(viewModel.bitmap, viewModel.currentLeft, viewModel.currentTop, viewModel.paint); 779 | } 780 | 781 | public void drawBitmapMatrix(Canvas canvas, ViewModel viewModel) { 782 | canvas.drawBitmap(viewModel.bitmap, viewModel.matrix, viewModel.paint); 783 | } 784 | 785 | public void drawBitmapRect(Canvas canvas, ViewModel viewModel) { 786 | RectF rect = new RectF(); 787 | rect.left = viewModel.currentLeft; 788 | rect.top = viewModel.currentTop; 789 | rect.right = rect.left + viewModel.currentWidth; 790 | rect.bottom = rect.top + viewModel.currentHeight; 791 | canvas.drawBitmap(viewModel.bitmap, null, rect, viewModel.paint); 792 | } 793 | 794 | /** 795 | * @desc 根据当前页面,绘制不同的view 796 | * @author listen 797 | */ 798 | @Override 799 | protected void onDraw(Canvas canvas) { 800 | super.onDraw(canvas); 801 | 802 | /** 按重叠顺序绘制 */ 803 | if (fromPage1ToPage2(mCurrentPageIndex)) { 804 | 805 | /** 绘制第1页,底部背景图 */ 806 | drawBitmap(canvas, mPage1BottomBg); 807 | 808 | /** 绘制第2页,裂变背景图 */ 809 | drawBitmap(canvas, mPage2Split[0]); 810 | drawBitmap(canvas, mPage2Split[1]); 811 | 812 | /** 绘制白色矩形背景 */ 813 | drawWhiteRectBackgroud(canvas); 814 | 815 | drawPage1InCanvas(canvas); 816 | drawPage2InCanvas(canvas); 817 | 818 | } else if (fromPage2ToPage3(mCurrentPageIndex)) { 819 | 820 | /** 绘制第2页,裂变背景图 */ 821 | drawBitmap(canvas, mPage2Split[0]); 822 | drawBitmap(canvas, mPage2Split[1]); 823 | 824 | /** 绘制矩形背景 */ 825 | drawWhiteRectBackgroud(canvas); 826 | 827 | drawPage2InCanvas(canvas); 828 | drawPage3InCanvas(canvas); 829 | 830 | } else if (fromPage3ToPage4(mCurrentPageIndex)) { 831 | /** 绘制第4页,裂变背景图 */ 832 | drawBitmapMatrix(canvas, mPage4Split[0]); 833 | drawBitmapMatrix(canvas, mPage4Split[1]); 834 | 835 | /** 绘制矩形背景 */ 836 | drawWhiteRectBackgroud(canvas); 837 | 838 | drawPage3InCanvas(canvas); 839 | drawPage4InCanvas(canvas); 840 | 841 | } else if (isPage4(mCurrentPageIndex)) { 842 | 843 | /** 绘制第4页,裂变背景图 */ 844 | drawBitmapMatrix(canvas, mPage4Split[0]); 845 | drawBitmapMatrix(canvas, mPage4Split[1]); 846 | 847 | /** 绘制矩形背景 */ 848 | drawWhiteRectBackgroud(canvas); 849 | 850 | drawPage4InCanvas(canvas); 851 | } 852 | } 853 | 854 | private void drawPage1InCanvas(Canvas canvas) { 855 | /** 绘制第1页,顶部模特图 */ 856 | drawBitmap(canvas, mPage1Top); 857 | 858 | /** 绘制第1页,底部文案图 */ 859 | drawBitmap(canvas, mPage1Bottom); 860 | } 861 | 862 | private void drawPage4InCanvas(Canvas canvas) { 863 | /** 绘制第4页,顶部模特图 */ 864 | drawBitmap(canvas, mPage4Top); 865 | 866 | /** 绘制第4页,底部3张模特图 */ 867 | for (int i = 0; i < mPage4Model.length; i++) { 868 | drawBitmapRect(canvas, mPage4Model[i]); 869 | } 870 | } 871 | 872 | private void drawPage3InCanvas(Canvas canvas) { 873 | /** 绘制第3页,6张模特图 */ 874 | for (int i = 0; i < mPage3Model.length; i++) { 875 | drawBitmapRect(canvas, mPage3Model[i]); 876 | } 877 | } 878 | 879 | private void drawPage2InCanvas(Canvas canvas) { 880 | /** 绘制第2页,顶部图 */ 881 | drawBitmapRect(canvas, mPage2Top); 882 | 883 | /** 绘制第2页,第1张模特图 */ 884 | drawBitmapRect(canvas, mPage2Center[0]); 885 | /** 在第1张模特图绘制到50%时,绘制第2张模特图 */ 886 | if (mPage2Center[0].currentWidth >= mPage2Center[0].defaultWidth / 2) { 887 | drawBitmapRect(canvas, mPage2Center[1]); 888 | /** 在第2张模特图绘制到50%时,绘制第3张模特图 */ 889 | if (mPage2Center[1].currentWidth >= mPage2Center[1].defaultWidth / 2) { 890 | drawBitmapRect(canvas, mPage2Center[2]); 891 | } 892 | } 893 | 894 | /** 绘制第2页,底部图 */ 895 | drawBitmapRect(canvas, mPage2Bottom); 896 | } 897 | 898 | @NonNull 899 | private RectF drawWhiteRectBackgroud(Canvas canvas) { 900 | RectF rect = new RectF(); 901 | rect.left = mRectBgCurrentLeft; 902 | rect.top = mRectBgCurrentTop; 903 | rect.right = rect.left + mRectBgCurrentWidth; 904 | rect.bottom = rect.top + mRectBgCurrentHeight; 905 | 906 | canvas.rotate(mRectBgCurrentDegree, rect.left + mRectBgCurrentWidth / 2, rect.top + mRectBgCurrentHeight / 2); 907 | canvas.drawRoundRect(rect, mRectBgDefaultCorner, mRectBgDefaultCorner, mRectBgPaint); 908 | return rect; 909 | } 910 | 911 | private int padding() { 912 | return dp2px(5); 913 | } 914 | 915 | public int getScreenWidth() { 916 | if (null != getResources() && null != getResources().getDisplayMetrics()) { 917 | return getResources().getDisplayMetrics().widthPixels; 918 | } 919 | return 0; 920 | } 921 | 922 | public int getScreenHeight() { 923 | if (null != getResources() && null != getResources().getDisplayMetrics()) { 924 | return getResources().getDisplayMetrics().heightPixels; 925 | } 926 | return 0; 927 | } 928 | 929 | public int dp2px(float dpValue) { 930 | final float scale = getResources().getDisplayMetrics().density; 931 | return (int) (dpValue * scale + 0.5f); 932 | } 933 | 934 | } 935 | -------------------------------------------------------------------------------- /app/src/main/java/com/listen/test_mogu_view/viewpager/TransforViewTag1.java: -------------------------------------------------------------------------------- 1 | package com.listen.test_mogu_view.viewpager; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.RectF; 8 | import android.support.annotation.NonNull; 9 | import android.util.AttributeSet; 10 | import android.util.Log; 11 | import android.view.View; 12 | 13 | public class TransforViewTag1 extends View { 14 | 15 | // ///////////////////////////////////////////////////////////////////////// 16 | // 页面切换时的,白色矩形背景的变化参数 17 | // ///////////////////////////////////////////////////////////////////////// 18 | /** 19 | * 第1页->第2页 20 | */ 21 | public static final float FIRST_HEIGHT = 0.4f;// 第1个页面高度缩放比例,正:放大,负:缩小 22 | public final int FIRST_TOP1 = -dp2px(30);// 第1个页面top移动距离,正:下移,负:上移 23 | public final int FIRST_TOP2 = dp2px(60);// 第1个页面top移动距离,正:下移,负:上移 24 | public static final float FIRST_RATE = 0.5f;// 在偏移50%处,进行下一页的显示 25 | /** 26 | * 第2页->第3页 27 | */ 28 | public static final float SECOND_WIDTH = -0.15f;// 第2个页面宽度缩放比例,正:放大,负:缩小 29 | public final int SECOND_TOP = -dp2px(20);// 第2个页面top移动距离比例,正:下移,负:上移 30 | public static final float SECOND_RATE = 0.5f;// 在偏移50%处,进行下一页的显示 31 | /** 32 | * 第3页->第4页 33 | */ 34 | public static final float THIRD_WIDTH = -0.1f;// 第3个页面宽度缩放比例,正:放大,负:缩小 35 | public static final float THIRD_HEIGHT = -0.1f;// 第3个页面高度缩放比例,正:放大,负:缩小 36 | public static final int THIRD_DEGREE = -10;// 第3个页面角度调整,正:顺时针,负:逆时针 37 | public static final float THIRD_RATE = 0.5f;// 在偏移50%处,进行下一页的显示 38 | 39 | // ///////////////////////////////////////////////////////////////////////// 40 | // 白色圆角矩形背景 41 | // ///////////////////////////////////////////////////////////////////////// 42 | private Paint mRectBgPaint; 43 | private int mRectBgDefaultCorner = dp2px(5);// 初始圆角5dp 44 | private float mRectBgCurrentDegree = 0;// 默认不旋转 45 | 46 | /** 47 | * 第1页初始化 48 | * default=默认初始宽高,left,top,如果是2,3,4页,则表示变化后的值 49 | * current=当前宽高,left,top,会根据偏移量变化 50 | */ 51 | private float mPage1RectBgDefaultWidth = dp2px(260); 52 | private float mPage1RectBgDefaultHeight = dp2px(230); 53 | private float mPage1RectBgDefaultLeft = getScreenWidth() / 2 - mPage1RectBgDefaultWidth / 2; 54 | private float mPage1RectBgDefaultTop = dp2px(80); 55 | 56 | private float mRectBgCurrentWidth = mPage1RectBgDefaultWidth; 57 | private float mRectBgCurrentHeight = mPage1RectBgDefaultHeight; 58 | private float mRectBgCurrentLeft = mPage1RectBgDefaultLeft; 59 | private float mRectBgCurrentTop = mPage1RectBgDefaultTop; 60 | 61 | /** 62 | * 第1页->第2页 63 | * 在第1页的基础上进行变化 64 | * 1.height放大 65 | * 2.top先上移n,在下移n*2 66 | */ 67 | private float mPage2RectBgDefaultWidth = mPage1RectBgDefaultWidth; 68 | private float mPage2RectBgDefaultHeight = mPage1RectBgDefaultHeight * (1 + FIRST_HEIGHT); 69 | private float mPage2RectBgDefaultLeft = mPage1RectBgDefaultLeft; 70 | private float mPage2RectBgDefaultTop = mPage1RectBgDefaultTop + FIRST_TOP1 + FIRST_TOP2; 71 | 72 | /** 73 | * 第2页->第3页 74 | * 在第2页的基础上进行变化 75 | * 1.宽度缩小 76 | * 2.top上移 77 | */ 78 | private float mPage3RectBgDefaultWidth = mPage2RectBgDefaultWidth * (1 + SECOND_WIDTH); 79 | private float mPage3RectBgDefaultHeight = mPage2RectBgDefaultHeight; 80 | private float mPage3RectBgDefaultLeft = getScreenWidth() / 2 - mPage3RectBgDefaultWidth / 2; 81 | private float mPage3RectBgDefaultTop = mPage2RectBgDefaultTop + SECOND_TOP; 82 | 83 | /** 84 | * 第3页->第4页 85 | * 在第3页的基础上进行变化 86 | * 1.宽度缩小 87 | * 2.高度缩小 88 | * 2.逆时针旋转 89 | */ 90 | private float mPage4RectBgDefaultWidth = mPage3RectBgDefaultWidth * (1 + THIRD_WIDTH); 91 | private float mPage4RectBgDefaultHeight = mPage3RectBgDefaultHeight * (1 + THIRD_HEIGHT); 92 | private float mPage4RectBgDefaultLeft = getScreenWidth() / 2 - mPage4RectBgDefaultWidth / 2; 93 | private float mPage4RectBgDefaultTop = mPage3RectBgDefaultTop; 94 | private float mPage4ModelDefaultWidth = (mPage4RectBgDefaultWidth - padding() * 4) / 3; 95 | 96 | /** 97 | * 当前页数索引 98 | */ 99 | private int mCurrentPageIndex; 100 | 101 | public TransforViewTag1(Context context) { 102 | super(context); 103 | init(); 104 | } 105 | 106 | public TransforViewTag1(Context context, AttributeSet attrs) { 107 | super(context, attrs); 108 | init(); 109 | } 110 | 111 | public TransforViewTag1(Context context, AttributeSet attrs, int defStyle) { 112 | super(context, attrs, defStyle); 113 | init(); 114 | } 115 | 116 | public void transfor(int position, float positionOffset, int positionOffsetPixels) { 117 | Log.e("ls_debug_log", "p=" + position + ",p_offset=" + positionOffset + ",p_px=" + positionOffsetPixels); 118 | mCurrentPageIndex = position; 119 | calculateOffset(position, positionOffset, positionOffsetPixels); 120 | } 121 | 122 | private void init() { 123 | /** 第1页,初始化矩形背景 */ 124 | mRectBgPaint = new Paint(); 125 | mRectBgPaint.setAntiAlias(true); // 设置画笔为无锯齿 126 | mRectBgPaint.setColor(Color.BLACK); // 设置画笔颜色 127 | mRectBgPaint.setStyle(Paint.Style.FILL); // 空心效果 128 | } 129 | 130 | /** 131 | * @desc 根据viewPager偏移,修改view的宽,高,left,top 132 | * @author listen 133 | */ 134 | private void calculateOffset(int position, float positionOffset, int positionOffsetPixels) { 135 | if (fromPage1ToPage2(position)) { 136 | if (positionOffset < FIRST_RATE) { 137 | /** 第1页,在0->50%区间偏移 */ 138 | /** 矩形背景,高度放大40%,向上移动30dp */ 139 | transformRectBgFrom1To2Before(positionOffset); 140 | 141 | } else { 142 | /** 第1页,在50%->100%区间偏移 */ 143 | /** 矩形背景,上移30dp后,向下偏移60dp */ 144 | transformRectBgFrom1To2After(positionOffset); 145 | 146 | } 147 | } else if (fromPage2ToPage3(position)) { 148 | /** 矩形背景,宽度缩小15%,上移20dp */ 149 | transformRectBgFrom2To3(positionOffset); 150 | 151 | } else if (fromPage3ToPage4(position)) { 152 | /** 背景矩形的宽度,高度减少10%,逆时针旋转10度 */ 153 | transformRectBgFrom3To4(positionOffset); 154 | } 155 | postInvalidate(); 156 | } 157 | 158 | private void transformRectBgFrom1To2After(float positionOffset) { 159 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次,保证变化完成 */ 160 | mRectBgCurrentHeight = mPage2RectBgDefaultHeight; 161 | mRectBgCurrentTop = mPage1RectBgDefaultTop + FIRST_TOP1; 162 | /** 第1页,在50%->100%区间偏移 */ 163 | /** 矩形背景,在上上偏移30dp后,向下偏移60dp */ 164 | mRectBgCurrentTop = 165 | (int) (mPage1RectBgDefaultTop + FIRST_TOP1 + (FIRST_TOP2 * (positionOffset - FIRST_RATE) * 1.0 / (1 - FIRST_RATE))); 166 | } 167 | 168 | private void transformRectBgFrom2To3(float positionOffset) { 169 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次,保证变化完成 */ 170 | mRectBgCurrentHeight = mPage2RectBgDefaultHeight; 171 | mRectBgCurrentTop = mPage2RectBgDefaultTop; 172 | 173 | /** 矩形背景,宽度缩小15% */ 174 | mRectBgCurrentWidth = (int) (mPage2RectBgDefaultWidth * (1 + SECOND_WIDTH * positionOffset)); 175 | mRectBgCurrentLeft = getScreenWidth() / 2 - mRectBgCurrentWidth / 2; 176 | 177 | /** 矩形背景,上移20dp */ 178 | mRectBgCurrentTop = (int) (mPage2RectBgDefaultTop + (SECOND_TOP * positionOffset)); 179 | } 180 | 181 | private void transformRectBgFrom3To4(float positionOffset) { 182 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次,保证变化完成 */ 183 | mRectBgCurrentWidth = mPage3RectBgDefaultWidth; 184 | mRectBgCurrentTop = mPage3RectBgDefaultTop; 185 | 186 | /** 调整第4页,背景矩形的宽高和角度 */ 187 | /** 背景矩形的宽度,在第3页调整宽度的基础上进行缩小 */ 188 | mRectBgCurrentWidth = mPage3RectBgDefaultWidth * (1 + THIRD_WIDTH * positionOffset); 189 | mRectBgCurrentLeft = getScreenWidth() / 2 - mRectBgCurrentWidth / 2; 190 | /** 背景矩形的高度,在第2页调整高度的基础上进行缩小 */ 191 | mRectBgCurrentHeight = mPage3RectBgDefaultHeight * (1 + THIRD_HEIGHT * positionOffset); 192 | /** 背景矩形逆时针旋转 */ 193 | mRectBgCurrentDegree = THIRD_DEGREE * positionOffset; 194 | } 195 | 196 | private void transformRectBgFrom1To2Before(float positionOffset) { 197 | /** 矩形背景,高度放大40% */ 198 | /** 199 | * 偏移到50%的时候height需要放大40%,defaultHeight=400,targetHeight=400*1.4=560 200 | * 201 | * offset=0 202 | * 400 * (1 + 0.4 * 0 * (1 / 0.5)) = 400 203 | * 204 | * offset=0.25 205 | * 400 * (1 + 0.4 * 0.25 * (1 / 0.5)) = 400 * 1.2 = 480 206 | * 207 | * offset=0.5 208 | * 400 * (1 + 0.4 * 0.5 * (1 / 0.5)) = 400 * 1.4 = 560 209 | * 210 | */ 211 | mRectBgCurrentHeight = 212 | (int) (mPage1RectBgDefaultHeight * (1 + FIRST_HEIGHT * positionOffset * (1 / FIRST_RATE))); 213 | /** 矩形背景,向上移动30dp */ 214 | mRectBgCurrentTop = (int) (mPage1RectBgDefaultTop + (FIRST_TOP1 * positionOffset * (1 / FIRST_RATE))); 215 | } 216 | 217 | private boolean isPage4(int currentPageIndex) { 218 | return currentPageIndex == 3; 219 | } 220 | 221 | private boolean fromPage3ToPage4(int position) { 222 | return position == 2; 223 | } 224 | 225 | private boolean fromPage2ToPage3(int position) { 226 | return position == 1; 227 | } 228 | 229 | private boolean fromPage1ToPage2(int position) { 230 | return position == 0; 231 | } 232 | 233 | public void drawBitmap(Canvas canvas, ViewModel viewModel) { 234 | canvas.drawBitmap(viewModel.bitmap, viewModel.currentLeft, viewModel.currentTop, viewModel.paint); 235 | } 236 | 237 | public void drawBitmapMatrix(Canvas canvas, ViewModel viewModel) { 238 | canvas.drawBitmap(viewModel.bitmap, viewModel.matrix, viewModel.paint); 239 | } 240 | 241 | 242 | public void drawBitmapRect(Canvas canvas, ViewModel viewModel) { 243 | RectF rect = new RectF(); 244 | rect.left = viewModel.currentLeft; 245 | rect.top = viewModel.currentTop; 246 | rect.right = rect.left + viewModel.currentWidth; 247 | rect.bottom = rect.top + viewModel.currentHeight; 248 | canvas.drawBitmap(viewModel.bitmap, null, rect, viewModel.paint); 249 | } 250 | 251 | /** 252 | * @desc 根据当前页面,绘制不同的view 253 | * @author listen 254 | */ 255 | @Override 256 | protected void onDraw(Canvas canvas) { 257 | super.onDraw(canvas); 258 | /** 绘制白色矩形背景 */ 259 | // drawWhiteRectBackgroud(canvas); 260 | 261 | RectF rect = new RectF(); 262 | rect.left = mRectBgCurrentLeft; 263 | rect.top = mRectBgCurrentTop; 264 | rect.right = rect.left + mRectBgCurrentWidth; 265 | rect.bottom = rect.top + mRectBgCurrentHeight; 266 | 267 | canvas.rotate(mRectBgCurrentDegree, rect.left + mRectBgCurrentWidth / 2, rect.top + mRectBgCurrentHeight / 2); 268 | canvas.drawRoundRect(rect, mRectBgDefaultCorner, mRectBgDefaultCorner, mRectBgPaint); 269 | } 270 | 271 | @NonNull 272 | private RectF drawWhiteRectBackgroud(Canvas canvas) { 273 | RectF rect = new RectF(); 274 | rect.left = mRectBgCurrentLeft; 275 | rect.top = mRectBgCurrentTop; 276 | rect.right = rect.left + mRectBgCurrentWidth; 277 | rect.bottom = rect.top + mRectBgCurrentHeight; 278 | 279 | canvas.rotate(mRectBgCurrentDegree, rect.left + mRectBgCurrentWidth / 2, rect.top + mRectBgCurrentHeight / 2); 280 | canvas.drawRoundRect(rect, mRectBgDefaultCorner, mRectBgDefaultCorner, mRectBgPaint); 281 | return rect; 282 | } 283 | 284 | private int padding() { 285 | return dp2px(5); 286 | } 287 | 288 | public int getScreenWidth() { 289 | if (null != getResources() && null != getResources().getDisplayMetrics()) { 290 | return getResources().getDisplayMetrics().widthPixels; 291 | } 292 | return 0; 293 | } 294 | 295 | 296 | 297 | public int dp2px(float dpValue) { 298 | final float scale = getResources().getDisplayMetrics().density; 299 | return (int) (dpValue * scale + 0.5f); 300 | } 301 | 302 | } 303 | -------------------------------------------------------------------------------- /app/src/main/java/com/listen/test_mogu_view/viewpager/TransforViewTag2.java: -------------------------------------------------------------------------------- 1 | package com.listen.test_mogu_view.viewpager; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.RectF; 8 | import android.support.annotation.NonNull; 9 | import android.util.AttributeSet; 10 | import android.util.Log; 11 | import android.view.View; 12 | 13 | import com.listen.test_mogu_view.R; 14 | 15 | 16 | public class TransforViewTag2 extends View { 17 | 18 | // ///////////////////////////////////////////////////////////////////////// 19 | // 页面切换时的,白色矩形背景的变化参数 20 | // ///////////////////////////////////////////////////////////////////////// 21 | /** 22 | * 第1页->第2页 23 | */ 24 | public static final float FIRST_HEIGHT = 0.4f;// 第1个页面高度缩放比例,正:放大,负:缩小 25 | public final int FIRST_TOP1 = -dp2px(30);// 第1个页面top移动距离,正:下移,负:上移 26 | public final int FIRST_TOP2 = dp2px(60);// 第1个页面top移动距离,正:下移,负:上移 27 | public static final float FIRST_RATE = 0.5f;// 在偏移50%处,进行下一页的显示 28 | /** 29 | * 第2页->第3页 30 | */ 31 | public static final float SECOND_WIDTH = -0.15f;// 第2个页面宽度缩放比例,正:放大,负:缩小 32 | public final int SECOND_TOP = -dp2px(20);// 第2个页面top移动距离比例,正:下移,负:上移 33 | public static final float SECOND_RATE = 0.5f;// 在偏移50%处,进行下一页的显示 34 | /** 35 | * 第3页->第4页 36 | */ 37 | public static final float THIRD_WIDTH = -0.1f;// 第3个页面宽度缩放比例,正:放大,负:缩小 38 | public static final float THIRD_HEIGHT = -0.1f;// 第3个页面高度缩放比例,正:放大,负:缩小 39 | public static final int THIRD_DEGREE = -10;// 第3个页面角度调整,正:顺时针,负:逆时针 40 | public static final float THIRD_RATE = 0.5f;// 在偏移50%处,进行下一页的显示 41 | 42 | // ///////////////////////////////////////////////////////////////////////// 43 | // 白色圆角矩形背景 44 | // ///////////////////////////////////////////////////////////////////////// 45 | private Paint mRectBgPaint; 46 | private int mRectBgDefaultCorner = dp2px(5);// 初始圆角5dp 47 | private float mRectBgCurrentDegree = 0;// 默认不旋转 48 | 49 | /** 50 | * 第1页初始化 51 | * default=默认初始宽高,left,top,如果是2,3,4页,则表示变化后的值 52 | * current=当前宽高,left,top,会根据偏移量变化 53 | */ 54 | private float mPage1RectBgDefaultWidth = dp2px(260); 55 | private float mPage1RectBgDefaultHeight = dp2px(230); 56 | private float mPage1RectBgDefaultLeft = getScreenWidth() / 2 - mPage1RectBgDefaultWidth / 2; 57 | private float mPage1RectBgDefaultTop = dp2px(80); 58 | 59 | private float mRectBgCurrentWidth = mPage1RectBgDefaultWidth; 60 | private float mRectBgCurrentHeight = mPage1RectBgDefaultHeight; 61 | private float mRectBgCurrentLeft = mPage1RectBgDefaultLeft; 62 | private float mRectBgCurrentTop = mPage1RectBgDefaultTop; 63 | 64 | /** 65 | * 第1页->第2页 66 | * 在第1页的基础上进行变化 67 | * 1.height放大 68 | * 2.top先上移n,在下移n*2 69 | */ 70 | private float mPage2RectBgDefaultWidth = mPage1RectBgDefaultWidth; 71 | private float mPage2RectBgDefaultHeight = mPage1RectBgDefaultHeight * (1 + FIRST_HEIGHT); 72 | private float mPage2RectBgDefaultLeft = mPage1RectBgDefaultLeft; 73 | private float mPage2RectBgDefaultTop = mPage1RectBgDefaultTop + FIRST_TOP1 + FIRST_TOP2; 74 | 75 | /** 76 | * 第2页->第3页 77 | * 在第2页的基础上进行变化 78 | * 1.宽度缩小 79 | * 2.top上移 80 | */ 81 | private float mPage3RectBgDefaultWidth = mPage2RectBgDefaultWidth * (1 + SECOND_WIDTH); 82 | private float mPage3RectBgDefaultHeight = mPage2RectBgDefaultHeight; 83 | private float mPage3RectBgDefaultLeft = getScreenWidth() / 2 - mPage3RectBgDefaultWidth / 2; 84 | private float mPage3RectBgDefaultTop = mPage2RectBgDefaultTop + SECOND_TOP; 85 | 86 | /** 87 | * 第3页->第4页 88 | * 在第3页的基础上进行变化 89 | * 1.宽度缩小 90 | * 2.高度缩小 91 | * 2.逆时针旋转 92 | */ 93 | private float mPage4RectBgDefaultWidth = mPage3RectBgDefaultWidth * (1 + THIRD_WIDTH); 94 | private float mPage4RectBgDefaultHeight = mPage3RectBgDefaultHeight * (1 + THIRD_HEIGHT); 95 | private float mPage4RectBgDefaultLeft = getScreenWidth() / 2 - mPage4RectBgDefaultWidth / 2; 96 | private float mPage4RectBgDefaultTop = mPage3RectBgDefaultTop; 97 | private float mPage4ModelDefaultWidth = (mPage4RectBgDefaultWidth - padding() * 4) / 3; 98 | 99 | // ///////////////////////////////////////////////////////////////////////// 100 | // 第1页,view元素 101 | // ///////////////////////////////////////////////////////////////////////// 102 | private ViewModel mPage1BottomBg;// 底部背景图 103 | 104 | // ///////////////////////////////////////////////////////////////////////// 105 | // 第2页,view元素 106 | // ///////////////////////////////////////////////////////////////////////// 107 | private ViewModel[] mPage2Split = new ViewModel[2];// 2张裂变背景图 108 | 109 | // ///////////////////////////////////////////////////////////////////////// 110 | // 第4页,view元素 111 | // ///////////////////////////////////////////////////////////////////////// 112 | private ViewModel[] mPage4Split = new ViewModel[2];// 2张裂变背景图 113 | 114 | /** 115 | * 当前页数索引 116 | */ 117 | private int mCurrentPageIndex; 118 | 119 | public TransforViewTag2(Context context) { 120 | super(context); 121 | init(); 122 | } 123 | 124 | public TransforViewTag2(Context context, AttributeSet attrs) { 125 | super(context, attrs); 126 | init(); 127 | } 128 | 129 | public TransforViewTag2(Context context, AttributeSet attrs, int defStyle) { 130 | super(context, attrs, defStyle); 131 | init(); 132 | } 133 | 134 | public void transfor(int position, float positionOffset, int positionOffsetPixels) { 135 | Log.e("ls_debug_log", "p=" + position + ",p_offset=" + positionOffset + ",p_px=" + positionOffsetPixels); 136 | mCurrentPageIndex = position; 137 | calculateOffset(position, positionOffset, positionOffsetPixels); 138 | } 139 | 140 | private void init() { 141 | { 142 | /** 第1页,初始化矩形背景 */ 143 | mRectBgPaint = new Paint(); 144 | mRectBgPaint.setAntiAlias(true); // 设置画笔为无锯齿 145 | mRectBgPaint.setColor(Color.BLACK); // 设置画笔颜色 146 | mRectBgPaint.setStyle(Paint.Style.FILL); // 空心效果 147 | } 148 | /** 第1页,底部背景图 */ 149 | mPage1BottomBg = 150 | new ViewModel(getContext(), R.drawable.one_bottom_bg).alpha(255) 151 | .width(mPage1RectBgDefaultWidth - padding() * 2) 152 | // .height()// 不传则按宽度比例缩放 153 | .left(mPage1RectBgDefaultLeft + padding()) 154 | // top距离=矩形背景top+height+5dp边距 155 | .top(mPage1RectBgDefaultTop + mPage1RectBgDefaultHeight + padding()) 156 | .create(); 157 | 158 | /** 第2页,裂变背景图 */ 159 | for (int i = 0; i < 2; i++) { 160 | mPage2Split[i] = 161 | new ViewModel(getContext(), R.drawable.two_bg).width(mPage2RectBgDefaultWidth) 162 | .height(mPage2RectBgDefaultHeight) 163 | .left(mPage2RectBgDefaultLeft) 164 | .top(mPage2RectBgDefaultTop) 165 | .create(); 166 | } 167 | /** 第4页,2张裂变背景图 */ 168 | for (int i = 0; i < mPage4Split.length; i++) { 169 | mPage4Split[i] = 170 | new ViewModel(getContext(), R.drawable.four_bg) 171 | .width(mPage4RectBgDefaultWidth) 172 | .height(mPage4RectBgDefaultHeight) 173 | .left(mPage4RectBgDefaultLeft) 174 | .top(mPage4RectBgDefaultTop); 175 | 176 | } 177 | } 178 | 179 | /** 180 | * @desc 根据viewPager偏移,修改view的宽,高,left,top 181 | * @author listen 182 | */ 183 | private void calculateOffset(int position, float positionOffset, int positionOffsetPixels) { 184 | if (fromPage1ToPage2(position)) { 185 | /** 第1页,底部背景图,根据页面向左偏移 */ 186 | mPage1BottomBg.currentLeft = mPage1BottomBg.defaultLeft - positionOffsetPixels; 187 | 188 | if (positionOffset < FIRST_RATE) { 189 | /** 第1页,在0->50%区间偏移 */ 190 | /** 矩形背景,高度放大40%,向上移动30dp */ 191 | transformRectBgFrom1To2Before(positionOffset); 192 | 193 | } else { 194 | /** 第1页,在50%->100%区间偏移 */ 195 | /** 矩形背景,上移30dp后,向下偏移60dp */ 196 | transformRectBgFrom1To2After(positionOffset); 197 | 198 | /** 第2页,渐渐裂变背景图 */ 199 | stepByShowPage2Views(positionOffset); 200 | } 201 | } else if (fromPage2ToPage3(position)) { 202 | /** 矩形背景,宽度缩小15%,上移20dp */ 203 | transformRectBgFrom2To3(positionOffset); 204 | 205 | if (positionOffset < SECOND_RATE) { 206 | /** 第2页,在0->50%区间偏移,渐渐隐藏裂变背景图 */ 207 | stepByHidePage2Views(positionOffset, positionOffsetPixels); 208 | } 209 | } else if (fromPage3ToPage4(position)) { 210 | 211 | /** 背景矩形的宽度,高度减少10%,逆时针旋转10度 */ 212 | transformRectBgFrom3To4(positionOffset); 213 | 214 | if (positionOffset < THIRD_RATE) { 215 | 216 | } else { 217 | /** 渐渐显示第4页,裂变背景图 */ 218 | stepByShowPage4Views(positionOffset); 219 | } 220 | } 221 | postInvalidate(); 222 | } 223 | 224 | private void stepByShowPage4Views(float positionOffset) { 225 | /** 显示第4页,裂变背景图,并向左右平移 */ 226 | float offset = (mPage4RectBgDefaultWidth + dp2px(40)) * ((positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE))); 227 | for (int i = 0; i < mPage4Split.length; i++) { 228 | mPage4Split[i].matrix.reset(); 229 | mPage4Split[i].matrix.postScale(mPage4RectBgDefaultWidth / mPage4Split[i].bitmap.getWidth(), mPage4RectBgDefaultHeight / mPage4Split[i].bitmap.getHeight()); 230 | 231 | float currentLeft = 0; 232 | if (i == 0) { 233 | // 左移 234 | currentLeft = mPage4RectBgDefaultLeft - offset; 235 | } else if (i == 1) { 236 | // 右移 237 | currentLeft = mPage4RectBgDefaultLeft + offset; 238 | } 239 | 240 | // 平移 241 | mPage4Split[i].matrix.postTranslate(currentLeft, mPage4RectBgDefaultTop); 242 | // 旋转角度 243 | mPage4Split[i].matrix.postRotate(THIRD_DEGREE, currentLeft + mPage4RectBgDefaultWidth/2, 244 | mPage4RectBgDefaultTop + mPage4RectBgDefaultHeight/2); 245 | 246 | mPage4Split[i].alpha((int) (255 * ((positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE))))); 247 | } 248 | } 249 | 250 | private void transformRectBgFrom3To4(float positionOffset) { 251 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次,保证变化完成 */ 252 | mRectBgCurrentWidth = mPage3RectBgDefaultWidth; 253 | mRectBgCurrentTop = mPage3RectBgDefaultTop; 254 | 255 | /** 调整第4页,背景矩形的宽高和角度 */ 256 | /** 背景矩形的宽度,在第3页调整宽度的基础上进行缩小 */ 257 | mRectBgCurrentWidth = mPage3RectBgDefaultWidth * (1 + THIRD_WIDTH * positionOffset); 258 | mRectBgCurrentLeft = getScreenWidth() / 2 - mRectBgCurrentWidth / 2; 259 | /** 背景矩形的高度,在第2页调整高度的基础上进行缩小 */ 260 | mRectBgCurrentHeight = mPage3RectBgDefaultHeight * (1 + THIRD_HEIGHT * positionOffset); 261 | /** 背景矩形逆时针旋转 */ 262 | mRectBgCurrentDegree = THIRD_DEGREE * positionOffset; 263 | } 264 | 265 | private void transformRectBgFrom1To2Before(float positionOffset) { 266 | /** 矩形背景,高度放大40% */ 267 | /** 268 | * 偏移到50%的时候height需要放大40%,defaultHeight=400,targetHeight=400*1.4=560 269 | * 270 | * offset=0 271 | * 400 * (1 + 0.4 * 0 * (1 / 0.5)) = 400 272 | * 273 | * offset=0.25 274 | * 400 * (1 + 0.4 * 0.25 * (1 / 0.5)) = 400 * 1.2 = 480 275 | * 276 | * offset=0.5 277 | * 400 * (1 + 0.4 * 0.5 * (1 / 0.5)) = 400 * 1.4 = 560 278 | * 279 | */ 280 | mRectBgCurrentHeight = 281 | (int) (mPage1RectBgDefaultHeight * (1 + FIRST_HEIGHT * positionOffset * (1 / FIRST_RATE))); 282 | /** 矩形背景,向上移动30dp */ 283 | mRectBgCurrentTop = (int) (mPage1RectBgDefaultTop + (FIRST_TOP1 * positionOffset * (1 / FIRST_RATE))); 284 | } 285 | 286 | private void transformRectBgFrom1To2After(float positionOffset) { 287 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次,保证变化完成 */ 288 | mRectBgCurrentHeight = mPage2RectBgDefaultHeight; 289 | mRectBgCurrentTop = mPage1RectBgDefaultTop + FIRST_TOP1; 290 | /** 第1页,在50%->100%区间偏移 */ 291 | /** 矩形背景,在上上偏移30dp后,向下偏移60dp */ 292 | mRectBgCurrentTop = 293 | (int) (mPage1RectBgDefaultTop + FIRST_TOP1 + (FIRST_TOP2 * (positionOffset - FIRST_RATE) * 1.0 / (1 - FIRST_RATE))); 294 | } 295 | 296 | private void stepByShowPage2Views(float positionOffset) { 297 | /** 第2页,计算裂变背景图的偏移px,并修改透明度渐变显示 */ 298 | float offset = 299 | (mPage1RectBgDefaultWidth + dp2px(15)) * ((positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE))); 300 | mPage2Split[0].currentLeft = mPage2Split[0].defaultLeft - offset; 301 | mPage2Split[1].currentLeft = mPage2Split[0].defaultLeft + offset; 302 | /** 303 | * 偏移到50%的时候alpha需要为0,偏移到100%,alpha需要为255,不过此时positionOffset的取值=0.5~1 304 | * 305 | * offset=0.5 306 | * 255 * (0.5 - 0.5) * (1 / (1 - 0.5)))=255 * 0 = 0 307 | * 308 | * offset=0.75 309 | * 255 * (0.75 - 0.5) * (1 / (1 - 0.5)))=255 * 0.5 = 127.5 310 | * 311 | * offset=1 312 | * 255 * (1 - 0.5) * (1 / (1 - 0.5)))=255 * 1 = 255 313 | */ 314 | mPage2Split[0].alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE)))); 315 | mPage2Split[1].alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE)))); 316 | } 317 | 318 | private void transformRectBgFrom2To3(float positionOffset) { 319 | /** 快速滑动的时候,可能丢失最后一次绘制,所以需要在这里调重新设置一次,保证变化完成 */ 320 | mRectBgCurrentHeight = mPage2RectBgDefaultHeight; 321 | mRectBgCurrentTop = mPage2RectBgDefaultTop; 322 | 323 | /** 矩形背景,宽度缩小15% */ 324 | mRectBgCurrentWidth = (int) (mPage2RectBgDefaultWidth * (1 + SECOND_WIDTH * positionOffset)); 325 | mRectBgCurrentLeft = getScreenWidth() / 2 - mRectBgCurrentWidth / 2; 326 | 327 | /** 矩形背景,上移20dp */ 328 | mRectBgCurrentTop = (int) (mPage2RectBgDefaultTop + (SECOND_TOP * positionOffset)); 329 | } 330 | 331 | private void stepByHidePage2Views(float positionOffset, int positionOffsetPixels) { 332 | /** 裂变背景图,跟随滑动,向左偏移至消失 */ 333 | mPage2Split[0].currentLeft = 334 | (mPage2Split[0].defaultLeft - mPage1RectBgDefaultWidth - dp2px(15)) - positionOffsetPixels 335 | * (1 / SECOND_RATE); 336 | mPage2Split[1].currentLeft = 337 | (mPage2Split[1].defaultLeft + mPage1RectBgDefaultWidth + dp2px(15)) - positionOffsetPixels 338 | * (1 / SECOND_RATE); 339 | mPage2Split[0].alpha((int) (255 - (255 * positionOffset * (1 / SECOND_RATE)))); 340 | mPage2Split[1].alpha((int) (255 - (255 * positionOffset * (1 / SECOND_RATE)))); 341 | } 342 | 343 | private boolean isPage4(int currentPageIndex) { 344 | return currentPageIndex == 3; 345 | } 346 | 347 | private boolean fromPage3ToPage4(int position) { 348 | return position == 2; 349 | } 350 | 351 | private boolean fromPage2ToPage3(int position) { 352 | return position == 1; 353 | } 354 | 355 | private boolean fromPage1ToPage2(int position) { 356 | return position == 0; 357 | } 358 | 359 | public void drawBitmap(Canvas canvas, ViewModel viewModel) { 360 | canvas.drawBitmap(viewModel.bitmap, viewModel.currentLeft, viewModel.currentTop, viewModel.paint); 361 | } 362 | 363 | public void drawBitmapMatrix(Canvas canvas, ViewModel viewModel) { 364 | canvas.drawBitmap(viewModel.bitmap, viewModel.matrix, viewModel.paint); 365 | } 366 | 367 | /** 368 | * @desc 根据当前页面,绘制不同的view 369 | * @author listen 370 | */ 371 | @Override 372 | protected void onDraw(Canvas canvas) { 373 | super.onDraw(canvas); 374 | 375 | /** 按重叠顺序绘制 */ 376 | if (fromPage1ToPage2(mCurrentPageIndex)) { 377 | 378 | /** 绘制第1页,底部背景图 */ 379 | drawBitmap(canvas, mPage1BottomBg); 380 | 381 | /** 绘制第2页,裂变背景图 */ 382 | drawBitmap(canvas, mPage2Split[0]); 383 | drawBitmap(canvas, mPage2Split[1]); 384 | 385 | /** 绘制白色矩形背景 */ 386 | drawWhiteRectBackgroud(canvas); 387 | 388 | } else if (fromPage2ToPage3(mCurrentPageIndex)) { 389 | 390 | /** 绘制第2页,裂变背景图 */ 391 | drawBitmap(canvas, mPage2Split[0]); 392 | drawBitmap(canvas, mPage2Split[1]); 393 | 394 | /** 绘制矩形背景 */ 395 | drawWhiteRectBackgroud(canvas); 396 | 397 | } else if (fromPage3ToPage4(mCurrentPageIndex)) { 398 | /** 绘制第4页,裂变背景图 */ 399 | drawBitmapMatrix(canvas, mPage4Split[0]); 400 | drawBitmapMatrix(canvas, mPage4Split[1]); 401 | 402 | /** 绘制矩形背景 */ 403 | drawWhiteRectBackgroud(canvas); 404 | 405 | } else if (isPage4(mCurrentPageIndex)) { 406 | 407 | /** 绘制第4页,裂变背景图 */ 408 | drawBitmapMatrix(canvas, mPage4Split[0]); 409 | drawBitmapMatrix(canvas, mPage4Split[1]); 410 | 411 | /** 绘制矩形背景 */ 412 | drawWhiteRectBackgroud(canvas); 413 | 414 | } 415 | } 416 | 417 | @NonNull 418 | private RectF drawWhiteRectBackgroud(Canvas canvas) { 419 | RectF rect = new RectF(); 420 | rect.left = mRectBgCurrentLeft; 421 | rect.top = mRectBgCurrentTop; 422 | rect.right = rect.left + mRectBgCurrentWidth; 423 | rect.bottom = rect.top + mRectBgCurrentHeight; 424 | 425 | canvas.rotate(mRectBgCurrentDegree, rect.left + mRectBgCurrentWidth / 2, rect.top + mRectBgCurrentHeight / 2); 426 | canvas.drawRoundRect(rect, mRectBgDefaultCorner, mRectBgDefaultCorner, mRectBgPaint); 427 | return rect; 428 | } 429 | 430 | private int padding() { 431 | return dp2px(5); 432 | } 433 | 434 | public int getScreenWidth() { 435 | if (null != getResources() && null != getResources().getDisplayMetrics()) { 436 | return getResources().getDisplayMetrics().widthPixels; 437 | } 438 | return 0; 439 | } 440 | 441 | public int dp2px(float dpValue) { 442 | final float scale = getResources().getDisplayMetrics().density; 443 | return (int) (dpValue * scale + 0.5f); 444 | } 445 | 446 | } 447 | -------------------------------------------------------------------------------- /app/src/main/java/com/listen/test_mogu_view/viewpager/ViewModel.java: -------------------------------------------------------------------------------- 1 | package com.listen.test_mogu_view.viewpager; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Matrix; 7 | import android.graphics.Paint; 8 | 9 | public class ViewModel { 10 | Bitmap bitmap; 11 | Paint paint; 12 | Matrix matrix; 13 | 14 | /** 15 | * 初始,固定的宽高left,top 16 | */ 17 | float defaultWidth; 18 | float defaultHeight; 19 | float defaultLeft; 20 | float defaultTop; 21 | 22 | /** 23 | * 当前的宽高left,top,会根据偏移量进行变化 24 | */ 25 | float currentWidth; 26 | float currentHeight; 27 | float currentLeft; 28 | float currentTop; 29 | 30 | public ViewModel(Context context, int resId) { 31 | bitmap = BitmapFactory.decodeResource(context.getResources(), resId); 32 | paint = new Paint(); 33 | paint.setAlpha(0); 34 | matrix = new Matrix(); 35 | } 36 | 37 | public ViewModel alpha(int alpha) { 38 | paint.setAlpha(alpha); 39 | return this; 40 | } 41 | 42 | public ViewModel width(float width) { 43 | this.currentWidth = this.defaultWidth = width; 44 | return this; 45 | } 46 | 47 | public ViewModel height(float height) { 48 | this.currentHeight = this.defaultHeight = height; 49 | return this; 50 | } 51 | 52 | public ViewModel left(float left) { 53 | this.currentLeft = this.defaultLeft = left; 54 | return this; 55 | } 56 | 57 | public ViewModel top(float top) { 58 | this.currentTop = this.defaultTop = top; 59 | return this; 60 | } 61 | 62 | public ViewModel create() { 63 | // 如果没传高度 64 | if (defaultHeight <= 0) { 65 | if (defaultWidth > 0) { 66 | // 有宽度,则按宽度比例缩放 67 | currentHeight = defaultHeight = (defaultWidth / bitmap.getWidth()) * bitmap.getHeight(); 68 | } else { 69 | // 没有宽度,则去图片默认高度 70 | currentHeight = defaultHeight = bitmap.getHeight(); 71 | } 72 | } 73 | 74 | // 如果没传宽度 75 | if (defaultWidth <= 0) { 76 | if (defaultHeight > 0) { 77 | // 有宽度,则按宽度比例缩放 78 | currentWidth = defaultWidth = (defaultHeight / bitmap.getHeight()) * bitmap.getWidth(); 79 | } else { 80 | // 没有高度,则去图片默认宽度 81 | currentWidth = defaultWidth = bitmap.getWidth(); 82 | } 83 | } 84 | 85 | /** 缩放图片尺寸到合适的比例 */ 86 | matrix.postScale(currentWidth / bitmap.getWidth(), currentHeight / bitmap.getHeight()); 87 | bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); 88 | return this; 89 | } 90 | 91 | /** 92 | * @desc 当前宽度变化比例 93 | */ 94 | public float widthRate() { 95 | return currentWidth / defaultWidth; 96 | } 97 | 98 | /** 99 | * @desc 当前高度变化比例 100 | */ 101 | public float heightRate() { 102 | return currentHeight / defaultHeight; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/four_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/four_bg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/four_bottom_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/four_bottom_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/four_bottom_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/four_bottom_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/four_bottom_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/four_bottom_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/four_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/four_top.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/one_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/one_bottom.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/one_bottom_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/one_bottom_bg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/one_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/one_top.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/three_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/three_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/three_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/three_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/three_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/three_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/three_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/three_4.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/three_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/three_5.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/three_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/three_6.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/two_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/two_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/two_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/two_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/two_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/two_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/two_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/two_bg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/two_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/two_bottom.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/two_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listen2code/Test_Mogu_View/8953e9e23ca4c3d28f4d61fd9b3d86f0ca344559/app/src/main/res/drawable-xhdpi/two_top.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/select_btn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/guide_view_four.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 21 | 22 |